diff options
Diffstat (limited to 'drivers/media')
264 files changed, 9534 insertions, 4993 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 4ef0d80b57f4..84d85b921d6d 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -135,6 +135,12 @@ config DVB_NET You may want to disable the network support on embedded devices. If unsure say Y. +# This Kconfig option is used by both PCI and USB drivers +config TTPCI_EEPROM + tristate + depends on I2C + default n + source "drivers/media/dvb-core/Kconfig" comment "Media drivers" @@ -158,17 +164,20 @@ source "drivers/media/firewire/Kconfig" # Common driver options source "drivers/media/common/Kconfig" +comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" + # # Ancillary drivers (tuners, i2c, frontends) # config MEDIA_SUBDRV_AUTOSELECT - bool "Autoselect tuners and i2c modules to build" + bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT default y help - By default, a media driver auto-selects all possible i2c - devices that are used by any of the supported devices. + By default, a media driver auto-selects all possible ancillary + devices such as tuners, sensors, video encoders/decoders and + frontends, that are used by any of the supported devices. This is generally the right thing to do, except when there are strict constraints with regards to the kernel size, @@ -177,12 +186,10 @@ config MEDIA_SUBDRV_AUTOSELECT Use this option with care, as deselecting ancillary drivers which are, in fact, necessary will result in the lack of the needed functionality for your device (it may not tune or may not have - the need demodulers). + the needed demodulators). If unsure say Y. -comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" - source "drivers/media/i2c/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index b3890bd49df6..eda01bc68ab2 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; @@ -265,8 +265,7 @@ static int fops_release(struct file *file) DEB_EE("file:%p\n", file); - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + mutex_lock(vdev->lock); if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 388c2eb4d747..7e1597dad568 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -172,6 +172,7 @@ #define USB_PID_TWINHAN_VP7045_WARM 0x3206 #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_TWINHAN_VP7049 0x3219 #define USB_PID_TINYTWIN 0x3226 #define USB_PID_TINYTWIN_2 0xe402 #define USB_PID_TINYTWIN_3 0x9016 @@ -233,6 +234,10 @@ #define USB_PID_AVERMEDIA_A815M 0x815a #define USB_PID_AVERMEDIA_A835 0xa835 #define USB_PID_AVERMEDIA_B835 0xb835 +#define USB_PID_AVERMEDIA_A835B_1835 0x1835 +#define USB_PID_AVERMEDIA_A835B_2835 0x2835 +#define USB_PID_AVERMEDIA_A835B_3835 0x3835 +#define USB_PID_AVERMEDIA_A835B_4835 0x4835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 9be65a3b931f..0aac3096728e 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -156,6 +156,9 @@ struct dvb_ca_private { /* Slot to start looking for data to read from in the next user-space read operation */ int next_read_slot; + + /* mutex serializing ioctls */ + struct mutex ioctl_mutex; }; static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); @@ -1191,6 +1194,9 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, dprintk("%s\n", __func__); + if (mutex_lock_interruptible(&ca->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: for (slot = 0; slot < ca->slot_count; slot++) { @@ -1221,8 +1227,10 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, case CA_GET_SLOT_INFO: { struct ca_slot_info *info = parg; - if ((info->num > ca->slot_count) || (info->num < 0)) - return -EINVAL; + if ((info->num > ca->slot_count) || (info->num < 0)) { + err = -EINVAL; + goto out_unlock; + } info->type = CA_CI_LINK; info->flags = 0; @@ -1241,6 +1249,8 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, break; } +out_unlock: + mutex_unlock(&ca->ioctl_mutex); return err; } @@ -1695,6 +1705,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, mutex_init(&ca->slot_info[i].slot_lock); } + mutex_init(&ca->ioctl_mutex); + if (signal_pending(current)) { ret = -EINTR; goto error; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 49d95040096a..b059abf572d6 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -603,6 +603,7 @@ static int dvb_frontend_thread(void *data) enum dvbfe_algo algo; bool re_tune = false; + bool semheld = false; dev_dbg(fe->dvb->device, "%s:\n", __func__); @@ -626,6 +627,8 @@ restart: if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ + if (!down_interruptible(&fepriv->sem)) + semheld = true; fepriv->exit = DVB_FE_NORMAL_EXIT; break; } @@ -741,6 +744,8 @@ restart: fepriv->exit = DVB_FE_NO_EXIT; mb(); + if (semheld) + up(&fepriv->sem); dvb_frontend_wakeup(fe); return 0; } @@ -1048,6 +1053,16 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), + + /* Statistics API */ + _DTV_CMD(DTV_STAT_SIGNAL_STRENGTH, 0, 0), + _DTV_CMD(DTV_STAT_CNR, 0, 0), + _DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0, 0), }; static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp) @@ -1438,7 +1453,35 @@ static int dtv_property_process_get(struct dvb_frontend *fe, tvp->u.data = c->lna; break; + /* Fill quality measures */ + case DTV_STAT_SIGNAL_STRENGTH: + tvp->u.st = c->strength; + break; + case DTV_STAT_CNR: + tvp->u.st = c->cnr; + break; + case DTV_STAT_PRE_ERROR_BIT_COUNT: + tvp->u.st = c->pre_bit_error; + break; + case DTV_STAT_PRE_TOTAL_BIT_COUNT: + tvp->u.st = c->pre_bit_count; + break; + case DTV_STAT_POST_ERROR_BIT_COUNT: + tvp->u.st = c->post_bit_error; + break; + case DTV_STAT_POST_TOTAL_BIT_COUNT: + tvp->u.st = c->post_bit_count; + break; + case DTV_STAT_ERROR_BLOCK_COUNT: + tvp->u.st = c->block_error; + break; + case DTV_STAT_TOTAL_BLOCK_COUNT: + tvp->u.st = c->block_count; + break; default: + dev_dbg(fe->dvb->device, + "%s: FE property %d doesn't exist\n", + __func__, tvp->cmd); return -EINVAL; } @@ -1823,16 +1866,20 @@ static int dvb_frontend_ioctl(struct file *file, int err = -ENOTTY; dev_dbg(fe->dvb->device, "%s: (%d)\n", __func__, _IOC_NR(cmd)); - if (fepriv->exit != DVB_FE_NO_EXIT) + if (down_interruptible(&fepriv->sem)) + return -ERESTARTSYS; + + if (fepriv->exit != DVB_FE_NO_EXIT) { + up(&fepriv->sem); return -ENODEV; + } if ((file->f_flags & O_ACCMODE) == O_RDONLY && (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT || - cmd == FE_DISEQC_RECV_SLAVE_REPLY)) + cmd == FE_DISEQC_RECV_SLAVE_REPLY)) { + up(&fepriv->sem); return -EPERM; - - if (down_interruptible (&fepriv->sem)) - return -ERESTARTSYS; + } if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) err = dvb_frontend_ioctl_properties(file, cmd, parg); @@ -2246,7 +2293,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, printk("%s switch command: 0x%04lx\n", __func__, swcmd); do_gettimeofday(&nexttime); if (dvb_frontend_debug) - memcpy(&tv[0], &nexttime, sizeof(struct timeval)); + tv[0] = nexttime; /* before sending a command, initialize by sending * a 32ms 18V to the switch */ diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 97112cd88a17..b34922a08156 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -393,6 +393,16 @@ struct dtv_frontend_properties { u8 atscmh_sccc_code_mode_d; u32 lna; + + /* statistics data */ + struct dtv_fe_stats strength; + struct dtv_fe_stats cnr; + struct dtv_fe_stats pre_bit_error; + struct dtv_fe_stats pre_bit_count; + struct dtv_fe_stats post_bit_error; + struct dtv_fe_stats post_bit_count; + struct dtv_fe_stats block_error; + struct dtv_fe_stats block_count; }; struct dvb_frontend { diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index c2117688aa23..44225b186f6d 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1345,26 +1345,35 @@ static int dvb_net_do_ioctl(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dvb_net *dvbnet = dvbdev->priv; + int ret = 0; if (((file->f_flags&O_ACCMODE)==O_RDONLY)) return -EPERM; + if (mutex_lock_interruptible(&dvbnet->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case NET_ADD_IF: { struct dvb_net_if *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1376,8 +1385,10 @@ static int dvb_net_do_ioctl(struct file *file, struct dvb_net_if *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1388,16 +1399,18 @@ static int dvb_net_do_ioctl(struct file *file, } case NET_REMOVE_IF: { - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) - return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } + if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) { + ret = -EINVAL; + goto ioctl_error; + } ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); if (!ret) module_put(dvbdev->adapter->module); - return ret; + break; } /* binary compatibility cruft */ @@ -1406,16 +1419,21 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1427,8 +1445,10 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1437,9 +1457,13 @@ static int dvb_net_do_ioctl(struct file *file, break; } default: - return -ENOTTY; + ret = -ENOTTY; + break; } - return 0; + +ioctl_error: + mutex_unlock(&dvbnet->ioctl_mutex); + return ret; } static long dvb_net_ioctl(struct file *file, @@ -1505,6 +1529,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, { int i; + mutex_init(&dvbnet->ioctl_mutex); dvbnet->demux = dmx; for (i=0; i<DVB_NET_DEVICES_MAX; i++) diff --git a/drivers/media/dvb-core/dvb_net.h b/drivers/media/dvb-core/dvb_net.h index 1e53acd50cf4..ede78e8c8aa8 100644 --- a/drivers/media/dvb-core/dvb_net.h +++ b/drivers/media/dvb-core/dvb_net.h @@ -40,6 +40,7 @@ struct dvb_net { int state[DVB_NET_DEVICES_MAX]; unsigned int exit:1; struct dmx_demux *demux; + struct mutex ioctl_mutex; }; void dvb_net_release(struct dvb_net *); diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index d33101aaf0b5..401ef64f92c6 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -418,10 +418,8 @@ int dvb_usercopy(struct file *file, } /* call driver */ - mutex_lock(&dvbdev_mutex); if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) err = -ENOTTY; - mutex_unlock(&dvbdev_mutex); if (err < 0) goto out; diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5efec73a32d2..6f809a70c78e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -207,6 +207,13 @@ config DVB_SI21XX help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TS2020 + tristate "Montage Tehnology TS2020 based tuners" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. + config DVB_DS3000 tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 7eb73bbd2e26..cebc0faffab5 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o obj-$(CONFIG_DVB_HD29L2) += hd29l2.o obj-$(CONFIG_DVB_DS3000) += ds3000.o +obj-$(CONFIG_DVB_TS2020) += ts2020.o obj-$(CONFIG_DVB_MB86A16) += mb86a16.o obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 464ad878490b..c9cad989b8b9 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -318,6 +318,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_fc2580); init = tuner_init_fc2580; break; + case AF9033_TUNER_FC0012: + len = ARRAY_SIZE(tuner_init_fc0012); + init = tuner_init_fc0012; + break; default: dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); @@ -331,6 +335,20 @@ static int af9033_init(struct dvb_frontend *fe) goto err; } + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); + if (ret < 0) + goto err; + } + state->bandwidth_hz = 0; /* force to program all parameters */ return 0; diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index bfa4313fde21..82bd8c1513b6 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -40,6 +40,7 @@ struct af9033_config { */ #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */ #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ #define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ #define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 34dddcd77538..e9bd78265543 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -199,10 +199,9 @@ static const struct reg_val ofsm_init[] = { { 0x8000a6, 0x01 }, { 0x8000a9, 0x00 }, { 0x8000aa, 0x01 }, - { 0x8000ab, 0x01 }, { 0x8000b0, 0x01 }, - { 0x8000c0, 0x05 }, - { 0x8000c4, 0x19 }, + { 0x8000c4, 0x05 }, + { 0x8000c8, 0x19 }, { 0x80f000, 0x0f }, { 0x80f016, 0x10 }, { 0x80f017, 0x04 }, @@ -322,8 +321,9 @@ static const struct reg_val tuner_init_tua9001[] = { { 0x80009b, 0x05 }, { 0x80009c, 0x80 }, { 0x8000b3, 0x00 }, - { 0x8000c1, 0x01 }, - { 0x8000c2, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, { 0x80f007, 0x00 }, { 0x80f01f, 0x82 }, { 0x80f020, 0x00 }, @@ -339,14 +339,14 @@ static const struct reg_val tuner_init_tua9001[] = { /* Fitipower fc0011 tuner init AF9033_TUNER_FC0011 = 0x28 */ static const struct reg_val tuner_init_fc0011[] = { - { 0x800046, AF9033_TUNER_FC0011 }, + { 0x800046, 0x28 }, { 0x800057, 0x00 }, { 0x800058, 0x01 }, { 0x80005f, 0x00 }, { 0x800060, 0x00 }, { 0x800068, 0xa5 }, { 0x80006e, 0x01 }, - { 0x800071, 0x0A }, + { 0x800071, 0x0a }, { 0x800072, 0x02 }, { 0x800074, 0x01 }, { 0x800079, 0x01 }, @@ -354,7 +354,7 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x800094, 0x00 }, { 0x800095, 0x00 }, { 0x800096, 0x00 }, - { 0x80009b, 0x2D }, + { 0x80009b, 0x2d }, { 0x80009c, 0x60 }, { 0x80009d, 0x23 }, { 0x8000a4, 0x50 }, @@ -362,39 +362,82 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x8000b3, 0x01 }, { 0x8000b7, 0x88 }, { 0x8000b8, 0xa6 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x01 }, - { 0x8000c7, 0x69 }, - { 0x80F007, 0x00 }, - { 0x80F00A, 0x1B }, - { 0x80F00B, 0x1B }, - { 0x80F00C, 0x1B }, - { 0x80F00D, 0x1B }, - { 0x80F00E, 0xFF }, - { 0x80F00F, 0x01 }, - { 0x80F010, 0x00 }, - { 0x80F011, 0x02 }, - { 0x80F012, 0xFF }, - { 0x80F013, 0x01 }, - { 0x80F014, 0x00 }, - { 0x80F015, 0x02 }, - { 0x80F01B, 0xEF }, - { 0x80F01C, 0x01 }, - { 0x80F01D, 0x0f }, - { 0x80F01E, 0x02 }, - { 0x80F01F, 0x6E }, - { 0x80F020, 0x00 }, - { 0x80F025, 0xDE }, - { 0x80F026, 0x00 }, - { 0x80F027, 0x0A }, - { 0x80F028, 0x03 }, - { 0x80F029, 0x6E }, - { 0x80F02A, 0x00 }, - { 0x80F047, 0x00 }, - { 0x80F054, 0x00 }, - { 0x80F055, 0x00 }, - { 0x80F077, 0x01 }, - { 0x80F1E6, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x01 }, + { 0x8000c9, 0x69 }, + { 0x80f007, 0x00 }, + { 0x80f00a, 0x1b }, + { 0x80f00b, 0x1b }, + { 0x80f00c, 0x1b }, + { 0x80f00d, 0x1b }, + { 0x80f00e, 0xff }, + { 0x80f00f, 0x01 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0xff }, + { 0x80f013, 0x01 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01b, 0xef }, + { 0x80f01c, 0x01 }, + { 0x80f01d, 0x0f }, + { 0x80f01e, 0x02 }, + { 0x80f01f, 0x6e }, + { 0x80f020, 0x00 }, + { 0x80f025, 0xde }, + { 0x80f026, 0x00 }, + { 0x80f027, 0x0a }, + { 0x80f028, 0x03 }, + { 0x80f029, 0x6e }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +/* Fitipower FC0012 tuner init + AF9033_TUNER_FC0012 = 0x2e */ +static const struct reg_val tuner_init_fc0012[] = { + { 0x800046, 0x2e }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x800059, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x01 }, + { 0x800078, 0x00 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x90 }, + { 0x80007b, 0x90 }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0xa0 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, }; /* MaxLinear MxL5007T tuner init @@ -482,11 +525,12 @@ static const struct reg_val tuner_init_fc2580[] = { { 0x800095, 0x00 }, { 0x800096, 0x05 }, { 0x8000b3, 0x01 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000d1, 0x01 }, { 0x80f007, 0x00 }, { 0x80f00c, 0x19 }, - { 0x80f00d, 0x1A }, + { 0x80f00d, 0x1a }, { 0x80f00e, 0x00 }, { 0x80f00f, 0x02 }, { 0x80f010, 0x00 }, diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index b48879186537..2916d7c74a1d 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -819,7 +819,7 @@ static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static void cx24116_clone_params(struct dvb_frontend *fe) { struct cx24116_state *state = fe->demodulator_priv; - memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); + state->dcur = state->dnxt; } /* Wait for LNB */ diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index e71cc60851e7..9a2134792cfa 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2965,7 +2965,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, return NULL; memset(state, 0, sizeof(*state)); - memcpy(&state->ops, &drxd_ops, sizeof(struct dvb_frontend_ops)); + state->ops = drxd_ops; state->dev = dev; state->config = *config; state->i2c = i2c; @@ -2976,10 +2976,13 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, if (Read16(state, 0, 0, 0) < 0) goto error; - memcpy(&state->frontend.ops, &drxd_ops, - sizeof(struct dvb_frontend_ops)); + state->frontend.ops = drxd_ops; state->frontend.demodulator_priv = state; ConfigureMPEGOutput(state, 0); + /* add few initialization to allow gate control */ + CDRXD(state, state->config.IF ? state->config.IF : 36000000); + InitHI(state); + return &state->frontend; error: diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 60a529e3833f..1e344b033277 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver - Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include <linux/firmware.h> #include "dvb_frontend.h" +#include "ts2020.h" #include "ds3000.h" static int debug; @@ -42,7 +43,6 @@ static int debug; #define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" #define DS3000_SAMPLE_RATE 96000 /* in kHz */ -#define DS3000_XTAL_FREQ 27000 /* in kHz */ /* Register values to initialise the demod in DVB-S mode */ static u8 ds3000_dvbs_init_tab[] = { @@ -256,22 +256,14 @@ static int ds3000_writereg(struct ds3000_state *state, int reg, int data) return 0; } -static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = 0x60, - .flags = 0, .buf = buf, .len = 2 }; - int err; - - dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + struct ds3000_state *state = fe->demodulator_priv; - ds3000_writereg(state, 0x03, 0x11); - err = i2c_transfer(state->i2c, &msg, 1); - if (err != 1) { - printk("%s: writereg error(err == %i, reg == 0x%02x," - " value == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } + if (enable) + ds3000_writereg(state, 0x03, 0x12); + else + ds3000_writereg(state, 0x03, 0x02); return 0; } @@ -280,15 +272,14 @@ static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) static int ds3000_writeFW(struct ds3000_state *state, int reg, const u8 *data, u16 len) { - int i, ret = -EREMOTEIO; + int i, ret = 0; struct i2c_msg msg; u8 *buf; buf = kmalloc(33, GFP_KERNEL); if (buf == NULL) { printk(KERN_ERR "Unable to kmalloc\n"); - ret = -ENOMEM; - goto error; + return -ENOMEM; } *(buf) = reg; @@ -308,8 +299,10 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg, printk(KERN_ERR "%s: write error(err == %i, " "reg == 0x%02x\n", __func__, ret, reg); ret = -EREMOTEIO; + goto error; } } + ret = 0; error: kfree(buf); @@ -348,38 +341,6 @@ static int ds3000_readreg(struct ds3000_state *state, u8 reg) return b1[0]; } -static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) -{ - int ret; - u8 b0[] = { reg }; - u8 b1[] = { 0 }; - struct i2c_msg msg[] = { - { - .addr = 0x60, - .flags = 0, - .buf = b0, - .len = 1 - }, { - .addr = 0x60, - .flags = I2C_M_RD, - .buf = b1, - .len = 1 - } - }; - - ds3000_writereg(state, 0x03, 0x12); - ret = i2c_transfer(state->i2c, msg, 2); - - if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); - return ret; - } - - dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); - - return b1[0]; -} - static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw); @@ -424,6 +385,7 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw) { struct ds3000_state *state = fe->demodulator_priv; + int ret = 0; dprintk("%s\n", __func__); dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", @@ -436,10 +398,10 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, /* Begin the firmware load process */ ds3000_writereg(state, 0xb2, 0x01); /* write the entire firmware */ - ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size); ds3000_writereg(state, 0xb2, 0x00); - return 0; + return ret; } static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) @@ -498,6 +460,9 @@ static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) return 1; } + if (state->config->set_lock_led) + state->config->set_lock_led(fe, *status == 0 ? 0 : 1); + dprintk("%s: status = 0x%02x\n", __func__, lock); return 0; @@ -568,33 +533,11 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) return 0; } -/* read TS2020 signal strength */ static int ds3000_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { - struct ds3000_state *state = fe->demodulator_priv; - u16 sig_reading, sig_strength; - u8 rfgain, bbgain; - - dprintk("%s()\n", __func__); - - rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; - bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; - - if (rfgain > 15) - rfgain = 15; - if (bbgain > 13) - bbgain = 13; - - sig_reading = rfgain * 2 + bbgain * 3; - - sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; - - /* cook the value to be suitable for szap-s2 human readable output */ - *signal_strength = sig_strength * 1000; - - dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, - sig_reading, *signal_strength); + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, signal_strength); return 0; } @@ -878,6 +821,10 @@ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, static void ds3000_release(struct dvb_frontend *fe) { struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s\n", __func__); kfree(state); } @@ -952,133 +899,17 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) int i; fe_status_t status; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; s32 offset_khz; - u16 value, ndiv; - u32 f3db; + u32 frequency; + u16 value; dprintk("%s() ", __func__); if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); /* Tune */ - /* unknown */ - ds3000_tuner_writereg(state, 0x07, 0x02); - ds3000_tuner_writereg(state, 0x10, 0x00); - ds3000_tuner_writereg(state, 0x60, 0x79); - ds3000_tuner_writereg(state, 0x08, 0x01); - ds3000_tuner_writereg(state, 0x00, 0x01); - div4 = 0; - - /* calculate and set freq divider */ - if (c->frequency < 1146000) { - ds3000_tuner_writereg(state, 0x10, 0x11); - div4 = 1; - ndiv = ((c->frequency * (6 + 8) * 4) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } else { - ds3000_tuner_writereg(state, 0x10, 0x01); - ndiv = ((c->frequency * (6 + 8) * 2) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } - - ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); - ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); - - /* set pll */ - ds3000_tuner_writereg(state, 0x03, 0x06); - ds3000_tuner_writereg(state, 0x51, 0x0f); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x10); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - value = ds3000_tuner_readreg(state, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } - - ds3000_tuner_writereg(state, 0x60, value); - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - - /* set low-pass filter period */ - ds3000_tuner_writereg(state, 0x04, 0x2e); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000; - if ((c->symbol_rate / 1000) < 5000) - f3db += 3000; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - /* set low-pass filter baseband */ - value = ds3000_tuner_readreg(state, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) - / (2766 * DS3000_XTAL_FREQ); - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - /* rounded to the closest integer */ - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - - if (mlpf_new < mlpf_min) { - nlpf++; - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - } - - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; - - ds3000_tuner_writereg(state, 0x04, mlpf_new); - ds3000_tuner_writereg(state, 0x06, nlpf); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x1e); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x01); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(60); - - offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2 - c->frequency; + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); /* ds3000 global reset */ ds3000_writereg(state, 0x07, 0x80); @@ -1186,7 +1017,11 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* start ds3000 build-in uC */ ds3000_writereg(state, 0xb2, 0x00); - ds3000_set_carrier_offset(fe, offset_khz); + if (fe->ops.tuner_ops.get_frequency) { + fe->ops.tuner_ops.get_frequency(fe, &frequency); + offset_khz = frequency - c->frequency; + ds3000_set_carrier_offset(fe, offset_khz); + } for (i = 0; i < 30 ; i++) { ds3000_read_status(fe, &status); @@ -1218,6 +1053,11 @@ static int ds3000_tune(struct dvb_frontend *fe, static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) { + struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s()\n", __func__); return DVBFE_ALGO_HW; } @@ -1237,10 +1077,6 @@ static int ds3000_initfe(struct dvb_frontend *fe) ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); msleep(1); - /* TS2020 init */ - ds3000_tuner_writereg(state, 0x42, 0x73); - ds3000_tuner_writereg(state, 0x05, 0x01); - ds3000_tuner_writereg(state, 0x62, 0xf5); /* Load the firmware if required */ ret = ds3000_firmware_ondemand(fe); if (ret != 0) { @@ -1251,17 +1087,10 @@ static int ds3000_initfe(struct dvb_frontend *fe) return 0; } -/* Put device to sleep */ -static int ds3000_sleep(struct dvb_frontend *fe) -{ - dprintk("%s()\n", __func__); - return 0; -} - static struct dvb_frontend_ops ds3000_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2}, + .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { - .name = "Montage Technology DS3000/TS2020", + .name = "Montage Technology DS3000", .frequency_min = 950000, .frequency_max = 2150000, .frequency_stepsize = 1011, /* kHz for QPSK frontends */ @@ -1279,7 +1108,7 @@ static struct dvb_frontend_ops ds3000_ops = { .release = ds3000_release, .init = ds3000_initfe, - .sleep = ds3000_sleep, + .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, .read_status = ds3000_read_status, .read_ber = ds3000_read_ber, .read_signal_strength = ds3000_read_signal_strength, @@ -1299,7 +1128,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " - "DS3000/TS2020 hardware"); -MODULE_AUTHOR("Konstantin Dimitrov"); + "DS3000 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE); diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 1b736888ea37..478ad66c63d7 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver - Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + */ #ifndef DS3000_H #define DS3000_H @@ -30,6 +30,8 @@ struct ds3000_config { u8 ci_mode; /* Set device param to start dma */ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; #if defined(CONFIG_DVB_DS3000) || \ diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index bc5a82082aaa..0e3387e00952 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -212,7 +212,7 @@ static int ix2505v_set_params(struct dvb_frontend *fe) lpf = 0xb; deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); - deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]); + deb_info("Data 0=[%4phN]\n", data); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 633815ed90ca..4da5272075cb 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -60,15 +60,13 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); #define info(format, arg...) \ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) -static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, +static int m88rs2000_writereg(struct m88rs2000_state *state, u8 reg, u8 data) { int ret; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; u8 buf[] = { reg, data }; struct i2c_msg msg = { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = buf, .len = 2 @@ -83,44 +81,20 @@ static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, return (ret != 1) ? -EREMOTEIO : 0; } -static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - return m88rs2000_writereg(state, 1, reg, data); -} - -static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - m88rs2000_demod_write(state, 0x81, 0x84); - udelay(10); - return m88rs2000_writereg(state, 0, reg, data); - -} - -static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len) -{ - struct m88rs2000_state *state = fe->demodulator_priv; - - if (len != 2) - return -EINVAL; - - return m88rs2000_writereg(state, 1, buf[0], buf[1]); -} - -static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) +static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) { int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; + struct i2c_msg msg[] = { { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = b0, .len = 1 }, { - .addr = addr, + .addr = state->config->demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 @@ -136,18 +110,6 @@ static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) return b1[0]; } -static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg) -{ - return m88rs2000_readreg(state, 1, reg); -} - -static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg) -{ - m88rs2000_demod_write(state, 0x81, 0x85); - udelay(10); - return m88rs2000_readreg(state, 0, reg); -} - static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -166,9 +128,9 @@ static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) b[0] = (u8) (temp >> 16) & 0xff; b[1] = (u8) (temp >> 8) & 0xff; b[2] = (u8) temp & 0xff; - ret = m88rs2000_demod_write(state, 0x93, b[2]); - ret |= m88rs2000_demod_write(state, 0x94, b[1]); - ret |= m88rs2000_demod_write(state, 0x95, b[0]); + ret = m88rs2000_writereg(state, 0x93, b[2]); + ret |= m88rs2000_writereg(state, 0x94, b[1]); + ret |= m88rs2000_writereg(state, 0x95, b[0]); deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); return ret; @@ -182,37 +144,37 @@ static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, int i; u8 reg; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; - m88rs2000_demod_write(state, 0xb2, reg); + m88rs2000_writereg(state, 0xb2, reg); for (i = 0; i < m->msg_len; i++) - m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]); + m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); reg &= 0x87; reg |= ((m->msg_len - 1) << 3) | 0x07; reg &= 0x7f; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); for (i = 0; i < 15; i++) { - if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0) + if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) break; msleep(20); } - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); if ((reg & 0x40) > 0x0) { reg &= 0x7f; reg |= 0x40; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); } - reg = m88rs2000_demod_read(state, 0xb2); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; reg |= 0x80; - m88rs2000_demod_write(state, 0xb2, reg); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; @@ -224,14 +186,14 @@ static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); + m88rs2000_writereg(state, 0x9a, 0x30); msleep(50); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); /* TODO complete this section */ - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -240,9 +202,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); reg1 &= 0x3f; @@ -257,9 +219,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) default: break; } - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -276,14 +238,6 @@ struct inittab m88rs2000_setup[] = { {DEMOD_WRITE, 0x00, 0x00}, {DEMOD_WRITE, 0x9a, 0xb0}, {DEMOD_WRITE, 0x81, 0xc1}, - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, {DEMOD_WRITE, 0x81, 0x81}, {DEMOD_WRITE, 0x86, 0xc6}, {DEMOD_WRITE, 0x9a, 0x30}, @@ -301,23 +255,10 @@ struct inittab m88rs2000_shutdown[] = { {DEMOD_WRITE, 0xf1, 0x89}, {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0x9a, 0xb0}, - {TUNER_WRITE, 0x00, 0x40}, {DEMOD_WRITE, 0x81, 0x81}, {0xff, 0xaa, 0xff} }; -struct inittab tuner_reset[] = { - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, - {0xff, 0xaa, 0xff} -}; - struct inittab fe_reset[] = { {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0xf1, 0xbf}, @@ -389,11 +330,7 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, for (i = 0; i < 255; i++) { switch (tab[i].cmd) { case 0x01: - ret = m88rs2000_demod_write(state, tab[i].reg, - tab[i].val); - break; - case 0x02: - ret = m88rs2000_tuner_write(state, tab[i].reg, + ret = m88rs2000_writereg(state, tab[i].reg, tab[i].val); break; case 0x10: @@ -419,7 +356,7 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) struct m88rs2000_state *state = fe->demodulator_priv; u8 data; - data = m88rs2000_demod_read(state, 0xb2); + data = m88rs2000_readreg(state, 0xb2); data |= 0x03; /* bit0 V/H, bit1 off/on */ switch (volt) { @@ -434,23 +371,11 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) break; } - m88rs2000_demod_write(state, 0xb2, data); + m88rs2000_writereg(state, 0xb2, data); return 0; } -static int m88rs2000_startup(struct m88rs2000_state *state) -{ - int ret = 0; - u8 reg; - - reg = m88rs2000_tuner_read(state, 0x00); - if ((reg & 0x40) == 0) - ret = -ENODEV; - - return ret; -} - static int m88rs2000_init(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -458,7 +383,11 @@ static int m88rs2000_init(struct dvb_frontend *fe) deb_info("m88rs2000: init chip\n"); /* Setup frontend from shutdown/cold */ - ret = m88rs2000_tab_set(state, m88rs2000_setup); + if (state->config->inittab) + ret = m88rs2000_tab_set(state, + (struct inittab *)state->config->inittab); + else + ret = m88rs2000_tab_set(state, m88rs2000_setup); return ret; } @@ -475,7 +404,7 @@ static int m88rs2000_sleep(struct dvb_frontend *fe) static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct m88rs2000_state *state = fe->demodulator_priv; - u8 reg = m88rs2000_demod_read(state, 0x8c); + u8 reg = m88rs2000_readreg(state, 0x8c); *status = 0; @@ -488,183 +417,64 @@ static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */ - static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) { - deb_info("m88rs2000_read_ber %d\n", *ber); - *ber = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp0, tmp1; + + m88rs2000_writereg(state, 0x9a, 0x30); + tmp0 = m88rs2000_readreg(state, 0xd8); + if ((tmp0 & 0x10) != 0) { + m88rs2000_writereg(state, 0x9a, 0xb0); + *ber = 0xffffffff; + return 0; + } + + *ber = (m88rs2000_readreg(state, 0xd7) << 8) | + m88rs2000_readreg(state, 0xd6); + + tmp1 = m88rs2000_readreg(state, 0xd9); + m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); + /* needs twice */ + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0x9a, 0xb0); + return 0; } static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - *strength = 0; - return 0; -} + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, strength); -static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - deb_info("m88rs2000_read_snr %d\n", *snr); - *snr = 0; return 0; } -static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - deb_info("m88rs2000_read_ber %d\n", *ucblocks); - *ucblocks = 0; - return 0; -} - -static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset) -{ - int ret; - ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset); - ret |= m88rs2000_tuner_write(state, 0x51, 0x1f); - ret |= m88rs2000_tuner_write(state, 0x50, offset); - ret |= m88rs2000_tuner_write(state, 0x50, 0x00); - msleep(20); - return ret; -} - -static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe) +static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) { struct m88rs2000_state *state = fe->demodulator_priv; - int reg; - reg = m88rs2000_tuner_read(state, 0x3d); - reg &= 0x7f; - if (reg < 0x16) - reg = 0xa1; - else if (reg == 0x16) - reg = 0x99; - else - reg = 0xf9; - m88rs2000_tuner_write(state, 0x60, reg); - reg = m88rs2000_tuner_gate_ctrl(state, 0x08); + *snr = 512 * m88rs2000_readreg(state, 0x65); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return reg; + return 0; } -static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) +static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct m88rs2000_state *state = fe->demodulator_priv; - int ret; - u32 frequency = c->frequency; - s32 offset_khz; - s32 tmp; - u32 symbol_rate = (c->symbol_rate / 1000); - u32 f3db, gdiv28; - u16 value, ndiv, lpf_coeff; - u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; - u8 lo = 0x01, div4 = 0x0; - - /* Reset Tuner */ - ret = m88rs2000_tab_set(state, tuner_reset); - - /* Calculate frequency divider */ - if (frequency < 1060000) { - lo |= 0x10; - div4 = 0x1; - ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ; - } else - ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ; - ndiv = ndiv + ndiv % 2; - ndiv = ndiv - 1024; - - ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo); - - /* Set frequency divider */ - ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf); - ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff); - - ret |= m88rs2000_tuner_write(state, 0x03, 0x06); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x10); - if (ret < 0) - return -ENODEV; - - /* Tuner Frequency Range */ - ret = m88rs2000_tuner_write(state, 0x10, lo); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x08); - - /* Tuner RF */ - ret |= m88rs2000_set_tuner_rf(fe); - - gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000; - ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - if (ret < 0) - return -ENODEV; - - value = m88rs2000_tuner_read(state, 0x26); - - f3db = (symbol_rate * 135) / 200 + 2000; - f3db += FREQ_OFFSET_LOW_SYM_RATE; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - gdiv28 = gdiv28 * 207 / (value * 2 + 151); - mlpf_max = gdiv28 * 135 / 100; - mlpf_min = gdiv28 * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - lpf_coeff = 2766; - - nlpf = (f3db * gdiv28 * 2 / lpf_coeff / - (FE_CRYSTAL_KHZ / 1000) + 1) / 2; - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - - if (lpf_mxdiv < mlpf_min) { - nlpf++; - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - } - - if (lpf_mxdiv > mlpf_max) - lpf_mxdiv = mlpf_max; - - ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv); - ret |= m88rs2000_tuner_write(state, 0x06, nlpf); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x01); - - msleep(80); - /* calculate offset assuming 96000kHz*/ - offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ - / 14 / (div4 + 1) / 2; + u8 tmp; - offset_khz -= frequency; + *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | + m88rs2000_readreg(state, 0xd4); + tmp = m88rs2000_readreg(state, 0xd8); + m88rs2000_writereg(state, 0xd8, tmp & ~0x20); + /* needs two times */ + m88rs2000_writereg(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); - tmp = offset_khz; - tmp *= 65536; - - tmp = (2 * tmp + 96000) / (2 * 96000); - if (tmp < 0) - tmp += 65536; - - *offset = tmp & 0xffff; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return (ret < 0) ? -EINVAL : 0; + return 0; } static int m88rs2000_set_fec(struct m88rs2000_state *state, @@ -692,7 +502,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, default: fec_set = 0x08; } - m88rs2000_demod_write(state, 0x76, fec_set); + m88rs2000_writereg(state, 0x76, fec_set); return 0; } @@ -701,9 +511,9 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) { u8 reg; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0x76); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0x76); + m88rs2000_writereg(state, 0x9a, 0xb0); switch (reg) { case 0x88: @@ -729,7 +539,9 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; fe_status_t status; - int i, ret; + int i, ret = 0; + s32 tmp; + u32 tuner_freq; u16 offset = 0; u8 reg; @@ -743,17 +555,37 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) } /* Set Tuner */ - ret = m88rs2000_set_tuner(fe, &offset); + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + + if (ret < 0) + return -ENODEV; + + if (fe->ops.tuner_ops.get_frequency) + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); + if (ret < 0) return -ENODEV; - ret = m88rs2000_demod_write(state, 0x9a, 0x30); + offset = tuner_freq - c->frequency; + + /* calculate offset assuming 96000kHz*/ + tmp = offset; + tmp *= 65536; + + tmp = (2 * tmp + 96000) / (2 * 96000); + if (tmp < 0) + tmp += 65536; + + offset = tmp & 0xffff; + + ret = m88rs2000_writereg(state, 0x9a, 0x30); /* Unknown usually 0xc6 sometimes 0xc1 */ - reg = m88rs2000_demod_read(state, 0x86); - ret |= m88rs2000_demod_write(state, 0x86, reg); + reg = m88rs2000_readreg(state, 0x86); + ret |= m88rs2000_writereg(state, 0x86, reg); /* Offset lower nibble always 0 */ - ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8)); - ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0); + ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8)); + ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0); /* Reset Demod */ @@ -762,16 +594,16 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; /* Unknown */ - reg = m88rs2000_demod_read(state, 0x70); - ret = m88rs2000_demod_write(state, 0x70, reg); + reg = m88rs2000_readreg(state, 0x70); + ret = m88rs2000_writereg(state, 0x70, reg); /* Set FEC */ ret |= m88rs2000_set_fec(state, c->fec_inner); - ret |= m88rs2000_demod_write(state, 0x85, 0x1); - ret |= m88rs2000_demod_write(state, 0x8a, 0xbf); - ret |= m88rs2000_demod_write(state, 0x8d, 0x1e); - ret |= m88rs2000_demod_write(state, 0x90, 0xf1); - ret |= m88rs2000_demod_write(state, 0x91, 0x08); + ret |= m88rs2000_writereg(state, 0x85, 0x1); + ret |= m88rs2000_writereg(state, 0x8a, 0xbf); + ret |= m88rs2000_writereg(state, 0x8d, 0x1e); + ret |= m88rs2000_writereg(state, 0x90, 0xf1); + ret |= m88rs2000_writereg(state, 0x91, 0x08); if (ret < 0) return -ENODEV; @@ -787,27 +619,25 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; for (i = 0; i < 25; i++) { - reg = m88rs2000_demod_read(state, 0x8c); + reg = m88rs2000_readreg(state, 0x8c); if ((reg & 0x7) == 0x7) { status = FE_HAS_LOCK; break; } state->no_lock_count++; if (state->no_lock_count == 15) { - reg = m88rs2000_demod_read(state, 0x70); + reg = m88rs2000_readreg(state, 0x70); reg ^= 0x4; - m88rs2000_demod_write(state, 0x70, reg); + m88rs2000_writereg(state, 0x70, reg); state->no_lock_count = 0; } - if (state->no_lock_count == 20) - m88rs2000_set_tuner_rf(fe); msleep(20); } if (status & FE_HAS_LOCK) { state->fec_inner = m88rs2000_get_fec(state); /* Uknown suspect SNR level */ - reg = m88rs2000_demod_read(state, 0x65); + reg = m88rs2000_readreg(state, 0x65); } state->tuner_frequency = c->frequency; @@ -830,9 +660,9 @@ static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) struct m88rs2000_state *state = fe->demodulator_priv; if (enable) - m88rs2000_demod_write(state, 0x81, 0x84); + m88rs2000_writereg(state, 0x81, 0x84); else - m88rs2000_demod_write(state, 0x81, 0x81); + m88rs2000_writereg(state, 0x81, 0x81); udelay(10); return 0; } @@ -863,7 +693,6 @@ static struct dvb_frontend_ops m88rs2000_ops = { .release = m88rs2000_release, .init = m88rs2000_init, .sleep = m88rs2000_sleep, - .write = m88rs2000_write, .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, .read_status = m88rs2000_read_status, .read_ber = m88rs2000_read_ber, @@ -896,9 +725,6 @@ struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, state->symbol_rate = 0; state->fec_inner = 0; - if (m88rs2000_startup(state) < 0) - goto error; - /* create dvb_frontend */ memcpy(&state->frontend.ops, &m88rs2000_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 59acdb696873..5a8023e5a4b8 100644 --- a/drivers/media/dvb-frontends/m88rs2000.h +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -26,8 +26,6 @@ struct m88rs2000_config { /* Demodulator i2c address */ u8 demod_addr; - /* Tuner address */ - u8 tuner_addr; u8 *inittab; @@ -55,12 +53,8 @@ static inline struct dvb_frontend *m88rs2000_attach( } #endif /* CONFIG_DVB_M88RS2000 */ -#define FE_CRYSTAL_KHZ 27000 -#define FREQ_OFFSET_LOW_SYM_RATE 3000 - enum { DEMOD_WRITE = 0x1, - TUNER_WRITE, WRITE_DELAY = 0x10, }; #endif /* M88RS2000_H */ diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index fade566927c3..f19cd7367040 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -1,11 +1,9 @@ /* * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver * - * Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com> + * Copyright (C) 2010-2013 Mauro Carvalho Chehab <mchehab@redhat.com> * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com> * - * FIXME: Need to port to DVB v5.2 API - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. @@ -26,24 +24,15 @@ static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); -#define rc(args...) do { \ - printk(KERN_ERR "mb86a20s: " args); \ -} while (0) - -#define dprintk(args...) \ - do { \ - if (debug) { \ - printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ - printk(args); \ - } \ - } while (0) - struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; + u32 last_frequency; struct dvb_frontend frontend; + u32 estimated_rate[3]; + bool need_init; }; @@ -52,6 +41,8 @@ struct regdata { u8 data; }; +#define BER_SAMPLING_RATE 1 /* Seconds */ + /* * Initialization sequence: Use whatevere default values that PV SBTVD * does on its initialisation, obtained via USB snoop @@ -94,41 +85,68 @@ static struct regdata mb86a20s_init[] = { { 0x04, 0x13 }, { 0x05, 0xff }, { 0x04, 0x15 }, { 0x05, 0x4e }, { 0x04, 0x16 }, { 0x05, 0x20 }, - { 0x52, 0x01 }, - { 0x50, 0xa7 }, { 0x51, 0xff }, + + /* + * On this demod, when the bit count reaches the count below, + * it collects the bit error count. The bit counters are initialized + * to 65535 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * will be adjusted later in the driver's code. + */ + { 0x52, 0x01 }, /* Turn on BER before Viterbi */ + { 0x50, 0xa7 }, { 0x51, 0x00 }, { 0x50, 0xa8 }, { 0x51, 0xff }, { 0x50, 0xa9 }, { 0x51, 0xff }, - { 0x50, 0xaa }, { 0x51, 0xff }, + { 0x50, 0xaa }, { 0x51, 0x00 }, { 0x50, 0xab }, { 0x51, 0xff }, { 0x50, 0xac }, { 0x51, 0xff }, - { 0x50, 0xad }, { 0x51, 0xff }, + { 0x50, 0xad }, { 0x51, 0x00 }, { 0x50, 0xae }, { 0x51, 0xff }, { 0x50, 0xaf }, { 0x51, 0xff }, - { 0x5e, 0x07 }, - { 0x50, 0xdc }, { 0x51, 0x01 }, - { 0x50, 0xdd }, { 0x51, 0xf4 }, - { 0x50, 0xde }, { 0x51, 0x01 }, - { 0x50, 0xdf }, { 0x51, 0xf4 }, - { 0x50, 0xe0 }, { 0x51, 0x01 }, - { 0x50, 0xe1 }, { 0x51, 0xf4 }, - { 0x50, 0xb0 }, { 0x51, 0x07 }, - { 0x50, 0xb2 }, { 0x51, 0xff }, - { 0x50, 0xb3 }, { 0x51, 0xff }, - { 0x50, 0xb4 }, { 0x51, 0xff }, - { 0x50, 0xb5 }, { 0x51, 0xff }, - { 0x50, 0xb6 }, { 0x51, 0xff }, - { 0x50, 0xb7 }, { 0x51, 0xff }, - { 0x50, 0x50 }, { 0x51, 0x02 }, - { 0x50, 0x51 }, { 0x51, 0x04 }, - { 0x45, 0x04 }, - { 0x48, 0x04 }, + + /* + * On this demod, post BER counts blocks. When the count reaches the + * value below, it collects the block error count. The block counters + * are initialized to 127 here. This warrants that all of them will be + * quickly calculated when device gets locked. As TMCC is parsed, the + * values will be adjusted later in the driver's code. + */ + { 0x5e, 0x07 }, /* Turn on BER after Viterbi */ + { 0x50, 0xdc }, { 0x51, 0x00 }, + { 0x50, 0xdd }, { 0x51, 0x7f }, + { 0x50, 0xde }, { 0x51, 0x00 }, + { 0x50, 0xdf }, { 0x51, 0x7f }, + { 0x50, 0xe0 }, { 0x51, 0x00 }, + { 0x50, 0xe1 }, { 0x51, 0x7f }, + + /* + * On this demod, when the block count reaches the count below, + * it collects the block error count. The block counters are initialized + * to 127 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * will be adjusted later in the driver's code. + */ + { 0x50, 0xb0 }, { 0x51, 0x07 }, /* Enable PER */ + { 0x50, 0xb2 }, { 0x51, 0x00 }, + { 0x50, 0xb3 }, { 0x51, 0x7f }, + { 0x50, 0xb4 }, { 0x51, 0x00 }, + { 0x50, 0xb5 }, { 0x51, 0x7f }, + { 0x50, 0xb6 }, { 0x51, 0x00 }, + { 0x50, 0xb7 }, { 0x51, 0x7f }, + + { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */ + { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ + { 0x45, 0x04 }, /* CN symbol 4 */ + { 0x48, 0x04 }, /* CN manual mode */ + { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, { 0x50, 0xd7 }, { 0x51, 0x3f }, { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, - { 0x04, 0x40 }, { 0x05, 0x01 }, + + { 0x04, 0x40 }, { 0x05, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x10 }, { 0x28, 0x05 }, { 0x29, 0x02 }, { 0x1c, 0x01 }, @@ -176,8 +194,24 @@ static struct regdata mb86a20s_reset_reception[] = { { 0x08, 0x00 }, }; +static struct regdata mb86a20s_per_ber_reset[] = { + { 0x53, 0x00 }, /* pre BER Counter reset */ + { 0x53, 0x07 }, + + { 0x5f, 0x00 }, /* post BER Counter reset */ + { 0x5f, 0x07 }, + + { 0x50, 0xb1 }, /* PER Counter reset */ + { 0x51, 0x07 }, + { 0x51, 0x00 }, +}; + +/* + * I2C read/write functions and macros + */ + static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, - u8 i2c_addr, int reg, int data) + u8 i2c_addr, u8 reg, u8 data) { u8 buf[] = { reg, data }; struct i2c_msg msg = { @@ -187,8 +221,9 @@ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, &msg, 1); if (rc != 1) { - printk("%s: writereg error (rc == %i, reg == 0x%02x," - " data == 0x%02x)\n", __func__, rc, reg, data); + dev_err(&state->i2c->dev, + "%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, rc, reg, data); return rc; } @@ -222,8 +257,9 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, msg, 2); if (rc != 2) { - rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); - return rc; + dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, rc); + return (rc < 0) ? rc : -EIO; } return val; @@ -237,100 +273,22 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ regdata, ARRAY_SIZE(regdata)) -static int mb86a20s_initfe(struct dvb_frontend *fe) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - int rc; - u8 regD5 = 1; - - dprintk("\n"); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - /* Initialize the frontend */ - rc = mb86a20s_writeregdata(state, mb86a20s_init); - if (rc < 0) - goto err; - - if (!state->config->is_serial) { - regD5 &= ~1; - - rc = mb86a20s_writereg(state, 0x50, 0xd5); - if (rc < 0) - goto err; - rc = mb86a20s_writereg(state, 0x51, regD5); - if (rc < 0) - goto err; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - -err: - if (rc < 0) { - state->need_init = true; - printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); - } else { - state->need_init = false; - dprintk("Initialization succeeded.\n"); - } - return rc; -} - -static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - unsigned rf_max, rf_min, rf; - u8 val; - - dprintk("\n"); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - /* Does a binary search to get RF strength */ - rf_max = 0xfff; - rf_min = 0; - do { - rf = (rf_max + rf_min) / 2; - mb86a20s_writereg(state, 0x04, 0x1f); - mb86a20s_writereg(state, 0x05, rf >> 8); - mb86a20s_writereg(state, 0x04, 0x20); - mb86a20s_writereg(state, 0x04, rf); - - val = mb86a20s_readreg(state, 0x02); - if (val & 0x08) - rf_min = (rf_max + rf_min) / 2; - else - rf_max = (rf_max + rf_min) / 2; - if (rf_max - rf_min < 4) { - *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; - break; - } - } while (1); - - dprintk("signal strength = %d\n", *strength); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - return 0; -} +/* + * Ancillary internal routines (likely compiled inlined) + * + * The functions below assume that gateway lock has already obtained + */ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - u8 val; + int val; - dprintk("\n"); *status = 0; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); val = mb86a20s_readreg(state, 0x0a) & 0xf; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + if (val < 0) + return val; if (val >= 2) *status |= FE_HAS_SIGNAL; @@ -347,49 +305,56 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) if (val >= 8) /* Maybe 9? */ *status |= FE_HAS_LOCK; - dprintk("val = %d, status = 0x%02x\n", val, *status); + dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", + __func__, *status, val); return 0; } -static int mb86a20s_set_frontend(struct dvb_frontend *fe) +static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; int rc; -#if 0 - /* - * FIXME: Properly implement the set frontend properties - */ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; -#endif - - dprintk("\n"); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - dprintk("Calling tuner set parameters\n"); - fe->ops.tuner_ops.set_params(fe); + unsigned rf_max, rf_min, rf; - /* - * Make it more reliable: if, for some reason, the initial - * device initialization doesn't happen, initialize it when - * a SBTVD parameters are adjusted. - * - * Unfortunately, due to a hard to track bug at tda829x/tda18271, - * the agc callback logic is not called during DVB attach time, - * causing mb86a20s to not be initialized with Kworld SBTVD. - * So, this hack is needed, in order to make Kworld SBTVD to work. - */ - if (state->need_init) - mb86a20s_initfe(fe); + /* Does a binary search to get RF strength */ + rf_max = 0xfff; + rf_min = 0; + do { + rf = (rf_max + rf_min) / 2; + rc = mb86a20s_writereg(state, 0x04, 0x1f); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x05, rf >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, 0x20); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, rf); + if (rc < 0) + return rc; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + rc = mb86a20s_readreg(state, 0x02); + if (rc < 0) + return rc; + if (rc & 0x08) + rf_min = (rf_max + rf_min) / 2; + else + rf_max = (rf_max + rf_min) / 2; + if (rf_max - rf_min < 4) { + rf = (rf_max + rf_min) / 2; + + /* Rescale it from 2^12 (4096) to 2^16 */ + rf <<= (16 - 12); + dev_dbg(&state->i2c->dev, + "%s: signal strength = %d (%d < RF=%d < %d)\n", + __func__, rf, rf_min, rf >> 4, rf_max); + return rf; + } + } while (1); - return rc; + return 0; } static int mb86a20s_get_modulation(struct mb86a20s_state *state, @@ -410,7 +375,7 @@ static int mb86a20s_get_modulation(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch ((rc & 0x70) >> 4) { + switch ((rc >> 4) & 0x07) { case 0: return DQPSK; case 1: @@ -443,7 +408,7 @@ static int mb86a20s_get_fec(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch (rc) { + switch ((rc >> 4) & 0x07) { case 0: return FEC_1_2; case 1: @@ -478,24 +443,38 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - if (rc > 3) - return -EINVAL; /* Not used */ - return rc; + + switch ((rc >> 4) & 0x07) { + case 1: + return GUARD_INTERVAL_1_4; + case 2: + return GUARD_INTERVAL_1_8; + case 3: + return GUARD_INTERVAL_1_16; + case 4: + return GUARD_INTERVAL_1_32; + + default: + case 0: + return GUARD_INTERVAL_AUTO; + } } static int mb86a20s_get_segment_count(struct mb86a20s_state *state, unsigned layer) { int rc, count; - static unsigned char reg[] = { [0] = 0x89, /* Layer A */ [1] = 0x8d, /* Layer B */ [2] = 0x91, /* Layer C */ }; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) return rc; @@ -504,113 +483,1451 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, return rc; count = (rc >> 4) & 0x0f; + dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count); + return count; } +static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Fixed parameters */ + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + + /* Initialize values that will be later autodetected */ + c->isdbt_layer_enabled = 0; + c->transmission_mode = TRANSMISSION_MODE_AUTO; + c->guard_interval = GUARD_INTERVAL_AUTO; + c->isdbt_sb_mode = 0; + c->isdbt_sb_segment_count = 0; +} + +/* + * Estimates the bit rate using the per-segment bit rate given by + * ABNT/NBR 15601 spec (table 4). + */ +static u32 isdbt_rate[3][5][4] = { + { /* DQPSK/QPSK */ + { 280850, 312060, 330420, 340430 }, /* 1/2 */ + { 374470, 416080, 440560, 453910 }, /* 2/3 */ + { 421280, 468090, 495630, 510650 }, /* 3/4 */ + { 468090, 520100, 550700, 567390 }, /* 5/6 */ + { 491500, 546110, 578230, 595760 }, /* 7/8 */ + }, { /* QAM16 */ + { 561710, 624130, 660840, 680870 }, /* 1/2 */ + { 748950, 832170, 881120, 907820 }, /* 2/3 */ + { 842570, 936190, 991260, 1021300 }, /* 3/4 */ + { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */ + { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */ + }, { /* QAM64 */ + { 842570, 936190, 991260, 1021300 }, /* 1/2 */ + { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */ + { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */ + { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */ + { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */ + } +}; + +static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, + u32 modulation, u32 fec, u32 interleaving, + u32 segment) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u32 rate; + int m, f, i; + + /* + * If modulation/fec/interleaving is not detected, the default is + * to consider the lowest bit rate, to avoid taking too long time + * to get BER. + */ + switch (modulation) { + case DQPSK: + case QPSK: + default: + m = 0; + break; + case QAM_16: + m = 1; + break; + case QAM_64: + m = 2; + break; + } + + switch (fec) { + default: + case FEC_1_2: + case FEC_AUTO: + f = 0; + break; + case FEC_2_3: + f = 1; + break; + case FEC_3_4: + f = 2; + break; + case FEC_5_6: + f = 3; + break; + case FEC_7_8: + f = 4; + break; + } + + switch (interleaving) { + default: + case GUARD_INTERVAL_1_4: + i = 0; + break; + case GUARD_INTERVAL_1_8: + i = 1; + break; + case GUARD_INTERVAL_1_16: + i = 2; + break; + case GUARD_INTERVAL_1_32: + i = 3; + break; + } + + /* Samples BER at BER_SAMPLING_RATE seconds */ + rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE; + + /* Avoids sampling too quickly or to overflow the register */ + if (rate < 256) + rate = 256; + else if (rate > (1 << 24) - 1) + rate = (1 << 24) - 1; + + dev_dbg(&state->i2c->dev, + "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", + __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000, + rate, rate); + + state->estimated_rate[i] = rate; +} + + static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i, rc; - /* Fixed parameters */ - p->delivery_system = SYS_ISDBT; - p->bandwidth_hz = 6000000; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + /* Reset frontend cache to default values */ + mb86a20s_reset_frontend_cache(fe); /* Check for partial reception */ rc = mb86a20s_writereg(state, 0x6d, 0x85); - if (rc >= 0) - rc = mb86a20s_readreg(state, 0x6e); - if (rc >= 0) - p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; /* Get per-layer data */ - p->isdbt_layer_enabled = 0; + for (i = 0; i < 3; i++) { + dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n", + __func__, 'A' + i); + rc = mb86a20s_get_segment_count(state, i); - if (rc >= 0 && rc < 14) - p->layer[i].segment_count = rc; - if (rc == 0x0f) + if (rc < 0) + goto noperlayer_error; + if (rc >= 0 && rc < 14) { + c->layer[i].segment_count = rc; + } else { + c->layer[i].segment_count = 0; + state->estimated_rate[i] = 0; continue; - p->isdbt_layer_enabled |= 1 << i; + } + c->isdbt_layer_enabled |= 1 << i; rc = mb86a20s_get_modulation(state, i); - if (rc >= 0) - p->layer[i].modulation = rc; + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: modulation %d.\n", + __func__, rc); + c->layer[i].modulation = rc; rc = mb86a20s_get_fec(state, i); - if (rc >= 0) - p->layer[i].fec = rc; + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: FEC %d.\n", + __func__, rc); + c->layer[i].fec = rc; rc = mb86a20s_get_interleaving(state, i); - if (rc >= 0) - p->layer[i].interleaving = rc; + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", + __func__, rc); + c->layer[i].interleaving = rc; + mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation, + c->layer[i].fec, + c->layer[i].interleaving, + c->layer[i].segment_count); } - p->isdbt_sb_mode = 0; rc = mb86a20s_writereg(state, 0x6d, 0x84); - if ((rc >= 0) && ((rc & 0x60) == 0x20)) { - p->isdbt_sb_mode = 1; + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + c->isdbt_sb_mode = 1; /* At least, one segment should exist */ - if (!p->isdbt_sb_segment_count) - p->isdbt_sb_segment_count = 1; - } else - p->isdbt_sb_segment_count = 0; + if (!c->isdbt_sb_segment_count) + c->isdbt_sb_segment_count = 1; + } /* Get transmission mode and guard interval */ - p->transmission_mode = TRANSMISSION_MODE_AUTO; - p->guard_interval = GUARD_INTERVAL_AUTO; rc = mb86a20s_readreg(state, 0x07); - if (rc >= 0) { - if ((rc & 0x60) == 0x20) { - switch (rc & 0x0c >> 2) { - case 0: - p->transmission_mode = TRANSMISSION_MODE_2K; - break; - case 1: - p->transmission_mode = TRANSMISSION_MODE_4K; - break; - case 2: - p->transmission_mode = TRANSMISSION_MODE_8K; - break; - } + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + switch (rc & 0x0c >> 2) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + } + if (!(rc & 0x10)) { + switch (rc & 0x3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + } + } + return 0; + +noperlayer_error: + + /* per-layer info is incomplete; discard all per-layer */ + c->isdbt_layer_enabled = 0; + + return rc; +} + +static int mb86a20s_reset_counters(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Reset the counters, if the channel changed */ + if (state->last_frequency != c->frequency) { + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error)); + memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + memset(&c->block_count, 0, sizeof(c->block_count)); + + state->last_frequency = c->frequency; + } + + /* Clear status for most stats */ + + /* BER/PER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_per_ber_reset); + if (rc < 0) + goto err; + + /* CNR counter reset */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + if (rc < 0) + goto err; + + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + goto err; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + goto err; + + goto ok; +err: + dev_err(&state->i2c->dev, + "%s: Can't reset FE statistics (error %d).\n", + __func__, rc); +ok: + return rc; +} + +static int mb86a20s_get_pre_ber(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= 3) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x54); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: preBER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x55 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x56 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x57 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit error before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit count before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (state->estimated_rate[layer] + && state->estimated_rate[layer] != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating layer %c preBER counter to %d.\n", + __func__, 'A' + layer, state->estimated_rate[layer]); + + /* Turn off BER before Viterbi */ + rc = mb86a20s_writereg(state, 0x52, 0x00); + + /* Update counter for this layer */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 16); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer]); + if (rc < 0) + return rc; + + /* Turn on BER before Viterbi */ + rc = mb86a20s_writereg(state, 0x52, 0x01); + + /* Reset all preBER counters */ + rc = mb86a20s_writereg(state, 0x53, 0x00); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, 0x07); + } else { + /* Reset counter to collect new data */ + rc = mb86a20s_readreg(state, 0x53); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x53, val & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, val | (1 << layer)); + } + + return rc; +} + +static int mb86a20s_get_post_ber(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u32 counter, collect_rate; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= 3) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x60); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: post BER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x64 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x65 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x66 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: post bit error for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + counter = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + counter |= rc; + *count = counter * 204 * 8; + + dev_dbg(&state->i2c->dev, + "%s: post bit count for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (!state->estimated_rate[layer]) + goto reset_measurement; + + collect_rate = state->estimated_rate[layer] / 204 / 8; + if (collect_rate < 32) + collect_rate = 32; + if (collect_rate > 65535) + collect_rate = 65535; + if (collect_rate != counter) { + dev_dbg(&state->i2c->dev, + "%s: updating postBER counter on layer %c to %d.\n", + __func__, 'A' + layer, collect_rate); + + /* Turn off BER after Viterbi */ + rc = mb86a20s_writereg(state, 0x5e, 0x00); + + /* Update counter for this layer */ + rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff); + if (rc < 0) + return rc; + + /* Turn on BER after Viterbi */ + rc = mb86a20s_writereg(state, 0x5e, 0x07); + + /* Reset all preBER counters */ + rc = mb86a20s_writereg(state, 0x5f, 0x00); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x5f, 0x07); + + return rc; + } + +reset_measurement: + /* Reset counter to collect new data */ + rc = mb86a20s_readreg(state, 0x5f); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x5f, val & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x5f, val | (1 << layer)); + + return rc; +} + +static int mb86a20s_get_blk_error(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc, val; + u32 collect_rate; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= 3) + return -EINVAL; + + /* Check if the PER measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0xb8); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: block counts for layer %c aren't available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Packet error Count */ + rc = mb86a20s_writereg(state, 0x50, 0xb9 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *error = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xba + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *error |= rc; + dev_err(&state->i2c->dev, "%s: block error for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: block count for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (!state->estimated_rate[layer]) + goto reset_measurement; + + collect_rate = state->estimated_rate[layer] / 204 / 8; + if (collect_rate < 32) + collect_rate = 32; + if (collect_rate > 65535) + collect_rate = 65535; + + if (collect_rate != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating PER counter on layer %c to %d.\n", + __func__, 'A' + layer, collect_rate); + + /* Stop PER measurement */ + rc = mb86a20s_writereg(state, 0x50, 0xb0); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x00); + if (rc < 0) + return rc; + + /* Update this layer's counter */ + rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff); + if (rc < 0) + return rc; + + /* start PER measurement */ + rc = mb86a20s_writereg(state, 0x50, 0xb0); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x07); + if (rc < 0) + return rc; + + /* Reset all counters to collect new data */ + rc = mb86a20s_writereg(state, 0x50, 0xb1); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x07); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x00); + + return rc; + } + +reset_measurement: + /* Reset counter to collect new data */ + rc = mb86a20s_writereg(state, 0x50, 0xb1); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | (1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & ~(1 << layer)); + + return rc; +} + +struct linear_segments { + unsigned x, y; +}; + +/* + * All tables below return a dB/1000 measurement + */ + +static struct linear_segments cnr_to_db_table[] = { + { 19648, 0}, + { 18187, 1000}, + { 16534, 2000}, + { 14823, 3000}, + { 13161, 4000}, + { 11622, 5000}, + { 10279, 6000}, + { 9089, 7000}, + { 8042, 8000}, + { 7137, 9000}, + { 6342, 10000}, + { 5641, 11000}, + { 5030, 12000}, + { 4474, 13000}, + { 3988, 14000}, + { 3556, 15000}, + { 3180, 16000}, + { 2841, 17000}, + { 2541, 18000}, + { 2276, 19000}, + { 2038, 20000}, + { 1800, 21000}, + { 1625, 22000}, + { 1462, 23000}, + { 1324, 24000}, + { 1175, 25000}, + { 1063, 26000}, + { 980, 27000}, + { 907, 28000}, + { 840, 29000}, + { 788, 30000}, +}; + +static struct linear_segments cnr_64qam_table[] = { + { 3922688, 0}, + { 3920384, 1000}, + { 3902720, 2000}, + { 3894784, 3000}, + { 3882496, 4000}, + { 3872768, 5000}, + { 3858944, 6000}, + { 3851520, 7000}, + { 3838976, 8000}, + { 3829248, 9000}, + { 3818240, 10000}, + { 3806976, 11000}, + { 3791872, 12000}, + { 3767040, 13000}, + { 3720960, 14000}, + { 3637504, 15000}, + { 3498496, 16000}, + { 3296000, 17000}, + { 3031040, 18000}, + { 2715392, 19000}, + { 2362624, 20000}, + { 1963264, 21000}, + { 1649664, 22000}, + { 1366784, 23000}, + { 1120768, 24000}, + { 890880, 25000}, + { 723456, 26000}, + { 612096, 27000}, + { 518912, 28000}, + { 448256, 29000}, + { 388864, 30000}, +}; + +static struct linear_segments cnr_16qam_table[] = { + { 5314816, 0}, + { 5219072, 1000}, + { 5118720, 2000}, + { 4998912, 3000}, + { 4875520, 4000}, + { 4736000, 5000}, + { 4604160, 6000}, + { 4458752, 7000}, + { 4300288, 8000}, + { 4092928, 9000}, + { 3836160, 10000}, + { 3521024, 11000}, + { 3155968, 12000}, + { 2756864, 13000}, + { 2347008, 14000}, + { 1955072, 15000}, + { 1593600, 16000}, + { 1297920, 17000}, + { 1043968, 18000}, + { 839680, 19000}, + { 672256, 20000}, + { 523008, 21000}, + { 424704, 22000}, + { 345088, 23000}, + { 280064, 24000}, + { 221440, 25000}, + { 179712, 26000}, + { 151040, 27000}, + { 128512, 28000}, + { 110080, 29000}, + { 95744, 30000}, +}; + +struct linear_segments cnr_qpsk_table[] = { + { 2834176, 0}, + { 2683648, 1000}, + { 2536960, 2000}, + { 2391808, 3000}, + { 2133248, 4000}, + { 1906176, 5000}, + { 1666560, 6000}, + { 1422080, 7000}, + { 1189632, 8000}, + { 976384, 9000}, + { 790272, 10000}, + { 633344, 11000}, + { 505600, 12000}, + { 402944, 13000}, + { 320768, 14000}, + { 255488, 15000}, + { 204032, 16000}, + { 163072, 17000}, + { 130304, 18000}, + { 105216, 19000}, + { 83456, 20000}, + { 65024, 21000}, + { 52480, 22000}, + { 42752, 23000}, + { 34560, 24000}, + { 27136, 25000}, + { 22016, 26000}, + { 18432, 27000}, + { 15616, 28000}, + { 13312, 29000}, + { 11520, 30000}, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx, dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i].y - segments[i - 1].y; + dx = segments[i - 1].x - segments[i].x; + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y - tmp64; + + return ret; +} + +static int mb86a20s_get_main_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 cnr_linear, cnr; + int rc, val; + + /* Check if CNR is available */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + return rc; + + if (!(rc & 0x40)) { + dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n", + __func__); + return -EBUSY; + } + val = rc; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear = rc << 8; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear |= rc; + + cnr = interpolate_value(cnr_linear, + cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table)); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + + dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n", + __func__, cnr / 1000, cnr % 1000, cnr_linear); + + /* CNR counter reset */ + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + + return rc; +} + +static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 mer, cnr; + int rc, val, i; + struct linear_segments *segs; + unsigned segs_len; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Check if the measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0x5b); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available */ + if (!(rc & 0x01)) { + dev_info(&state->i2c->dev, + "%s: MER measures aren't available yet.\n", __func__); + return -EBUSY; + } + + /* Read all layers */ + for (i = 0; i < 3; i++) { + if (!(c->isdbt_layer_enabled & (1 << i))) { + c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + continue; + } + + rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc; + + switch (c->layer[i].modulation) { + case DQPSK: + case QPSK: + segs = cnr_qpsk_table; + segs_len = ARRAY_SIZE(cnr_qpsk_table); + break; + case QAM_16: + segs = cnr_16qam_table; + segs_len = ARRAY_SIZE(cnr_16qam_table); + break; + default: + case QAM_64: + segs = cnr_64qam_table; + segs_len = ARRAY_SIZE(cnr_64qam_table); + break; } - if (!(rc & 0x10)) { - switch (rc & 0x3) { - case 0: - p->guard_interval = GUARD_INTERVAL_1_4; - break; - case 1: - p->guard_interval = GUARD_INTERVAL_1_8; - break; - case 2: - p->guard_interval = GUARD_INTERVAL_1_16; - break; + cnr = interpolate_value(mer, segs, segs_len); + + c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL; + c->cnr.stat[1 + i].svalue = cnr; + + dev_dbg(&state->i2c->dev, + "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n", + __func__, 'A' + i, cnr / 1000, cnr % 1000, mer); + + } + + /* Start a new MER measurement */ + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + return rc; + + return 0; +} + +static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Fill the length of each status counter */ + + /* Only global stats */ + c->strength.len = 1; + + /* Per-layer stats - 3 layers + global */ + c->cnr.len = 4; + c->pre_bit_error.len = 4; + c->pre_bit_count.len = 4; + c->post_bit_error.len = 4; + c->post_bit_count.len = 4; + c->block_error.len = 4; + c->block_count.len = 4; + + /* Signal is always available */ + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = 0; + + /* Put all of them at FE_SCALE_NOT_AVAILABLE */ + for (i = 0; i < 4; i++) { + c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static int mb86a20s_get_stats(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc = 0, i; + u32 bit_error = 0, bit_count = 0; + u32 t_pre_bit_error = 0, t_pre_bit_count = 0; + u32 t_post_bit_error = 0, t_post_bit_count = 0; + u32 block_error = 0, block_count = 0; + u32 t_block_error = 0, t_block_count = 0; + int active_layers = 0, pre_ber_layers = 0, post_ber_layers = 0; + int per_layers = 0; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + mb86a20s_get_main_CNR(fe); + + /* Get per-layer stats */ + mb86a20s_get_blk_error_layer_CNR(fe); + + for (i = 0; i < 3; i++) { + if (c->isdbt_layer_enabled & (1 << i)) { + /* Layer is active and has rc segments */ + active_layers++; + + /* Handle BER before vterbi */ + rc = mb86a20s_get_pre_ber(fe, i, + &bit_error, &bit_count); + if (rc >= 0) { + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[1 + i].uvalue += bit_error; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[1 + i].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + i, rc); } + if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + pre_ber_layers++; + + /* Handle BER post vterbi */ + rc = mb86a20s_get_post_ber(fe, i, + &bit_error, &bit_count); + if (rc >= 0) { + c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[1 + i].uvalue += bit_error; + c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[1 + i].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->post_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + i, rc); + } + if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + post_ber_layers++; + + /* Handle Block errors for PER/UCB reports */ + rc = mb86a20s_get_blk_error(fe, i, + &block_error, + &block_count); + if (rc >= 0) { + c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->block_error.stat[1 + i].uvalue += block_error; + c->block_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->block_count.stat[1 + i].uvalue += block_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->block_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get PER for layer %c (error %d).\n", + __func__, 'A' + i, rc); + + } + if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + per_layers++; + + /* Update total preBER */ + t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue; + t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue; + + /* Update total postBER */ + t_post_bit_error += c->post_bit_error.stat[1 + i].uvalue; + t_post_bit_count += c->post_bit_count.stat[1 + i].uvalue; + + /* Update total PER */ + t_block_error += c->block_error.stat[1 + i].uvalue; + t_block_count += c->block_count.stat[1 + i].uvalue; } } + /* + * Start showing global count if at least one error count is + * available. + */ + if (pre_ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue = t_pre_bit_error; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue = t_pre_bit_count; + } else { + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + } + + /* + * Start showing global count if at least one error count is + * available. + */ + if (post_ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = t_post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = t_post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + } + + if (per_layers) { + /* + * At least one per-layer UCB measure was read. We can now + * calculate the total UCB + * + * Total block Error/Count is calculated as the sum of the + * block errors on all active layers. + */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = t_block_error; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue = t_block_count; + } else { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + } + + return rc; +} + +/* + * The functions below are called via DVB callbacks, so they need to + * properly use the I2C gate control + */ + +static int mb86a20s_initfe(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; + u8 regD5 = 1; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Initialize the frontend */ + rc = mb86a20s_writeregdata(state, mb86a20s_init); + if (rc < 0) + goto err; + + if (!state->config->is_serial) { + regD5 &= ~1; + + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; + } + +err: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); + if (rc < 0) { + state->need_init = true; + dev_info(&state->i2c->dev, + "mb86a20s: Init failed. Will try again later\n"); + } else { + state->need_init = false; + dev_dbg(&state->i2c->dev, "Initialization succeeded.\n"); + } + return rc; +} + +static int mb86a20s_set_frontend(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; +#if 0 + /* + * FIXME: Properly implement the set frontend properties + */ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; +#endif + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + + /* + * Make it more reliable: if, for some reason, the initial + * device initialization doesn't happen, initialize it when + * a SBTVD parameters are adjusted. + * + * Unfortunately, due to a hard to track bug at tda829x/tda18271, + * the agc callback logic is not called during DVB attach time, + * causing mb86a20s to not be initialized with Kworld SBTVD. + * So, this hack is needed, in order to make Kworld SBTVD to work. + */ + if (state->need_init) + mb86a20s_initfe(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + mb86a20s_reset_counters(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + +static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Get lock */ + rc = mb86a20s_read_status(fe, status); + if (!(*status & FE_HAS_LOCK)) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + } + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't read frontend lock status\n", __func__); + goto error; + } + + /* Get signal strength */ + rc = mb86a20s_read_signal_strength(fe); + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't reset VBER registers.\n", __func__); + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + + rc = 0; /* Status is OK */ + goto error; + } + /* Fill signal strength */ + c->strength.stat[0].uvalue = rc; + + if (*status & FE_HAS_LOCK) { + /* Get TMCC info*/ + rc = mb86a20s_get_frontend(fe); + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't get FE TMCC data.\n", __func__); + rc = 0; /* Status is OK */ + goto error; + } + + /* Get statistics */ + rc = mb86a20s_get_stats(fe); + if (rc < 0 && rc != -EBUSY) { + dev_err(&state->i2c->dev, + "%s: Can't get FE statistics.\n", __func__); + rc = 0; + goto error; + } + rc = 0; /* Don't return EBUSY to userspace */ + } + goto ok; + +error: + mb86a20s_stats_not_ready(fe); + +ok: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + +static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + + *strength = c->strength.stat[0].uvalue; + return 0; } +static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe) +{ + /* + * get_frontend is now handled together with other stats + * retrival, when read_status() is called, as some statistics + * will depend on the layers detection. + */ + return 0; +}; + static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, fe_status_t *status) { + struct mb86a20s_state *state = fe->demodulator_priv; int rc = 0; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (re_tune) rc = mb86a20s_set_frontend(fe); if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - mb86a20s_read_status(fe, status); + mb86a20s_read_status_and_stats(fe, status); return rc; } @@ -619,7 +1936,7 @@ static void mb86a20s_release(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); kfree(state); } @@ -629,15 +1946,16 @@ static struct dvb_frontend_ops mb86a20s_ops; struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct i2c_adapter *i2c) { + struct mb86a20s_state *state; u8 rev; - /* allocate memory for the internal state */ - struct mb86a20s_state *state = - kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); + dev_dbg(&i2c->dev, "%s called.\n", __func__); - dprintk("\n"); + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); if (state == NULL) { - rc("Unable to kzalloc\n"); + dev_err(&i2c->dev, + "%s: unable to allocate memory for state\n", __func__); goto error; } @@ -654,9 +1972,11 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, rev = mb86a20s_readreg(state, 0); if (rev == 0x13) { - printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); + dev_info(&i2c->dev, + "Detected a Fujitsu mb86a20s frontend\n"); } else { - printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", + dev_dbg(&i2c->dev, + "Frontend revision %d is unknown - aborting.\n", rev); goto error; } @@ -690,9 +2010,9 @@ static struct dvb_frontend_ops mb86a20s_ops = { .init = mb86a20s_initfe, .set_frontend = mb86a20s_set_frontend, - .get_frontend = mb86a20s_get_frontend, - .read_status = mb86a20s_read_status, - .read_signal_strength = mb86a20s_read_signal_strength, + .get_frontend = mb86a20s_get_frontend_dummy, + .read_status = mb86a20s_read_status_and_stats, + .read_signal_strength = mb86a20s_read_signal_strength_from_cache, .tune = mb86a20s_tune, }; diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index c625b57b4333..10cfc0579168 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + /* * This driver needs external firmware. Please use the command * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to @@ -44,9 +46,7 @@ static int debug; #define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "or51211: " args); \ - } while (0) + do { if (debug) pr_debug(args); } while (0) static u8 run_buf[] = {0x7f,0x01}; static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC @@ -80,8 +80,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, msg.buf = (u8 *)buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_writebytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -98,8 +97,7 @@ static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) msg.buf = buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_readbytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -118,11 +116,11 @@ static int or51211_load_firmware (struct dvb_frontend* fe, /* Get eprom data */ tudata[0] = 17; if (i2c_writebytes(state,0x50,tudata,1)) { - printk(KERN_WARNING "or51211:load_firmware error eprom addr\n"); + pr_warn("error eprom addr\n"); return -1; } if (i2c_readbytes(state,0x50,&tudata[145],192)) { - printk(KERN_WARNING "or51211: load_firmware error eprom\n"); + pr_warn("error eprom\n"); return -1; } @@ -136,32 +134,32 @@ static int or51211_load_firmware (struct dvb_frontend* fe, state->config->reset(fe); if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { - printk(KERN_WARNING "or51211: load_firmware error 1\n"); + pr_warn("error 1\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address, &fw->data[393],8125)) { - printk(KERN_WARNING "or51211: load_firmware error 2\n"); + pr_warn("error 2\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 3\n"); + pr_warn("error 3\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 4\n"); + pr_warn("error 4\n"); return -1; } msleep(10); - printk("or51211: Done.\n"); + pr_info("Done.\n"); return 0; }; @@ -173,14 +171,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) state->config->setmode(fe, mode); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 1\n"); + pr_warn("error 1\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 2\n"); + pr_warn("error 2\n"); return -1; } @@ -196,7 +194,7 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) * normal +/-150kHz Carrier acquisition range */ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 3\n"); + pr_warn("error 3\n"); return -1; } @@ -206,14 +204,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) rec_buf[3] = 0x00; msleep(20); if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 5\n"); + pr_warn("error 5\n"); } msleep(3); if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { - printk(KERN_WARNING "or51211: setmode error 6"); + pr_warn("error 6\n"); return -1; } - dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]); + dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); return 0; } @@ -248,15 +246,15 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Receiver Status */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51132: read_status write error\n"); + pr_warn("write error\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); + pr_warn("read error\n"); return -1; } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); + dprintk("%x %x\n", rec_buf[0], rec_buf[1]); if (rec_buf[0] & 0x01) { /* Receiver Lock */ *status |= FE_HAS_SIGNAL; @@ -306,20 +304,18 @@ static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) snd_buf[2] = 0x04; if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "%s: error writing snr reg\n", - __func__); + pr_warn("error writing snr reg\n"); return -1; } if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "%s: read_status read error\n", - __func__); + pr_warn("read_status read error\n"); return -1; } state->snr = calculate_snr(rec_buf[0], 89599047); *snr = (state->snr) >> 16; - dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0], + dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; @@ -375,25 +371,24 @@ static int or51211_init(struct dvb_frontend* fe) if (!state->initialized) { /* Request the firmware, this will block until it uploads */ - printk(KERN_INFO "or51211: Waiting for firmware upload " - "(%s)...\n", OR51211_DEFAULT_FIRMWARE); + pr_info("Waiting for firmware upload (%s)...\n", + OR51211_DEFAULT_FIRMWARE); ret = config->request_firmware(fe, &fw, OR51211_DEFAULT_FIRMWARE); - printk(KERN_INFO "or51211:Got Hotplug firmware\n"); + pr_info("Got Hotplug firmware\n"); if (ret) { - printk(KERN_WARNING "or51211: No firmware uploaded " - "(timeout or file not found?)\n"); + pr_warn("No firmware uploaded " + "(timeout or file not found?)\n"); return ret; } ret = or51211_load_firmware(fe, fw); release_firmware(fw); if (ret) { - printk(KERN_WARNING "or51211: Writing firmware to " - "device failed!\n"); + pr_warn("Writing firmware to device failed!\n"); return ret; } - printk(KERN_INFO "or51211: Firmware upload complete.\n"); + pr_info("Firmware upload complete.\n"); /* Set operation mode in Receiver 1 register; * type 1: @@ -406,7 +401,7 @@ static int or51211_init(struct dvb_frontend* fe) */ if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 5\n"); + pr_warn("Load DVR Error 5\n"); return -1; } @@ -419,13 +414,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(30); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error A\n"); + pr_warn("Load DVR Error A\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[10],2)) { - printk(KERN_WARNING "or51211: Load DVR Error B\n"); + pr_warn("Load DVR Error B\n"); return -1; } @@ -436,13 +431,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error C\n"); + pr_warn("Load DVR Error C\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[12],2)) { - printk(KERN_WARNING "or51211: Load DVR Error D\n"); + pr_warn("Load DVR Error D\n"); return -1; } @@ -454,16 +449,14 @@ static int or51211_init(struct dvb_frontend* fe) get_ver_buf[4] = i+1; if (i2c_writebytes(state,state->config->demod_address, get_ver_buf,5)) { - printk(KERN_WARNING "or51211:Load DVR Error 6" - " - %d\n",i); + pr_warn("Load DVR Error 6 - %d\n", i); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[i*2],2)) { - printk(KERN_WARNING "or51211:Load DVR Error 7" - " - %d\n",i); + pr_warn("Load DVR Error 7 - %d\n", i); return -1; } /* If we didn't receive the right index, try again */ @@ -471,15 +464,11 @@ static int or51211_init(struct dvb_frontend* fe) i--; } } - dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n", - rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3], - rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7], - rec_buf[8], rec_buf[9]); + dprintk("read_fwbits %10ph\n", rec_buf); - printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" - " Status %02x\n", - rec_buf[2], rec_buf[4],rec_buf[6], - rec_buf[12],rec_buf[10]); + pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", + rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], + rec_buf[10]); rec_buf[0] = 0x04; rec_buf[1] = 0x00; @@ -488,13 +477,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 8\n"); + pr_warn("Load DVR Error 8\n"); return -1; } msleep(20); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[8],2)) { - printk(KERN_WARNING "or51211: Load DVR Error 9\n"); + pr_warn("Load DVR Error 9\n"); return -1; } state->initialized = 1; diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 92a6075cd82f..b57ecf42e75a 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -420,7 +420,7 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long do_gettimeofday (&nexttime); if (debug_legacy_dish_switch) - memcpy (&tv[0], &nexttime, sizeof (struct timeval)); + tv[0] = nexttime; stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ dvb_frontend_sleep_until(&nexttime, 32000); diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index b551ca350e00..0fb34e1909d1 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1558,6 +1558,27 @@ static int stv0900_status(struct stv0900_internal *intp, return locked; } +static int stv0900_set_mis(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, int mis) +{ + enum fe_stv0900_error error = STV0900_NO_ERROR; + + dprintk("%s\n", __func__); + + if (mis < 0 || mis > 255) { + dprintk("Disable MIS filtering\n"); + stv0900_write_bits(intp, FILTER_EN, 0); + } else { + dprintk("Enable MIS filtering - %d\n", mis); + stv0900_write_bits(intp, FILTER_EN, 1); + stv0900_write_reg(intp, ISIENTRY, mis); + stv0900_write_reg(intp, ISIBITENA, 0xff); + } + + return error; +} + + static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; @@ -1578,6 +1599,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); + stv0900_set_mis(intp, demod, c->stream_id); + p_result.locked = FALSE; p_search.path = demod; p_search.frequency = c->frequency; @@ -1935,6 +1958,9 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, if (err_stv0900) goto error; + if (state->internal->chip_id >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + break; default: goto error; diff --git a/drivers/media/dvb-frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h index 731afe93a823..511ed2a2d987 100644 --- a/drivers/media/dvb-frontends/stv0900_reg.h +++ b/drivers/media/dvb-frontends/stv0900_reg.h @@ -3446,8 +3446,11 @@ extern s32 shiftx(s32 x, int demod, s32 shift); #define R0900_P1_PDELCTRL1 0xf550 #define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) #define F0900_P1_INV_MISMASK 0xf5500080 +#define INV_MISMASK FLDx(F0900_P1_INV_MISMASK) #define F0900_P1_FILTER_EN 0xf5500020 +#define FILTER_EN FLDx(F0900_P1_FILTER_EN) #define F0900_P1_EN_MIS00 0xf5500002 +#define EN_MIS00 FLDx(F0900_P1_EN_MIS00) #define F0900_P1_ALGOSWRST 0xf5500001 #define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 13caec013902..f36eeefb76a6 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -4267,7 +4267,7 @@ err: return -1; } -static int stv090x_set_tspath(struct stv090x_state *state) +static int stv0900_set_tspath(struct stv090x_state *state) { u32 reg; @@ -4538,6 +4538,121 @@ err: return -1; } +static int stv0903_set_tspath(struct stv090x_state *state) +{ + u32 reg; + + if (state->internal->dev_ver >= 0x20) { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL, 0x00); + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c); + break; + } + } else { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10); + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14); + break; + } + } + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_DVBCI: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_CONTINUOUS: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + default: + break; + } + + if (state->config->ts1_clk > 0) { + u32 speed; + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts1_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts1_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) + goto err; + } + + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + static int stv090x_init(struct dvb_frontend *fe) { struct stv090x_state *state = fe->demodulator_priv; @@ -4600,8 +4715,13 @@ static int stv090x_init(struct dvb_frontend *fe) if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; - if (stv090x_set_tspath(state) < 0) - goto err; + if (state->device == STV0900) { + if (stv0900_set_tspath(state) < 0) + goto err; + } else { + if (stv0903_set_tspath(state) < 0) + goto err; + } return 0; @@ -4642,23 +4762,26 @@ static int stv090x_setup(struct dvb_frontend *fe) /* Stop Demod */ if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0) goto err; - if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) - goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) + goto err; msleep(5); /* Set No Tuner Mode */ if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0) goto err; - if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) - goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) + goto err; /* I2C repeater OFF */ STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level); if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0) goto err; - if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) - goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) + goto err; if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */ goto err; diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 16a4bc54dbe7..2521f7e23018 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -30,7 +30,7 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len+1]; struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = sizeof(buf), .buf = buf, @@ -59,12 +59,12 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len]; struct i2c_msg msg[2] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = I2C_M_RD, .len = sizeof(buf), .buf = buf, @@ -1064,7 +1064,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 0x00; cmd.args[3] = 0x00; cmd.args[4] = 0x00; - cmd.args[5] = 0x14; + cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14; cmd.args[6] = 0x00; cmd.args[7] = 0x03; cmd.args[8] = 0x02; @@ -1202,6 +1202,20 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, goto error; } + /* make sure demod i2c address is specified */ + if (!config->demod_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + ret = -EINVAL; + goto error; + } + + /* make sure tuner i2c address is specified */ + if (!config->tuner_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + ret = -EINVAL; + goto error; + } + /* setup the priv */ priv->i2c = i2c; memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index 21163c4b555c..bff1c38df802 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -28,7 +28,13 @@ struct tda10071_config { * Default: none, must set * Values: 0x55, */ - u8 i2c_address; + u8 demod_i2c_addr; + + /* Tuner I2C address. + * Default: none, must set + * Values: 0x14, 0x54, ... + */ + u8 tuner_i2c_addr; /* Max bytes I2C provider can write at once. * Note: Buffer is taken from the stack currently! diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 1af1ee49b542..46710744173b 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -78,7 +78,7 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return err; } *bandwidth = t_state.bandwidth; + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); } - printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); return 0; } diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c new file mode 100644 index 000000000000..ad7ad857ab2a --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.c @@ -0,0 +1,373 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb_frontend.h" +#include "ts2020.h" + +#define TS2020_XTAL_FREQ 27000 /* in kHz */ +#define FREQ_OFFSET_LOW_SYM_RATE 3000 + +struct ts2020_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u8 clk_out_div; + u32 frequency; +}; + +static int ts2020_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +{ + struct ts2020_priv *priv = fe->tuner_priv; + u8 buf[] = { reg, data }; + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + } + }; + int err; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + err = i2c_transfer(priv->i2c, msg, 1); + if (err != 1) { + printk(KERN_ERR + "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) +{ + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", + __func__, reg, ret); + return ret; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return b1[0]; +} + +static int ts2020_sleep(struct dvb_frontend *fe) +{ + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u8 buf[] = { 10, 0 }; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + printk(KERN_ERR "%s: i2c error\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int ts2020_init(struct dvb_frontend *fe) +{ + struct ts2020_priv *priv = fe->tuner_priv; + + ts2020_writereg(fe, 0x42, 0x73); + ts2020_writereg(fe, 0x05, priv->clk_out_div); + ts2020_writereg(fe, 0x20, 0x27); + ts2020_writereg(fe, 0x07, 0x02); + ts2020_writereg(fe, 0x11, 0xff); + ts2020_writereg(fe, 0x60, 0xf9); + ts2020_writereg(fe, 0x08, 0x01); + ts2020_writereg(fe, 0x00, 0x41); + + return 0; +} + +static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) +{ + int ret; + ret = ts2020_writereg(fe, 0x51, 0x1f - offset); + ret |= ts2020_writereg(fe, 0x51, 0x1f); + ret |= ts2020_writereg(fe, 0x50, offset); + ret |= ts2020_writereg(fe, 0x50, 0x00); + msleep(20); + return ret; +} + +static int ts2020_set_tuner_rf(struct dvb_frontend *fe) +{ + int reg; + + reg = ts2020_readreg(fe, 0x3d); + reg &= 0x7f; + if (reg < 0x16) + reg = 0xa1; + else if (reg == 0x16) + reg = 0x99; + else + reg = 0xf9; + + ts2020_writereg(fe, 0x60, reg); + reg = ts2020_tuner_gate_ctrl(fe, 0x08); + + return reg; +} + +static int ts2020_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u32 frequency = c->frequency; + s32 offset_khz; + u32 symbol_rate = (c->symbol_rate / 1000); + u32 f3db, gdiv28; + u16 value, ndiv, lpf_coeff; + u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + u8 lo = 0x01, div4 = 0x0; + + /* Calculate frequency divider */ + if (frequency < 1060000) { + lo |= 0x10; + div4 = 0x1; + ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ; + } else + ndiv = (frequency * 14 * 2) / TS2020_XTAL_FREQ; + ndiv = ndiv + ndiv % 2; + ndiv = ndiv - 1024; + + ret = ts2020_writereg(fe, 0x10, 0x80 | lo); + + /* Set frequency divider */ + ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); + ret |= ts2020_writereg(fe, 0x02, ndiv & 0xff); + + ret |= ts2020_writereg(fe, 0x03, 0x06); + ret |= ts2020_tuner_gate_ctrl(fe, 0x10); + if (ret < 0) + return -ENODEV; + + /* Tuner Frequency Range */ + ret = ts2020_writereg(fe, 0x10, lo); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x08); + + /* Tuner RF */ + ret |= ts2020_set_tuner_rf(fe); + + gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; + ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + if (ret < 0) + return -ENODEV; + + value = ts2020_readreg(fe, 0x26); + + f3db = (symbol_rate * 135) / 200 + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + gdiv28 = gdiv28 * 207 / (value * 2 + 151); + mlpf_max = gdiv28 * 135 / 100; + mlpf_min = gdiv28 * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + lpf_coeff = 2766; + + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / + (TS2020_XTAL_FREQ / 1000) + 1) / 2; + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + + if (lpf_mxdiv < mlpf_min) { + nlpf++; + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + } + + if (lpf_mxdiv > mlpf_max) + lpf_mxdiv = mlpf_max; + + ret = ts2020_writereg(fe, 0x04, lpf_mxdiv); + ret |= ts2020_writereg(fe, 0x06, nlpf); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x01); + + msleep(80); + /* calculate offset assuming 96000kHz*/ + offset_khz = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2; + + priv->frequency = offset_khz; + + return (ret < 0) ? -EINVAL : 0; +} + +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ts2020_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +/* read TS2020 signal strength */ +static int ts2020_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + u16 sig_reading, sig_strength; + u8 rfgain, bbgain; + + rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; + bbgain = ts2020_readreg(fe, 0x21) & 0x1f; + + if (rfgain > 15) + rfgain = 15; + if (bbgain > 13) + bbgain = 13; + + sig_reading = rfgain * 2 + bbgain * 3; + + sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + + /* cook the value to be suitable for szap-s2 human readable output */ + *signal_strength = sig_strength * 1000; + + return 0; +} + +static struct dvb_tuner_ops ts2020_tuner_ops = { + .info = { + .name = "TS2020", + .frequency_min = 950000, + .frequency_max = 2150000 + }, + .init = ts2020_init, + .release = ts2020_release, + .sleep = ts2020_sleep, + .set_params = ts2020_set_params, + .get_frequency = ts2020_get_frequency, + .get_rf_strength = ts2020_read_signal_strength, +}; + +struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + struct ts2020_priv *priv = NULL; + u8 buf; + + priv = kzalloc(sizeof(struct ts2020_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = config->tuner_address; + priv->i2c = i2c; + priv->clk_out_div = config->clk_out_div; + fe->tuner_priv = priv; + + /* Wake Up the tuner */ + if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) { + ts2020_writereg(fe, 0x00, 0x01); + msleep(2); + } + + ts2020_writereg(fe, 0x00, 0x03); + msleep(2); + + /* Check the tuner version */ + buf = ts2020_readreg(fe, 0x00); + if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) + printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); + else { + printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); + kfree(priv); + return NULL; + } + + memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(ts2020_attach); + +MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); +MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h new file mode 100644 index 000000000000..c7e64afa614a --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.h @@ -0,0 +1,50 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef TS2020_H +#define TS2020_H + +#include <linux/dvb/frontend.h> + +struct ts2020_config { + u8 tuner_address; + u8 clk_out_div; +}; + +#if defined(CONFIG_DVB_TS2020) || \ + (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* TS2020_H */ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 24d78e28e493..1e4b2d0f23ce 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -477,7 +477,7 @@ config VIDEO_MT9V032 config VIDEO_TCM825X tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the Toshiba TCM825x VGA camera sensor. diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index 38ce76ed1924..9ae977b5983a 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -1251,13 +1251,11 @@ int cx25840_ir_probe(struct v4l2_subdev *sd) cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); mutex_init(&ir_state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&ir_state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); return 0; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e7c82b297514..882ddf6f66d3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -353,7 +353,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { { REG_RGB444, 0 }, /* No RGB444 please */ { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ { 0x50, 0x80 }, /* "matrix coefficient 2" */ { 0x51, 0 }, /* vb */ diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index f8534eec9de9..a2a5cbbdbe28 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -271,9 +271,9 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd, static int imx074_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int imx074_g_mbus_config(struct v4l2_subdev *sd, @@ -430,10 +430,9 @@ static int imx074_probe(struct i2c_client *client, { struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - int ret; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; } @@ -444,7 +443,7 @@ static int imx074_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct imx074), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -452,23 +451,15 @@ static int imx074_probe(struct i2c_client *client, priv->fmt = &imx074_colour_fmts[0]; - ret = imx074_video_probe(client); - if (ret < 0) { - kfree(priv); - return ret; - } - - return ret; + return imx074_video_probe(client); } static int imx074_remove(struct i2c_client *client) { - struct imx074 *priv = to_imx074(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); - kfree(priv); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 19f8a07764f9..bcdc86175549 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -23,7 +23,7 @@ /* * mt9m001 i2c address 0x5d * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ /* mt9m001 selected register addresses */ @@ -380,9 +380,9 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, static int mt9m001_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -482,7 +482,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_link *icl, +static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -526,8 +526,8 @@ static int mt9m001_video_probe(struct soc_camera_link *icl, * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -558,10 +558,10 @@ done: return ret; } -static void mt9m001_video_remove(struct soc_camera_link *icl) +static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) { - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); } static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) @@ -605,14 +605,14 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -621,12 +621,12 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9m001 *mt9m001 = to_mt9m001(client); unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - if (icl->set_bus_param) - return icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) + return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); /* * Without board specific bus width settings we only support the @@ -663,10 +663,10 @@ static int mt9m001_probe(struct i2c_client *client, { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; } @@ -677,7 +677,7 @@ static int mt9m001_probe(struct i2c_client *client, return -EIO; } - mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL); + mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); if (!mt9m001) return -ENOMEM; @@ -697,12 +697,9 @@ static int mt9m001_probe(struct i2c_client *client, &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m001->subdev.ctrl_handler = &mt9m001->hdl; - if (mt9m001->hdl.error) { - int err = mt9m001->hdl.error; + if (mt9m001->hdl.error) + return mt9m001->hdl.error; - kfree(mt9m001); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -713,11 +710,9 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - ret = mt9m001_video_probe(icl, client); - if (ret) { + ret = mt9m001_video_probe(ssdd, client); + if (ret) v4l2_ctrl_handler_free(&mt9m001->hdl); - kfree(mt9m001); - } return ret; } @@ -725,12 +720,11 @@ static int mt9m001_probe(struct i2c_client *client, static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(icl); - kfree(mt9m001); + mt9m001_video_remove(ssdd); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 62fd94af599b..bbc4ff99603c 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -24,7 +24,8 @@ /* * MT9M111, MT9M112 and MT9M131: * i2c address is 0x48 or 0x5d (depending on SADDR pin) - * The platform has to define i2c_board_info and call i2c_register_board_info() + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* @@ -799,17 +800,17 @@ static int mt9m111_init(struct mt9m111 *mt9m111) static int mt9m111_power_on(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; ret = mt9m111_resume(mt9m111); if (ret < 0) { dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return ret; @@ -818,10 +819,10 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) static void mt9m111_power_off(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); mt9m111_suspend(mt9m111); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -879,13 +880,13 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -956,10 +957,10 @@ static int mt9m111_probe(struct i2c_client *client, { struct mt9m111 *mt9m111; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; } @@ -970,7 +971,7 @@ static int mt9m111_probe(struct i2c_client *client, return -EIO; } - mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL); + mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL); if (!mt9m111) return -ENOMEM; @@ -988,12 +989,8 @@ static int mt9m111_probe(struct i2c_client *client, &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m111->subdev.ctrl_handler = &mt9m111->hdl; - if (mt9m111->hdl.error) { - int err = mt9m111->hdl.error; - - kfree(mt9m111); - return err; - } + if (mt9m111->hdl.error) + return mt9m111->hdl.error; /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; @@ -1005,10 +1002,8 @@ static int mt9m111_probe(struct i2c_client *client, mutex_init(&mt9m111->power_lock); ret = mt9m111_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); - } return ret; } @@ -1019,7 +1014,6 @@ static int mt9m111_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m111->subdev); v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 40800b10a080..d80d044ebf15 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -31,8 +31,8 @@ /* * mt9t031 i2c address 0x5d - * The platform has to define i2c_board_info and link to it from - * struct soc_camera_link + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* mt9t031 selected register addresses */ @@ -608,18 +608,18 @@ static struct device_type mt9t031_dev_type = { static int mt9t031_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct video_device *vdev = soc_camera_i2c_to_vdev(client); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; vdev->dev.type = &mt9t031_dev_type; } else { vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -707,13 +707,13 @@ static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -722,9 +722,9 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_FALLING) return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); else @@ -758,11 +758,11 @@ static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9T031 driver needs platform data\n"); return -EINVAL; } @@ -773,7 +773,7 @@ static int mt9t031_probe(struct i2c_client *client, return -EIO; } - mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL); + mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); if (!mt9t031) return -ENOMEM; @@ -797,12 +797,9 @@ static int mt9t031_probe(struct i2c_client *client, V4L2_CID_EXPOSURE, 1, 255, 1, 255); mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) { - int err = mt9t031->hdl.error; + if (mt9t031->hdl.error) + return mt9t031->hdl.error; - kfree(mt9t031); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -816,10 +813,8 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->yskip = 1; ret = mt9t031_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); - } return ret; } @@ -830,7 +825,6 @@ static int mt9t031_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9t031->subdev); v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index de7cd836b0a2..c75d83168700 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -779,9 +779,9 @@ static int mt9t112_s_register(struct v4l2_subdev *sd, static int mt9t112_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { @@ -991,13 +991,13 @@ static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1006,10 +1006,10 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9t112_priv *priv = to_mt9t112(client); - if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) priv->flags |= PCLK_RISING; return 0; @@ -1078,7 +1078,7 @@ static int mt9t112_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t112_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct v4l2_rect rect = { .width = VGA_WIDTH, .height = VGA_HEIGHT, @@ -1087,24 +1087,22 @@ static int mt9t112_probe(struct i2c_client *client, }; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "mt9t112: missing platform data!\n"); return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); ret = mt9t112_camera_probe(client); - if (ret) { - kfree(priv); + if (ret) return ret; - } /* Cannot fail: using the default supported pixel code */ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); @@ -1114,9 +1112,6 @@ static int mt9t112_probe(struct i2c_client *client, static int mt9t112_remove(struct i2c_client *client) { - struct mt9t112_priv *priv = to_mt9t112(client); - - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index d40a8858be01..a5e65d6a0781 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -25,7 +25,7 @@ /* * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ static char *sensor_type; @@ -508,9 +508,9 @@ static int mt9v022_s_register(struct v4l2_subdev *sd, static int mt9v022_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -655,7 +655,7 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) static int mt9v022_video_probe(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); s32 data; int ret; unsigned long flags; @@ -715,8 +715,8 @@ static int mt9v022_video_probe(struct i2c_client *client) * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -784,7 +784,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -792,7 +792,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -801,15 +801,15 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; int ret; u16 pixclk = 0; - if (icl->set_bus_param) { - ret = icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) { + ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); if (ret) return ret; } else if (bps != 10) { @@ -873,12 +873,12 @@ static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9v022_platform_data *pdata = icl->priv; + struct mt9v022_platform_data *pdata; int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; } @@ -889,10 +889,11 @@ static int mt9v022_probe(struct i2c_client *client, return -EIO; } - mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL); + mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); if (!mt9v022) return -ENOMEM; + pdata = ssdd->drv_priv; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); v4l2_ctrl_handler_init(&mt9v022->hdl, 6); v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, @@ -929,7 +930,6 @@ static int mt9v022_probe(struct i2c_client *client, int err = mt9v022->hdl.error; dev_err(&client->dev, "control initialisation err %d\n", err); - kfree(mt9v022); return err; } v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, @@ -949,10 +949,8 @@ static int mt9v022_probe(struct i2c_client *client, mt9v022->rect.height = MT9V022_MAX_HEIGHT; ret = mt9v022_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); - } return ret; } @@ -960,13 +958,12 @@ static int mt9v022_probe(struct i2c_client *client, static int mt9v022_remove(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9v022->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 66698a83bda2..0f520f693b6e 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -771,9 +771,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd, static int ov2640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* Select the nearest higher resolution for capture */ @@ -1046,13 +1046,13 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1080,11 +1080,11 @@ static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov2640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&adapter->dev, "OV2640: Missing platform_data for driver\n"); return -EINVAL; @@ -1096,7 +1096,7 @@ static int ov2640_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); if (!priv) { dev_err(&adapter->dev, "Failed to allocate memory for private data!\n"); @@ -1110,20 +1110,14 @@ static int ov2640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov2640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } else { + else dev_info(&adapter->dev, "OV2640 Probed\n"); - } return ret; } @@ -1134,7 +1128,6 @@ static int ov2640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 8577e0cfb7fe..9d53309619d2 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -934,13 +934,13 @@ static int ov5642_g_mbus_config(struct v4l2_subdev *sd, static int ov5642_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; if (!on) - return soc_camera_power_off(&client->dev, icl); + return soc_camera_power_off(&client->dev, ssdd); - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -1020,15 +1020,14 @@ static int ov5642_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5642 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - int ret; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; } - priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1043,25 +1042,15 @@ static int ov5642_probe(struct i2c_client *client, priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; - ret = ov5642_video_probe(client); - if (ret < 0) - goto error; - - return 0; - -error: - kfree(priv); - return ret; + return ov5642_video_probe(client); } static int ov5642_remove(struct i2c_client *client) { - struct ov5642 *priv = to_ov5642(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); - kfree(priv); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index e87feb0881e3..dbe4f564e6b2 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -435,9 +435,9 @@ static int ov6650_set_register(struct v4l2_subdev *sd, static int ov6650_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) @@ -892,7 +892,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -900,7 +900,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -910,8 +910,8 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); int ret; if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) @@ -963,15 +963,15 @@ static int ov6650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov6650 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -1009,12 +1009,9 @@ static int ov6650_probe(struct i2c_client *client, V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; + if (priv->hdl.error) + return priv->hdl.error; - kfree(priv); - return err; - } v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); v4l2_ctrl_auto_cluster(2, &priv->autoexposure, @@ -1029,10 +1026,8 @@ static int ov6650_probe(struct i2c_client *client, priv->colorspace = V4L2_COLORSPACE_JPEG; ret = ov6650_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -1043,7 +1038,6 @@ static int ov6650_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index e4a10751894d..fbeb5b2f3ae5 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -667,9 +667,9 @@ static int ov772x_s_register(struct v4l2_subdev *sd, static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) @@ -1019,13 +1019,13 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1054,11 +1054,11 @@ static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov772x_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "OV772X: missing platform data!\n"); return -EINVAL; } @@ -1070,11 +1070,11 @@ static int ov772x_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); @@ -1085,22 +1085,15 @@ static int ov772x_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - ret = priv->hdl.error; - goto done; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov772x_video_probe(priv); - if (ret < 0) - goto done; - - priv->cfmt = &ov772x_cfmts[0]; - priv->win = &ov772x_win_sizes[0]; - -done: - if (ret) { + if (ret < 0) { v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); + } else { + priv->cfmt = &ov772x_cfmts[0]; + priv->win = &ov772x_win_sizes[0]; } return ret; } @@ -1111,7 +1104,6 @@ static int ov772x_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index b323684eaf77..05993041be31 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -336,9 +336,9 @@ static int ov9640_set_register(struct v4l2_subdev *sd, static int ov9640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* select nearest higher resolution for capture */ @@ -657,13 +657,13 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -690,15 +690,15 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } - priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9640_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -713,19 +713,13 @@ static int ov9640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -737,7 +731,6 @@ static int ov9640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 7a55889e397b..2f236da80165 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -787,12 +787,12 @@ static int ov9740_g_chip_ident(struct v4l2_subdev *sd, static int ov9740_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov9740_priv *priv = to_ov9740(sd); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -806,7 +806,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on) priv->current_enable = true; } - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -905,13 +905,13 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -951,15 +951,15 @@ static int ov9740_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9740_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } - priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9740_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate private data!\n"); return -ENOMEM; @@ -972,18 +972,12 @@ static int ov9740_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9740_video_probe(client); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -994,7 +988,6 @@ static int ov9740_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 02f0400051d9..5c92679bfefb 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1183,9 +1183,9 @@ static int rj54n1_s_register(struct v4l2_subdev *sd, static int rj54n1_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1245,14 +1245,14 @@ static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1261,10 +1261,10 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); else @@ -1334,17 +1334,17 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct rj54n1_pdata *rj54n1_priv; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; } - rj54n1_priv = icl->priv; + rj54n1_priv = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, @@ -1352,7 +1352,7 @@ static int rj54n1_probe(struct i2c_client *client, return -EIO; } - rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); if (!rj54n1) return -ENOMEM; @@ -1367,12 +1367,8 @@ static int rj54n1_probe(struct i2c_client *client, v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) { - int err = rj54n1->hdl.error; - - kfree(rj54n1); - return err; - } + if (rj54n1->hdl.error) + return rj54n1->hdl.error; rj54n1->clk_div = clk_div; rj54n1->rect.left = RJ54N1_COLUMN_SKIP; @@ -1387,10 +1383,8 @@ static int rj54n1_probe(struct i2c_client *client, (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); - } return ret; } @@ -1398,13 +1392,12 @@ static int rj54n1_probe(struct i2c_client *client, static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&rj54n1->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); return 0; } diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 140716e71a15..7d2074601881 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -569,9 +569,9 @@ static int tw9910_s_register(struct v4l2_subdev *sd, static int tw9910_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) @@ -847,14 +847,14 @@ static int tw9910_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -863,9 +863,9 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); u8 val = VSSL_VVALID | HSSL_DVALID; - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); /* * set OUTCTR1 @@ -911,15 +911,14 @@ static int tw9910_probe(struct i2c_client *client, struct tw9910_video_info *info; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - int ret; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "TW9910: missing platform data!\n"); return -EINVAL; } - info = icl->priv; + info = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, @@ -928,7 +927,7 @@ static int tw9910_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -936,18 +935,11 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - ret = tw9910_video_probe(client); - if (ret) - kfree(priv); - - return ret; + return tw9910_video_probe(client); } static int tw9910_remove(struct i2c_client *client) { - struct tw9910_priv *priv = to_tw9910(client); - - kfree(priv); return 0; } diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 31104a960652..5967e1a0c809 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1096,13 +1096,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .s_std = tvp5150_s_std, .reset = tvp5150_reset, .g_chip_ident = tvp5150_g_chip_ident, diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 45e5d0661b60..ccd18e4ee789 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3835,7 +3835,7 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, { struct timeval ts; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3878,7 +3878,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); wakeup->vb.ts = ts; wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; @@ -3949,7 +3949,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - do_gettimeofday(&wakeup->vb.ts); + v4l2_get_timestamp(&wakeup->vb.ts); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index 5039b8826e0a..c63c643ed1f8 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -173,7 +173,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) if (i2c_debug) pr_cont(" %02x", msg->buf[cnt]); } - if (!(xmit & BT878_I2C_NOSTOP)) + if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) pr_cont(">\n"); return msg->len; @@ -366,8 +366,7 @@ int init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); - memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, - sizeof(bttv_i2c_algo_bit_template)); + btv->i2c_algo = bttv_i2c_algo_bit_template; btv->i2c_algo.udelay = i2c_udelay; btv->i2c_algo.data = btv; btv->c.i2c_adap.algo_data = &btv->i2c_algo; diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index 4908eb7bcf6c..d61ac6393e7e 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -240,15 +240,13 @@ int init_cx18_i2c(struct cx18 *cx) for (i = 0; i < 2; i++) { /* Setup algorithm for adapter */ - memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + cx->i2c_algo[i] = cx18_i2c_algo_template; cx->i2c_algo_cb_data[i].cx = cx; cx->i2c_algo_cb_data[i].bus_index = i; cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; /* Setup adapter */ - memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, - sizeof(struct i2c_adapter)); + cx->i2c_adap[i] = cx18_i2c_adap_template; cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), " #%d-%d", cx->instance, i); diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index eafa1144b17d..b3688aa8acc3 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -25,7 +25,10 @@ config VIDEO_CX23885 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 6277e145f0b8..7e923f8dd2f5 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -572,6 +572,39 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_PROF_8000] = { .name = "Prof Revolution DVB-S2 8000", .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR4400] = { + .name = "Hauppauge WinTV-HVR4400", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_AVERMEDIA_HC81R] = { + .name = "AVerTV Hybrid Express Slim HC81R", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, /* 0xc2 >> 1 */ + .tuner_bus = 1, + .porta = CX23885_ANALOG_VIDEO, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN2_CH1 | + CX25840_VIN5_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO8, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN8_CH1 | + CX25840_NONE_CH2 | + CX25840_VIN7_CH3 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO6, + }, { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_VIN1_CH1 | + CX25840_NONE_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO6, + } }, } }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -788,6 +821,26 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x8000, .subdevice = 0x3034, .card = CX23885_BOARD_PROF_8000, + }, { + .subvendor = 0x0070, + .subdevice = 0xc108, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc138, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc12a, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc1f8, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x1461, + .subdevice = 0xd939, + .card = CX23885_BOARD_AVERMEDIA_HC81R, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1012,6 +1065,10 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: altera_ci_tuner_reset(dev, port->nr); break; + case CX23885_BOARD_AVERMEDIA_HC81R: + /* XC3028L Reset Command */ + bitmask = 1 << 2; + break; } if (bitmask) { @@ -1301,6 +1358,42 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* enable irq */ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + /* GPIO-8 tda10071 demod reset */ + + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8, 1); + cx23885_gpio_clear(dev, GPIO_8); + mdelay(100); + cx23885_gpio_set(dev, GPIO_8); + mdelay(100); + break; + case CX23885_BOARD_AVERMEDIA_HC81R: + cx_clear(MC417_CTL, 1); + /* GPIO-0,1,2 setup direction as output */ + cx_set(GP0_IO, 0x00070000); + mdelay(10); + /* AF9013 demod reset */ + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00010001); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + /* demod tune? */ + cx_clear(GP0_IO, 0x00030003); + mdelay(10); + cx_set(GP0_IO, 0x00020002); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00020002); + /* XC3028L tuner reset */ + cx_set(GP0_IO, 0x00040004); + cx_clear(GP0_IO, 0x00040004); + cx_set(GP0_IO, 0x00040004); + mdelay(60); + break; } } @@ -1378,6 +1471,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) break; case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_MYGICA_X8507: if (!enable_885_ir) break; dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); @@ -1420,6 +1514,7 @@ void cx23885_ir_fini(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: cx23885_irq_remove(dev, PCI_MSK_AV_CORE); /* sd_ir is a duplicate pointer to the AV Core, just clear it */ dev->sd_ir = NULL; @@ -1464,6 +1559,7 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: if (dev->sd_ir) cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); break; @@ -1509,12 +1605,24 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR4400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; } switch (dev->board) { + case CX23885_BOARD_AVERMEDIA_HC81R: + /* Defaults for VID B */ + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + /* Defaults for VID C */ + /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ + ts2->gen_ctrl_val = 0x10e; + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ @@ -1581,6 +1689,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -1636,6 +1749,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_MPX885: case CX23885_BOARD_MYGICA_X8507: case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_AVERMEDIA_HC81R: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index f0416a668b4c..268654ac9a9f 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -439,7 +439,7 @@ void cx23885_wakeup(struct cx23885_tsport *port, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 2f5b902e63ae..9c5ed10b2c5e 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -57,6 +57,7 @@ #include "netup-init.h" #include "lgdt3305.h" #include "atbm8830.h" +#include "ts2020.h" #include "ds3000.h" #include "cx23885-f300.h" #include "altera-ci.h" @@ -66,6 +67,8 @@ #include "stv090x.h" #include "stb6100.h" #include "stb6100_cfg.h" +#include "tda10071.h" +#include "a8293.h" static unsigned int debug; @@ -469,6 +472,11 @@ static struct ds3000_config tevii_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 1, +}; + static struct cx24116_config dvbworld_cx24116_config = { .demod_address = 0x05, }; @@ -493,20 +501,20 @@ static struct xc5000_config mygica_x8506_xc5000_config = { }; static struct stv090x_config prof_8000_stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - .xtal = 27000000, - .address = 0x6A, - .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, - .repeater_level = STV090x_RPTLEVEL_64, - .adc1_range = STV090x_ADC_2Vpp, - .diseqc_envelope_mode = false, - - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + .xtal = 27000000, + .address = 0x6A, + .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, + .repeater_level = STV090x_RPTLEVEL_64, + .adc1_range = STV090x_ADC_2Vpp, + .diseqc_envelope_mode = false, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, }; static struct stb6100_config prof_8000_stb6100_config = { @@ -659,6 +667,20 @@ static struct mt2063_config terratec_mt2063_config[] = { }, }; +static const struct tda10071_config hauppauge_tda10071_config = { + .demod_i2c_addr = 0x05, + .tuner_i2c_addr = 0x54, + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config hauppauge_a8293_config = { + .i2c_addr = 0x0b, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1011,8 +1033,11 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &i2c_bus->i2c_adap); fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; + } break; case CX23885_BOARD_DVBWORLD_2005: @@ -1242,6 +1267,17 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; } break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10071_attach, + &hauppauge_tda10071_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 4f1055a194b5..7875dfbe09ff 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -89,6 +89,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar @@ -140,6 +141,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -289,6 +291,13 @@ int cx23885_input_init(struct cx23885_dev *dev) /* A guess at the remote */ rc_map = RC_MAP_TEVII_NEC; break; + case CX23885_BOARD_MYGICA_X8507: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = RC_BIT_ALL; + /* A guess at the remote */ + rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; + break; default: return -ENODEV; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 1a21926ca412..5991bc8dc158 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -300,7 +300,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; @@ -509,7 +509,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || - (dev->board == CX23885_BOARD_MYGICA_X8507)) { + (dev->board == CX23885_BOARD_MYGICA_X8507) || + (dev->board == CX23885_BOARD_AVERMEDIA_HC81R)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, INPUT(input)->amux, 0, 0); @@ -1818,8 +1819,7 @@ int cx23885_video_register(struct cx23885_dev *dev) spin_lock_init(&dev->slock); /* Initialize VBI template */ - memcpy(&cx23885_vbi_template, &cx23885_video_template, - sizeof(cx23885_vbi_template)); + cx23885_vbi_template = cx23885_video_template; strcpy(cx23885_vbi_template.name, "cx23885-vbi"); dev->tvnorm = cx23885_video_template.current_norm; @@ -1878,6 +1878,18 @@ int cx23885_video_register(struct cx23885_dev *dev) }; v4l2_subdev_call(sd, tuner, s_config, &cfg); } + + if (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) { + struct xc2028_ctrl ctrl = { + .fname = "xc3028L-v36.fw", + .max_len = 64 + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl + }; + v4l2_subdev_call(sd, tuner, s_config, &cfg); + } } } diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 67f40d31450b..59c322d870f2 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -91,6 +91,8 @@ #define CX23885_BOARD_TEVII_S471 35 #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 +#define CX23885_BOARD_HAUPPAUGE_HVR4400 38 +#define CX23885_BOARD_AVERMEDIA_HC81R 39 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index c4bd1e95d33f..d51eed051d59 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -1237,13 +1237,11 @@ int cx23888_ir_probe(struct cx23885_dev *dev) cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); mutex_init(&state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); } else { kfifo_free(&state->rx_kfifo); diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 53b16dd70320..d4de021dc844 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -130,7 +130,7 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index d27fccbf03c4..bb05eca2da29 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -62,6 +62,8 @@ config VIDEO_CX88_DVB select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 0c255248cbcd..e2e0b8faf7a4 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3743,7 +3743,7 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) cx88_card_list(core, pci); } - memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board)); + core->board = cx88_boards[core->boardnr]; if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB)) core->board.num_frontends = 1; diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 19a58754c6e1..39f095c37ffd 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -549,7 +549,7 @@ void cx88_wakeup(struct cx88_core *core, * up to 32767 buffers in flight... */ if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 666f83b2f3c0..50b5ac5b3983 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -58,6 +58,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "mb86a16.h" +#include "ts2020.h" #include "ds3000.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); @@ -700,6 +701,11 @@ static struct ds3000_config tevii_ds3000_config = { .set_ts_params = ds3000_set_ts_param, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 1, +}; + static const struct stv0900_config prof_7301_stv0900_config = { .demod_address = 0x6a, /* demod_mode = 0,*/ @@ -1466,9 +1472,12 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &core->i2c_adap); fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; + } break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: diff --git a/drivers/media/pci/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c index de0f1af74e41..cf2d69615838 100644 --- a/drivers/media/pci/cx88/cx88-i2c.c +++ b/drivers/media/pci/cx88/cx88-i2c.c @@ -139,8 +139,7 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) if (i2c_udelay<5) i2c_udelay=5; - memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, - sizeof(core->i2c_algo)); + core->i2c_algo = cx8800_i2c_algo_template; core->i2c_adap.dev.parent = &pci->dev; diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c index d77f8ecab9d7..deede6e25d94 100644 --- a/drivers/media/pci/cx88/cx88-vp3054-i2c.c +++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c @@ -118,8 +118,7 @@ int vp3054_i2c_probe(struct cx8802_dev *dev) return -ENOMEM; dev->vp3054 = vp3054_i2c; - memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, - sizeof(vp3054_i2c->algo)); + vp3054_i2c->algo = vp3054_i2c_algo_template; vp3054_i2c->adap.dev.parent = &dev->pci->dev; strlcpy(vp3054_i2c->adap.name, core->name, diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index 013df4e015cd..173daf0c0847 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -8,6 +8,7 @@ config DVB_DM1105 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 904c3ea350f5..026767bed5cd 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -45,6 +45,7 @@ #include "si21xx.h" #include "cx24116.h" #include "z0194a.h" +#include "ts2020.h" #include "ds3000.h" #define MODULE_NAME "dm1105" @@ -849,6 +850,11 @@ static struct ds3000_config dvbworld_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dvbworld_ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 1, +}; + static int frontend_init(struct dm1105_dev *dev) { int ret; @@ -898,8 +904,11 @@ static int frontend_init(struct dm1105_dev *dev) dev->fe = dvb_attach( ds3000_attach, &dvbworld_ds3000_config, &dev->i2c_adap); - if (dev->fe) + if (dev->fe) { + dvb_attach(ts2020_attach, dev->fe, + &dvbworld_ts2020_config, &dev->i2c_adap); dev->fe->ops.set_voltage = dm1105_set_voltage; + } break; case DM1105_BOARD_DVBWORLD_2002: diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index df88dc4ab555..2928e7287da8 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -304,7 +304,7 @@ static void request_modules(struct ivtv *dev) static void flush_request_modules(struct ivtv *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 46e262becb67..a1811054fde4 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -719,13 +719,10 @@ int init_ivtv_i2c(struct ivtv *itv) return -ENODEV; } if (itv->options.newi2c > 0) { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, - sizeof(struct i2c_adapter)); + itv->i2c_adap = ivtv_i2c_adap_hw_template; } else { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, - sizeof(struct i2c_adapter)); - memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + itv->i2c_adap = ivtv_i2c_adap_template; + itv->i2c_algo = ivtv_i2c_algo_template; } itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; @@ -735,8 +732,7 @@ int init_ivtv_i2c(struct ivtv *itv) itv->instance); i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev); - memcpy(&itv->i2c_client, &ivtv_i2c_client_template, - sizeof(struct i2c_client)); + itv->i2c_client = ivtv_i2c_client_template; itv->i2c_client.adapter = &itv->i2c_adap; itv->i2c_adap.dev.parent = &itv->pdev->dev; diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c index 3d7046909009..60c6c2f24066 100644 --- a/drivers/media/pci/mantis/mantis_ca.c +++ b/drivers/media/pci/mantis/mantis_ca.c @@ -198,11 +198,12 @@ void mantis_ca_exit(struct mantis_pci *mantis) struct mantis_ca *ca = mantis->mantis_ca; dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); + if (!ca) + return; mantis_evmgr_exit(ca); dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); - if (ca) - dvb_ca_en50221_release(&ca->en50221); + dvb_ca_en50221_release(&ca->en50221); kfree(ca); } diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 049e18667cd0..3b39deaa8f6b 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -811,7 +811,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -832,7 +832,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -1426,7 +1426,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) return -EINVAL; buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (meye.grab_buffer[index].state == MEYE_BUF_USING) buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1499,7 +1499,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = reqnr; buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = meye.grab_buffer[reqnr].timestamp; buf->sequence = meye.grab_buffer[reqnr].sequence; diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index fad214113669..9e82d2105d53 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -327,6 +327,14 @@ static int demod_attach_drxd(struct ngene_channel *chan) pr_err("No DRXD found!\n"); return -ENODEV; } + return 0; +} + +static int tuner_attach_dtt7520x(struct ngene_channel *chan) +{ + struct drxd_config *feconf; + + feconf = chan->dev->card_info->fe_config[chan->number]; if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, &chan->i2c_adapter, @@ -724,6 +732,7 @@ static struct ngene_info ngene_info_terratec = { .name = "Terratec Integra/Cinergy2400i Dual DVB-T", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .tuner_attach = {tuner_attach_dtt7520x, tuner_attach_dtt7520x}, .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, .i2c_access = 1, }; diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index e359d200d698..8fd24e7c9403 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -308,7 +308,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, /* finish current buffer */ q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; } diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index b209de40a4f8..27915e501db9 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -607,6 +607,9 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + if (!fe0) + return -EINVAL; + fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (fe0->dvb.frontend) { if (cdec_conf->i2c_gate) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 3abf52711e13..7c503fb68526 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2248,6 +2248,17 @@ static int saa7134_streamon(struct file *file, void *priv, if (!res_get(dev, fh, res)) return -EBUSY; + /* The SAA7134 has a 1K FIFO; the datasheet suggests that when + * configured conservatively, there's 22 usec of buffering for video. + * We therefore request a DMA latency of 20 usec, giving us 2 usec of + * margin in case the FIFO is configured differently to the datasheet. + * Unfortunately, I lack register-level documentation to check the + * Linux FIFO setup and confirm the perfect value. + */ + pm_qos_add_request(&fh->qos_request, + PM_QOS_CPU_DMA_LATENCY, + 20); + return videobuf_streamon(saa7134_queue(fh)); } @@ -2259,6 +2270,8 @@ static int saa7134_streamoff(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int res = saa7134_resource(fh); + pm_qos_remove_request(&fh->qos_request); + err = videobuf_streamoff(saa7134_queue(fh)); if (err < 0) return err; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 075908fae4d9..6eae4321229a 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -29,6 +29,7 @@ #include <linux/notifier.h> #include <linux/delay.h> #include <linux/mutex.h> +#include <linux/pm_qos.h> #include <asm/io.h> @@ -469,6 +470,7 @@ struct saa7134_fh { enum v4l2_buf_type type; unsigned int resources; enum v4l2_priority prio; + struct pm_qos_request qos_request; /* video overlay */ struct v4l2_window win; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 27ae48842656..1352e50e457b 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1088,7 +1088,7 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); if (vip->active) { - do_gettimeofday(&vip->active->ts); + v4l2_get_timestamp(&vip->active->ts); vip->active->field_count++; vip->active->state = VIDEOBUF_DONE; wake_up(&vip->active->done); diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 314e417addae..0dcb8cd77676 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -1,8 +1,3 @@ -config TTPCI_EEPROM - tristate - depends on I2C - default n - config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 4656d4a10af0..9f281f1db45e 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -2723,6 +2723,8 @@ static int av7110_attach(struct saa7146_dev* dev, if (ret < 0) goto err_av7110_exit_v4l_12; + mutex_init(&av7110->ioctl_mutex); + #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) av7110_ir_init(av7110); #endif diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index a378662b1dcf..ef3d9606b269 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h @@ -271,6 +271,8 @@ struct av7110 { struct dvb_frontend* fe; fe_status_t fe_status; + struct mutex ioctl_mutex; + /* crash recovery */ void (*recover)(struct av7110* av7110); fe_sec_voltage_t saved_voltage; diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 952b33dbac4f..301029ca4535 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -1109,6 +1109,9 @@ static int dvb_video_ioctl(struct file *file, } } + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case VIDEO_STOP: av7110->videostate.play_state = VIDEO_STOPPED; @@ -1297,6 +1300,7 @@ static int dvb_video_ioctl(struct file *file, break; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } @@ -1314,6 +1318,9 @@ static int dvb_audio_ioctl(struct file *file, (cmd != AUDIO_GET_STATUS)) return -EPERM; + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case AUDIO_STOP: if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) @@ -1442,6 +1449,7 @@ static int dvb_audio_ioctl(struct file *file, ret = -ENOIOCTLCMD; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index 9fc1dd0ba4c3..a6079b90252a 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -253,12 +253,17 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; + int ret = 0; dprintk(8, "av7110:%p\n",av7110); + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: - return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); + ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, + &av7110->ci_slot[0]); break; case CA_GET_CAP: { @@ -277,8 +282,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_slot_info_t *info=(ca_slot_info_t *)parg; - if (info->num < 0 || info->num > 1) + if (info->num < 0 || info->num > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110->ci_slot[info->num].num = info->num; av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? CA_CI_LINK : CA_CI; @@ -306,10 +313,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_descr_t *descr = (ca_descr_t*) parg; - if (descr->index >= 16) - return -EINVAL; - if (descr->parity > 1) + if (descr->index >= 16 || descr->parity > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, (descr->index<<8)|descr->parity, (descr->cw[0]<<8)|descr->cw[1], @@ -320,9 +327,12 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) } default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; + + mutex_unlock(&av7110->ioctl_mutex); + return ret; } static ssize_t dvb_ca_write(struct file *file, const char __user *buf, diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index a90a3b9b09bf..bb53d2488ad0 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -708,8 +708,7 @@ static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { static int zoran_register_i2c (struct zoran *zr) { - memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, - sizeof(struct i2c_algo_bit_data)); + zr->i2c_algo = zoran_i2c_bit_data_template; zr->i2c_algo.data = zr; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index a4cd504b8eee..519164c572c8 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1169,7 +1169,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - do_gettimeofday(&buffer->bs.timestamp); + v4l2_get_timestamp(&buffer->bs.timestamp); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1407,7 +1407,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index e60ae41e2319..2e8f518f298f 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1334,7 +1334,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, struct zoran *zr = fh->zr; unsigned long flags; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3dcfea612c42..2433e2bbeee4 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -92,7 +92,7 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP2 tristate "OMAP2 Camera Capture Interface driver" - depends on VIDEO_DEV && ARCH_OMAP2 + depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE select VIDEOBUF_DMA_SG ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface @@ -202,6 +202,15 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_SH_VEU + tristate "SuperH VEU mem2mem video processing driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Video Engine Unit (VEU) on SuperH and + SH-Mobile SoCs. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4817d2802171..42089ba3600f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -25,6 +25,8 @@ obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o + obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index ecd5323768b7..cc239972fa2c 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -7,4 +7,9 @@ config VIDEO_BLACKFIN_CAPTURE Choose PPI or EPPI as its interface. To compile this driver as a module, choose M here: the - module will be called bfin_video_capture. + module will be called bfin_capture. + +config VIDEO_BLACKFIN_PPI + tristate + depends on VIDEO_BLACKFIN_CAPTURE + default VIDEO_BLACKFIN_CAPTURE diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile index aa3a0a216387..30421bc23080 100644 --- a/drivers/media/platform/blackfin/Makefile +++ b/drivers/media/platform/blackfin/Makefile @@ -1,2 +1,2 @@ -bfin_video_capture-objs := bfin_capture.o ppi.o -obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 1aad2a65d2f3..12e0faf53403 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -52,6 +52,7 @@ struct bcap_format { u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code; int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ }; struct bcap_buffer { @@ -76,10 +77,14 @@ struct bcap_device { unsigned int cur_input; /* current selected standard */ v4l2_std_id std; + /* current selected dv_timings */ + struct v4l2_dv_timings dv_timings; /* used to store pixel format */ struct v4l2_pix_format fmt; /* bits per pixel*/ int bpp; + /* data length for ppi in bits */ + int dlen; /* used to store sensor supported format */ struct bcap_format *sensor_formats; /* number of sensor formats array */ @@ -116,24 +121,35 @@ static const struct bcap_format bcap_formats[] = { .pixelformat = V4L2_PIX_FMT_UYVY, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, .bpp = 16, + .dlen = 8, }, { .desc = "YCbCr 4:2:2 Interleaved YUYV", .pixelformat = V4L2_PIX_FMT_YUYV, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, .bpp = 16, + .dlen = 8, + }, + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, + .bpp = 16, + .dlen = 16, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, .bpp = 16, + .dlen = 8, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 16, + .dlen = 8, }, }; @@ -366,9 +382,39 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.width = bcap_dev->fmt.width; params.height = bcap_dev->fmt.height; params.bpp = bcap_dev->bpp; + params.dlen = bcap_dev->dlen; params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; - params.blank_clocks = bcap_dev->cfg->blank_clocks; + if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; + + params.hdelay = bt->hsync + bt->hbackporch; + params.vdelay = bt->vsync + bt->vbackporch; + params.line = bt->hfrontporch + bt->hsync + + bt->hbackporch + bt->width; + params.frame = bt->vfrontporch + bt->vsync + + bt->vbackporch + bt->height; + if (bt->interlaced) + params.frame += bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_STD) { + params.hdelay = 0; + params.vdelay = 0; + if (bcap_dev->std & V4L2_STD_525_60) { + params.line = 858; + params.frame = 525; + } else { + params.line = 864; + params.frame = 625; + } + } else { + params.hdelay = 0; + params.vdelay = 0; + params.line = params.width + bcap_dev->cfg->blank_pixels; + params.frame = params.height; + } ret = ppi->ops->set_params(ppi, ¶ms); if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, @@ -484,15 +530,13 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; struct bcap_device *bcap_dev = ppi->priv; - struct timeval timevalue; struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; dma_addr_t addr; spin_lock(&bcap_dev->lock); if (bcap_dev->cur_frm != bcap_dev->next_frm) { - do_gettimeofday(&timevalue); - vb->v4l2_buf.timestamp = timevalue; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb2_buffer_done(vb, VB2_BUF_STATE_DONE); bcap_dev->cur_frm = bcap_dev->next_frm; } @@ -602,6 +646,37 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } +static int bcap_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + +static int bcap_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + static int bcap_enum_input(struct file *file, void *priv, struct v4l2_input *input) { @@ -650,13 +725,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index) return ret; } bcap_dev->cur_input = index; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; return 0; } static int bcap_try_format(struct bcap_device *bcap, struct v4l2_pix_format *pixfmt, - enum v4l2_mbus_pixelcode *mbus_code, - int *bpp) + struct bcap_format *bcap_fmt) { struct bcap_format *sf = bcap->sensor_formats; struct bcap_format *fmt = NULL; @@ -671,16 +748,20 @@ static int bcap_try_format(struct bcap_device *bcap, if (i == bcap->num_sensor_formats) fmt = &sf[0]; - if (mbus_code) - *mbus_code = fmt->mbus_code; - if (bpp) - *bpp = fmt->bpp; v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); ret = v4l2_subdev_call(bcap->sd, video, try_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; v4l2_fill_pix_format(pixfmt, &mbus_fmt); + if (bcap_fmt) { + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (mbus_fmt.code == fmt->mbus_code) + break; + } + *bcap_fmt = *fmt; + } pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; return 0; @@ -709,7 +790,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv, struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); + return bcap_try_format(bcap_dev, pixfmt, NULL); } static int bcap_g_fmt_vid_cap(struct file *file, void *priv, @@ -726,24 +807,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, { struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; - enum v4l2_mbus_pixelcode mbus_code; + struct bcap_format bcap_fmt; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret, bpp; + int ret; if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; /* see if format works */ - ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); if (ret < 0) return ret; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; bcap_dev->fmt = *pixfmt; - bcap_dev->bpp = bpp; + bcap_dev->bpp = bcap_fmt.bpp; + bcap_dev->dlen = bcap_fmt.dlen; return 0; } @@ -834,6 +916,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_querystd = bcap_querystd, .vidioc_s_std = bcap_s_std, .vidioc_g_std = bcap_g_std, + .vidioc_s_dv_timings = bcap_s_dv_timings, + .vidioc_g_dv_timings = bcap_g_dv_timings, .vidioc_reqbufs = bcap_reqbufs, .vidioc_querybuf = bcap_querybuf, .vidioc_qbuf = bcap_qbuf, @@ -869,6 +953,7 @@ static int bcap_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct bfin_capture_config *config; struct vb2_queue *q; + struct bcap_route *route; int ret; config = pdev->dev.platform_data; @@ -978,6 +1063,12 @@ static int bcap_probe(struct platform_device *pdev) NULL); if (bcap_dev->sd) { int i; + if (!config->num_inputs) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to work without input\n"); + goto err_unreg_vdev; + } + /* update tvnorms from the sub devices */ for (i = 0; i < config->num_inputs; i++) vfd->tvnorms |= config->inputs[i].std; @@ -989,8 +1080,24 @@ static int bcap_probe(struct platform_device *pdev) v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + /* + * explicitly set input, otherwise some boards + * may not work at the state as we expected + */ + route = &config->routes[0]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + goto err_unreg_vdev; + } + bcap_dev->cur_input = 0; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; + /* now we can probe the default state */ - if (vfd->tvnorms) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); if (ret) { @@ -1000,6 +1107,17 @@ static int bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } + if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_dv_timings dv_timings; + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, &dv_timings); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get dv timings\n"); + goto err_unreg_vdev; + } + bcap_dev->dv_timings = dv_timings; + } ret = bcap_init_sensor_formats(bcap_dev); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index d29592186b02..1e24584605f2 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -17,6 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/module.h> #include <linux/slab.h> #include <asm/bfin_ppi.h> @@ -67,6 +68,13 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) bfin_write16(®->status, 0xffff); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + bfin_write32(®->stat, 0xc0ff); + break; + } default: break; } @@ -128,6 +136,12 @@ static int ppi_start(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -155,6 +169,12 @@ static int ppi_stop(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -171,17 +191,23 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) { const struct ppi_info *info = ppi->info; int dma32 = 0; - int dma_config, bytes_per_line, lines_per_frame; + int dma_config, bytes_per_line; + int hcount, hdelay, samples_per_line; bytes_per_line = params->width * params->bpp / 8; - lines_per_frame = params->height; + /* convert parameters unit from pixels to samples */ + hcount = params->width * params->bpp / params->dlen; + hdelay = params->hdelay * params->bpp / params->dlen; + samples_per_line = params->line * params->bpp / params->dlen; if (params->int_mask == 0xFFFFFFFF) ppi->err_int = false; else ppi->err_int = true; - dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); ppi->ppi_control = params->ppi_control & ~PORT_EN; + if (!(ppi->ppi_control & PORT_DIR)) + dma_config |= WNR; switch (info->type) { case PPI_TYPE_PPI: { @@ -191,8 +217,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write16(®->control, ppi->ppi_control); - bfin_write16(®->count, bytes_per_line - 1); - bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->count, samples_per_line - 1); + bfin_write16(®->frame, params->frame); break; } case PPI_TYPE_EPPI: @@ -204,12 +230,31 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write32(®->control, ppi->ppi_control); - bfin_write16(®->line, bytes_per_line + params->blank_clocks); - bfin_write16(®->frame, lines_per_frame); - bfin_write16(®->hdelay, 0); - bfin_write16(®->vdelay, 0); - bfin_write16(®->hcount, bytes_per_line); - bfin_write16(®->vcount, lines_per_frame); + bfin_write16(®->line, samples_per_line); + bfin_write16(®->frame, params->frame); + bfin_write16(®->hdelay, hdelay); + bfin_write16(®->vdelay, params->vdelay); + bfin_write16(®->hcount, hcount); + bfin_write16(®->vcount, params->height); + break; + } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x70000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->ctl, ppi->ppi_control); + bfin_write32(®->line, samples_per_line); + bfin_write32(®->frame, params->frame); + bfin_write32(®->hdly, hdelay); + bfin_write32(®->vdly, params->vdelay); + bfin_write32(®->hcnt, hcount); + bfin_write32(®->vcnt, params->height); + if (params->int_mask) + bfin_write32(®->imsk, params->int_mask & 0xFF); break; } default: @@ -217,17 +262,17 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) } if (dma32) { - dma_config |= WDSIZE_32; + dma_config |= WDSIZE_32 | PSIZE_32; set_dma_x_count(info->dma_ch, bytes_per_line >> 2); set_dma_x_modify(info->dma_ch, 4); set_dma_y_modify(info->dma_ch, 4); } else { - dma_config |= WDSIZE_16; + dma_config |= WDSIZE_16 | PSIZE_16; set_dma_x_count(info->dma_ch, bytes_per_line >> 1); set_dma_x_modify(info->dma_ch, 2); set_dma_y_modify(info->dma_ch, 2); } - set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_y_count(info->dma_ch, params->height); set_dma_config(info->dma_ch, dma_config); SSYNC(); @@ -263,9 +308,15 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) pr_info("ppi probe success\n"); return ppi; } +EXPORT_SYMBOL(ppi_create_instance); void ppi_delete_instance(struct ppi_if *ppi) { peripheral_free_list(ppi->info->pin_req); kfree(ppi); } +EXPORT_SYMBOL(ppi_delete_instance); + +MODULE_DESCRIPTION("Analog Devices PPI driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 1cf8293c0fb0..8d20b644bf2c 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,6 +178,10 @@ struct coda_ctx { int idx; }; +static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; + static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, @@ -944,6 +948,24 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d return 0; } +static int coda_h264_padding(int size, char *p) +{ + int nal_size; + int diff; + + diff = size - (size & ~0x7); + if (diff == 0) + return 0; + + nal_size = coda_filler_size[diff]; + memcpy(p, coda_filler_nal, nal_size); + + /* Add rbsp stop bit and trailing at the end */ + *(p + nal_size - 1) = 0x80; + + return nal_size; +} + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); @@ -1171,7 +1193,15 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ctx->vpu_header_size[1]); - ctx->vpu_header_size[2] = 0; + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); break; case V4L2_PIX_FMT_MPEG4: /* diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 3c56037c82fc..ccfde4eb626a 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -97,25 +97,15 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. -config VIDEO_DM644X_VPBE - tristate "DM644X VPBE HW module" - depends on ARCH_DAVINCI_DM644x +config VIDEO_DAVINCI_VPBE_DISPLAY + tristate "DM644X/DM365/DM355 VPBE HW module" + depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 select VIDEO_VPSS_SYSTEM select VIDEOBUF2_DMA_CONTIG help - Enables VPBE modules used for display on a DM644x - SoC. + Enables Davinci VPBE module used for display devices. + This module is common for following DM644x/DM365/DM355 + based display devices. To compile this driver as a module, choose M here: the module will be called vpbe. - - -config VIDEO_VPBE_DISPLAY - tristate "VPBE V4L2 Display driver" - depends on ARCH_DAVINCI_DM644x - select VIDEO_DM644X_VPBE - help - Enables VPBE V4L2 Display driver on a DM644x device - - To compile this driver as a module, choose M here: the - module will be called vpbe_display. diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index 74ed92d09257..f40f5219ca50 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o -obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ + vpbe_venc.o vpbe_display.o diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 841b91a3d255..4d9469733fd9 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -558,9 +558,9 @@ static int platform_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct vpbe_device *vpbe_dev = data; - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_dev->osd_device = platform_get_drvdata(pdev); - if (strcmp("vpbe-venc", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) vpbe_dev->venc_device = dev_get_platdata(&pdev->dev); return 0; @@ -584,7 +584,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) struct v4l2_subdev **enc_subdev; struct osd_state *osd_device; struct i2c_adapter *i2c_adap; - int output_index; int num_encoders; int ret = 0; int err; @@ -731,7 +730,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) /* set the current encoder and output to that of venc by default */ vpbe_dev->current_sd_index = 0; vpbe_dev->current_out_index = 0; - output_index = 0; mutex_unlock(&vpbe_dev->lock); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index e707a6f2325b..5e6b0cab514b 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -791,7 +791,6 @@ static int vpbe_display_g_crop(struct file *file, void *priv, struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; struct osd_state *osd_device = fh->disp_dev->osd_device; struct v4l2_rect *rect = &crop->c; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_CROP, layer id = %d\n", @@ -799,7 +798,7 @@ static int vpbe_display_g_crop(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); - ret = -EINVAL; + return -EINVAL; } osd_device->ops.get_layer_config(osd_device, layer->layer_info.id, cfg); @@ -1393,9 +1392,9 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, } /* Initialize videobuf queue as per the buffer type */ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); - if (!layer->alloc_ctx) { + if (IS_ERR(layer->alloc_ctx)) { v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(layer->alloc_ctx); } q = &layer->buffer_queue; memset(q, 0, sizeof(*q)); @@ -1656,7 +1655,7 @@ static int vpbe_device_get(struct device *dev, void *data) if (strcmp("vpbe_controller", pdev->name) == 0) vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_disp->osd_device = platform_get_drvdata(pdev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 707f243f810d..12ad17c52ef3 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -39,7 +39,22 @@ #include <linux/io.h> #include "vpbe_osd_regs.h" -#define MODULE_NAME VPBE_OSD_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-osd" + +static struct platform_device_id vpbe_osd_devtype[] = { + { + .name = DM644X_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); /* register access routines */ static inline u32 osd_read(struct osd_state *sd, u32 offset) @@ -129,7 +144,7 @@ static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, struct osd_platform_data *pdata; pdata = (struct osd_platform_data *)sd->dev->platform_data; - if (pdata->field_inv_wa_enable) { + if (pdata != NULL && pdata->field_inv_wa_enable) { if (!field_inversion || !lconfig->interlaced) { osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); @@ -1526,7 +1541,7 @@ static const struct vpbe_osd_ops osd_ops = { static int osd_probe(struct platform_device *pdev) { - struct osd_platform_data *pdata; + const struct platform_device_id *pdev_id; struct osd_state *osd; struct resource *res; int ret = 0; @@ -1535,16 +1550,15 @@ static int osd_probe(struct platform_device *pdev) if (osd == NULL) return -ENOMEM; - osd->dev = &pdev->dev; - pdata = (struct osd_platform_data *)pdev->dev.platform_data; - osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type; - if (NULL == pdev->dev.platform_data) { - dev_err(osd->dev, "No platform data defined for OSD" - " sub device\n"); - ret = -ENOENT; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; goto free_mem; } + osd->dev = &pdev->dev; + osd->vpbe_type = pdev_id->driver_data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(osd->dev, "Unable to get OSD register address map\n"); @@ -1595,6 +1609,7 @@ static struct platform_driver osd_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_osd_devtype }; module_platform_driver(osd_driver); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index aed7369b962a..bdbebd59df98 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -38,7 +38,22 @@ #include "vpbe_venc_regs.h" -#define MODULE_NAME VPBE_VENC_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-venc" + +static struct platform_device_id vpbe_venc_devtype[] = { + { + .name = DM644X_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); static int debug = 2; module_param(debug, int, 0644); @@ -54,6 +69,7 @@ struct venc_state { spinlock_t lock; void __iomem *venc_base; void __iomem *vdaccfg_reg; + enum vpbe_version venc_type; }; static inline struct venc_state *to_state(struct v4l2_subdev *sd) @@ -127,7 +143,7 @@ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); if (benable) { @@ -159,7 +175,7 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) /* Disable LCD output control (accepting default polarity) */ venc_write(sd, VENC_LCDOUT, 0); - if (pdata->venc_type != VPBE_VERSION_3) + if (venc->venc_type != VPBE_VERSION_3) venc_write(sd, VENC_CMPNT, 0x100); venc_write(sd, VENC_HSPLS, 0); venc_write(sd, VENC_HINT, 0); @@ -203,11 +219,11 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -238,7 +254,6 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) static int venc_set_pal(struct v4l2_subdev *sd) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); @@ -249,11 +264,11 @@ static int venc_set_pal(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -293,8 +308,8 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ @@ -303,12 +318,12 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -341,8 +356,8 @@ static int venc_set_576p50(struct v4l2_subdev *sd) v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) @@ -350,13 +365,13 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -460,14 +475,14 @@ static int venc_s_dv_timings(struct v4l2_subdev *sd, else if (height == 480) return venc_set_480p59_94(sd); else if ((height == 720) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 720p mode here */ ret = venc_set_720p60_internal(sd); /* for DM365 VPBE, there is DAC inside */ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); return ret; } else if ((height == 1080) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 1080i mode here */ ret = venc_set_1080i30_internal(sd); /* for DM365 VPBE, there is DAC inside */ @@ -556,7 +571,7 @@ static int venc_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct venc_state **venc = data; - if (strcmp(MODULE_NAME, pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) *venc = platform_get_drvdata(pdev); return 0; @@ -593,6 +608,7 @@ EXPORT_SYMBOL(venc_sub_dev_init); static int venc_probe(struct platform_device *pdev) { + const struct platform_device_id *pdev_id; struct venc_state *venc; struct resource *res; int ret; @@ -601,6 +617,12 @@ static int venc_probe(struct platform_device *pdev) if (venc == NULL) return -ENOMEM; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; + goto free_mem; + } + venc->venc_type = pdev_id->driver_data; venc->pdev = &pdev->dev; venc->pdata = pdev->dev.platform_data; if (NULL == venc->pdata) { @@ -630,7 +652,7 @@ static int venc_probe(struct platform_device *pdev) goto release_venc_mem_region; } - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(venc->pdev, @@ -681,7 +703,7 @@ static int venc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap((void *)venc->venc_base); release_mem_region(res->start, resource_size(res)); - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); iounmap((void *)venc->vdaccfg_reg); release_mem_region(res->start, resource_size(res)); @@ -698,6 +720,7 @@ static struct platform_driver venc_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_venc_devtype }; module_platform_driver(venc_driver); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index be9d3e1b4868..28d019da4c01 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -560,10 +560,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - struct timeval timevalue; - - do_gettimeofday(&timevalue); - vpfe_dev->cur_frm->ts = timevalue; + v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index a409ccefb380..5892d2bc8eee 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -411,7 +411,7 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 9f2b603be9c9..dd249c96126d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -402,7 +402,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); /* Change status of the cur_frm */ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); @@ -462,8 +462,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - do_gettimeofday(&common->cur_frm->vb. - v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb. + v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index cdbff88e0f1e..494d32231fb5 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -51,13 +51,29 @@ MODULE_AUTHOR("Texas Instruments"); /* VENCINT - vpss_int8 */ #define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 -#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) +#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) +#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) +#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) +#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) +#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) +#define DM365_ISP5_PCCR_RSV BIT(6) + +#define DM365_ISP5_BCR 0x08 +#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) + #define DM365_ISP5_INTSEL1 0x10 #define DM365_ISP5_INTSEL2 0x14 #define DM365_ISP5_INTSEL3 0x18 #define DM365_ISP5_CCDCMUX 0x20 #define DM365_ISP5_PG_FRAME_SIZE 0x28 #define DM365_VPBE_CLK_CTRL 0x00 + +#define VPSS_CLK_CTRL 0x01c40044 +#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) +#define VPSS_CLK_CTRL_DACCLKEN BIT(4) + /* * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, * AF - vpss_int3 @@ -95,12 +111,19 @@ struct vpss_hw_ops { void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); /* clear wbl overflow bit */ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); + /* set sync polarity */ + void (*set_sync_pol)(struct vpss_sync_pol); + /* set the PG_FRAME_SIZE register*/ + void (*set_pg_frame_size)(struct vpss_pg_frame_size); + /* check and clear interrupt if occured */ + int (*dma_complete_interrupt)(void); }; /* vpss configuration */ struct vpss_oper_config { __iomem void *vpss_regs_base0; __iomem void *vpss_regs_base1; + resource_size_t *vpss_regs_base2; enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; @@ -158,6 +181,14 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); } +int vpss_dma_complete_interrupt(void) +{ + if (!oper_cfg.hw_ops.dma_complete_interrupt) + return 2; + return oper_cfg.hw_ops.dma_complete_interrupt(); +} +EXPORT_SYMBOL(vpss_dma_complete_interrupt); + int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) { if (!oper_cfg.hw_ops.select_ccdc_source) @@ -183,6 +214,15 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) return 0; } +void vpss_set_sync_pol(struct vpss_sync_pol sync) +{ + if (!oper_cfg.hw_ops.set_sync_pol) + return; + + oper_cfg.hw_ops.set_sync_pol(sync); +} +EXPORT_SYMBOL(vpss_set_sync_pol); + int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) { if (!oper_cfg.hw_ops.clear_wbl_overflow) @@ -348,6 +388,15 @@ void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) } EXPORT_SYMBOL(dm365_vpss_set_sync_pol); +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) +{ + if (!oper_cfg.hw_ops.set_pg_frame_size) + return; + + oper_cfg.hw_ops.set_pg_frame_size(frame_size); +} +EXPORT_SYMBOL(vpss_set_pg_frame_size); + void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) { int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; @@ -426,6 +475,16 @@ static int vpss_probe(struct platform_device *pdev) oper_cfg.hw_ops.enable_clock = dm365_enable_clock; oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; /* Setup vpss interrupts */ + isp5_write((isp5_read(DM365_ISP5_PCCR) | + DM365_ISP5_PCCR_BL_CLK_ENABLE | + DM365_ISP5_PCCR_ISIF_CLK_ENABLE | + DM365_ISP5_PCCR_H3A_CLK_ENABLE | + DM365_ISP5_PCCR_RSZ_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | + DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); + isp5_write((isp5_read(DM365_ISP5_BCR) | + DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); @@ -471,11 +530,20 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + iounmap(oper_cfg.vpss_regs_base2); + release_mem_region(VPSS_CLK_CTRL, 4); platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) { + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) + return -EBUSY; + + oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + writel(VPSS_CLK_CTRL_VENCCLKEN | + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + return platform_driver_register(&vpss_driver); } subsys_initcall(vpss_init); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 2b1b9f30e1f9..99b841d2f8fc 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -185,6 +185,15 @@ static const struct gsc_fmt gsc_formats[] = { .corder = GSC_CRCB, .num_planes = 3, .num_comp = 3, + }, { + .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled", + .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, } }; @@ -935,8 +944,8 @@ static struct gsc_variant gsc_v_100_variant = { .pix_max = &gsc_v_100_max, .pix_min = &gsc_v_100_min, .pix_align = &gsc_v_100_align, - .in_buf_cnt = 8, - .out_buf_cnt = 16, + .in_buf_cnt = 32, + .out_buf_cnt = 32, .sc_up_max = 8, .sc_down_max = 16, .poly_sc_down_max = 4, @@ -993,12 +1002,8 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (IS_ERR_OR_NULL(gsc->clock)) - return; - - clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; + if (!IS_ERR(gsc->clock)) + clk_unprepare(gsc->clock); } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1007,27 +1012,22 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); - gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); - if (IS_ERR(gsc->clock)) - goto err_print; + gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); + if (IS_ERR(gsc->clock)) { + dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", + GSC_CLOCK_GATE_NAME); + return PTR_ERR(gsc->clock); + } ret = clk_prepare(gsc->clock); if (ret < 0) { - clk_put(gsc->clock); - gsc->clock = NULL; - goto err; + dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", + GSC_CLOCK_GATE_NAME); + gsc->clock = ERR_PTR(-EINVAL); + return ret; } return 0; - -err: - dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", - GSC_CLOCK_GATE_NAME); - gsc_clk_put(gsc); -err_print: - dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", - GSC_CLOCK_GATE_NAME); - return -ENXIO; } static int gsc_m2m_suspend(struct gsc_dev *gsc) @@ -1096,6 +1096,7 @@ static int gsc_probe(struct platform_device *pdev) init_waitqueue_head(&gsc->irq_queue); spin_lock_init(&gsc->slock); mutex_init(&gsc->lock); + gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gsc->regs = devm_request_and_ioremap(dev, res); @@ -1159,6 +1160,7 @@ static int gsc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); + gsc_clk_put(gsc); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 5f157efd24f0..cc19bba09bd1 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -427,6 +427,11 @@ static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); } +static inline int is_tiled(const struct gsc_fmt *fmt) +{ + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; +} + static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) { u32 cfg = readl(dev->regs + GSC_ENABLE); diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index c267c57c76fd..386c0a7a3a52 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -99,22 +99,28 @@ static void gsc_m2m_job_abort(void *priv) gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); } -static int gsc_fill_addr(struct gsc_ctx *ctx) +static int gsc_get_bufs(struct gsc_ctx *ctx) { struct gsc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; int ret; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); if (ret) return ret; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr); + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); + if (ret) + return ret; + + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + + return 0; } static void gsc_m2m_device_run(void *priv) @@ -148,7 +154,7 @@ static void gsc_m2m_device_run(void *priv) goto put_device; } - ret = gsc_fill_addr(ctx); + ret = gsc_get_bufs(ctx); if (ret) { pr_err("Wrong address"); goto put_device; @@ -367,6 +373,13 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh, return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } +static int gsc_m2m_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct gsc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + static int gsc_m2m_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { @@ -548,6 +561,7 @@ static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, .vidioc_reqbufs = gsc_m2m_reqbufs, + .vidioc_expbuf = gsc_m2m_expbuf, .vidioc_querybuf = gsc_m2m_querybuf, .vidioc_qbuf = gsc_m2m_qbuf, .vidioc_dqbuf = gsc_m2m_dqbuf, @@ -565,7 +579,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; @@ -577,7 +591,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; @@ -597,7 +611,7 @@ static int gsc_m2m_open(struct file *file) if (mutex_lock_interruptible(&gsc->lock)) return -ERESTARTSYS; - ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { ret = -ENOMEM; goto unlock; diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 0146b354dc22..6f5b5a486cf3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -214,6 +214,9 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; + writel(cfg, dev->regs + GSC_IN_CON); } @@ -334,6 +337,9 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; + end_set: writel(cfg, dev->regs + GSC_OUT_CON); } diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 9115a2c8d075..5f7db3f1f6f5 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1181,7 +1181,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 05c560f2ef06..6c4db9b98989 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.1"); -static bool debug = true; +static bool debug; module_param(debug, bool, 0644); /* Flags that indicate a format can be used for capture/output */ @@ -917,10 +917,8 @@ static int deinterlace_open(struct file *file) ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + sizeof(struct data_chunk), GFP_KERNEL); if (!ctx->xt) { - int ret = PTR_ERR(ctx->xt); - kfree(ctx); - return ret; + return -ENOMEM; } ctx->colorspace = V4L2_COLORSPACE_REC709; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index ce2b7b4788d6..7012913f34a2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -30,13 +30,6 @@ #include "mcam-core.h" -/* - * Basic frame stats - to be deleted shortly - */ -static int frames; -static int singles; -static int delivered; - #ifdef MCAM_MODE_VMALLOC /* * Internal DMA buffer management. Since the controller cannot do S/G I/O, @@ -367,10 +360,10 @@ static void mcam_frame_tasklet(unsigned long data) if (!test_bit(bufno, &cam->flags)) continue; if (list_empty(&cam->buffers)) { - singles++; + cam->frame_state.singles++; break; /* Leave it valid, hope for better later */ } - delivered++; + cam->frame_state.delivered++; clear_bit(bufno, &cam->flags); buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); @@ -452,7 +445,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); set_bit(CF_SINGLE_BUFFER, &cam->flags); - singles++; + cam->frame_state.singles++; return; } /* @@ -485,7 +478,7 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) struct mcam_vb_buffer *buf = cam->vb_bufs[frame]; if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) { - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } mcam_set_contig_buffer(cam, frame); @@ -578,13 +571,13 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) */ } else { set_bit(CF_SG_RESTART, &cam->flags); - singles++; + cam->frame_state.singles++; cam->vb_bufs[0] = NULL; } /* * Now we can give the completed frame back to user space. */ - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } @@ -1545,7 +1538,9 @@ static int mcam_v4l_open(struct file *filp) filp->private_data = cam; - frames = singles = delivered = 0; + cam->frame_state.frames = 0; + cam->frame_state.singles = 0; + cam->frame_state.delivered = 0; mutex_lock(&cam->s_mutex); if (cam->users == 0) { ret = mcam_setup_vb2(cam); @@ -1566,8 +1561,9 @@ static int mcam_v4l_release(struct file *filp) { struct mcam_camera *cam = filp->private_data; - cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames, - singles, delivered); + cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", + cam->frame_state.frames, cam->frame_state.singles, + cam->frame_state.delivered); mutex_lock(&cam->s_mutex); (cam->users)--; if (cam->users == 0) { @@ -1660,7 +1656,7 @@ static void mcam_frame_complete(struct mcam_camera *cam, int frame) clear_bit(CF_DMA_ACTIVE, &cam->flags); cam->next_buf = frame; cam->buf_seq[frame] = ++(cam->sequence); - frames++; + cam->frame_state.frames++; /* * "This should never happen" */ diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index bd6acba9fb37..5e802c6b398e 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -73,6 +73,14 @@ static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode) } } +/* + * Basic frame states + */ +struct mcam_frame_state { + unsigned int frames; + unsigned int singles; + unsigned int delivered; +}; /* * A description of one of our devices. @@ -108,6 +116,7 @@ struct mcam_camera { unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ + struct mcam_frame_state frame_state; /* Frame state counter */ /* * Subsystem structures. */ diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 390ab094f9f2..37ad446b35b3 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -6,7 +6,7 @@ config VIDEO_OMAP2_VOUT depends on ARCH_OMAP2 || ARCH_OMAP3 select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG - select OMAP2_DSS + select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB default n diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 35cc526e6c93..96c4a17e4280 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -205,19 +205,21 @@ static u32 omap_vout_uservirt_to_phys(u32 virtp) struct vm_area_struct *vma; struct mm_struct *mm = current->mm; - vma = find_vma(mm, virtp); /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) { - physp = virt_to_phys((void *) virtp); - } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { + if (virtp >= PAGE_OFFSET) + return virt_to_phys((void *) virtp); + + down_read(¤t->mm->mmap_sem); + vma = find_vma(mm, virtp); + if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { /* this will catch, kernel-allocated, mmaped-to-usermode addresses */ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + up_read(¤t->mm->mmap_sem); } else { /* otherwise, use get_user_pages() for general userland pages */ int res, nr_pages = 1; struct page *pages; - down_read(¤t->mm->mmap_sem); res = get_user_pages(current, current->mm, virtp, nr_pages, 1, 0, &pages, NULL); @@ -595,7 +597,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - do_gettimeofday(&timevalue); + v4l2_get_timestamp(&timevalue); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: @@ -1230,21 +1232,6 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, return ret; } -static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - if (index >= NUM_OUTPUT_FORMATS) - return -EINVAL; - - fmt->flags = omap_formats[index].flags; - strlcpy(fmt->description, omap_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = omap_formats[index].pixelformat; - return 0; -} - static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { @@ -1858,10 +1845,9 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, .vidioc_s_crop = vidioc_s_crop, diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c index 8b7ccea982e7..debb44ceb185 100644 --- a/drivers/media/platform/omap24xxcam.c +++ b/drivers/media/platform/omap24xxcam.c @@ -402,7 +402,7 @@ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, omap24xxcam_core_disable(cam); spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = atomic_add_return(2, &fh->field_count); if (csr & csr_error) { vb->state = VIDEOBUF_ERROR; diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 3d56b33f85e8..c09de32f986a 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -32,7 +32,8 @@ #include "ispreg.h" #include "ispcsiphy.h" -static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, +static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, + enum isp_interface_type iface, bool ccp2_strobe) { u32 reg = isp_reg_readl( @@ -40,6 +41,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, u32 shift, mode; switch (iface) { + default: + /* Should not happen in practice, but let's keep the compiler happy. */ case ISP_INTERFACE_CCP2B_PHY1: reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; @@ -59,9 +62,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, } /* Select data/clock or data/strobe mode for CCP2 */ - switch (iface) { - case ISP_INTERFACE_CCP2B_PHY1: - case ISP_INTERFACE_CCP2B_PHY2: + if (iface == ISP_INTERFACE_CCP2B_PHY1 || + iface == ISP_INTERFACE_CCP2B_PHY2) { if (ccp2_strobe) mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; else @@ -110,7 +112,8 @@ static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, * and 3630, so they will not hold their contents in off-mode. This isn't an * issue since the MPU power domain is forced on whilst the ISP is in use. */ -static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on, +static void csiphy_routing_cfg(struct isp_csiphy *phy, + enum isp_interface_type iface, bool on, bool ccp2_strobe) { if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL] diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 15bf3eab2224..e15f01342058 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -366,7 +366,7 @@ static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) unsigned long this_pfn; unsigned long start; unsigned long end; - dma_addr_t pa; + dma_addr_t pa = 0; int ret = -EFAULT; start = buf->vbuf.m.userptr; @@ -419,7 +419,7 @@ done: static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) { struct vm_area_struct *vma; - pgprot_t vm_page_prot; + pgprot_t uninitialized_var(vm_page_prot); unsigned long start; unsigned long end; int ret = -EFAULT; @@ -674,6 +674,7 @@ static int isp_video_queue_alloc(struct isp_video_queue *queue, buf->vbuf.index = i; buf->vbuf.length = size; buf->vbuf.type = queue->type; + buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->vbuf.field = V4L2_FIELD_NONE; buf->vbuf.memory = memory; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index e2716c35f8f1..8f0ca02c29a9 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -27,6 +27,7 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/version.h> #include <media/media-device.h> #include <media/v4l2-ctrls.h> diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index fdb6740248a7..18a70e4a0b0f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -510,8 +510,8 @@ static int fimc_capture_open(struct file *file) dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_m2m_active(fimc)) goto unlock; @@ -546,6 +546,7 @@ static int fimc_capture_open(struct file *file) } unlock: mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); return ret; } @@ -626,8 +627,8 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; u32 mask = FMT_FLAGS_CAM; @@ -699,8 +700,8 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; u32 align_sz = 0, align_h = 4; @@ -793,6 +794,21 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, return 0; } +static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + + while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { + pad = media_entity_remote_source(pad); + if (!pad) + break; + me = pad->entity; + pad = &me->pads[0]; + } + + return me; +} + /** * fimc_pipeline_try_format - negotiate and/or set formats at pipeline * elements @@ -808,19 +824,23 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, { struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; - struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; - struct fimc_fmt *ffmt = NULL; - int ret, i = 0; + struct media_entity *me; + struct fimc_fmt *ffmt; + struct media_pad *pad; + int ret, i = 1; + u32 fcc; if (WARN_ON(!sd || !tfmt)) return -EINVAL; memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; + + me = fimc_pipeline_get_head(&sd->entity); + while (1) { ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, FMT_FLAGS_CAM, i++); @@ -833,40 +853,52 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, } mf->code = tfmt->code = ffmt->mbus_code; - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); - if (ret) - return ret; - if (mf->code != tfmt->code) { - mf->code = 0; - continue; - } - if (mf->width != tfmt->width || mf->height != tfmt->height) { - u32 fcc = ffmt->fourcc; - tfmt->width = mf->width; - tfmt->height = mf->height; - ffmt = fimc_capture_try_format(ctx, - &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SOURCE); - if (ffmt && ffmt->mbus_code) - mf->code = ffmt->mbus_code; - if (mf->width != tfmt->width || - mf->height != tfmt->height) - continue; - tfmt->code = mf->code; + /* set format on all pipeline subdevs */ + while (me != &fimc->vid_cap.subdev.entity) { + sd = media_entity_to_v4l2_subdev(me); + + sfmt.pad = 0; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); + if (ret) + return ret; + + if (me->pads[0].flags & MEDIA_PAD_FL_SINK) { + sfmt.pad = me->num_pads - 1; + mf->code = tfmt->code; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, + &sfmt); + if (ret) + return ret; + } + + pad = media_entity_remote_source(&me->pads[sfmt.pad]); + if (!pad) + return -EINVAL; + me = pad->entity; } - if (csis) - ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); - if (mf->code == tfmt->code && - mf->width == tfmt->width && mf->height == tfmt->height) - break; + if (mf->code != tfmt->code) + continue; + + fcc = ffmt->fourcc; + tfmt->width = mf->width; + tfmt->height = mf->height; + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SINK); + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SOURCE); + if (ffmt && ffmt->mbus_code) + mf->code = ffmt->mbus_code; + if (mf->width != tfmt->width || mf->height != tfmt->height) + continue; + tfmt->code = mf->code; + break; } if (fmt_id && ffmt) *fmt_id = ffmt; *tfmt = *mf; - dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; } @@ -884,14 +916,16 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, { struct v4l2_mbus_frame_desc fd; int i, ret; + int pad; for (i = 0; i < num_planes; i++) fd.entry[i].length = plane_fmt[i].sizeimage; + pad = sensor->entity.num_pads - 1; if (try) - ret = v4l2_subdev_call(sensor, pad, set_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd); else - ret = v4l2_subdev_call(sensor, pad, get_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd); if (ret < 0) return ret; @@ -929,6 +963,10 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_mbus_framefmt mf; struct fimc_fmt *ffmt = NULL; + int ret = 0; + + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, @@ -940,16 +978,16 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, FIMC_SD_PAD_SOURCE); - if (!ffmt) - return -EINVAL; + if (!ffmt) { + ret = -EINVAL; + goto unlock; + } if (!fimc->vid_cap.user_subdev_api) { mf.width = pix->width; mf.height = pix->height; mf.code = ffmt->mbus_code; - fimc_md_graph_lock(fimc); fimc_pipeline_try_format(ctx, &mf, &ffmt, false); - fimc_md_graph_unlock(fimc); pix->width = mf.width; pix->height = mf.height; if (ffmt) @@ -961,8 +999,11 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, if (ffmt->flags & FMT_FLAGS_COMPRESSED) fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], pix->plane_fmt, ffmt->memplanes, true); +unlock: + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); - return 0; + return ret; } static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, @@ -979,7 +1020,8 @@ static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); } -static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) +static int __fimc_capture_set_format(struct fimc_dev *fimc, + struct v4l2_format *f) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; @@ -1014,12 +1056,10 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) mf->code = ff->fmt->mbus_code; mf->width = pix->width; mf->height = pix->height; - - fimc_md_graph_lock(fimc); ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); - fimc_md_graph_unlock(fimc); if (ret) return ret; + pix->width = mf->width; pix->height = mf->height; } @@ -1058,8 +1098,23 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); + int ret; + + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); + /* + * The graph is walked within __fimc_capture_set_format() to set + * the format at subdevs thus the graph mutex needs to be held at + * this point and acquired before the video mutex, to avoid AB-BA + * deadlock when fimc_md_link_notify() is called by other thread. + * Ideally the graph walking and setting format at the whole pipeline + * should be removed from this driver and handled in userspace only. + */ + ret = __fimc_capture_set_format(fimc, f); - return fimc_capture_set_format(fimc, f); + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); + return ret; } static int fimc_cap_enum_input(struct file *file, void *priv, @@ -1528,6 +1583,10 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, *mf = fmt->format; return 0; } + /* There must be a bug in the driver if this happens */ + if (WARN_ON(ffmt == NULL)) + return -EINVAL; + /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); @@ -1690,7 +1749,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) }, }; - return fimc_capture_set_format(fimc, &fmt); + return __fimc_capture_set_format(fimc, &fmt); } /* fimc->lock must be already initialized */ @@ -1752,6 +1811,12 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) goto err_ent; + /* + * For proper order of acquiring/releasing the video + * and the graph mutex. + */ + v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT); + v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 545b46ae12a1..2e86f44edd23 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -241,7 +241,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -440,7 +440,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -524,7 +524,7 @@ static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -591,7 +591,7 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -881,7 +881,7 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { - struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; @@ -1053,7 +1053,7 @@ static int fimc_remove(struct platform_device *pdev) } /* Image pixel limits, similar across several FIMC HW revisions. */ -static struct fimc_pix_limit s5p_pix_limit[4] = { +static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, @@ -1088,7 +1088,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct fimc_variant fimc0_variant_s5p = { +static const struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1100,7 +1100,7 @@ static struct fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct fimc_variant fimc2_variant_s5p = { +static const struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1110,7 +1110,7 @@ static struct fimc_variant fimc2_variant_s5p = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc0_variant_s5pv210 = { +static const struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1123,7 +1123,7 @@ static struct fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc1_variant_s5pv210 = { +static const struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1137,7 +1137,7 @@ static struct fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc2_variant_s5pv210 = { +static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1148,7 +1148,7 @@ static struct fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc0_variant_exynos4 = { +static const struct fimc_variant fimc0_variant_exynos4210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1164,9 +1164,8 @@ static struct fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc3_variant_exynos4 = { +static const struct fimc_variant fimc3_variant_exynos4210 = { .pix_hoff = 1, - .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .has_alpha = 1, @@ -1178,8 +1177,38 @@ static struct fimc_variant fimc3_variant_exynos4 = { .pix_limit = &s5p_pix_limit[3], }; +static const struct fimc_variant fimc0_variant_exynos4x12 = { + .pix_hoff = 1, + .has_inp_rot = 1, + .has_out_rot = 1, + .has_cam_if = 1, + .has_isp_wb = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[1], +}; + +static const struct fimc_variant fimc3_variant_exynos4x12 = { + .pix_hoff = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[3], +}; + /* S5PC100 */ -static struct fimc_drvdata fimc_drvdata_s5p = { +static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1190,7 +1219,7 @@ static struct fimc_drvdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct fimc_drvdata fimc_drvdata_s5pv210 = { +static const struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1201,18 +1230,30 @@ static struct fimc_drvdata fimc_drvdata_s5pv210 = { }; /* EXYNOS4210, S5PV310, S5PC210 */ -static struct fimc_drvdata fimc_drvdata_exynos4 = { +static const struct fimc_drvdata fimc_drvdata_exynos4210 = { + .variant = { + [0] = &fimc0_variant_exynos4210, + [1] = &fimc0_variant_exynos4210, + [2] = &fimc0_variant_exynos4210, + [3] = &fimc3_variant_exynos4210, + }, + .num_entities = 4, + .lclk_frequency = 166000000UL, +}; + +/* EXYNOS4212, EXYNOS4412 */ +static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { .variant = { - [0] = &fimc0_variant_exynos4, - [1] = &fimc0_variant_exynos4, - [2] = &fimc0_variant_exynos4, - [3] = &fimc3_variant_exynos4, + [0] = &fimc0_variant_exynos4x12, + [1] = &fimc0_variant_exynos4x12, + [2] = &fimc0_variant_exynos4x12, + [3] = &fimc3_variant_exynos4x12, }, .num_entities = 4, .lclk_frequency = 166000000UL, }; -static struct platform_device_id fimc_driver_ids[] = { +static const struct platform_device_id fimc_driver_ids[] = { { .name = "s5p-fimc", .driver_data = (unsigned long)&fimc_drvdata_s5p, @@ -1221,7 +1262,10 @@ static struct platform_device_id fimc_driver_ids[] = { .driver_data = (unsigned long)&fimc_drvdata_s5pv210, }, { .name = "exynos4-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4, + .driver_data = (unsigned long)&fimc_drvdata_exynos4210, + }, { + .name = "exynos4x12-fimc", + .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, {}, }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index c0040d792499..424ff960f0d9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -372,6 +372,7 @@ struct fimc_pix_limit { * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface + * @has_isp_wb: set if this instance has ISP writeback input * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -386,8 +387,9 @@ struct fimc_variant { unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; + unsigned int has_isp_wb:1; unsigned int has_alpha:1; - struct fimc_pix_limit *pix_limit; + const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; @@ -402,7 +404,7 @@ struct fimc_variant { * @lclk_frequency: local bus clock frequency */ struct fimc_drvdata { - struct fimc_variant *variant[FIMC_MAX_DEVS]; + const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; }; @@ -435,7 +437,7 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct fimc_variant *variant; + const struct fimc_variant *variant; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index a22d7eb05c82..ad63ebf082c5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -292,9 +292,11 @@ void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) }; u32 i; - pr_info("--- %s ---\n", label); + v4l2_info(&dev->subdev, "--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { u32 cfg = readl(dev->regs + registers[i].offset); - pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + v4l2_info(&dev->subdev, "%9s: 0x%08x\n", + registers[i].name, cfg); } } diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index ed67220d0a64..c67dd2ebcff5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } -static int fimc_lite_hw_init(struct fimc_lite *fimc) +static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { struct fimc_pipeline *pipeline = &fimc->pipeline; - struct fimc_sensor_info *sensor; + struct v4l2_subdev *sensor; + struct fimc_sensor_info *si; unsigned long flags; - if (pipeline->subdevs[IDX_SENSOR] == NULL) + sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; + + if (sensor == NULL) return -ENXIO; if (fimc->fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + /* Get sensor configuration data from the sensor subdev */ + si = v4l2_get_subdev_hostdata(sensor); spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &sensor->pdata); + flite_hw_set_camera_bus(fimc, &si->pdata); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); - flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); flite_hw_set_interrupt_mask(fimc); flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); @@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc->frame_count = 0; - ret = fimc_lite_hw_init(fimc); + ret = fimc_lite_hw_init(fimc, false); if (ret) { fimc_lite_reinit(fimc, false); return ret; @@ -455,10 +459,16 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) static int fimc_lite_open(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); + struct media_entity *me = &fimc->vfd.entity; int ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + mutex_lock(&me->parent->graph_mutex); + + mutex_lock(&fimc->lock); + if (fimc->out_path != FIMC_IO_DMA) { + ret = -EBUSY; + goto done; + } set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); @@ -483,6 +493,7 @@ static int fimc_lite_open(struct file *file) } done: mutex_unlock(&fimc->lock); + mutex_unlock(&me->parent->graph_mutex); return ret; } @@ -962,6 +973,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_streamoff = fimc_lite_streamoff, }; +/* Called with the media graph mutex held */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -970,46 +1004,59 @@ static int fimc_lite_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); unsigned int remote_ent_type = media_entity_type(remote->entity); + int ret = 0; if (WARN_ON(fimc == NULL)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", - __func__, local->entity->name, remote->entity->name, + __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); - switch (local->index) { - case FIMC_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) - return -EINVAL; + mutex_lock(&fimc->lock); + switch (local->index) { + case FLITE_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + ret = -EINVAL; + break; + } if (flags & MEDIA_LNK_FL_ENABLED) { - if (fimc->source_subdev_grp_id != 0) - return -EBUSY; - fimc->source_subdev_grp_id = sd->grp_id; - return 0; + if (fimc->source_subdev_grp_id == 0) + fimc->source_subdev_grp_id = sd->grp_id; + else + ret = -EBUSY; + } else { + fimc->source_subdev_grp_id = 0; + fimc->sensor = NULL; } + break; - fimc->source_subdev_grp_id = 0; + case FLITE_SD_PAD_SOURCE_DMA: + if (!(flags & MEDIA_LNK_FL_ENABLED)) + fimc->out_path = FIMC_IO_NONE; + else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + fimc->out_path = FIMC_IO_DMA; + else + ret = -EINVAL; break; - case FIMC_SD_PAD_SOURCE: - if (!(flags & MEDIA_LNK_FL_ENABLED)) { + case FLITE_SD_PAD_SOURCE_ISP: + if (!(flags & MEDIA_LNK_FL_ENABLED)) fimc->out_path = FIMC_IO_NONE; - return 0; - } - if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) fimc->out_path = FIMC_IO_ISP; else - fimc->out_path = FIMC_IO_DMA; + ret = -EINVAL; break; default: v4l2_err(sd, "Invalid pad index\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&fimc->lock); + return ret; } static const struct media_entity_operations fimc_lite_subdev_media_ops = { @@ -1188,13 +1235,49 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned long flags; + int ret; - if (fimc->out_path == FIMC_IO_DMA) + /* + * Find sensor subdev linked to FIMC-LITE directly or through + * MIPI-CSIS. This is required for configuration where FIMC-LITE + * is used as a subdev only and feeds data internally to FIMC-IS. + * The pipeline links are protected through entity.stream_count + * so there is no need to take the media graph mutex here. + */ + fimc->sensor = __find_remote_sensor(&sd->entity); + + mutex_lock(&fimc->lock); + if (fimc->out_path != FIMC_IO_ISP) { + mutex_unlock(&fimc->lock); return -ENOIOCTLCMD; + } - /* TODO: */ + if (on) { + flite_hw_reset(fimc); + ret = fimc_lite_hw_init(fimc, true); + if (!ret) { + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + } + } else { + set_bit(ST_FLITE_OFF, &fimc->state); - return 0; + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + ret = wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + msecs_to_jiffies(200)); + if (ret == 0) + v4l2_err(sd, "s_stream(0) timeout\n"); + clear_bit(ST_FLITE_RUN, &fimc->state); + } + + mutex_unlock(&fimc->lock); + return ret; } static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) @@ -1347,9 +1430,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); - fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, fimc->subdev_pads, 0); if (ret) return ret; @@ -1518,7 +1602,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc); + fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 3081db35c5b0..4576922952f3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -45,8 +45,9 @@ enum { }; #define FLITE_SD_PAD_SINK 0 -#define FLITE_SD_PAD_SOURCE 1 -#define FLITE_SD_PADS_NUM 2 +#define FLITE_SD_PAD_SOURCE_DMA 1 +#define FLITE_SD_PAD_SOURCE_ISP 2 +#define FLITE_SD_PADS_NUM 3 struct flite_variant { unsigned short max_width; @@ -104,6 +105,7 @@ struct flite_buffer { * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads + * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS * @ctrl_handler: v4l2 control handler * @test_pattern: test pattern controls * @index: FIMC-LITE platform device index @@ -139,6 +141,7 @@ struct fimc_lite { struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; u32 index; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d21da4bd24b..1d57f3b87aa3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -300,7 +300,7 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index b4a68ecf0ca7..27d346195dbd 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -62,16 +62,17 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { - case SENSOR_GROUP_ID: + case GRP_ID_FIMC_IS_SENSOR: + case GRP_ID_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; - case CSIS_GROUP_ID: + case GRP_ID_CSIS: p->subdevs[IDX_CSIS] = sd; break; - case FLITE_GROUP_ID: + case GRP_ID_FLITE: p->subdevs[IDX_FLITE] = sd; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; default: @@ -141,7 +142,7 @@ static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) * @me: media entity to start graph walk with * @prep: true to acquire sensor (and csis) subdevs * - * This function must be called with the graph mutex held. + * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct fimc_pipeline *p, struct media_entity *me, bool prep) @@ -161,30 +162,19 @@ static int __fimc_pipeline_open(struct fimc_pipeline *p, return fimc_pipeline_s_power(p, 1); } -static int fimc_pipeline_open(struct fimc_pipeline *p, - struct media_entity *me, bool prep) -{ - int ret; - - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_open(p, me, prep); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** * __fimc_pipeline_close - disable the sensor clock and pipeline power * @fimc: fimc device terminating the pipeline * - * Disable power of all subdevs in the pipeline and turn off the external - * sensor clock. - * Called with the graph mutex held. + * Disable power of all subdevs and turn the external sensor clock off. */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { int ret = 0; + if (!p || !p->subdevs[IDX_SENSOR]) + return -EINVAL; + if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); @@ -192,28 +182,12 @@ static int __fimc_pipeline_close(struct fimc_pipeline *p) return ret == -ENXIO ? 0 : ret; } -static int fimc_pipeline_close(struct fimc_pipeline *p) -{ - struct media_entity *me; - int ret; - - if (!p || !p->subdevs[IDX_SENSOR]) - return -EINVAL; - - me = &p->subdevs[IDX_SENSOR]->entity; - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_close(p); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** - * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * __fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) +static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { int i, ret; @@ -235,9 +209,9 @@ static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ static const struct fimc_pipeline_ops fimc_pipeline_ops = { - .open = fimc_pipeline_open, - .close = fimc_pipeline_close, - .set_stream = fimc_pipeline_s_stream, + .open = __fimc_pipeline_open, + .close = __fimc_pipeline_close, + .set_stream = __fimc_pipeline_s_stream, }; /* @@ -269,7 +243,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); - sd->grp_id = SENSOR_GROUP_ID; + sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", s_info->pdata.board_info->type); @@ -351,7 +325,7 @@ static int fimc_register_callback(struct device *dev, void *p) return 0; sd = &fimc->vid_cap.subdev; - sd->grp_id = FIMC_GROUP_ID; + sd->grp_id = GRP_ID_FIMC; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -374,7 +348,7 @@ static int fimc_lite_register_callback(struct device *dev, void *p) if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) return 0; - fimc->subdev.grp_id = FLITE_GROUP_ID; + fimc->subdev.grp_id = GRP_ID_FLITE; v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); @@ -404,7 +378,7 @@ static int csis_register_callback(struct device *dev, void *p) v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); id = pdev->id < 0 ? 0 : pdev->id; - sd->grp_id = CSIS_GROUP_ID; + sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) @@ -602,7 +576,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, flags); if (ret) break; @@ -626,7 +600,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) */ static int fimc_md_create_links(struct fimc_md *fmd) { - struct v4l2_subdev *csi_sensors[2] = { NULL }; + struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct s5p_fimc_isp_info *pdata; struct fimc_sensor_info *s_info; @@ -658,7 +632,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) "but s5p-csis module is not loaded!\n")) return -EINVAL; - ret = media_entity_create_link(&sensor->entity, 0, + pad = sensor->entity.num_pads - 1; + ret = media_entity_create_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -690,7 +665,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) pad, link_mask); } - for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; source = &fmd->csis[i].sd->entity; @@ -730,7 +705,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); clock = clk_get(NULL, clk_name); - if (IS_ERR_OR_NULL(clock)) { + if (IS_ERR(clock)) { v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", clk_name); return -ENXIO; @@ -820,7 +795,9 @@ static int fimc_md_link_notify(struct media_pad *source, struct fimc_dev *fimc = NULL; struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; + struct mutex *lock; int ret = 0; + int ref_count; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; @@ -828,28 +805,33 @@ static int fimc_md_link_notify(struct media_pad *source, sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { - case FLITE_GROUP_ID: + case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc_lite == NULL)) + return 0; pipeline = &fimc_lite->pipeline; + lock = &fimc_lite->lock; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc == NULL)) + return 0; pipeline = &fimc->pipeline; + lock = &fimc->lock; break; default: return 0; } if (!(flags & MEDIA_LNK_FL_ENABLED)) { + int i; + mutex_lock(lock); ret = __fimc_pipeline_close(pipeline); - pipeline->subdevs[IDX_SENSOR] = NULL; - pipeline->subdevs[IDX_CSIS] = NULL; - - if (fimc) { - mutex_lock(&fimc->lock); + for (i = 0; i < IDX_MAX; i++) + pipeline->subdevs[i] = NULL; + if (fimc) fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); - } + mutex_unlock(lock); return ret; } /* @@ -857,23 +839,15 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - if (fimc) { - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - if (!ret) - ret = fimc_capture_ctrls_create(fimc); - } - mutex_unlock(&fimc->lock); - } else { - mutex_lock(&fimc_lite->lock); - if (fimc_lite->ref_count > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - } - mutex_unlock(&fimc_lite->lock); - } + mutex_lock(lock); + + ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; + if (ref_count > 0) + ret = __fimc_pipeline_open(pipeline, source->entity, true); + if (!ret && fimc) + ret = fimc_capture_ctrls_create(fimc); + + mutex_unlock(lock); return ret ? -EPIPE : ret; } diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index 2d8d41d82620..da7d9922cf58 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -22,11 +22,13 @@ #include "mipi-csis.h" /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define SENSOR_GROUP_ID (1 << 8) -#define CSIS_GROUP_ID (1 << 9) -#define WRITEBACK_GROUP_ID (1 << 10) -#define FIMC_GROUP_ID (1 << 11) -#define FLITE_GROUP_ID (1 << 12) +#define GRP_ID_SENSOR (1 << 8) +#define GRP_ID_FIMC_IS_SENSOR (1 << 9) +#define GRP_ID_WRITEBACK (1 << 10) +#define GRP_ID_CSIS (1 << 11) +#define GRP_ID_FIMC (1 << 12) +#define GRP_ID_FLITE (1 << 13) +#define GRP_ID_FIMC_IS (1 << 14) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 2c9d0c06c9e8..c05d0444192f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -44,9 +44,9 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; - if (ctx->vflip) flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; + if (ctx->vflip) + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -59,9 +59,9 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; - if (ctx->vflip) flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; + if (ctx->vflip) + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -312,7 +312,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct fimc_variant *variant = dev->variant; + const struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; @@ -344,30 +344,31 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) } } -void fimc_hw_en_capture(struct fimc_ctx *ctx) +void fimc_hw_enable_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; + u32 cfg; - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - - if (ctx->out_path == FIMC_IO_DMA) { - /* one shot mode */ - cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_IMGCPTEN; - } else { - /* Continuous frame capture mode (freerun). */ - cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; - } + cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE; if (ctx->scaler.enabled) cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; + else + cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } +void fimc_hw_disable_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | + FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; @@ -737,13 +738,6 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) writel(cfg, dev->regs + FIMC_REG_MSCTRL); } -void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); -} - /* Return an index to the buffer actually being written. */ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { @@ -776,13 +770,13 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) void fimc_activate_capture(struct fimc_ctx *ctx) { fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); + fimc_hw_enable_capture(ctx); } void fimc_deactivate_capture(struct fimc_dev *fimc) { fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); + fimc_hw_disable_capture(fimc); fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index b6abfc7b72ac..f3e0b78a3736 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -287,7 +287,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_enable_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); @@ -306,7 +306,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); -void fimc_hw_dis_capture(struct fimc_dev *dev); +void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); void fimc_activate_capture(struct fimc_ctx *ctx); diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index ec3fa7d75306..cde510f0eea6 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -187,7 +187,7 @@ struct csis_state { const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; - struct spinlock slock; + spinlock_t slock; struct csis_pktbuf pkt_buf; struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; }; @@ -220,6 +220,18 @@ static const struct csis_pix_format s5pcsis_formats[] = { .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), .data_alignment = 32, + }, { + .code = V4L2_MBUS_FMT_SGRBG8_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_RAW8, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .fmt_reg = S5PCSIS_CFG_FMT_RAW10, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG12_1X12, + .fmt_reg = S5PCSIS_CFG_FMT_RAW12, + .data_alignment = 24, } }; @@ -261,7 +273,8 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - u32 val; + struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; + u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); if (on) @@ -271,10 +284,11 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) s5pcsis_write(state, S5PCSIS_CTRL, val); val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); - if (on) - val |= S5PCSIS_DPHYCTRL_ENABLE; - else - val &= ~S5PCSIS_DPHYCTRL_ENABLE; + val &= ~S5PCSIS_DPHYCTRL_ENABLE; + if (on) { + mask = (1 << (pdata->lanes + 1)) - 1; + val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); + } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); } @@ -369,6 +383,30 @@ err: return -ENXIO; } +static void dump_regs(struct csis_state *state, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CTRL" }, + { 0x04, "DPHYCTRL" }, + { 0x08, "CONFIG" }, + { 0x0c, "DPHYSTS" }, + { 0x10, "INTMSK" }, + { 0x2c, "RESOL" }, + { 0x38, "SDW_CONFIG" }, + }; + u32 i; + + v4l2_info(&state->sd, "--- %s ---\n", label); + + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = s5pcsis_read(state, registers[i].offset); + v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); + } +} + static void s5pcsis_start_stream(struct csis_state *state) { s5pcsis_reset(state); @@ -401,12 +439,12 @@ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) spin_lock_irqsave(&state->slock, flags); - for (i--; i >= 0; i--) - if (state->events[i].counter >= 0) + for (i--; i >= 0; i--) { + if (state->events[i].counter > 0 || debug) v4l2_info(&state->sd, "%s events: %d\n", state->events[i].name, state->events[i].counter); - + } spin_unlock_irqrestore(&state->slock, flags); } @@ -569,7 +607,11 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) { struct csis_state *state = sd_to_csis_state(sd); + mutex_lock(&state->lock); s5pcsis_log_counters(state, true); + if (debug && (state->flags & ST_POWERED)) + dump_regs(state, __func__); + mutex_unlock(&state->lock); return 0; } diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 1bfbc325836b..dcd53357704d 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -715,7 +715,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); - if (IS_ERR_OR_NULL(dev->clk)) { + if (IS_ERR(dev->clk)) { dev_err(&pdev->dev, "failed to get g2d clock\n"); return -ENXIO; } @@ -727,7 +727,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->gate = clk_get(&pdev->dev, "fimg2d"); - if (IS_ERR_OR_NULL(dev->gate)) { + if (IS_ERR(dev->gate)) { dev_err(&pdev->dev, "failed to get g2d clock gate\n"); ret = -ENXIO; goto unprep_clk; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 022b9b9baff9..8a4013e3aee7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -62,7 +62,7 @@ */ struct s5p_jpeg { struct mutex lock; - struct spinlock slock; + spinlock_t slock; struct v4l2_device v4l2_dev; struct video_device *vfd_encoder; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 681bc6ba149d..6347f09ca783 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -21,6 +21,7 @@ #include <linux/videodev2.h> #include <media/v4l2-event.h> #include <linux/workqueue.h> +#include <linux/of.h> #include <media/videobuf2-core.h> #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -273,7 +274,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) struct s5p_mfc_buf *dst_buf; size_t dspl_y_addr; unsigned int frame_type; - unsigned int index; dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); @@ -310,7 +310,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) vb2_buffer_done(dst_buf->b, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - index = dst_buf->b->v4l2_buf.index; break; } } @@ -326,8 +325,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, unsigned long flags; unsigned int res_change; - unsigned int index; - dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) @@ -387,7 +384,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, mfc_debug(2, "Running again the same buffer\n"); ctx->after_packed_pb = 1; } else { - index = src_buf->b->v4l2_buf.index; mfc_debug(2, "MFC needs next buffer\n"); ctx->consumed_stream = 0; list_del(&src_buf->list); @@ -676,6 +672,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) s5p_mfc_handle_stream_complete(ctx, reason, err); break; + case S5P_MFC_R2H_CMD_DPB_FLUSH_RET: + clear_work_bit(ctx); + ctx->state = MFCINST_RUNNING; + wake_up(&ctx->queue); + goto irq_cleanup_hw; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); @@ -777,14 +779,16 @@ static int s5p_mfc_open(struct file *file) goto err_pwr_enable; } s5p_mfc_clock_on(); - ret = s5p_mfc_alloc_and_load_firmware(dev); - if (ret) - goto err_alloc_fw; + ret = s5p_mfc_load_firmware(dev); + if (ret) { + s5p_mfc_clock_off(); + goto err_load_fw; + } /* Init the FW */ ret = s5p_mfc_init_hw(dev); + s5p_mfc_clock_off(); if (ret) goto err_init_hw; - s5p_mfc_clock_off(); } /* Init videobuf2 queue for CAPTURE */ q = &ctx->vq_dst; @@ -833,21 +837,20 @@ static int s5p_mfc_open(struct file *file) return ret; /* Deinit when failure occured */ err_queue_init: + if (dev->num_inst == 1) + s5p_mfc_deinit_hw(dev); err_init_hw: - s5p_mfc_release_firmware(dev); -err_alloc_fw: - dev->ctx[ctx->num] = NULL; - del_timer_sync(&dev->watchdog_timer); - s5p_mfc_clock_off(); +err_load_fw: err_pwr_enable: if (dev->num_inst == 1) { if (s5p_mfc_power_off() < 0) mfc_err("power off failed\n"); - s5p_mfc_release_firmware(dev); + del_timer_sync(&dev->watchdog_timer); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); err_bad_node: + dev->ctx[ctx->num] = NULL; err_no_ctx: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -901,11 +904,8 @@ static int s5p_mfc_release(struct file *file) clear_bit(0, &dev->hw_lock); dev->num_inst--; if (dev->num_inst == 0) { - mfc_debug(2, "Last instance - release firmware\n"); - /* reset <-> F/W release */ - s5p_mfc_reset(dev); + mfc_debug(2, "Last instance\n"); s5p_mfc_deinit_hw(dev); - s5p_mfc_release_firmware(dev); del_timer_sync(&dev->watchdog_timer); if (s5p_mfc_power_off() < 0) mfc_err("Power off failed\n"); @@ -1013,6 +1013,8 @@ static int match_child(struct device *dev, void *data) return !strcmp(dev_name(dev), (char *)data); } +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1020,6 +1022,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int ret; + unsigned int mem_info[2]; pr_debug("%s++\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -1036,8 +1039,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) return -ENODEV; } - dev->variant = (struct s5p_mfc_variant *) - platform_get_device_id(pdev)->driver_data; + dev->variant = mfc_get_drv_data(pdev); ret = s5p_mfc_init_pm(dev); if (ret < 0) { @@ -1067,35 +1069,74 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", - match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + if (pdev->dev.of_node) { + dev->mem_dev_l = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_l) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-l", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + mfc_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r", - match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; + dev->mem_dev_r = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_r) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-r", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + pr_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } + } else { + dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-l", match_child); + if (!dev->mem_dev_l) { + mfc_err("Mem child (L) device get failed\n"); + ret = -ENODEV; + goto err_res; + } + dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-r", match_child); + if (!dev->mem_dev_r) { + mfc_err("Mem child (R) device get failed\n"); + ret = -ENODEV; + goto err_res; + } } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); - if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) { + if (IS_ERR(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); goto err_res; } dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); - if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) { + if (IS_ERR(dev->alloc_ctx[1])) { ret = PTR_ERR(dev->alloc_ctx[1]); goto err_mem_init_ctx_1; } mutex_init(&dev->mfc_mutex); + ret = s5p_mfc_alloc_firmware(dev); + if (ret) + goto err_alloc_fw; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) goto err_v4l2_dev_reg; @@ -1177,6 +1218,8 @@ err_dec_reg: err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev_reg: + s5p_mfc_release_firmware(dev); +err_alloc_fw: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); err_mem_init_ctx_1: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); @@ -1202,6 +1245,7 @@ static int s5p_mfc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); + s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); @@ -1352,6 +1396,35 @@ static struct platform_device_id mfc_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, mfc_driver_ids); +static const struct of_device_id exynos_mfc_match[] = { + { + .compatible = "samsung,mfc-v5", + .data = &mfc_drvdata_v5, + }, { + .compatible = "samsung,mfc-v6", + .data = &mfc_drvdata_v6, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_mfc_match); + +static void *mfc_get_drv_data(struct platform_device *pdev) +{ + struct s5p_mfc_variant *driver_data = NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(of_match_ptr(exynos_mfc_match), + pdev->dev.of_node); + if (match) + driver_data = (struct s5p_mfc_variant *)match->data; + } else { + driver_data = (struct s5p_mfc_variant *) + platform_get_device_id(pdev)->driver_data; + } + return driver_data; +} + static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, .remove = s5p_mfc_remove, @@ -1359,7 +1432,8 @@ static struct platform_driver s5p_mfc_driver = { .driver = { .name = S5P_MFC_NAME, .owner = THIS_MODULE, - .pm = &s5p_mfc_pm_ops + .pm = &s5p_mfc_pm_ops, + .of_match_table = exynos_mfc_match, }, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index f02e0497ca98..202d1d7a37a8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -145,6 +145,7 @@ enum s5p_mfc_inst_state { MFCINST_RETURN_INST, MFCINST_ERROR, MFCINST_ABORT, + MFCINST_FLUSH, MFCINST_RES_CHANGE_INIT, MFCINST_RES_CHANGE_FLUSH, MFCINST_RES_CHANGE_END, @@ -277,8 +278,9 @@ struct s5p_mfc_priv_buf { * @int_err: error number for last interrupt * @queue: waitqueue for waiting for completion of device commands * @fw_size: size of firmware - * @bank1: address of the beggining of bank 1 memory - * @bank2: address of the beggining of bank 2 memory + * @fw_virt_addr: virtual firmware address + * @bank1: address of the beginning of bank 1 memory + * @bank2: address of the beginning of bank 2 memory * @hw_lock: used for hardware locking * @ctx: array of driver contexts * @curr_ctx: number of the currently running context @@ -317,8 +319,9 @@ struct s5p_mfc_dev { unsigned int int_err; wait_queue_head_t queue; size_t fw_size; - size_t bank1; - size_t bank2; + void *fw_virt_addr; + dma_addr_t bank1; + dma_addr_t bank2; unsigned long hw_lock; struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; int curr_ctx; @@ -493,15 +496,9 @@ struct s5p_mfc_codec_ops { * flushed * @head_processed: flag mentioning whether the header data is processed * completely or not - * @bank1_buf: handle to memory allocated for temporary buffers from + * @bank1: handle to memory allocated for temporary buffers from * memory bank 1 - * @bank1_phys: address of the temporary buffers from memory bank 1 - * @bank1_size: size of the memory allocated for temporary buffers from - * memory bank 1 - * @bank2_buf: handle to memory allocated for temporary buffers from - * memory bank 2 - * @bank2_phys: address of the temporary buffers from memory bank 2 - * @bank2_size: size of the memory allocated for temporary buffers from + * @bank2: handle to memory allocated for temporary buffers from * memory bank 2 * @capture_state: state of the capture buffers queue * @output_state: state of the output buffers queue @@ -581,14 +578,8 @@ struct s5p_mfc_ctx { unsigned int dpb_flush_flag; unsigned int head_processed; - /* Buffers */ - void *bank1_buf; - size_t bank1_phys; - size_t bank1_size; - - void *bank2_buf; - size_t bank2_phys; - size_t bank2_size; + struct s5p_mfc_priv_buf bank1; + struct s5p_mfc_priv_buf bank2; enum s5p_mfc_queue_state capture_state; enum s5p_mfc_queue_state output_state; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 585b7b0ed8ec..1682271c2453 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -22,16 +22,64 @@ #include "s5p_mfc_opr.h" #include "s5p_mfc_pm.h" -static void *s5p_mfc_bitproc_buf; -static size_t s5p_mfc_bitproc_phys; -static unsigned char *s5p_mfc_bitproc_virt; +/* Allocate memory for firmware */ +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) +{ + void *bank2_virt; + dma_addr_t bank2_dma_addr; + + dev->fw_size = dev->variant->buf_size->fw; + + if (dev->fw_virt_addr) { + mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); + return -ENOMEM; + } + + dev->fw_virt_addr = dma_alloc_coherent(dev->mem_dev_l, dev->fw_size, + &dev->bank1, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + dev->fw_virt_addr = NULL; + mfc_err("Allocating bitprocessor buffer failed\n"); + return -ENOMEM; + } + + dev->bank1 = dev->bank1; + + if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { + bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + &bank2_dma_addr, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + mfc_err("Allocating bank2 base failed\n"); + dma_free_coherent(dev->mem_dev_l, dev->fw_size, + dev->fw_virt_addr, dev->bank1); + dev->fw_virt_addr = NULL; + return -ENOMEM; + } + + /* Valid buffers passed to MFC encoder with LAST_FRAME command + * should not have address of bank2 - MFC will treat it as a null frame. + * To avoid such situation we set bank2 address below the pool address. + */ + dev->bank2 = bank2_dma_addr - (1 << MFC_BASE_ALIGN_ORDER); + + dma_free_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + bank2_virt, bank2_dma_addr); + + } else { + /* In this case bank2 can point to the same address as bank1. + * Firmware will always occupy the beggining of this area so it is + * impossible having a video frame buffer with zero address. */ + dev->bank2 = dev->bank1; + } + return 0; +} -/* Allocate and load firmware */ -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) +/* Load firmware */ +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) { struct firmware *fw_blob; - size_t bank2_base_phys; - void *b_base; int err; /* Firmare has to be present as a separate file or compiled @@ -44,77 +92,17 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; } - dev->fw_size = dev->variant->buf_size->fw; if (fw_blob->size > dev->fw_size) { mfc_err("MFC firmware is too big to be loaded\n"); release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf) { - mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); - if (IS_ERR(s5p_mfc_bitproc_buf)) { - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bitprocessor buffer failed\n"); + if (!dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); - return -ENOMEM; - } - s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf); - if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf); - if (!s5p_mfc_bitproc_virt) { - mfc_err("Bitprocessor memory remap failed\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - dev->bank1 = s5p_mfc_bitproc_phys; - if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { - b_base = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], - 1 << MFC_BASE_ALIGN_ORDER); - if (IS_ERR(b_base)) { - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bank2 base failed\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - bank2_base_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base); - vb2_dma_contig_memops.put(b_base); - if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - /* Valid buffers passed to MFC encoder with LAST_FRAME command - * should not have address of bank2 - MFC will treat it as a null frame. - * To avoid such situation we set bank2 address below the pool address. - */ - dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER); - } else { - dev->bank2 = dev->bank1; + return -EINVAL; } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -142,12 +130,12 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) { - mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); + if (dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); return -EINVAL; } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -159,12 +147,11 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) { /* Before calling this function one has to make sure * that MFC is no longer processing */ - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) return -EINVAL; - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_virt = NULL; - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; + dma_free_coherent(dev->mem_dev_l, dev->fw_size, dev->fw_virt_addr, + dev->bank1); + dev->fw_virt_addr = NULL; return 0; } @@ -257,8 +244,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) int ret; mfc_debug_enter(); - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) { + mfc_err("Firmware memory is not allocated.\n"); return -EINVAL; + } /* 0. MFC reset */ mfc_debug(2, "MFC reset..\n"); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h index 90aa9b9886d5..6a9b6f8606bb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h @@ -16,7 +16,8 @@ #include "s5p_mfc_common.h" int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev); -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_init_hw(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 6dad9a74f61c..4582473978ca 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -991,24 +991,35 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) S5P_MFC_R2H_CMD_FRAME_DONE_RET, 0); aborted = 1; } - spin_lock_irqsave(&dev->irqlock, flags); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, &ctx->vq_dst); INIT_LIST_HEAD(&ctx->dst_queue); ctx->dst_queue_cnt = 0; ctx->dpb_flush_flag = 1; ctx->dec_dst_flag = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) { + ctx->state = MFCINST_FLUSH; + set_work_bit_irqsave(ctx); + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + if (s5p_mfc_wait_for_done_ctx(ctx, + S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0)) + mfc_err("Err flushing buffers\n"); + } } if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, &ctx->vq_src); INIT_LIST_HEAD(&ctx->src_queue); ctx->src_queue_cnt = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); } if (aborted) ctx->state = MFCINST_RUNNING; - spin_unlock_irqrestore(&dev->irqlock, flags); return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 6932e90d4065..b4c194331d8c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include "s5p_mfc_debug.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_opr_v5.h" #include "s5p_mfc_opr_v6.h" @@ -29,3 +30,32 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev) } dev->mfc_ops = s5p_mfc_ops; } + +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + + mfc_debug(3, "Allocating priv: %d\n", b->size); + + b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); + + if (!b->virt) { + mfc_err("Allocating private buffer failed\n"); + return -ENOMEM; + } + + mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma); + return 0; +} + +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + if (b->virt) { + dma_free_coherent(dev, b->size, b->virt, b->dma); + b->virt = 0; + b->dma = 0; + b->size = 0; + } +} + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 420abecafec0..754c540e7a7e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -80,5 +80,10 @@ struct s5p_mfc_hw_ops { }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); + #endif /* S5P_MFC_OPR_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index bf7d010a4107..f61dba837899 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -38,39 +38,26 @@ int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; - ctx->dsc.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - buf_size->dsc); - if (IS_ERR_VALUE((int)ctx->dsc.alloc)) { - ctx->dsc.alloc = NULL; - mfc_err("Allocating DESC buffer failed\n"); - return -ENOMEM; + ctx->dsc.size = buf_size->dsc; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc); + if (ret) { + mfc_err("Failed to allocate temporary buffer\n"); + return ret; } - ctx->dsc.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->dsc.alloc); + BUG_ON(ctx->dsc.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->dsc.virt = vb2_dma_contig_memops.vaddr(ctx->dsc.alloc); - if (ctx->dsc.virt == NULL) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.dma = 0; - ctx->dsc.alloc = NULL; - mfc_err("Remapping DESC buffer failed\n"); - return -ENOMEM; - } - memset(ctx->dsc.virt, 0, buf_size->dsc); + memset(ctx->dsc.virt, 0, ctx->dsc.size); wmb(); return 0; } + /* Release temporary buffers for decoding */ void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->dsc.dma) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.alloc = NULL; - ctx->dsc.dma = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc); } /* Allocate codec buffers */ @@ -80,6 +67,7 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) unsigned int enc_ref_y_size = 0; unsigned int enc_ref_c_size = 0; unsigned int guard_width, guard_height; + int ret; if (ctx->type == MFCINST_DECODER) { mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n", @@ -113,100 +101,93 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Codecs have different memory requirements */ switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_IP_SIZE + S5P_FIMV_DEC_VERT_NB_MV_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size; + ctx->bank2.size = ctx->total_dpb_count * ctx->mv_size; break; case S5P_MFC_CODEC_MPEG4_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_STX_PARSER_SIZE + S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE + 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H264_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_INTRAMD_SIZE + S5P_FIMV_ENC_NBORINFO_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4) + S5P_FIMV_ENC_INTRAPRED_SIZE; break; case S5P_MFC_CODEC_MPEG4_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; case S5P_MFC_CODEC_H263_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = NULL; - printk(KERN_ERR - "Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 temporary buffer\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } /* Allocate only if memory from bank 2 is necessary */ - if (ctx->bank2_size > 0) { - ctx->bank2_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size); - if (IS_ERR(ctx->bank2_buf)) { - ctx->bank2_buf = NULL; - mfc_err("Buf alloc for decoding failed (port B)\n"); - return -ENOMEM; + if (ctx->bank2.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2); + if (ret) { + mfc_err("Failed to allocate Bank2 temporary buffer\n"); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + return ret; } - ctx->bank2_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf); - BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank2.dma & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); } return 0; } @@ -214,18 +195,8 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = NULL; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } - if (ctx->bank2_buf) { - vb2_dma_contig_memops.put(ctx->bank2_buf); - ctx->bank2_buf = NULL; - ctx->bank2_phys = 0; - ctx->bank2_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2); } /* Allocate memory for instance data buffer */ @@ -233,58 +204,38 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_ENC) ctx->ctx.size = buf_size->h264_ctx; else ctx->ctx.size = buf_size->non_h264_ctx; - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed\n"); - ctx->ctx.alloc = NULL; - return -ENOMEM; + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - BUG_ON(ctx->ctx.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); ctx->ctx.ofs = OFFSETA(ctx->ctx.dma); - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - mfc_err("Remapping instance buffer failed\n"); - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.dma = 0; - return -ENOMEM; - } + /* Zero content of the allocated memory */ memset(ctx->ctx.virt, 0, ctx->ctx.size); wmb(); /* Initialize shared memory */ - ctx->shm.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->shm); - if (IS_ERR(ctx->shm.alloc)) { - mfc_err("failed to allocate shared memory\n"); - return PTR_ERR(ctx->shm.alloc); + ctx->shm.size = buf_size->shm; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); + if (ret) { + mfc_err("Failed to allocate shared memory buffer\n"); + return ret; } + /* shared memory offset only keeps the offset from base (port a) */ - ctx->shm.ofs = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->shm.alloc) - - dev->bank1; + ctx->shm.ofs = ctx->shm.dma - dev->bank1; BUG_ON(ctx->shm.ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->shm.virt = vb2_dma_contig_memops.vaddr(ctx->shm.alloc); - if (!ctx->shm.virt) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - mfc_err("failed to virt addr of shared memory\n"); - return -ENOMEM; - } - memset((void *)ctx->shm.virt, 0, buf_size->shm); + memset(ctx->shm.virt, 0, buf_size->shm); wmb(); return 0; } @@ -292,19 +243,8 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.virt = NULL; - ctx->ctx.dma = 0; - } - if (ctx->shm.alloc) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - ctx->shm.virt = NULL; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm); } int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) @@ -443,10 +383,10 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) size_t buf_addr1, buf_addr2; int buf_size1, buf_size2; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~S5P_FIMV_DPB_COUNT_MASK; mfc_write(dev, ctx->total_dpb_count | dpb, @@ -523,7 +463,6 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) mfc_err("Unknown codec for decoding (%x)\n", ctx->codec_mode); return -EINVAL; - break; } frame_size = ctx->luma_size; frame_size_ch = ctx->chroma_size; @@ -607,10 +546,10 @@ int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) unsigned int guard_width, guard_height; int i; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN); enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 3a8cfd9fc1bd..beb6dbacebd9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -73,6 +73,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; + int ret; mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); @@ -112,7 +113,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + (ctx->mv_count * ctx->mv_size); break; @@ -123,7 +124,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: @@ -133,11 +134,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: ctx->scratch_buf_size = @@ -146,7 +147,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: ctx->scratch_buf_size = @@ -155,7 +156,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_H264_ENC: ctx->scratch_buf_size = @@ -164,11 +165,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: @@ -178,28 +179,24 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = 0; - pr_err("Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 memory\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } return 0; @@ -208,12 +205,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = 0; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); } /* Allocate memory for instance data buffer */ @@ -221,6 +213,7 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); @@ -250,25 +243,10 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; } - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed.\n"); - return PTR_ERR(ctx->ctx.alloc); - } - - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - - mfc_err("Remapping context buffer failed.\n"); - return -ENOMEM; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } memset(ctx->ctx.virt, 0, ctx->ctx.size); @@ -282,44 +260,22 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { - mfc_debug_enter(); - - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - } - - mfc_debug_leave(); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); } /* Allocate context buffers for SYS_INIT */ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); - dev->ctx_buf.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->dev_ctx); - if (IS_ERR(dev->ctx_buf.alloc)) { - mfc_err("Allocating DESC buffer failed.\n"); - return PTR_ERR(dev->ctx_buf.alloc); - } - - dev->ctx_buf.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - dev->ctx_buf.alloc); - - dev->ctx_buf.virt = vb2_dma_contig_memops.vaddr(dev->ctx_buf.alloc); - if (!dev->ctx_buf.virt) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - - mfc_err("Remapping DESC buffer failed.\n"); - return -ENOMEM; + dev->ctx_buf.size = buf_size->dev_ctx; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf); + if (ret) { + mfc_err("Failed to allocate device context buffer\n"); + return ret; } memset(dev->ctx_buf.virt, 0, buf_size->dev_ctx); @@ -333,12 +289,7 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) /* Release context buffers for SYS_INIT */ void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { - if (dev->ctx_buf.alloc) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - dev->ctx_buf.virt = NULL; - } + s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf); } static int calc_plane(int width, int height) @@ -417,8 +368,8 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) int buf_size1; int align_gap; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count); @@ -535,13 +486,13 @@ void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - size_t buf_addr1, buf_size1; - int i; + size_t buf_addr1; + int i, buf_size1; mfc_debug_enter(); - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); @@ -1253,12 +1204,14 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) { struct s5p_mfc_dev *dev = ctx->dev; - unsigned int dpb; - if (flush) - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (1 << 14); - else - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(1 << 14); - WRITEL(dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + if (flush) { + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + S5P_FIMV_H2R_CMD_FLUSH_V6, NULL); + } } /* Decode a single frame */ @@ -1408,7 +1361,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) struct s5p_mfc_buf *temp_vb; unsigned long flags; int last_frame = 0; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1427,8 +1379,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); - index = temp_vb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); if (temp_vb->b->v4l2_planes[0].bytesused == 0) { @@ -1452,7 +1402,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) unsigned int src_y_size, src_c_size; */ unsigned int dst_size; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1487,8 +1436,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) spin_unlock_irqrestore(&dev->irqlock, flags); - index = src_mb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); s5p_mfc_encode_one_frame_v6(ctx); @@ -1656,6 +1603,9 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) case MFCINST_HEAD_PARSED: ret = s5p_mfc_run_init_dec_buffers(ctx); break; + case MFCINST_FLUSH: + s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag); + break; case MFCINST_RES_CHANGE_INIT: s5p_mfc_run_dec_last_frames(ctx); break; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 2895333866fc..6aa38a56aaf2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -46,7 +46,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) ret = clk_prepare(pm->clock_gate); if (ret) { - mfc_err("Failed to preapre clock-gating control\n"); + mfc_err("Failed to prepare clock-gating control\n"); goto err_p_ip_clk; } diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 7c1116c73bf3..8de1b3dce459 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -656,7 +656,7 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - memset(fmt, 0, sizeof *fmt); + memset(fmt, 0, sizeof(*fmt)); fmt->width = t->hact.end - t->hact.beg; fmt->height = t->vact[0].end - t->vact[0].beg; fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ @@ -760,7 +760,7 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev) clk_put(res->sclk_hdmi); if (!IS_ERR_OR_NULL(res->hdmi)) clk_put(res->hdmi); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); } static int hdmi_resources_init(struct hdmi_device *hdev) @@ -777,31 +777,31 @@ static int hdmi_resources_init(struct hdmi_device *hdev) dev_dbg(dev, "HDMI resource init\n"); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR_OR_NULL(res->hdmi)) { + if (IS_ERR(res->hdmi)) { dev_err(dev, "failed to get clock 'hdmi'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR_OR_NULL(res->sclk_pixel)) { + if (IS_ERR(res->sclk_pixel)) { dev_err(dev, "failed to get clock 'sclk_pixel'\n"); goto fail; } res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { + if (IS_ERR(res->sclk_hdmiphy)) { dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); goto fail; } res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR_OR_NULL(res->hdmiphy)) { + if (IS_ERR(res->hdmiphy)) { dev_err(dev, "failed to get clock 'hdmiphy'\n"); goto fail; } @@ -955,7 +955,7 @@ static int hdmi_probe(struct platform_device *pdev) v4l2_subdev_init(sd, &hdmi_sd_ops); sd->owner = THIS_MODULE; - strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); + strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index 06b5d2dbb2d9..80717cec76ae 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -284,7 +284,7 @@ static int hdmiphy_probe(struct i2c_client *client, { struct hdmiphy_ctx *ctx; - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index 02faea03aa7d..5733033a6ead 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -240,27 +240,27 @@ static int mxr_acquire_clocks(struct mxr_device *mdev) struct device *dev = mdev->dev; res->mixer = clk_get(dev, "mixer"); - if (IS_ERR_OR_NULL(res->mixer)) { + if (IS_ERR(res->mixer)) { mxr_err(mdev, "failed to get clock 'mixer'\n"); goto fail; } res->vp = clk_get(dev, "vp"); - if (IS_ERR_OR_NULL(res->vp)) { + if (IS_ERR(res->vp)) { mxr_err(mdev, "failed to get clock 'vp'\n"); goto fail; } res->sclk_mixer = clk_get(dev, "sclk_mixer"); - if (IS_ERR_OR_NULL(res->sclk_mixer)) { + if (IS_ERR(res->sclk_mixer)) { mxr_err(mdev, "failed to get clock 'sclk_mixer'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(res->sclk_dac)) { + if (IS_ERR(res->sclk_dac)) { mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); goto fail; } @@ -298,7 +298,7 @@ static void mxr_release_resources(struct mxr_device *mdev) { mxr_release_clocks(mdev); mxr_release_plat_resources(mdev); - memset(&mdev->res, 0, sizeof mdev->res); + memset(&mdev->res, 0, sizeof(mdev->res)); } static void mxr_release_layers(struct mxr_device *mdev) @@ -382,7 +382,7 @@ static int mxr_probe(struct platform_device *pdev) /* mdev does not exist yet so no mxr_dbg is used */ dev_info(dev, "probe start\n"); - mdev = kzalloc(sizeof *mdev, GFP_KERNEL); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c index 3b1670a045f4..b713403024ef 100644 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/drivers/media/platform/s5p-tv/mixer_reg.c @@ -470,11 +470,11 @@ static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, static void mxr_reg_vp_default_filter(struct mxr_device *mdev) { mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL, - filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); + filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8)); mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, - filter_y_vert_tap4, sizeof filter_y_vert_tap4); + filter_y_vert_tap4, sizeof(filter_y_vert_tap4)); mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL, - filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); + filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); } static void mxr_reg_mxr_dump(struct mxr_device *mdev) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 1f3b7436511c..c087b66099fd 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -95,7 +95,7 @@ int mxr_acquire_video(struct mxr_device *mdev, /* trying to register next output */ if (sd == NULL) continue; - out = kzalloc(sizeof *out, GFP_KERNEL); + out = kzalloc(sizeof(*out), GFP_KERNEL); if (out == NULL) { mxr_err(mdev, "no memory for '%s'\n", conf->output_name); @@ -127,7 +127,7 @@ fail_output: /* kfree is NULL-safe */ for (i = 0; i < mdev->output_cnt; ++i) kfree(mdev->output[i]); - memset(mdev->output, 0, sizeof mdev->output); + memset(mdev->output, 0, sizeof(mdev->output)); fail_vb2_allocator: /* freeing allocator context */ @@ -160,8 +160,8 @@ static int mxr_querycap(struct file *file, void *priv, mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); - strlcpy(cap->card, layer->vfd.name, sizeof cap->card); + strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, layer->vfd.name, sizeof(cap->card)); sprintf(cap->bus_info, "%d", layer->idx); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -192,7 +192,7 @@ static void mxr_layer_default_geo(struct mxr_layer *layer) struct mxr_device *mdev = layer->mdev; struct v4l2_mbus_framefmt mbus_fmt; - memset(&layer->geo, 0, sizeof layer->geo); + memset(&layer->geo, 0, sizeof(layer->geo)); mxr_get_mbus_fmt(mdev, &mbus_fmt); @@ -425,7 +425,7 @@ static int mxr_s_selection(struct file *file, void *fh, struct mxr_geometry tmp; struct v4l2_rect res; - memset(&res, 0, sizeof res); + memset(&res, 0, sizeof(res)); mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, s->r.width, s->r.height, s->r.left, s->r.top); @@ -464,7 +464,7 @@ static int mxr_s_selection(struct file *file, void *fh, /* apply change and update geometry if needed */ if (target) { /* backup current geometry if setup fails */ - memcpy(&tmp, geo, sizeof tmp); + memcpy(&tmp, geo, sizeof(tmp)); /* apply requested selection */ target->x_offset = s->r.left; @@ -496,7 +496,7 @@ static int mxr_s_selection(struct file *file, void *fh, fail: /* restore old geometry, which is not touched if target is NULL */ if (target) - memcpy(geo, &tmp, sizeof tmp); + memcpy(geo, &tmp, sizeof(tmp)); return -ERANGE; } @@ -1071,7 +1071,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, { struct mxr_layer *layer; - layer = kzalloc(sizeof *layer, GFP_KERNEL); + layer = kzalloc(sizeof(*layer), GFP_KERNEL); if (layer == NULL) { mxr_err(mdev, "not enough memory for layer.\n"); goto fail; diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 91a6939a270a..ab6f9ef89423 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -301,7 +301,7 @@ static int sdo_probe(struct platform_device *pdev) struct clk *sclk_vpll; dev_info(dev, "probe start\n"); - sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; @@ -341,47 +341,50 @@ static int sdo_probe(struct platform_device *pdev) /* acquire clocks */ sdev->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(sdev->sclk_dac)) { + if (IS_ERR(sdev->sclk_dac)) { dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->sclk_dac); goto fail; } sdev->dac = clk_get(dev, "dac"); - if (IS_ERR_OR_NULL(sdev->dac)) { + if (IS_ERR(sdev->dac)) { dev_err(dev, "failed to get clock 'dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dac); goto fail_sclk_dac; } sdev->dacphy = clk_get(dev, "dacphy"); - if (IS_ERR_OR_NULL(sdev->dacphy)) { + if (IS_ERR(sdev->dacphy)) { dev_err(dev, "failed to get clock 'dacphy'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dacphy); goto fail_dac; } sclk_vpll = clk_get(dev, "sclk_vpll"); - if (IS_ERR_OR_NULL(sclk_vpll)) { + if (IS_ERR(sclk_vpll)) { dev_err(dev, "failed to get clock 'sclk_vpll'\n"); - ret = -ENXIO; + ret = PTR_ERR(sclk_vpll); goto fail_dacphy; } clk_set_parent(sdev->sclk_dac, sclk_vpll); clk_put(sclk_vpll); sdev->fout_vpll = clk_get(dev, "fout_vpll"); - if (IS_ERR_OR_NULL(sdev->fout_vpll)) { + if (IS_ERR(sdev->fout_vpll)) { dev_err(dev, "failed to get clock 'fout_vpll'\n"); + ret = PTR_ERR(sdev->fout_vpll); goto fail_dacphy; } dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll)); /* acquire regulator */ sdev->vdac = devm_regulator_get(dev, "vdd33a_dac"); - if (IS_ERR_OR_NULL(sdev->vdac)) { + if (IS_ERR(sdev->vdac)) { dev_err(dev, "failed to get regulator 'vdac'\n"); + ret = PTR_ERR(sdev->vdac); goto fail_fout_vpll; } sdev->vdet = devm_regulator_get(dev, "vdet"); - if (IS_ERR_OR_NULL(sdev->vdet)) { + if (IS_ERR(sdev->vdet)) { dev_err(dev, "failed to get regulator 'vdet'\n"); + ret = PTR_ERR(sdev->vdet); goto fail_fout_vpll; } @@ -394,7 +397,7 @@ static int sdo_probe(struct platform_device *pdev) /* configuration of interface subdevice */ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); sdev->sd.owner = THIS_MODULE; - strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); + strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name)); /* set default format */ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 49191aac9634..d90d2286090b 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -338,7 +338,7 @@ static int sii9234_probe(struct i2c_client *client, } ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); if (ret) { dev_err(dev, "failed to acquire MHL_RST gpio\n"); return ret; @@ -370,7 +370,6 @@ fail_pm_get: fail_pm: pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); fail: dev_err(dev, "probe failed\n"); @@ -381,11 +380,8 @@ fail: static int sii9234_remove(struct i2c_client *client) { struct device *dev = &client->dev; - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sii9234_context *ctx = sd_to_context(sd); pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); dev_info(dev, "remove successful\n"); diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c new file mode 100644 index 000000000000..cb54c69d5748 --- /dev/null +++ b/drivers/media/platform/sh_veu.c @@ -0,0 +1,1266 @@ +/* + * sh-mobile VEU mem2mem driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> + * Copyright (C) 2008 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#define VEU_STR 0x00 /* start register */ +#define VEU_SWR 0x10 /* src: line length */ +#define VEU_SSR 0x14 /* src: image size */ +#define VEU_SAYR 0x18 /* src: y/rgb plane address */ +#define VEU_SACR 0x1c /* src: c plane address */ +#define VEU_BSSR 0x20 /* bundle mode register */ +#define VEU_EDWR 0x30 /* dst: line length */ +#define VEU_DAYR 0x34 /* dst: y/rgb plane address */ +#define VEU_DACR 0x38 /* dst: c plane address */ +#define VEU_TRCR 0x50 /* transform control */ +#define VEU_RFCR 0x54 /* resize scale */ +#define VEU_RFSR 0x58 /* resize clip */ +#define VEU_ENHR 0x5c /* enhance */ +#define VEU_FMCR 0x70 /* filter mode */ +#define VEU_VTCR 0x74 /* lowpass vertical */ +#define VEU_HTCR 0x78 /* lowpass horizontal */ +#define VEU_APCR 0x80 /* color match */ +#define VEU_ECCR 0x84 /* color replace */ +#define VEU_AFXR 0x90 /* fixed mode */ +#define VEU_SWPR 0x94 /* swap */ +#define VEU_EIER 0xa0 /* interrupt mask */ +#define VEU_EVTR 0xa4 /* interrupt event */ +#define VEU_STAR 0xb0 /* status */ +#define VEU_BSRR 0xb4 /* reset */ + +#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */ +#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */ +#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */ +#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */ +#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */ +#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */ +#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */ +#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */ +#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */ +#define VEU_COFFR 0x224 /* color conversion offset */ +#define VEU_CBR 0x228 /* color conversion clip */ + +/* + * 4092x4092 max size is the normal case. In some cases it can be reduced to + * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188. + */ +#define MAX_W 4092 +#define MAX_H 4092 +#define MIN_W 8 +#define MIN_H 8 +#define ALIGN_W 4 + +/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */ +#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024) + +#define MEM2MEM_DEF_TRANSLEN 1 + +struct sh_veu_dev; + +struct sh_veu_file { + struct sh_veu_dev *veu_dev; + bool cfg_needed; +}; + +struct sh_veu_format { + char *name; + u32 fourcc; + unsigned int depth; + unsigned int ydepth; +}; + +/* video data format */ +struct sh_veu_vfmt { + /* Replace with v4l2_rect */ + struct v4l2_rect frame; + unsigned int bytesperline; + unsigned int offset_y; + unsigned int offset_c; + const struct sh_veu_format *fmt; +}; + +struct sh_veu_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct device *dev; + struct v4l2_m2m_ctx *m2m_ctx; + struct sh_veu_vfmt vfmt_out; + struct sh_veu_vfmt vfmt_in; + /* Only single user per direction so far */ + struct sh_veu_file *capture; + struct sh_veu_file *output; + struct mutex fop_lock; + void __iomem *base; + struct vb2_alloc_ctx *alloc_ctx; + spinlock_t lock; + bool is_2h; + unsigned int xaction; + bool aborting; +}; + +enum sh_veu_fmt_idx { + SH_VEU_FMT_NV12, + SH_VEU_FMT_NV16, + SH_VEU_FMT_NV24, + SH_VEU_FMT_RGB332, + SH_VEU_FMT_RGB444, + SH_VEU_FMT_RGB565, + SH_VEU_FMT_RGB666, + SH_VEU_FMT_RGB24, +}; + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +#define DEFAULT_IN_WIDTH VGA_WIDTH +#define DEFAULT_IN_HEIGHT VGA_HEIGHT +#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12 +#define DEFAULT_OUT_WIDTH VGA_WIDTH +#define DEFAULT_OUT_HEIGHT VGA_HEIGHT +#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565 + +/* + * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte + * aligned for NV24. + */ +static const struct sh_veu_format sh_veu_fmt[] = { + [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .name = "NV12", .fourcc = V4L2_PIX_FMT_NV12 }, + [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .name = "NV16", .fourcc = V4L2_PIX_FMT_NV16 }, + [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .name = "NV24", .fourcc = V4L2_PIX_FMT_NV24 }, + [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .name = "RGB332", .fourcc = V4L2_PIX_FMT_RGB332 }, + [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444 }, + [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565 }, + [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666 }, + [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .name = "RGB24", .fourcc = V4L2_PIX_FMT_RGB24 }, +}; + +#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \ +} + +#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \ +} + +/* + * TODO: add support for further output formats: + * SH_VEU_FMT_NV12, + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB332, + * SH_VEU_FMT_RGB444, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ + +static const int sh_veu_fmt_out[] = { + SH_VEU_FMT_RGB565, +}; + +/* + * TODO: add support for further input formats: + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB565, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ +static const int sh_veu_fmt_in[] = { + SH_VEU_FMT_NV12, +}; + +static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) +{ + switch (fourcc) { + default: + BUG(); + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + return V4L2_COLORSPACE_JPEG; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + return V4L2_COLORSPACE_SRGB; + } +} + +static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg) +{ + return ioread32(veu->base + reg); +} + +static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg, + u32 value) +{ + iowrite32(value, veu->base + reg); +} + + /* ========== mem2mem callbacks ========== */ + +static void sh_veu_job_abort(void *priv) +{ + struct sh_veu_dev *veu = priv; + + /* Will cancel the transaction in the next interrupt handler */ + veu->aborting = true; +} + +static void sh_veu_lock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_lock(&veu->fop_lock); +} + +static void sh_veu_unlock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_unlock(&veu->fop_lock); +} + +static void sh_veu_process(struct sh_veu_dev *veu, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + + sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); + sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? + addr + veu->vfmt_out.offset_c : 0); + dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); + sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? + addr + veu->vfmt_in.offset_c : 0); + dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + + sh_veu_reg_write(veu, VEU_STR, 1); + + sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */ +} + +/** + * sh_veu_device_run() - prepares and starts the device + * + * This will be called by the framework when it decides to schedule a particular + * instance. + */ +static void sh_veu_device_run(void *priv) +{ + struct sh_veu_dev *veu = priv; + struct vb2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); + + if (src_buf && dst_buf) + sh_veu_process(veu, src_buf, dst_buf); +} + + /* ========== video ioctls ========== */ + +static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + veu_file == veu->capture) || + (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + veu_file == veu->output); +} + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +/* + * It is not unusual to have video nodes open()ed multiple times. While some + * V4L2 operations are non-intrusive, like querying formats and various + * parameters, others, like setting formats, starting and stopping streaming, + * queuing and dequeuing buffers, directly affect hardware configuration and / + * or execution. This function verifies availability of the requested interface + * and, if available, reserves it for the requesting user. + */ +static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + struct sh_veu_file **stream; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + stream = &veu->capture; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + stream = &veu->output; + break; + default: + return -EINVAL; + } + + if (*stream == veu_file) + return 0; + + if (*stream) + return -EBUSY; + + *stream = veu_file; + + return 0; +} + +static int sh_veu_context_init(struct sh_veu_dev *veu) +{ + if (veu->m2m_ctx) + return 0; + + veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, + sh_veu_queue_init); + + if (IS_ERR(veu->m2m_ctx)) + return PTR_ERR(veu->m2m_ctx); + + return 0; +} + +static int sh_veu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "sh-veu", sizeof(cap->driver)); + strlcpy(cap->card, "sh-mobile VEU", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num) +{ + if (f->index >= fmt_num) + return -EINVAL; + + strlcpy(f->description, sh_veu_fmt[fmt[f->index]].name, sizeof(f->description)); + f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc; + return 0; +} + +static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out)); +} + +static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in)); +} + +static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &veu->vfmt_out; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &veu->vfmt_in; + default: + return NULL; + } +} + +static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + + vfmt = sh_veu_get_vfmt(veu, f->type); + + pix->width = vfmt->frame.width; + pix->height = vfmt->frame.height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = vfmt->fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->bytesperline = vfmt->bytesperline; + pix->sizeimage = vfmt->bytesperline * pix->height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + pix->priv = 0; + dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, + f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); + + return 0; +} + +static int sh_veu_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int y_bytes_used; + + /* + * V4L2 specification suggests, that the driver should correct the + * format struct if any of the dimensions is unsupported + */ + switch (pix->field) { + default: + case V4L2_FIELD_ANY: + pix->field = V4L2_FIELD_NONE; + /* fall through: continue handling V4L2_FIELD_NONE */ + case V4L2_FIELD_NONE: + break; + } + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W, + &pix->height, MIN_H, MAX_H, 0, 0); + + y_bytes_used = (pix->width * fmt->ydepth) >> 3; + + if (pix->bytesperline < y_bytes_used) + pix->bytesperline = y_bytes_used; + pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth; + + pix->pixelformat = fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->priv = 0; + + pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); + + return 0; +} + +static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f) +{ + const int *fmt; + int i, n, dflt; + + pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt = sh_veu_fmt_out; + n = ARRAY_SIZE(sh_veu_fmt_out); + dflt = DEFAULT_OUT_FMTIDX; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + default: + fmt = sh_veu_fmt_in; + n = ARRAY_SIZE(sh_veu_fmt_in); + dflt = DEFAULT_IN_FMTIDX; + break; + } + + for (i = 0; i < n; i++) + if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat) + return &sh_veu_fmt[fmt[i]]; + + return &sh_veu_fmt[dflt]; +} + +static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static int sh_veu_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt) +{ + /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ + unsigned int left = vfmt->frame.left & ~0x03; + unsigned int top = vfmt->frame.top; + dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) + + top * veu->vfmt_out.bytesperline; + unsigned int y_line; + + vfmt->offset_y = offset; + + switch (vfmt->fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + y_line = ALIGN(vfmt->frame.width, 16); + vfmt->offset_c = offset + y_line * vfmt->frame.height; + break; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + vfmt->offset_c = 0; + break; + default: + BUG(); + } +} + +static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + struct vb2_queue *vq; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + vfmt = sh_veu_get_vfmt(veu, f->type); + /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */ + + vfmt->fmt = sh_veu_find_fmt(f); + /* vfmt->fmt != NULL following the same argument as above */ + vfmt->frame.width = pix->width; + vfmt->frame.height = pix->height; + vfmt->bytesperline = pix->bytesperline; + + sh_veu_colour_offset(veu, vfmt); + + /* + * We could also verify and require configuration only if any parameters + * actually have changed, but it is unlikely, that the user requests the + * same configuration several times without closing the device. + */ + veu_file->cfg_needed = true; + + dev_dbg(veu->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %x\n", + f->type, pix->width, pix->height, vfmt->fmt->fourcc); + + return 0; +} + +static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct sh_veu_file *veu_file = priv; + struct sh_veu_dev *veu = veu_file->veu_dev; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + ret = sh_veu_stream_init(veu, veu_file, reqbufs->type); + if (ret < 0) + return ret; + + return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs); +} + +static int sh_veu_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static void sh_veu_calc_scale(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out, + u32 *mant, u32 *frac, u32 *rep) +{ + u32 fixpoint; + + /* calculate FRAC and MANT */ + *rep = *mant = *frac = 0; + + if (size_in == size_out) { + if (crop_out != size_out) + *mant = 1; /* needed for cropping */ + return; + } + + /* VEU2H special upscale */ + if (veu->is_2h && size_out > size_in) { + u32 fixpoint = (4096 * size_in) / size_out; + *mant = fixpoint / 4096; + *frac = (fixpoint - (*mant * 4096)) & ~0x07; + + switch (*frac) { + case 0x800: + *rep = 1; + break; + case 0x400: + *rep = 3; + break; + case 0x200: + *rep = 7; + break; + } + if (*rep) + return; + } + + fixpoint = (4096 * (size_in - 1)) / (size_out + 1); + *mant = fixpoint / 4096; + *frac = fixpoint - (*mant * 4096); + + if (*frac & 0x07) { + /* + * FIXME: do we really have to round down twice in the + * up-scaling case? + */ + *frac &= ~0x07; + if (size_out > size_in) + *frac -= 8; /* round down if scaling up */ + else + *frac += 8; /* round up if scaling down */ + } +} + +static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) | + (((mant << 12) | frac) << 16); + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) | + (((rep << 12) | crop_out) << 16); + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) | + (mant << 12) | frac; + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) | + (rep << 12) | crop_out; + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static void sh_veu_configure(struct sh_veu_dev *veu) +{ + u32 src_width, src_stride, src_height; + u32 dst_width, dst_stride, dst_height; + u32 real_w, real_h; + + /* reset VEU */ + sh_veu_reg_write(veu, VEU_BSRR, 0x100); + + src_width = veu->vfmt_in.frame.width; + src_height = veu->vfmt_in.frame.height; + src_stride = ALIGN(veu->vfmt_in.frame.width, 16); + + dst_width = real_w = veu->vfmt_out.frame.width; + dst_height = real_h = veu->vfmt_out.frame.height; + /* Datasheet is unclear - whether it's always number of bytes or not */ + dst_stride = veu->vfmt_out.bytesperline; + + /* + * So far real_w == dst_width && real_h == dst_height, but it wasn't + * necessarily the case in the original vidix driver, so, it may change + * here in the future too. + */ + src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width); + src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height); + + sh_veu_reg_write(veu, VEU_SWR, src_stride); + sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16)); + sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */ + + sh_veu_reg_write(veu, VEU_EDWR, dst_stride); + sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */ + + sh_veu_reg_write(veu, VEU_SWPR, 0x67); + sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4); + + if (veu->is_2h) { + sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5); + sh_veu_reg_write(veu, VEU_MCR01, 0x0950); + sh_veu_reg_write(veu, VEU_MCR02, 0x0000); + + sh_veu_reg_write(veu, VEU_MCR10, 0x397f); + sh_veu_reg_write(veu, VEU_MCR11, 0x0950); + sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd); + + sh_veu_reg_write(veu, VEU_MCR20, 0x0000); + sh_veu_reg_write(veu, VEU_MCR21, 0x0950); + sh_veu_reg_write(veu, VEU_MCR22, 0x1023); + + sh_veu_reg_write(veu, VEU_COFFR, 0x00800010); + } +} + +static int sh_veu_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + if (veu_file->cfg_needed) { + struct sh_veu_dev *veu = veu_file->veu_dev; + veu_file->cfg_needed = false; + sh_veu_configure(veu_file->veu_dev); + veu->xaction = 0; + veu->aborting = false; + } + + return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type); +} + +static int sh_veu_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { + .vidioc_querycap = sh_veu_querycap, + + .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out, + + .vidioc_reqbufs = sh_veu_reqbufs, + .vidioc_querybuf = sh_veu_querybuf, + + .vidioc_qbuf = sh_veu_qbuf, + .vidioc_dqbuf = sh_veu_dqbuf, + + .vidioc_streamon = sh_veu_streamon, + .vidioc_streamoff = sh_veu_streamoff, +}; + + /* ========== Queue operations ========== */ + +static int sh_veu_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *f, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vq); + struct sh_veu_vfmt *vfmt; + unsigned int size, count = *nbuffers; + + if (f) { + const struct v4l2_pix_format *pix = &f->fmt.pix; + const struct sh_veu_format *fmt = sh_veu_find_fmt(f); + struct v4l2_format ftmp = *f; + + if (fmt->fourcc != pix->pixelformat) + return -EINVAL; + sh_veu_try_fmt(&ftmp, fmt); + if (ftmp.fmt.pix.width != pix->width || + ftmp.fmt.pix.height != pix->height) + return -EINVAL; + size = pix->bytesperline ? pix->bytesperline * pix->height : + pix->width * pix->height * fmt->depth >> 3; + } else { + vfmt = sh_veu_get_vfmt(veu, vq->type); + size = vfmt->bytesperline * vfmt->frame.height; + } + + if (count < 2) + *nbuffers = count = 2; + + if (size * count > VIDEO_MEM_LIMIT) { + count = VIDEO_MEM_LIMIT / size; + *nbuffers = count; + } + + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = veu->alloc_ctx; + + dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int sh_veu_buf_prepare(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + struct sh_veu_vfmt *vfmt; + unsigned int sizeimage; + + vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type); + sizeimage = vfmt->bytesperline * vfmt->frame.height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + + if (vb2_plane_size(vb, 0) < sizeimage) { + dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, sizeimage); + + return 0; +} + +static void sh_veu_buf_queue(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type); + v4l2_m2m_buf_queue(veu->m2m_ctx, vb); +} + +static void sh_veu_wait_prepare(struct vb2_queue *q) +{ + sh_veu_unlock(vb2_get_drv_priv(q)); +} + +static void sh_veu_wait_finish(struct vb2_queue *q) +{ + sh_veu_lock(vb2_get_drv_priv(q)); +} + +static const struct vb2_ops sh_veu_qops = { + .queue_setup = sh_veu_queue_setup, + .buf_prepare = sh_veu_buf_prepare, + .buf_queue = sh_veu_buf_queue, + .wait_prepare = sh_veu_wait_prepare, + .wait_finish = sh_veu_wait_finish, +}; + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = priv; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &sh_veu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret < 0) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = priv; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &sh_veu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + + /* ========== File operations ========== */ + +static int sh_veu_open(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file; + + veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL); + if (!veu_file) + return -ENOMEM; + + veu_file->veu_dev = veu; + veu_file->cfg_needed = true; + + file->private_data = veu_file; + + pm_runtime_get_sync(veu->dev); + + dev_dbg(veu->dev, "Created instance %p\n", veu_file); + + return 0; +} + +static int sh_veu_release(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file = file->private_data; + + dev_dbg(veu->dev, "Releasing instance %p\n", veu_file); + + pm_runtime_put(veu->dev); + + if (veu_file == veu->capture) { + veu->capture = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)); + } + + if (veu_file == veu->output) { + veu->output = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT)); + } + + if (!veu->output && !veu->capture && veu->m2m_ctx) { + v4l2_m2m_ctx_release(veu->m2m_ctx); + veu->m2m_ctx = NULL; + } + + kfree(veu_file); + + return 0; +} + +static unsigned int sh_veu_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait); +} + +static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma); +} + +static const struct v4l2_file_operations sh_veu_fops = { + .owner = THIS_MODULE, + .open = sh_veu_open, + .release = sh_veu_release, + .poll = sh_veu_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = sh_veu_mmap, +}; + +static const struct video_device sh_veu_videodev = { + .name = "sh-veu", + .fops = &sh_veu_fops, + .ioctl_ops = &sh_veu_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, +}; + +static const struct v4l2_m2m_ops sh_veu_m2m_ops = { + .device_run = sh_veu_device_run, + .job_abort = sh_veu_job_abort, +}; + +static irqreturn_t sh_veu_bh(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + + if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) { + v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx); + veu->xaction = 0; + } else { + sh_veu_device_run(veu); + } + + return IRQ_HANDLED; +} + +static irqreturn_t sh_veu_isr(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + struct vb2_buffer *dst; + struct vb2_buffer *src; + u32 status = sh_veu_reg_read(veu, VEU_EVTR); + + /* bundle read mode not used */ + if (!(status & 1)) + return IRQ_NONE; + + /* disable interrupt in VEU */ + sh_veu_reg_write(veu, VEU_EIER, 0); + /* halt operation */ + sh_veu_reg_write(veu, VEU_STR, 0); + /* ack int, write 0 to clear bits */ + sh_veu_reg_write(veu, VEU_EVTR, status & ~1); + + /* conversion completed */ + dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx); + src = v4l2_m2m_src_buf_remove(veu->m2m_ctx); + if (!src || !dst) + return IRQ_NONE; + + spin_lock(&veu->lock); + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + spin_unlock(&veu->lock); + + veu->xaction++; + + if (!veu->aborting) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static int sh_veu_probe(struct platform_device *pdev) +{ + struct sh_veu_dev *veu; + struct resource *reg_res; + struct video_device *vdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VEU platform information.\n"); + return -ENODEV; + } + + veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL); + if (!veu) + return -ENOMEM; + + veu->is_2h = resource_size(reg_res) == 0x22c; + + veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); + if (!veu->base) + return -ENOMEM; + + ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, + 0, "veu", veu); + if (ret < 0) + return ret; + + ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + return ret; + } + + vdev = &veu->vdev; + + veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(veu->alloc_ctx)) { + ret = PTR_ERR(veu->alloc_ctx); + goto einitctx; + } + + *vdev = sh_veu_videodev; + spin_lock_init(&veu->lock); + mutex_init(&veu->fop_lock); + vdev->lock = &veu->fop_lock; + + video_set_drvdata(vdev, veu); + + veu->dev = &pdev->dev; + veu->vfmt_out = DEFAULT_OUT_VFMT; + veu->vfmt_in = DEFAULT_IN_VFMT; + + veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops); + if (IS_ERR(veu->m2m_dev)) { + ret = PTR_ERR(veu->m2m_dev); + v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret); + goto em2minit; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + pm_runtime_suspend(&pdev->dev); + if (ret < 0) + goto evidreg; + + return ret; + +evidreg: + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); +em2minit: + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); +einitctx: + v4l2_device_unregister(&veu->v4l2_dev); + return ret; +} + +static int sh_veu_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_veu_dev *veu = container_of(v4l2_dev, + struct sh_veu_dev, v4l2_dev); + + video_unregister_device(&veu->vdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); + v4l2_device_unregister(&veu->v4l2_dev); + + return 0; +} + +static struct platform_driver __refdata sh_veu_pdrv = { + .remove = sh_veu_remove, + .driver = { + .name = "sh_veu", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_veu_init(void) +{ + return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); +} + +static void __exit sh_veu_exit(void) +{ + platform_driver_unregister(&sh_veu_pdrv); +} + +module_init(sh_veu_init); +module_exit(sh_veu_exit); + +MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); +MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index f3c4571ac01e..ab5bca40a5b3 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -207,6 +207,7 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, #endif switch (vou_dev->pix.pixelformat) { + default: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: row_coeff = 1; @@ -595,9 +596,9 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max, - img_height_max; - int i, idx; + unsigned int best_err = UINT_MAX, best = geo->in_width, + width_max, height_max, img_height_max; + int i, idx = 0; if (std & V4L2_STD_525_60) { width_max = 858; @@ -1091,7 +1092,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) list_del(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index cb6791e62bd4..b139b525bb16 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -70,13 +70,12 @@ config VIDEO_MX2_HOSTSUPPORT bool config VIDEO_MX2 - tristate "i.MX27/i.MX25 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || (ARCH_MX25 && BROKEN)) + tristate "i.MX27 Camera Sensor Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27 select VIDEOBUF2_DMA_CONTIG select VIDEO_MX2_HOSTSUPPORT ---help--- - This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor - Interface + This is a v4l2 driver for the i.MX27 Camera Sensor Interface config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index d96c8c7e01d9..82dbf99d347c 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -166,7 +166,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) struct frame_buffer *buf = isi->active; list_del_init(&buf->list); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = isi->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } @@ -745,7 +745,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, return formats; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int isi_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -770,7 +770,7 @@ static int isi_camera_add_device(struct soc_camera_device *icd) icd->devnum); return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void isi_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 032b8c9097f9..4b661e87d8b1 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -26,7 +26,6 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/slab.h> @@ -307,7 +306,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); @@ -373,7 +372,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &icd->video_lock); + sizeof(struct mx1_buffer), icd, &icd->host_lock); } static int mclk_get_divisor(struct mx1_camera_dev *pcdev) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 8bda2c908aba..27b2e96f27a6 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1,5 +1,5 @@ /* - * V4L2 Driver for i.MX27/i.MX25 camera host + * V4L2 Driver for i.MX27 camera host * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography @@ -28,7 +28,6 @@ #include <linux/time.h> #include <linux/device.h> #include <linux/platform_device.h> -#include <linux/mutex.h> #include <linux/clk.h> #include <media/v4l2-common.h> @@ -64,9 +63,7 @@ #define CSICR1_RF_OR_INTEN (1 << 24) #define CSICR1_STATFF_LEVEL (3 << 22) #define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */ -#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */ -#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */ +#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) #define CSICR1_RXFF_INTEN (1 << 18) #define CSICR1_SOF_POL (1 << 17) #define CSICR1_SOF_INTEN (1 << 16) @@ -88,45 +85,15 @@ #define SHIFT_RXFF_LEVEL 19 #define SHIFT_MCLKDIV 12 -/* control reg 3 */ -#define CSICR3_FRMCNT (0xFFFF << 16) -#define CSICR3_FRMCNT_RST (1 << 15) -#define CSICR3_DMA_REFLASH_RFF (1 << 14) -#define CSICR3_DMA_REFLASH_SFF (1 << 13) -#define CSICR3_DMA_REQ_EN_RFF (1 << 12) -#define CSICR3_DMA_REQ_EN_SFF (1 << 11) -#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */ -#define CSICR3_CSI_SUP (1 << 3) -#define CSICR3_ZERO_PACK_EN (1 << 2) -#define CSICR3_ECC_INT_EN (1 << 1) -#define CSICR3_ECC_AUTO_EN (1 << 0) - #define SHIFT_FRMCNT 16 -/* csi status reg */ -#define CSISR_SFF_OR_INT (1 << 25) -#define CSISR_RFF_OR_INT (1 << 24) -#define CSISR_STATFF_INT (1 << 21) -#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */ -#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */ -#define CSISR_RXFF_INT (1 << 18) -#define CSISR_EOF_INT (1 << 17) -#define CSISR_SOF_INT (1 << 16) -#define CSISR_F2_INT (1 << 15) -#define CSISR_F1_INT (1 << 14) -#define CSISR_COF_INT (1 << 13) -#define CSISR_ECC_INT (1 << 1) -#define CSISR_DRDY (1 << 0) - #define CSICR1 0x00 #define CSICR2 0x04 -#define CSISR_IMX25 0x18 -#define CSISR_IMX27 0x08 +#define CSISR 0x08 #define CSISTATFIFO 0x0c #define CSIRFIFO 0x10 #define CSIRXCNT 0x14 -#define CSICR3_IMX25 0x08 -#define CSICR3_IMX27 0x1c +#define CSICR3 0x1c #define CSIDMASA_STATFIFO 0x20 #define CSIDMATA_STATFIFO 0x24 #define CSIDMASA_FB1 0x28 @@ -249,12 +216,6 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; -enum mx2_buffer_state { - MX2_STATE_QUEUED, - MX2_STATE_ACTIVE, - MX2_STATE_DONE, -}; - struct mx2_buf_internal { struct list_head queue; int bufnum; @@ -265,12 +226,10 @@ struct mx2_buf_internal { struct mx2_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; - enum mx2_buffer_state state; struct mx2_buf_internal internal; }; enum mx2_camera_type { - IMX25_CAMERA, IMX27_CAMERA, }; @@ -298,8 +257,6 @@ struct mx2_camera_dev { struct mx2_buffer *fb2_active; u32 csicr1; - u32 reg_csisr; - u32 reg_csicr3; enum mx2_camera_type devtype; struct mx2_buf_internal buf_discard[2]; @@ -315,9 +272,6 @@ struct mx2_camera_dev { static struct platform_device_id mx2_camera_devtype[] = { { - .name = "imx25-camera", - .driver_data = IMX25_CAMERA, - }, { .name = "imx27-camera", .driver_data = IMX27_CAMERA, }, { @@ -326,16 +280,6 @@ static struct platform_device_id mx2_camera_devtype[] = { }; MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); -static inline int is_imx25_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX25_CAMERA; -} - -static inline int is_imx27_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX27_CAMERA; -} - static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) { return container_of(int_buf, struct mx2_buffer, internal); @@ -463,21 +407,10 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { - unsigned long flags; - clk_disable_unprepare(pcdev->clk_csi_ahb); clk_disable_unprepare(pcdev->clk_csi_per); writel(0, pcdev->base_csi + CSICR1); - if (is_imx27_camera(pcdev)) { - writel(0, pcdev->base_emma + PRP_CNTL); - } else if (is_imx25_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); - pcdev->fb1_active = NULL; - pcdev->fb2_active = NULL; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - writel(0, pcdev->base_csi + CSIDMASA_FB2); - spin_unlock_irqrestore(&pcdev->lock, flags); - } + writel(0, pcdev->base_emma + PRP_CNTL); } /* @@ -502,11 +435,8 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) if (ret < 0) goto exit_csi_ahb; - csicr1 = CSICR1_MCLKEN; - - if (is_imx27_camera(pcdev)) - csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); + csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | + CSICR1_RXFF_LEVEL(0); pcdev->csicr1 = csicr1; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -540,65 +470,6 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; } -static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, - int state) -{ - struct vb2_buffer *vb; - struct mx2_buffer *buf; - struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : - &pcdev->fb2_active; - u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (*fb_active == NULL) - goto out; - - vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - do_gettimeofday(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - - if (list_empty(&pcdev->capture)) { - buf = NULL; - writel(0, pcdev->base_csi + fb_reg); - } else { - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - vb = &buf->vb; - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + fb_reg); - } - - *fb_active = buf; - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t mx25_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + pcdev->reg_csisr); - - if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); - else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); - - /* FIXME: handle CSISR_RFF_OR_INT */ - - writel(status, pcdev->base_csi + pcdev->reg_csisr); - - return IRQ_HANDLED; -} - /* * Videobuf operations */ @@ -676,97 +547,8 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pcdev->lock, flags); - buf->state = MX2_STATE_QUEUED; list_add_tail(&buf->internal.queue, &pcdev->capture); - if (is_imx25_camera(pcdev)) { - u32 csicr3, dma_inten = 0; - - if (pcdev->fb1_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = buf; - dma_inten = CSICR1_FB1_DMA_INTEN; - } else if (pcdev->fb2_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = buf; - dma_inten = CSICR1_FB2_DMA_INTEN; - } - - if (dma_inten) { - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - - csicr3 = readl(pcdev->base_csi + pcdev->reg_csicr3); - - /* Reflash DMA */ - writel(csicr3 | CSICR3_DMA_REFLASH_RFF, - pcdev->base_csi + pcdev->reg_csicr3); - - /* clear & enable interrupts */ - writel(dma_inten, pcdev->base_csi + pcdev->reg_csisr); - pcdev->csicr1 |= dma_inten; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - /* enable DMA */ - csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1); - writel(csicr3, pcdev->base_csi + pcdev->reg_csicr3); - } - } - - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx2_videobuf_release(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); - unsigned long flags; - -#ifdef DEBUG - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - switch (buf->state) { - case MX2_STATE_ACTIVE: - dev_info(icd->parent, "%s (active)\n", __func__); - break; - case MX2_STATE_QUEUED: - dev_info(icd->parent, "%s (queued)\n", __func__); - break; - default: - dev_info(icd->parent, "%s (unknown) %d\n", __func__, - buf->state); - break; - } -#endif - - /* - * Terminate only queued but inactive buffers. Active buffers are - * released when they become inactive after videobuf_waiton(). - * - * FIXME: implement forced termination of active buffers for mx27 and - * mx27 eMMA, so that the user won't get stuck in an uninterruptible - * state. This requires a specific handling for each of the these DMA - * types. - */ - - spin_lock_irqsave(&pcdev->lock, flags); - if (is_imx25_camera(pcdev) && buf->state == MX2_STATE_ACTIVE) { - if (pcdev->fb1_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = NULL; - } else if (pcdev->fb2_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = NULL; - } - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - } spin_unlock_irqrestore(&pcdev->lock, flags); } @@ -877,91 +659,87 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) struct mx2_buffer *buf; unsigned long phys; int bytesperline; + unsigned long flags; - if (is_imx27_camera(pcdev)) { - unsigned long flags; - if (count < 2) - return -EINVAL; + if (count < 2) + return -EINVAL; - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) { + spin_unlock_irqrestore(&pcdev->lock, flags); + return bytesperline; + } - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, + &pcdev->discard_buffer_dma, GFP_ATOMIC); + if (!pcdev->discard_buffer) { + spin_unlock_irqrestore(&pcdev->lock, flags); + return -ENOMEM; + } - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); - mx2_prp_resize_commit(pcdev); + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); - mx27_camera_emma_buf_init(icd, bytesperline); + mx2_prp_resize_commit(pcdev); - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } - spin_unlock_irqrestore(&pcdev->lock, flags); + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH1_LEN | + PRP_CNTL_CH1BYP | + PRP_CNTL_CH1_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); } + spin_unlock_irqrestore(&pcdev->lock, flags); return 0; } @@ -977,29 +755,27 @@ static int mx2_stop_streaming(struct vb2_queue *q) void *b; u32 cntl; - if (is_imx27_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); + } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock_irqrestore(&pcdev->lock, flags); - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); - } + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); return 0; } @@ -1008,7 +784,6 @@ static struct vb2_ops mx2_videobuf_ops = { .queue_setup = mx2_videobuf_setup, .buf_prepare = mx2_videobuf_prepare, .buf_queue = mx2_videobuf_queue, - .buf_cleanup = mx2_videobuf_release, .start_streaming = mx2_start_streaming, .stop_streaming = mx2_stop_streaming, }; @@ -1129,16 +904,9 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (is_imx27_camera(pcdev)) { - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - } else if (is_imx25_camera(pcdev)) { - writel((bytesperline * icd->user_height) >> 2, - pcdev->base_csi + CSIRXCNT); - writel((bytesperline << 16) | icd->user_height, - pcdev->base_csi + CSIIMAG_PARA); - } + ret = mx27_camera_emma_prp_reset(pcdev); + if (ret) + return ret; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -1425,7 +1193,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *emma_prp; - unsigned int width_limit; int ret; dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", @@ -1437,40 +1204,11 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* FIXME: implement MX27 limits */ - - /* limit to MX25 hardware capabilities */ - if (is_imx25_camera(pcdev)) { - if (xlate->host_fmt->bits_per_sample <= 8) - width_limit = 0xffff * 4; - else - width_limit = 0xffff * 2; - /* CSIIMAG_PARA limit */ - if (pix->width > width_limit) - pix->width = width_limit; - if (pix->height > 0xffff) - pix->height = 0xffff; - - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - if (pix->bytesperline < 0) - return pix->bytesperline; - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - /* Check against the CSIRXCNT limit */ - if (pix->sizeimage > 4 * 0x3ffff) { - /* Adjust geometry, preserve aspect ratio */ - unsigned int new_height = int_sqrt(div_u64(0x3ffffULL * - 4 * pix->height, pix->bytesperline)); - pix->width = new_height * pix->width / pix->height; - pix->height = new_height; - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - BUG_ON(pix->bytesperline < 0); - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - } - } + /* + * limit to MX27 hardware capabilities: width must be a multiple of 8 as + * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). + */ + pix->width &= ~0x7; /* limit to sensor capabilities */ mf.width = pix->width; @@ -1488,7 +1226,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, /* If the sensor does not support image size try PrP resizing */ emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + xlate->host_fmt->fourcc); if ((mf.width != pix->width || mf.height != pix->height) && emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { @@ -1600,7 +1338,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, vb2_get_plane_payload(vb, 0)); list_del_init(&buf->internal.queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = pcdev->frame_count; if (err) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); @@ -1634,7 +1372,6 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, bufnum); @@ -1774,20 +1511,6 @@ static int mx2_camera_probe(struct platform_device *pdev) goto exit; } - pcdev->devtype = pdev->id_entry->driver_data; - switch (pcdev->devtype) { - case IMX25_CAMERA: - pcdev->reg_csisr = CSISR_IMX25; - pcdev->reg_csicr3 = CSICR3_IMX25; - break; - case IMX27_CAMERA: - pcdev->reg_csisr = CSISR_IMX27; - pcdev->reg_csicr3 = CSICR3_IMX27; - break; - default: - break; - } - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(pcdev->clk_csi_ahb)) { dev_err(&pdev->dev, "Could not get csi ahb clock\n"); @@ -1833,20 +1556,9 @@ static int mx2_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; platform_set_drvdata(pdev, pcdev); - if (is_imx25_camera(pcdev)) { - err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit; - } - } - - if (is_imx27_camera(pcdev)) { - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - } + err = mx27_camera_emma_init(pdev); + if (err) + goto exit; /* * We're done with drvdata here. Clear the pointer so that @@ -1859,8 +1571,6 @@ static int mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; - if (is_imx25_camera(pcdev)) - pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { @@ -1879,10 +1589,8 @@ static int mx2_camera_probe(struct platform_device *pdev) exit_free_emma: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); eallocctx: - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); exit: return err; } @@ -1897,10 +1605,8 @@ static int mx2_camera_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); @@ -1913,23 +1619,12 @@ static struct platform_driver mx2_camera_driver = { }, .id_table = mx2_camera_devtype, .remove = mx2_camera_remove, + .probe = mx2_camera_probe, }; +module_platform_driver(mx2_camera_driver); -static int __init mx2_camera_init(void) -{ - return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe); -} - -static void __exit mx2_camera_exit(void) -{ - return platform_driver_unregister(&mx2_camera_driver); -} - -module_init(mx2_camera_init); -module_exit(mx2_camera_exit); - -MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver"); +MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); MODULE_LICENSE("GPL"); MODULE_VERSION(MX2_CAM_VERSION); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 45aef1053a49..f5cbb92db545 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -156,7 +156,7 @@ static void mx3_cam_dma_done(void *arg) struct mx3_camera_buffer *buf = to_mx3_vb(vb); list_del_init(&buf->queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.field = mx3_cam->field; vb->v4l2_buf.sequence = mx3_cam->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); @@ -510,7 +510,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_set_rate(mx3_cam->clk, rate); } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int mx3_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -530,7 +530,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void mx3_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 39a77f0b8860..dcf7be814776 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -592,7 +592,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, suspend_capture(pcdev); } vb->state = result; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); if (result != VIDEOBUF_ERROR) vb->field_count++; wake_up(&vb->done); @@ -1383,12 +1383,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); /* use videobuf mode (auto)selected with the module parameter */ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 523330d00dee..1fbec5202e4f 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -681,7 +681,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", @@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, */ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &icd->video_lock); + sizeof(struct pxa_buffer), icd, &icd->host_lock); } static u32 mclk_get_divisor(struct platform_device *pdev, @@ -958,7 +958,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) /* * The following two functions absolutely depend on the fact, that * there can be only one camera on PXA quick capture interface - * Called with .video_lock held + * Called with .host_lock held */ static int pxa_camera_add_device(struct soc_camera_device *icd) { @@ -978,7 +978,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index ebbc126e71a6..602584dc9330 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -516,7 +516,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) pcdev->active = NULL; ret = sh_mobile_ceu_capture(pcdev); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); if (!ret) { vb->v4l2_buf.field = pcdev->field; vb->v4l2_buf.sequence = pcdev->sequence++; @@ -543,7 +543,7 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) return NULL; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -572,7 +572,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } @@ -587,7 +587,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -612,7 +612,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irq(&pcdev->lock); - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", @@ -2088,15 +2088,13 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - err = -ENODEV; - goto exit; + return -ENODEV; } - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } INIT_LIST_HEAD(&pcdev->capture); @@ -2105,19 +2103,17 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (!pcdev->pdata) { - err = -EINVAL; dev_err(&pdev->dev, "CEU platform data not set.\n"); - goto exit_kfree; + return -EINVAL; } pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = ioremap_nocache(res->start, resource_size(res)); + base = devm_request_and_ioremap(&pdev->dev, res); if (!base) { - err = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - goto exit_kfree; + return -ENXIO; } pcdev->irq = irq; @@ -2133,16 +2129,15 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) DMA_MEMORY_EXCLUSIVE); if (!err) { dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - err = -ENXIO; - goto exit_iounmap; + return -ENXIO; } pcdev->video_limit = resource_size(res); } /* request irq */ - err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - dev_name(&pdev->dev), pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, + IRQF_DISABLED, dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -2246,15 +2241,9 @@ exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); exit_release_mem: if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); -exit_iounmap: - iounmap(base); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -2267,10 +2256,8 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - iounmap(pcdev->base); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2_pdev && csi2_pdev->dev.driver) { struct module *csi2_drv = csi2_pdev->dev.driver->owner; @@ -2279,7 +2266,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) platform_device_put(csi2_pdev); module_put(csi2_drv); } - kfree(pcdev); return 0; } diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index a17aba9a0104..42c559eb4937 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -318,23 +318,16 @@ static int sh_csi2_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); if (!priv) return -ENOMEM; priv->irq = irq; - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "CSI2 register region already claimed\n"); - ret = -EBUSY; - goto ereqreg; - } - - priv->base = ioremap(res->start, resource_size(res)); + priv->base = devm_request_and_ioremap(&pdev->dev, res); if (!priv->base) { - ret = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - goto eremap; + return -ENXIO; } priv->pdev = pdev; @@ -357,11 +350,7 @@ static int sh_csi2_probe(struct platform_device *pdev) return 0; esdreg: - iounmap(priv->base); -eremap: - release_mem_region(res->start, resource_size(res)); -ereqreg: - kfree(priv); + platform_set_drvdata(pdev, NULL); return ret; } @@ -369,14 +358,10 @@ ereqreg: static int sh_csi2_remove(struct platform_device *pdev) { struct sh_csi2 *priv = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); v4l2_device_unregister_subdev(&priv->subdev); pm_runtime_disable(&pdev->dev); - iounmap(priv->base); - release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 2ec90eae6ba0..8ec98051ea73 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -50,22 +50,22 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ -int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) { - int ret = regulator_bulk_enable(icl->num_regulators, - icl->regulators); + int ret = regulator_bulk_enable(ssdd->num_regulators, + ssdd->regulators); if (ret < 0) { dev_err(dev, "Cannot enable regulators\n"); return ret; } - if (icl->power) { - ret = icl->power(dev, 1); + if (ssdd->power) { + ret = ssdd->power(dev, 1); if (ret < 0) { dev_err(dev, "Platform failed to power-on the camera.\n"); - regulator_bulk_disable(icl->num_regulators, - icl->regulators); + regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); } } @@ -73,13 +73,13 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) } EXPORT_SYMBOL(soc_camera_power_on); -int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd) { int ret = 0; int err; - if (icl->power) { - err = icl->power(dev, 0); + if (ssdd->power) { + err = ssdd->power(dev, 0); if (err < 0) { dev_err(dev, "Platform failed to power-off the camera.\n"); @@ -87,8 +87,8 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) } } - err = regulator_bulk_disable(icl->num_regulators, - icl->regulators); + err = regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); if (err < 0) { dev_err(dev, "Cannot disable regulators\n"); ret = ret ? : err; @@ -136,29 +136,29 @@ EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); /** * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @icl: camera platform parameters + * @ssdd: camera platform parameters * @cfg: media bus configuration * @return: resulting flags */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, const struct v4l2_mbus_config *cfg) { unsigned long f, flags = cfg->flags; /* If only one of the two polarities is supported, switch to the opposite */ - if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; @@ -383,7 +383,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv, return vb2_prepare_buf(&icd->vb2_vidq, b); } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -450,7 +450,7 @@ egfmt: return ret; } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -509,7 +509,7 @@ static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct soc_camera_host *ici; int ret; @@ -517,9 +517,16 @@ static int soc_camera_open(struct file *file) /* No device driver attached */ return -ENODEV; + /* + * Don't mess with the host during probe: wait until the loop in + * scan_add_host() completes + */ + if (mutex_lock_interruptible(&list_lock)) + return -ERESTARTSYS; ici = to_soc_camera_host(icd->parent); + mutex_unlock(&list_lock); - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (!try_module_get(ici->ops->owner)) { dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); @@ -545,13 +552,10 @@ static int soc_camera_open(struct file *file) }; /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (sdesc->subdev_desc.reset) + sdesc->subdev_desc.reset(icd->pdev); - /* Don't mess with the host during probe */ - mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); - mutex_unlock(&ici->host_lock); if (ret < 0) { dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; @@ -570,7 +574,7 @@ static int soc_camera_open(struct file *file) * Try to configure with default parameters. Notice: this is the * very first open, so, we cannot race against other calls, * apart from someone else calling open() simultaneously, but - * .video_lock is protecting us against it. + * .host_lock is protecting us against it. */ ret = soc_camera_set_fmt(icd, &f); if (ret < 0) @@ -585,7 +589,7 @@ static int soc_camera_open(struct file *file) } v4l2_ctrl_handler_setup(&icd->ctrl_handler); } - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); file->private_data = icd; dev_dbg(icd->pdev, "camera device open\n"); @@ -593,7 +597,7 @@ static int soc_camera_open(struct file *file) return 0; /* - * First four errors are entered with the .video_lock held + * First four errors are entered with the .host_lock held * and use_count == 1 */ einitvb: @@ -607,7 +611,7 @@ eiciadd: icd->use_count--; module_put(ici->ops->owner); emodule: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return ret; } @@ -617,7 +621,7 @@ static int soc_camera_close(struct file *file) struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); icd->use_count--; if (!icd->use_count) { pm_runtime_suspend(&icd->vdev->dev); @@ -632,7 +636,7 @@ static int soc_camera_close(struct file *file) if (icd->streamer == file) icd->streamer = NULL; - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); module_put(ici->ops->owner); @@ -669,13 +673,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) if (icd->streamer != file) return -EBUSY; - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (ici->ops->init_videobuf) err = videobuf_mmap_mapper(&icd->vb_vidq, vma); else err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -694,26 +698,28 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) if (icd->streamer != file) return POLLERR; - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) dev_err(icd->pdev, "Trying to poll with no queued buffers!\n"); else res = ici->ops->poll(file, pt); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return res; } void soc_camera_lock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_lock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_lock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_lock); void soc_camera_unlock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_unlock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_unlock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_unlock); @@ -908,6 +914,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); + current_crop.type = a->type; + /* If get_crop fails, we'll let host and / or client drivers decide */ ret = ici->ops->get_crop(icd, ¤t_crop); @@ -1050,7 +1058,7 @@ static void scan_add_host(struct soc_camera_host *ici) { struct soc_camera_device *icd; - mutex_lock(&ici->host_lock); + mutex_lock(&list_lock); list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { @@ -1059,28 +1067,29 @@ static void scan_add_host(struct soc_camera_host *ici) } } - mutex_unlock(&ici->host_lock); + mutex_unlock(&list_lock); } #ifdef CONFIG_I2C_BOARDINFO static int soc_camera_init_i2c(struct soc_camera_device *icd, - struct soc_camera_link *icl) + struct soc_camera_desc *sdesc) { struct i2c_client *client; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id); struct v4l2_subdev *subdev; if (!adap) { dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - icl->i2c_adapter_id); + shd->i2c_adapter_id); goto ei2cga; } - icl->board_info->platform_data = icl; + shd->board_info->platform_data = &sdesc->subdev_desc; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - icl->board_info, NULL); + shd->board_info, NULL); if (!subdev) goto ei2cnd; @@ -1108,7 +1117,7 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) i2c_put_adapter(adap); } #else -#define soc_camera_init_i2c(icd, icl) (-ENODEV) +#define soc_camera_init_i2c(icd, sdesc) (-ENODEV) #define soc_camera_free_i2c(icd) do {} while (0) #endif @@ -1118,7 +1127,9 @@ static int video_dev_create(struct soc_camera_device *icd); static int soc_camera_probe(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct device *control = NULL; struct v4l2_subdev *sd; struct v4l2_mbus_framefmt mf; @@ -1137,16 +1148,13 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); - if (ret < 0) - goto ereg; - /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (ssdd->reset) + ssdd->reset(icd->pdev); + mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); + mutex_unlock(&ici->host_lock); if (ret < 0) goto eadd; @@ -1156,18 +1164,18 @@ static int soc_camera_probe(struct soc_camera_device *icd) goto evdc; /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (icl->board_info) { - ret = soc_camera_init_i2c(icd, icl); + if (shd->board_info) { + ret = soc_camera_init_i2c(icd, sdesc); if (ret < 0) goto eadddev; - } else if (!icl->add_device || !icl->del_device) { + } else if (!shd->add_device || !shd->del_device) { ret = -EINVAL; goto eadddev; } else { - if (icl->module_name) - ret = request_module(icl->module_name); + if (shd->module_name) + ret = request_module(shd->module_name); - ret = icl->add_device(icd); + ret = shd->add_device(icd); if (ret < 0) goto eadddev; @@ -1178,7 +1186,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) control = to_soc_camera_control(icd); if (!control || !control->driver || !dev_get_drvdata(control) || !try_module_get(control->driver->owner)) { - icl->del_device(icd); + shd->del_device(icd); ret = -ENODEV; goto enodrv; } @@ -1204,7 +1212,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) * itself is protected against concurrent open() calls, but we also have * to protect our data. */ - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); ret = soc_camera_video_start(icd); if (ret < 0) @@ -1220,19 +1228,19 @@ static int soc_camera_probe(struct soc_camera_device *icd) ici->ops->remove(icd); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return 0; evidstart: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); soc_camera_free_user_formats(icd); eiufmt: ectrl: - if (icl->board_info) { + if (shd->board_info) { soc_camera_free_i2c(icd); } else { - icl->del_device(icd); + shd->del_device(icd); module_put(control->driver->owner); } enodrv: @@ -1240,10 +1248,10 @@ eadddev: video_device_release(icd->vdev); icd->vdev = NULL; evdc: + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); eadd: - regulator_bulk_free(icl->num_regulators, icl->regulators); -ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } @@ -1254,7 +1262,7 @@ ereg: */ static int soc_camera_remove(struct soc_camera_device *icd) { - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct video_device *vdev = icd->vdev; BUG_ON(!icd->parent); @@ -1265,19 +1273,17 @@ static int soc_camera_remove(struct soc_camera_device *icd) icd->vdev = NULL; } - if (icl->board_info) { + if (sdesc->host_desc.board_info) { soc_camera_free_i2c(icd); } else { struct device_driver *drv = to_soc_camera_control(icd)->driver; if (drv) { - icl->del_device(icd); + sdesc->host_desc.del_device(icd); module_put(drv->owner); } } soc_camera_free_user_formats(icd); - regulator_bulk_free(icl->num_regulators, icl->regulators); - return 0; } @@ -1442,7 +1448,6 @@ static int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->use_count = 0; icd->host_priv = NULL; - mutex_init(&icd->video_lock); list_add_tail(&icd->list, &devices); @@ -1500,7 +1505,7 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->release = video_device_release; vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &icd->video_lock; + vdev->lock = &ici->host_lock; icd->vdev = vdev; @@ -1508,7 +1513,7 @@ static int video_dev_create(struct soc_camera_device *icd) } /* - * Called from soc_camera_probe() above (with .video_lock held???) + * Called from soc_camera_probe() above with .host_lock held */ static int soc_camera_video_start(struct soc_camera_device *icd) { @@ -1532,18 +1537,25 @@ static int soc_camera_video_start(struct soc_camera_device *icd) static int soc_camera_pdrv_probe(struct platform_device *pdev) { - struct soc_camera_link *icl = pdev->dev.platform_data; + struct soc_camera_desc *sdesc = pdev->dev.platform_data; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct soc_camera_device *icd; + int ret; - if (!icl) + if (!sdesc) return -EINVAL; icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); if (!icd) return -ENOMEM; - icd->iface = icl->bus_id; - icd->link = icl; + ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, + ssdd->regulators); + if (ret < 0) + return ret; + + icd->iface = sdesc->host_desc.bus_id; + icd->sdesc = sdesc; icd->pdev = &pdev->dev; platform_set_drvdata(pdev, icd); diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 7cf7fd16481f..ce3b1d6a4734 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return soc_camera_set_power(p->icd->control, p->icd->link, on); + return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on); } static struct v4l2_subdev_core_ops platform_subdev_core_ops = { @@ -148,7 +148,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -173,7 +173,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) evdrs: platform_set_drvdata(pdev, NULL); - kfree(priv); return ret; } @@ -185,7 +184,6 @@ static int soc_camera_platform_remove(struct platform_device *pdev) p->icd->control = NULL; v4l2_device_unregister_subdev(&priv->subdev); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index a397812635d6..89dce097a827 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -378,9 +378,6 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) return width * mf->bits_per_sample / 8; @@ -403,9 +400,6 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line); s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, u32 bytes_per_line, u32 height) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) return bytes_per_line * height; diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index d854d08a6c7f..c3a2a4484401 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -130,7 +130,7 @@ static void timblogiw_dma_cb(void *data) if (vb->state != VIDEOBUF_ERROR) { list_del(&vb->queue); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = fh->frame_count * 2; vb->state = VIDEOBUF_DONE; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 70b0bf4b2900..eb5d6f955709 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2474,8 +2474,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_a) && (done_a || skip_a)) { if (!skip_a) { - do_gettimeofday(&vino_drvdata-> - a.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->a.int_data.timestamp); vino_drvdata->a.int_data.frame_counter = fc_a; } vino_drvdata->a.int_data.skip = skip_a; @@ -2489,8 +2489,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_b) && (done_b || skip_b)) { if (!skip_b) { - do_gettimeofday(&vino_drvdata-> - b.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->b.int_data.timestamp); vino_drvdata->b.int_data.frame_counter = fc_b; } vino_drvdata->b.int_data.skip = skip_b; @@ -3410,6 +3410,9 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, if (fb->map_count > 0) b->flags |= V4L2_BUF_FLAG_MAPPED; + b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->index = fb->id; b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 0d59b9db83cb..8a33a712f480 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -36,9 +36,17 @@ #define VIVI_MODULE_NAME "vivi" -/* Wake up at about 30 fps */ -#define WAKE_NUMERATOR 30 -#define WAKE_DENOMINATOR 1001 +/* Maximum allowed frame rate + * + * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range. + * + * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that + * might hit application errors when they manipulate these values. + * + * Besides, for tpf < 1ms image-generation logic should be changed, to avoid + * producing frames with equal content. + */ +#define FPS_MAX 1000 #define MAX_WIDTH 1920 #define MAX_HEIGHT 1200 @@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* Global font descriptor */ static const u8 *font8x16; +/* timeperframe: min/max and default */ +static const struct v4l2_fract + tpf_min = {.numerator = 1, .denominator = FPS_MAX}, + tpf_max = {.numerator = FPS_MAX, .denominator = 1}, + tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */ + #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -77,13 +91,13 @@ static const u8 *font8x16; ------------------------------------------------------------------*/ struct vivi_fmt { - char *name; + const char *name; u32 fourcc; /* v4l2 format id */ u8 depth; bool is_yuv; }; -static struct vivi_fmt formats[] = { +static const struct vivi_fmt formats[] = { { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, @@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *get_format(struct v4l2_format *f) +static const struct vivi_fmt *__get_format(u32 pixelformat) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { fmt = &formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) + if (fmt->fourcc == pixelformat) break; } @@ -167,12 +181,17 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) return &formats[k]; } +static const struct vivi_fmt *get_format(struct v4l2_format *f) +{ + return __get_format(f->fmt.pix.pixelformat); +} + /* buffer for one video frame */ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; struct list_head list; - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; }; struct vivi_dmaqueue { @@ -231,15 +250,17 @@ struct vivi_dev { int input; /* video capture */ - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; + struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; unsigned int field_count; u8 bars[9][3]; - u8 line[MAX_WIDTH * 8]; + u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); unsigned int pixelsize; u8 alpha_component; + u32 textfg, textbg; }; /* ------------------------------------------------------------------ @@ -276,7 +297,7 @@ struct bar_std { /* Maximum number of bars are 10 - otherwise, the input print code should be modified */ -static struct bar_std bars[] = { +static const struct bar_std bars[] = { { /* Standard ITU-R color bar sequence */ { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } @@ -511,66 +532,100 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) static void precalculate_line(struct vivi_dev *dev) { - int w; - - for (w = 0; w < dev->width * 2; w++) { - int colorpos = w / (dev->width / 8) % 8; - - gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); + unsigned pixsize = dev->pixelsize; + unsigned pixsize2 = 2*pixsize; + int colorpos; + u8 *pos; + + for (colorpos = 0; colorpos < 16; ++colorpos) { + u8 pix[8]; + int wstart = colorpos * dev->width / 8; + int wend = (colorpos+1) * dev->width / 8; + int w; + + gen_twopix(dev, &pix[0], colorpos % 8, 0); + gen_twopix(dev, &pix[pixsize], colorpos % 8, 1); + + for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2) + memcpy(pos, pix, pixsize2); } } +/* need this to do rgb24 rendering */ +typedef struct { u16 __; u8 _; } __attribute__((packed)) x24; + static void gen_text(struct vivi_dev *dev, char *basep, int y, int x, char *text) { int line; + unsigned int width = dev->width; /* Checks if it is possible to show string */ - if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + if (y + 16 >= dev->height || x + strlen(text) * 8 >= width) return; /* Print stream time */ - for (line = y; line < y + 16; line++) { - int j = 0; - char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; - char *s; - - for (s = text; *s; s++) { - u8 chr = font8x16[*s * 16 + line - y]; - int i; - - for (i = 0; i < 7; i++, j++) { - /* Draw white font on black background */ - if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); - else - gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); - } - } +#define PRINTSTR(PIXTYPE) do { \ + PIXTYPE fg; \ + PIXTYPE bg; \ + memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \ + memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \ + \ + for (line = 0; line < 16; line++) { \ + PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \ + u8 *s; \ + \ + for (s = text; *s; s++) { \ + u8 chr = font8x16[*s * 16 + line]; \ + \ + pos[0] = (chr & (0x01 << 7) ? fg : bg); \ + pos[1] = (chr & (0x01 << 6) ? fg : bg); \ + pos[2] = (chr & (0x01 << 5) ? fg : bg); \ + pos[3] = (chr & (0x01 << 4) ? fg : bg); \ + pos[4] = (chr & (0x01 << 3) ? fg : bg); \ + pos[5] = (chr & (0x01 << 2) ? fg : bg); \ + pos[6] = (chr & (0x01 << 1) ? fg : bg); \ + pos[7] = (chr & (0x01 << 0) ? fg : bg); \ + \ + pos += 8; \ + } \ + } \ +} while (0) + + switch (dev->pixelsize) { + case 2: + PRINTSTR(u16); break; + case 4: + PRINTSTR(u32); break; + case 3: + PRINTSTR(x24); break; } } static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - int wmax = dev->width; + int stride = dev->width * dev->pixelsize; int hmax = dev->height; - struct timeval ts; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; int h, line = 1; + u8 *linestart; s32 gain; if (!vbuf) return; + linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize; + for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * dev->pixelsize, - dev->line + (dev->mv_count % wmax) * dev->pixelsize, - wmax * dev->pixelsize); + memcpy(vbuf + h * stride, linestart, stride); /* Updates stream time */ + gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0); + gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; ms = dev->ms; @@ -622,8 +677,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; dev->field_count++; buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - do_gettimeofday(&ts); - buf->vb.v4l2_buf.timestamp = ts; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); } static void vivi_thread_tick(struct vivi_dev *dev) @@ -645,7 +699,7 @@ static void vivi_thread_tick(struct vivi_dev *dev) list_del(&buf->list); spin_unlock_irqrestore(&dev->slock, flags); - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); /* Fill buffer */ vivi_fillbuff(dev, buf); @@ -655,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev) dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); } -#define frames_to_ms(frames) \ - ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) +#define frames_to_ms(dev, frames) \ + ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator) static void vivi_sleep(struct vivi_dev *dev) { @@ -672,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev) goto stop_task; /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(1)); + timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); vivi_thread_tick(dev); @@ -872,7 +926,7 @@ static void vivi_unlock(struct vb2_queue *vq) } -static struct vb2_ops vivi_video_qops = { +static const struct vb2_ops vivi_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -903,7 +957,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; @@ -939,7 +993,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; fmt = get_format(f); if (!fmt) { @@ -1044,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +/* timeperframe is arbitrary and continous */ +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + const struct vivi_fmt *fmt; + + if (fival->index) + return -EINVAL; + + fmt = __get_format(fival->pixel_format); + if (!fmt) + return -EINVAL; + + /* regarding width & height - we support any */ + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + + /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ + fival->stepwise.min = tpf_min; + fival->stepwise.max = tpf_max; + fival->stepwise.step = (struct v4l2_fract) {1, 1}; + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = dev->timeperframe; + parm->parm.capture.readbuffers = 1; + return 0; +} + +#define FRACT_CMP(a, OP, b) \ + ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator) + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_fract tpf; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + tpf = parm->parm.capture.timeperframe; + + /* tpf: {*, 0} resets timing; clip to [min, max]*/ + tpf = tpf.denominator ? tpf : tpf_default; + tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + + dev->timeperframe = tpf; + parm->parm.capture.timeperframe = tpf; + parm->parm.capture.readbuffers = 1; + return 0; +} + /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1202,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, @@ -1209,7 +1330,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static struct video_device vivi_template = { +static const struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, @@ -1260,6 +1381,7 @@ static int __init vivi_create_instance(int inst) goto free_dev; dev->fmt = &formats[0]; + dev->timeperframe = tpf_default; dev->width = 640; dev->height = 480; dev->pixelsize = dev->fmt->depth / 8; diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..ead992853730 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -124,6 +124,18 @@ config USB_KEENE To compile this driver as a module, choose M here: the module will be called radio-keene. +config USB_MA901 + tristate "Masterkit MA901 USB FM radio support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers or headphones. + + To compile this driver as a module, choose M here: the + module will be called radio-ma901. + config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index c03ce4fe74e9..303eaebdb85a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_USB_KEENE) += radio-keene.o +obj-$(CONFIG_USB_MA901) += radio-ma901.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c new file mode 100644 index 000000000000..c61f590029ad --- /dev/null +++ b/drivers/media/radio/radio-ma901.c @@ -0,0 +1,460 @@ +/* + * Driver for the MasterKit MA901 USB FM radio. This device plugs + * into the USB port and an analog audio input or headphones, so this thing + * only deals with initialization, frequency setting, volume. + * + * Copyright (c) 2012 Alexey Klimov <klimov.linux@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <linux/usb.h> +#include <linux/mutex.h> + +#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>" +#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" +#define DRIVER_VERSION "0.0.1" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +#define USB_MA901_VENDOR 0x16c0 +#define USB_MA901_PRODUCT 0x05df + +/* dev_warn macro with driver name */ +#define MA901_DRIVER_NAME "radio-ma901" +#define ma901radio_dev_warn(dev, fmt, arg...) \ + dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +#define ma901radio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + +#define MA901_VOLUME_MAX 16 +#define MA901_VOLUME_MIN 0 + +/* Commands that device should understand + * List isn't full and will be updated with implementation of new functions + */ +#define MA901_RADIO_SET_FREQ 0x03 +#define MA901_RADIO_SET_VOLUME 0x04 +#define MA901_RADIO_SET_MONO_STEREO 0x05 + +/* Comfortable defines for ma901radio_set_stereo */ +#define MA901_WANT_STEREO 0x50 +#define MA901_WANT_MONO 0xd0 + +/* module parameter */ +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Radio file number"); + +/* Data for one (physical) device */ +struct ma901radio_device { + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + + u8 *buffer; + struct mutex lock; /* buffer locking */ + int curfreq; + u16 volume; + int stereo; + bool muted; +}; + +static inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int ma901radio_set_freq(struct ma901radio_device *radio, int freq) +{ + unsigned int freq_send = 0x300 + (freq >> 5) / 25; + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_FREQ; + radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; + radio->buffer[3] = freq_send & 0xff; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->curfreq = freq; + return 0; +} + +static int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_VOLUME; + radio->buffer[2] = 0xc2; + radio->buffer[3] = vol_to_set + 0x20; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->volume = vol_to_set; + return retval; +} + +static int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; + radio->buffer[2] = stereo; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (retval < 0) + return retval; + + if (stereo == MA901_WANT_STEREO) + radio->stereo = V4L2_TUNER_MODE_STEREO; + else + radio->stereo = V4L2_TUNER_MODE_MONO; + + return retval; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_ma901radio_device_release. + */ +static void usb_ma901radio_disconnect(struct usb_interface *intf) +{ + struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); + + mutex_lock(&radio->lock); + video_unregister_device(&radio->vdev); + usb_set_intfdata(intf, NULL); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* vidioc_querycap - query device capabilities */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-ma901", sizeof(v->driver)); + strlcpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* vidioc_g_tuner - get tuner attributes */ +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + v->signal = 0; + + /* TODO: the same words like in _probe() goes here. + * When receiving of stats will be implemented then we can call + * ma901radio_get_stat(). + * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); + */ + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; +} + +/* vidioc_s_tuner - set tuner attributes */ +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + /* mono/stereo selector */ + switch (v->audmode) { + case V4L2_TUNER_MODE_MONO: + return ma901_set_stereo(radio, MA901_WANT_MONO); + default: + return ma901_set_stereo(radio, MA901_WANT_STEREO); + } +} + +/* vidioc_s_frequency - set tuner radio frequency */ +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + + return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); +} + +/* vidioc_g_frequency - get tuner radio frequency */ +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->frequency = radio->curfreq; + + return 0; +} + +static int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ma901radio_device *radio = + container_of(ctrl->handler, struct ma901radio_device, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: /* set volume */ + return ma901radio_set_volume(radio, (u16)ctrl->val); + } + + return -EINVAL; +} + +/* TODO: Should we really need to implement suspend and resume functions? + * Radio has it's own memory and will continue playing if power is present + * on usb port and on resume it will start to play again based on freq, volume + * values in device memory. + */ +static int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) +{ + return 0; +} + +static int usb_ma901radio_resume(struct usb_interface *intf) +{ + return 0; +} + +static const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { + .s_ctrl = usb_ma901radio_s_ctrl, +}; + +/* File system interface */ +static const struct v4l2_file_operations usb_ma901radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void usb_ma901radio_release(struct v4l2_device *v4l2_dev) +{ + struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); + + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->buffer); + kfree(radio); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_ma901radio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ma901radio_device *radio; + int retval = 0; + + radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); + if (!radio) { + dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); + retval = -ENOMEM; + goto err; + } + + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + if (!radio->buffer) { + dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); + retval = -ENOMEM; + goto err_nobuf; + } + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&radio->hdl, 1); + + /* TODO:It looks like this radio doesn't have mute/unmute control + * and windows program just emulate it using volume control. + * Let's plan to do the same in this driver. + * + * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + */ + + v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, + MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); + + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } + mutex_init(&radio->lock); + + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_ma901radio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_ma901radio_fops; + radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + radio->curfreq = 95.21 * FREQ_MUL; + + video_set_drvdata(&radio->vdev, radio); + + /* TODO: we can get some statistics (freq, volume) from device + * but it's not implemented yet. After insertion in usb-port radio + * setups frequency and starts playing without any initialization. + * So we don't call usb_ma901radio_init/get_stat() here. + * retval = usb_ma901radio_init(radio); + */ + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, + radio_nr); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto err_vdev; + } + + return 0; + +err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; +} + +/* USB Device ID List */ +static struct usb_device_id usb_ma901radio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_ma901radio_driver = { + .name = MA901_DRIVER_NAME, + .probe = usb_ma901radio_probe, + .disconnect = usb_ma901radio_disconnect, + .suspend = usb_ma901radio_suspend, + .resume = usb_ma901radio_resume, + .reset_resume = usb_ma901radio_resume, + .id_table = usb_ma901radio_device_table, +}; + +module_usb_driver(usb_ma901radio_driver); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index c48be195bbad..681d4aeee530 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -2084,8 +2084,7 @@ static int wl1273_fm_radio_probe(struct platform_device *pdev) } /* V4L2 configuration */ - memcpy(&radio->videodev, &wl1273_viddev_template, - sizeof(wl1273_viddev_template)); + radio->videodev = wl1273_viddev_template; radio->videodev.v4l2_dev = &radio->v4l2dev; diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 602ef7ac8c24..a002234ed5de 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1563,8 +1563,7 @@ int fmc_prepare(struct fmdev *fmdev) fmdev->irq_info.mask = FM_MAL_EVENT; /* Region info */ - memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], - sizeof(struct region_info)); + fmdev->rx.region = region_configs[default_radio_region]; fmdev->rx.mute_mode = FM_MUTE_OFF; fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2d6fb26a0170..4d6a63fe6c5e 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -872,11 +872,11 @@ static int ati_remote_probe(struct usb_interface *interface, ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); rc_dev = rc_allocate_device(); if (!ati_remote || !rc_dev) - goto fail1; + goto exit_free_dev_rdev; /* Allocate URB buffers, URBs */ if (ati_remote_alloc_buffers(udev, ati_remote)) - goto fail2; + goto exit_free_buffers; ati_remote->endpoint_in = endpoint_in; ati_remote->endpoint_out = endpoint_out; @@ -924,12 +924,12 @@ static int ati_remote_probe(struct usb_interface *interface, /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ err = ati_remote_initialize(ati_remote); if (err) - goto fail3; + goto exit_kill_urbs; /* Set up and register rc device */ err = rc_register_device(ati_remote->rdev); if (err) - goto fail3; + goto exit_kill_urbs; /* use our delay for rc_dev */ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay; @@ -939,7 +939,7 @@ static int ati_remote_probe(struct usb_interface *interface, input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; - goto fail4; + goto exit_unregister_device; } ati_remote->idev = input_dev; @@ -947,19 +947,24 @@ static int ati_remote_probe(struct usb_interface *interface, err = input_register_device(input_dev); if (err) - goto fail5; + goto exit_free_input_device; } usb_set_intfdata(interface, ati_remote); return 0; - fail5: input_free_device(input_dev); - fail4: rc_unregister_device(rc_dev); + exit_free_input_device: + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); rc_dev = NULL; - fail3: usb_kill_urb(ati_remote->irq_urb); + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); - fail2: ati_remote_free_buffers(ati_remote); - fail1: rc_free_device(rc_dev); + exit_free_buffers: + ati_remote_free_buffers(ati_remote); + exit_free_dev_rdev: + rc_free_device(rc_dev); kfree(ati_remote); return err; } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index cef04786b52f..ee6c984cade2 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); rdev = rc_allocate_device(); if (!dev || !rdev) - goto failure; + goto exit_free_dev_rdev; /* validate resources */ error = -ENODEV; @@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) - goto failure; + goto exit_free_dev_rdev; if (!pnp_irq_valid(pnp_dev, 0)) - goto failure; + goto exit_free_dev_rdev; spin_lock_init(&dev->hw_lock); @@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* detect hardware version and features */ error = ene_hw_detect(dev); if (error) - goto failure; + goto exit_free_dev_rdev; if (!dev->hw_learning_and_tx_capable && txsim) { dev->hw_learning_and_tx_capable = true; @@ -1075,30 +1075,30 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) device_set_wakeup_capable(&pnp_dev->dev, true); device_set_wakeup_enable(&pnp_dev->dev, true); + error = rc_register_device(rdev); + if (error < 0) + goto exit_free_dev_rdev; + /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto failure; + goto exit_unregister_device; } dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { - goto failure2; + goto exit_release_hw_io; } - error = rc_register_device(rdev); - if (error < 0) - goto failure3; - pr_notice("driver has been successfully loaded\n"); return 0; -failure3: - free_irq(dev->irq, dev); -failure2: +exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); return error; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 1df410e13688..d6fa441655d2 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -500,18 +500,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } fintek->cir_addr = pnp_port_start(pdev, 0); @@ -528,7 +528,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id ret = fintek_hw_detect(fintek); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ fintek_config_mode_enable(fintek); @@ -557,33 +557,35 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + fintek->rdev = rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(fintek->cir_addr, fintek->cir_port_len, FINTEK_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure2; + goto exit_free_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; device_init_wakeup(&pdev->dev, true); - fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) cir_dump_regs(fintek); return 0; -failure3: +exit_free_irq: free_irq(fintek->cir_irq, fintek); -failure2: +exit_free_cir_addr: release_region(fintek->cir_addr, fintek->cir_port_len); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 4f71a7d1f019..382f362b69af 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -129,12 +129,12 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) err_request_irq: platform_set_drvdata(pdev, NULL); rc_unregister_device(rcdev); + rcdev = NULL; err_register_rc_device: err_gpio_direction_input: gpio_free(pdata->gpio_nr); err_gpio_request: rc_free_device(rcdev); - rcdev = NULL; err_allocate_device: kfree(gpio_dev); return rc; @@ -148,7 +148,6 @@ static int gpio_ir_recv_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rc_unregister_device(gpio_dev->rcdev); gpio_free(gpio_dev->gpio_nr); - rc_free_device(gpio_dev->rcdev); kfree(gpio_dev); return 0; } diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 97dc8d13b06b..17c94be9f24c 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -31,11 +31,6 @@ static DEFINE_MUTEX(ir_raw_handler_lock); static LIST_HEAD(ir_raw_handler_list); static u64 available_protocols; -#ifdef MODULE -/* Used to load the decoders */ -static struct work_struct wq_load; -#endif - static int ir_raw_event_thread(void *data) { struct ir_raw_event ev; @@ -347,8 +342,7 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) } EXPORT_SYMBOL(ir_raw_handler_unregister); -#ifdef MODULE -static void init_decoders(struct work_struct *work) +void ir_raw_init(void) { /* Load the decoder modules */ @@ -365,12 +359,3 @@ static void init_decoders(struct work_struct *work) it is needed to change the CONFIG_MODULE test at rc-core.h */ } -#endif - -void ir_raw_init(void) -{ -#ifdef MODULE - INIT_WORK(&wq_load, init_decoders); - schedule_work(&wq_load); -#endif -} diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 1b8669b6d042..dd8237324c09 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; itdev->rdev = rdev; ret = -ENODEV; @@ -1498,12 +1498,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (!pnp_port_valid(pdev, io_rsrc_no) || pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } /* store resource values */ @@ -1591,29 +1591,29 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->driver_name = ITE_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto failure; + goto exit_unregister_device; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure2; - - ret = rc_register_device(rdev); - if (ret) - goto failure3; + goto exit_release_cir_addr; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -failure3: - free_irq(itdev->cir_irq, itdev); -failure2: +exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index ab84d66c67c1..778661971aed 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tevii-nec.o \ rc-tivo.o \ rc-total-media-in-hand.o \ + rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ rc-twinhan1027.o \ diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c new file mode 100644 index 000000000000..47270f72ebf0 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c @@ -0,0 +1,86 @@ +/* + * Total Media In Hand_02 remote controller keytable for Mygica X8507 + * + * Copyright (C) 2012 Alfredo J. Delaiti <alfredodelaiti@netscape.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + + +static struct rc_map_table total_media_in_hand_02[] = { + { 0x0000, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x000b, KEY_STOP }, /* Stop */ + { 0x000c, KEY_POWER2 }, /* Turn on/off application */ + { 0x000d, KEY_OK }, /* OK */ + { 0x000e, KEY_CAMERA }, /* Snapshot */ + { 0x000f, KEY_ZOOM }, /* Full Screen/Restore */ + { 0x0010, KEY_RIGHT }, /* Right arrow */ + { 0x0011, KEY_LEFT }, /* Left arrow */ + { 0x0012, KEY_CHANNELUP }, + { 0x0013, KEY_CHANNELDOWN }, + { 0x0014, KEY_SHUFFLE }, + { 0x0016, KEY_PAUSE }, + { 0x0017, KEY_PLAY }, /* Play */ + { 0x001e, KEY_TIME }, /* Time Shift */ + { 0x001f, KEY_RECORD }, + { 0x0020, KEY_UP }, + { 0x0021, KEY_DOWN }, + { 0x0025, KEY_POWER }, /* Turn off computer */ + { 0x0026, KEY_REWIND }, /* FR << */ + { 0x0027, KEY_FASTFORWARD }, /* FF >> */ + { 0x0029, KEY_ESC }, + { 0x002b, KEY_VOLUMEUP }, + { 0x002c, KEY_VOLUMEDOWN }, + { 0x002d, KEY_CHANNEL }, /* CH Surfing */ + { 0x0038, KEY_VIDEO }, /* TV/AV/S-Video/YPbPr */ +}; + +static struct rc_map_list total_media_in_hand_02_map = { + .map = { + .scan = total_media_in_hand_02, + .size = ARRAY_SIZE(total_media_in_hand_02), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_TOTAL_MEDIA_IN_HAND_02, + } +}; + +static int __init init_rc_map_total_media_in_hand_02(void) +{ + return rc_map_register(&total_media_in_hand_02_map); +} + +static void __exit exit_rc_map_total_media_in_hand_02(void) +{ + rc_map_unregister(&total_media_in_hand_02_map); +} + +module_init(init_rc_map_total_media_in_hand_02) +module_exit(exit_rc_map_total_media_in_hand_02) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(" Alfredo J. Delaiti <alfredodelaiti@netscape.net>"); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index b8aa9abb31ff..40125d779049 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0) || pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_port_valid(pdev, 1) || pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "Wake PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } nvt->cir_addr = pnp_port_start(pdev, 0); @@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) ret = nvt_hw_detect(nvt); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ nvt_efm_enable(nvt); @@ -1065,31 +1065,32 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* tx bits */ rdev->tx_resolution = XYZ; #endif + nvt->rdev = rdev; + + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; ret = -EBUSY; /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto exit_unregister_device; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure2; + goto exit_release_cir_addr; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure3; + goto exit_free_irq; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure4; - - ret = rc_register_device(rdev); - if (ret) - goto failure5; + goto exit_release_cir_wake_addr; device_init_wakeup(&pdev->dev, true); - nvt->rdev = rdev; + nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) { cir_dump_regs(nvt); @@ -1098,15 +1099,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -failure5: - free_irq(nvt->cir_wake_irq, nvt); -failure4: +exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); -failure3: +exit_free_irq: free_irq(nvt->cir_irq, nvt); -failure2: +exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); -failure: +exit_unregister_device: + rc_unregister_device(rdev); +exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 96f0a8bb39ea..5d87287ed372 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -165,56 +165,56 @@ void ir_raw_init(void); /* from ir-nec-decoder.c */ #ifdef CONFIG_IR_NEC_DECODER_MODULE -#define load_nec_decode() request_module("ir-nec-decoder") +#define load_nec_decode() request_module_nowait("ir-nec-decoder") #else static inline void load_nec_decode(void) { } #endif /* from ir-rc5-decoder.c */ #ifdef CONFIG_IR_RC5_DECODER_MODULE -#define load_rc5_decode() request_module("ir-rc5-decoder") +#define load_rc5_decode() request_module_nowait("ir-rc5-decoder") #else static inline void load_rc5_decode(void) { } #endif /* from ir-rc6-decoder.c */ #ifdef CONFIG_IR_RC6_DECODER_MODULE -#define load_rc6_decode() request_module("ir-rc6-decoder") +#define load_rc6_decode() request_module_nowait("ir-rc6-decoder") #else static inline void load_rc6_decode(void) { } #endif /* from ir-jvc-decoder.c */ #ifdef CONFIG_IR_JVC_DECODER_MODULE -#define load_jvc_decode() request_module("ir-jvc-decoder") +#define load_jvc_decode() request_module_nowait("ir-jvc-decoder") #else static inline void load_jvc_decode(void) { } #endif /* from ir-sony-decoder.c */ #ifdef CONFIG_IR_SONY_DECODER_MODULE -#define load_sony_decode() request_module("ir-sony-decoder") +#define load_sony_decode() request_module_nowait("ir-sony-decoder") #else static inline void load_sony_decode(void) { } #endif /* from ir-sanyo-decoder.c */ #ifdef CONFIG_IR_SANYO_DECODER_MODULE -#define load_sanyo_decode() request_module("ir-sanyo-decoder") +#define load_sanyo_decode() request_module_nowait("ir-sanyo-decoder") #else static inline void load_sanyo_decode(void) { } #endif /* from ir-mce_kbd-decoder.c */ #ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE -#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder") +#define load_mce_kbd_decode() request_module_nowait("ir-mce_kbd-decoder") #else static inline void load_mce_kbd_decode(void) { } #endif /* from ir-lirc-codec.c */ #ifdef CONFIG_IR_LIRC_CODEC_MODULE -#define load_lirc_codec() request_module("ir-lirc-codec") +#define load_lirc_codec() request_module_nowait("ir-lirc-codec") #else static inline void load_lirc_codec(void) { } #endif diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 601d1ac1c688..759a40a42eaa 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -789,8 +789,10 @@ static ssize_t show_protocols(struct device *device, } else if (dev->raw) { enabled = dev->raw->enabled_protocols; allowed = ir_raw_get_allowed_protocols(); - } else + } else { + mutex_unlock(&dev->lock); return -ENODEV; + } IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", (long long)allowed, @@ -890,7 +892,8 @@ static ssize_t store_protocols(struct device *device, if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + ret = -EINVAL; + goto out; } count++; diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 930c61499037..85424856ae7f 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1093,11 +1093,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->rx_resolution = US_TO_NS(2); data->dev->allowed_protos = RC_BIT_ALL; + err = rc_register_device(data->dev); + if (err) + goto exit_free_rc; + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); err = -EBUSY; - goto exit_free_rc; + goto exit_unregister_device; } if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { @@ -1122,24 +1126,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit_release_sbase; } - err = rc_register_device(data->dev); - if (err) - goto exit_free_irq; - device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_free_irq: - free_irq(data->irq, device); exit_release_sbase: release_region(data->sbase, SP_IOMEM_LEN); exit_release_ebase: release_region(data->ebase, EHFUNC_IOMEM_LEN); exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_unregister_device: + rc_unregister_device(data->dev); exit_free_rc: rc_free_device(data->dev); exit_unregister_led: diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 4577c917e616..1a86ce1d3fcf 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -21,20 +21,9 @@ #ifndef _FC0012_PRIV_H_ #define _FC0012_PRIV_H_ -#define LOG_PREFIX "fc0012" - -#undef err -#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) -#undef info -#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef warn -#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) - struct fc0012_priv { struct i2c_adapter *i2c; - u8 addr; - u8 dual_master; - u8 xtal_freq; + const struct fc0012_config *cfg; u32 frequency; u32 bandwidth; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 308135abd54c..f4d0e797a6cc 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -25,11 +25,13 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) { u8 buf[2] = {reg, val}; struct i2c_msg msg = { - .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + dev_err(&priv->i2c->dev, + "%s: I2C write reg failed, reg: %02x, val: %02x\n", + KBUILD_MODNAME, reg, val); return -EREMOTEIO; } return 0; @@ -38,12 +40,16 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) { struct i2c_msg msg[2] = { - { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = val, .len = 1 }, }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { - err("I2C read reg failed, reg: %02x", reg); + dev_err(&priv->i2c->dev, + "%s: I2C read reg failed, reg: %02x\n", + KBUILD_MODNAME, reg); return -EREMOTEIO; } return 0; @@ -88,7 +94,7 @@ static int fc0012_init(struct dvb_frontend *fe) 0x04, /* reg. 0x15: Enable LNA COMPS */ }; - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: case FC_XTAL_28_8_MHZ: reg[0x07] |= 0x20; @@ -98,9 +104,12 @@ static int fc0012_init(struct dvb_frontend *fe) break; } - if (priv->dual_master) + if (priv->cfg->dual_master) reg[0x0c] |= 0x02; + if (priv->cfg->loop_through) + reg[0x09] |= 0x01; + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ @@ -114,17 +123,12 @@ static int fc0012_init(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - err("fc0012_writereg failed: %d", ret); + dev_err(&priv->i2c->dev, "%s: fc0012_writereg failed: %d\n", + KBUILD_MODNAME, ret); return ret; } -static int fc0012_sleep(struct dvb_frontend *fe) -{ - /* nothing to do here */ - return 0; -} - static int fc0012_set_params(struct dvb_frontend *fe) { struct fc0012_priv *priv = fe->tuner_priv; @@ -144,7 +148,7 @@ static int fc0012_set_params(struct dvb_frontend *fe) goto exit; } - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: xtal_freq_khz_2 = 27000 / 2; break; @@ -256,7 +260,8 @@ static int fc0012_set_params(struct dvb_frontend *fe) break; } } else { - err("%s: modulation type not supported!", __func__); + dev_err(&priv->i2c->dev, "%s: modulation type not supported!\n", + KBUILD_MODNAME); return -EINVAL; } @@ -318,7 +323,8 @@ exit: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } @@ -331,8 +337,7 @@ static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - /* CHECK: always ? */ - *frequency = 0; + *frequency = 0; /* Zero-IF */ return 0; } @@ -408,7 +413,8 @@ err: fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ exit: if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } @@ -424,7 +430,6 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { .release = fc0012_release, .init = fc0012_init, - .sleep = fc0012_sleep, .set_params = fc0012_set_params, @@ -436,27 +441,73 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { }; struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + struct i2c_adapter *i2c, const struct fc0012_config *cfg) { - struct fc0012_priv *priv = NULL; + struct fc0012_priv *priv; + int ret; + u8 chip_id; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + if (!priv) { + ret = -ENOMEM; + dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } + priv->cfg = cfg; priv->i2c = i2c; - priv->dual_master = dual_master; - priv->addr = i2c_address; - priv->xtal_freq = xtal_freq; - info("Fitipower FC0012 successfully attached."); + /* check if the tuner is there */ + ret = fc0012_readreg(priv, 0x00, &chip_id); + if (ret < 0) + goto err; - fe->tuner_priv = priv; + dev_dbg(&i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + + switch (chip_id) { + case 0xa1: + break; + default: + ret = -ENODEV; + goto err; + } + + dev_info(&i2c->dev, "%s: Fitipower FC0012 successfully identified\n", + KBUILD_MODNAME); + + if (priv->cfg->loop_through) { + ret = fc0012_writereg(priv, 0x09, 0x6f); + if (ret < 0) + goto err; + } + + /* + * TODO: Clock out en or div? + * For dual tuner configuration clearing bit [0] is required. + */ + if (priv->cfg->clock_out) { + ret = fc0012_writereg(priv, 0x0b, 0x82); + if (ret < 0) + goto err; + } + fe->tuner_priv = priv; memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret) { + dev_dbg(&i2c->dev, "%s: failed: %d\n", __func__, ret); + kfree(priv); + return NULL; + } + return fe; } EXPORT_SYMBOL(fc0012_attach); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 4dbd5efe8845..54508fcc3469 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -24,19 +24,41 @@ #include "dvb_frontend.h" #include "fc001x-common.h" +struct fc0012_config { + /* + * I2C address + */ + u8 i2c_address; + + /* + * clock + */ + enum fc001x_xtal_freq xtal_freq; + + bool dual_master; + + /* + * RF loop-through + */ + bool loop_through; + + /* + * clock output + */ + bool clock_out; +}; + #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq); + const struct fc0012_config *cfg); #else static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + const struct fc0012_config *cfg) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5d9f02842501..e4a84ee231cf 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, { struct tda18212_priv *priv = NULL; int ret; - u8 uninitialized_var(val); + u8 val; priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); if (priv == NULL) @@ -296,8 +296,8 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != 0xc7) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 18198537be9f..2d31aeb6b088 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tda18218_config *cfg) { struct tda18218_priv *priv = NULL; - u8 uninitialized_var(val); + u8 val; int ret; /* chip default registers values */ static u8 def_regs[] = { @@ -302,8 +302,8 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, /* check if the tuner is there */ ret = tda18218_rd_reg(priv, R00_ID, &val); - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != def_regs[R00_ID]) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 72c26fd77922..e7786862dab2 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1122,6 +1122,7 @@ static int tda18271_dump_std_map(struct dvb_frontend *fe) tda18271_dump_std_item(dvbt_7, "dvbt 7"); tda18271_dump_std_item(dvbt_8, "dvbt 8"); tda18271_dump_std_item(qam_6, "qam 6 "); + tda18271_dump_std_item(qam_7, "qam 7 "); tda18271_dump_std_item(qam_8, "qam 8 "); return 0; @@ -1149,6 +1150,7 @@ static int tda18271_update_std_map(struct dvb_frontend *fe, tda18271_update_std(dvbt_7, "dvbt 7"); tda18271_update_std(dvbt_8, "dvbt 8"); tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_7, "qam 7"); tda18271_update_std(qam_8, "qam 8"); return 0; diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c index fb881c667c94..b62e925f643f 100644 --- a/drivers/media/tuners/tda18271-maps.c +++ b/drivers/media/tuners/tda18271-maps.c @@ -1290,13 +1290,11 @@ int tda18271_assign_map_layout(struct dvb_frontend *fe) switch (priv->id) { case TDA18271HDC1: priv->maps = &tda18271c1_map_layout; - memcpy(&priv->std, &tda18271c1_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c1_std_map; break; case TDA18271HDC2: priv->maps = &tda18271c2_map_layout; - memcpy(&priv->std, &tda18271c2_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c2_std_map; break; default: ret = -EINVAL; diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 7bcb6b0ff1df..09451737c77e 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -870,7 +870,7 @@ check_device: } read_not_reliable: - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 5c0fd787cc8f..2018befabb5a 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1066,7 +1066,7 @@ check_device: goto fail; } - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 6746994d03fe..0a7d520636a9 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -21,7 +21,6 @@ endif if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" -source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" @@ -31,6 +30,7 @@ endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Analog/digital TV USB devices" +source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" source "drivers/media/usb/tm6000/Kconfig" endif diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 1766c0ce93be..953a37c613b1 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -1,17 +1,28 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on I2C && INPUT && DVB_CORE && USB select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT - select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- - This is a video4linux driver for Auvitek's USB device. + This is a hybrid analog/digital tv capture driver for + Auvitek's AU0828 USB device. To compile this driver as a module, choose M here: the module will be called au0828 + +config VIDEO_AU0828_V4L2 + bool "Auvitek AU0828 v4l2 analog video support" + depends on VIDEO_AU0828 && VIDEO_V4L2 + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + default y + ---help--- + This is a video4linux driver for Auvitek's USB device. + + Choose Y here to include support for v4l2 analog video + capture within the au0828 driver. diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile index 98cc20cc0ffb..be3bdf698022 100644 --- a/drivers/media/usb/au0828/Makefile +++ b/drivers/media/usb/au0828/Makefile @@ -1,4 +1,8 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y) + au0828-objs += au0828-video.o au0828-vbi.o +endif obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 0cb7c28dcb17..dd32decb237d 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -169,7 +169,9 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; @@ -183,16 +185,15 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) __func__, tv.model); } +void au0828_card_analog_fe_setup(struct au0828_dev *dev); + void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; - struct tuner_setup tun_setup; - struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV; dprintk(1, "%s()\n", __func__); - memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + dev->board = au0828_boards[dev->boardnr]; if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; @@ -209,6 +210,16 @@ void au0828_card_setup(struct au0828_dev *dev) break; } + au0828_card_analog_fe_setup(dev); +} + +void au0828_card_analog_fe_setup(struct au0828_dev *dev) +{ +#ifdef CONFIG_VIDEO_AU0828_V4L2 + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV; + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different @@ -234,6 +245,7 @@ void au0828_card_setup(struct au0828_dev *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); } +#endif } /* @@ -333,6 +345,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7213), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7270), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 745a80a798c8..1e6f40ef1c6b 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -134,13 +134,17 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_unregister(dev); +#endif /* I2C */ au0828_i2c_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 v4l2_device_unregister(&dev->v4l2_dev); +#endif usb_set_intfdata(interface, NULL); @@ -155,7 +159,10 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum, retval; + int ifnum; +#ifdef CONFIG_VIDEO_AU0828_V4L2 + int retval; +#endif struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -194,6 +201,7 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->usbdev = usbdev; dev->boardnr = id->driver_info; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Create the v4l2_device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { @@ -203,6 +211,7 @@ static int au0828_usb_probe(struct usb_interface *interface, kfree(dev); return -EIO; } +#endif /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -216,9 +225,11 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog TV */ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_register(dev, interface); +#endif /* Digital TV */ au0828_dvb_register(dev); diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 4ded17fe1957..17ec3651b10e 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -364,12 +364,9 @@ int au0828_i2c_register(struct au0828_dev *dev) { dprintk(1, "%s()\n", __func__); - memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, - sizeof(dev->i2c_adap)); - memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, - sizeof(dev->i2c_algo)); - memcpy(&dev->i2c_client, &au0828_i2c_client_template, - sizeof(dev->i2c_client)); + dev->i2c_adap = au0828_i2c_adap_template; + dev->i2c_algo = au0828_i2c_algo_template; + dev->i2c_client = au0828_i2c_client_template; dev->i2c_adap.dev.parent = &dev->usbdev->dev; @@ -378,7 +375,11 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; +#ifdef CONFIG_VIDEO_AU0828_V4L2 i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); +#else + i2c_set_adapdata(&dev->i2c_adap, dev); +#endif i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 45387aab10c7..8b9e8268e911 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -304,7 +304,7 @@ static inline void buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.buf = NULL; @@ -321,7 +321,7 @@ static inline void vbi_buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 66a56ef7bbe4..e579ff69ca4a 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -199,8 +199,10 @@ struct au0828_dev { struct au0828_dvb dvb; struct work_struct restart_streaming; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ struct v4l2_device v4l2_dev; +#endif int users; unsigned int resources; /* resources in use */ struct video_device *vdev; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index 95b5d6e7cdc4..be1719283609 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -328,7 +328,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - do_gettimeofday(&cam->workbuff->timestamp); + v4l2_get_timestamp(&cam->workbuff->timestamp); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index aeb9d2275725..d5d42b6e94be 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -825,6 +825,8 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) else buf->flags = 0; + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + switch (cam->buffers[buf->index].status) { case FRAME_EMPTY: case FRAME_ERROR: @@ -943,7 +945,8 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = frame; buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = cam->buffers[buf->index].timestamp; buf->sequence = cam->buffers[buf->index].seq; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b024e5197a75..28688dbcb609 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1291,7 +1291,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1327,7 +1327,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index bbed1e40eeda..8d529565f163 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -603,6 +603,33 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2] = { + .name = "Elgato Video Capture V2", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -642,6 +669,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, {USB_DEVICE(0x1f4d, 0x0237), .driver_info = CX231XX_BOARD_ICONBIT_U100}, + {USB_DEVICE(0x0fd9, 0x0037), + .driver_info = CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2}, {}, }; @@ -707,7 +736,7 @@ static void cx231xx_sleep_s5h1432(struct cx231xx *dev) static inline void cx231xx_set_model(struct cx231xx *dev) { - memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); + dev->board = cx231xx_boards[dev->model]; } /* Since cx231xx_pre_card_setup() requires a proper dev->model, diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index ac7db52f404f..46e3892557c2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -530,7 +530,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index fedf7852a355..93dfc182ec51 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -235,7 +235,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; @@ -2627,8 +2627,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->name, video_device_node_name(dev->vdev)); /* Initialize VBI template */ - memcpy(&cx231xx_vbi_template, &cx231xx_video_template, - sizeof(cx231xx_vbi_template)); + cx231xx_vbi_template = cx231xx_video_template; strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); /* Allocate and fill vbi video_device struct */ diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index a89d020de948..3e11462be0d0 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -68,6 +68,7 @@ #define CX231XX_BOARD_ICONBIT_U100 13 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 +#define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 834bfecbed73..7b5773fe6367 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -1,6 +1,6 @@ config DVB_USB_V2 tristate "Support for various USB DVB devices v2" - depends on DVB_CORE && USB && I2C && RC_CORE + depends on DVB_CORE && USB && I2C help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -113,6 +113,7 @@ config DVB_USB_IT913X config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" depends on DVB_USB_V2 + depends on RC_CORE select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT @@ -120,6 +121,7 @@ config DVB_USB_LME2510 select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 943d93423705..b86d0f27a398 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1156,6 +1156,7 @@ error: return ret; } +#if IS_ENABLED(CONFIG_RC_CORE) struct af9015_rc_setup { unsigned int id; char *rc_codes; @@ -1312,6 +1313,9 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define af9015_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 61ae7f9d0b27..f11cc42454f0 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -209,10 +209,15 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40 || msg[1].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { - /* integrated demod */ + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + + if (msg[0].addr == state->af9033_config[1].i2c_addr) + reg |= 0x100000; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { @@ -220,6 +225,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), buf, msg[1].len, msg[1].buf }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[1].len; buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ @@ -232,10 +238,15 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { - /* integrated demod */ + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + + if (msg[0].addr == state->af9033_config[1].i2c_addr) + reg |= 0x100000; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { @@ -243,6 +254,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, 0, NULL }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ @@ -313,12 +325,57 @@ static int af9035_download_firmware(struct dvb_usb_device *d, struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; - u8 hdr_core; + u8 hdr_core, tmp; u16 hdr_addr, hdr_data_len, hdr_checksum; #define MAX_DATA 58 #define HDR_SIZE 7 /* + * In case of dual tuner configuration we need to do some extra + * initialization in order to download firmware to slave demod too, + * which is done by master demod. + * Master feeds also clock and controls power via GPIO. + */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* configure gpioh1, reset & power slave demod */ + ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8b1, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x01, 0x01); + if (ret < 0) + goto err; + + /* tell the slave I2C address */ + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + /* enable clock out */ + ret = af9035_wr_reg_mask(d, 0x00d81a, 0x01, 0x01); + if (ret < 0) + goto err; + } + + /* * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info! * * byte 0: MCS 51 core @@ -380,6 +437,10 @@ static int af9035_download_firmware(struct dvb_usb_device *d, __func__, fw->size - i); } + /* print warn if firmware is bad, continue and see what happens */ + if (i) + dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); + /* firmware loaded, request boot */ req.cmd = CMD_FW_BOOT; ret = af9035_ctrl_msg(d, &req); @@ -489,14 +550,28 @@ static int af9035_read_config(struct dvb_usb_device *d) u8 tmp; u16 tmp16; + /* demod I2C "address" */ + state->af9033_config[0].i2c_addr = 0x38; + /* check if there is dual tuners */ ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); if (ret < 0) goto err; state->dual_mode = tmp; - dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", - __func__, state->dual_mode); + dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__, + state->dual_mode); + + if (state->dual_mode) { + /* read 2nd demodulator I2C address */ + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); + if (ret < 0) + goto err; + + state->af9033_config[1].i2c_addr = tmp; + dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", + __func__, tmp); + } for (i = 0; i < state->dual_mode + 1; i++) { /* tuner */ @@ -514,6 +589,7 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_MXL5007T: case AF9033_TUNER_TDA18218: case AF9033_TUNER_FC2580: + case AF9033_TUNER_FC0012: state->af9033_config[i].spec_inv = 1; break; default: @@ -522,6 +598,18 @@ static int af9035_read_config(struct dvb_usb_device *d) KBUILD_MODNAME, tmp); } + /* disable dual mode if driver does not support it */ + if (i == 1) + switch (tmp) { + case AF9033_TUNER_FC0012: + break; + default: + state->dual_mode = false; + dev_info(&d->udev->dev, "%s: driver does not " \ + "support 2nd tuner and will " \ + "disable it", KBUILD_MODNAME); + } + /* tuner IF frequency */ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); if (ret < 0) @@ -730,6 +818,12 @@ static int af9035_frontend_callback(void *adapter_priv, int component, return 0; } +static int af9035_get_adapter_count(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + return state->dual_mode + 1; +} + static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -751,15 +845,14 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) if (ret < 0) goto err; - ret = af9035_wr_reg(d, 0x00d81a, - state->dual_mode); + ret = af9035_wr_reg(d, 0x00d81a, state->dual_mode); if (ret < 0) goto err; } /* attach demodulator */ - adap->fe[0] = dvb_attach(af9033_attach, - &state->af9033_config[adap->id], &d->i2c_adap); + adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id], + &d->i2c_adap); if (adap->fe[0] == NULL) { ret = -ENODEV; goto err; @@ -785,13 +878,22 @@ static const struct fc0011_config af9035_fc0011_config = { .i2c_address = 0x60, }; -static struct mxl5007t_config af9035_mxl5007t_config = { - .xtal_freq_hz = MxL_XTAL_24_MHZ, - .if_freq_hz = MxL_IF_4_57_MHZ, - .invert_if = 0, - .loop_thru_enable = 0, - .clk_out_enable = 0, - .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +static struct mxl5007t_config af9035_mxl5007t_config[] = { + { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + }, { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 1, + .clk_out_enable = 1, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + } }; static struct tda18218_config af9035_tda18218_config = { @@ -804,12 +906,32 @@ static const struct fc2580_config af9035_fc2580_config = { .clock = 16384000, }; +static const struct fc0012_config af9035_fc0012_config[] = { + { + .i2c_address = 0x63, + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = true, + .loop_through = true, + .clock_out = true, + }, { + .i2c_address = 0x63 | 0x80, /* I2C bus select hack */ + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = true, + } +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); int ret; struct dvb_frontend *fe; + struct i2c_msg msg[1]; + u8 tuner_addr; + /* + * XXX: Hack used in that function: we abuse unused I2C address bit [7] + * to carry info about used I2C bus for dual tuner configuration. + */ switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: @@ -842,46 +964,59 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) &d->i2c_adap, &af9035_fc0011_config); break; case AF9033_TUNER_MXL5007T: - ret = af9035_wr_reg(d, 0x00d8e0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8e1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8df, 0); - if (ret < 0) - goto err; + if (adap->id == 0) { + ret = af9035_wr_reg(d, 0x00d8e0, 1); + if (ret < 0) + goto err; - msleep(30); + ret = af9035_wr_reg(d, 0x00d8e1, 1); + if (ret < 0) + goto err; - ret = af9035_wr_reg(d, 0x00d8df, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); + if (ret < 0) + goto err; - msleep(300); + msleep(30); - ret = af9035_wr_reg(d, 0x00d8c0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8c1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8bf, 0); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b4, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b5, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b3, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8df, 1); + if (ret < 0) + goto err; + + msleep(300); + + ret = af9035_wr_reg(d, 0x00d8c0, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d8c1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d8bf, 0); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d8b4, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d8b5, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d8b3, 1); + if (ret < 0) + goto err; + + tuner_addr = 0x60; + } else { + tuner_addr = 0x60 | 0x80; /* I2C bus hack */ + } /* attach tuner */ - fe = dvb_attach(mxl5007t_attach, adap->fe[0], - &d->i2c_adap, 0x60, &af9035_mxl5007t_config); + fe = dvb_attach(mxl5007t_attach, adap->fe[0], &d->i2c_adap, + tuner_addr, &af9035_mxl5007t_config[adap->id]); break; case AF9033_TUNER_TDA18218: /* attach tuner */ @@ -907,6 +1042,46 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, &af9035_fc2580_config); break; + case AF9033_TUNER_FC0012: + /* + * AF9035 gpiot2 = FC0012 enable + * XXX: there seems to be something on gpioh8 too, but on my + * my test I didn't find any difference. + */ + + if (adap->id == 0) { + /* configure gpiot2 as output and high */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + /* + * FIXME: That belongs for the FC0012 driver. + * Write 02 to FC0012 master tuner register 0d directly + * in order to make slave tuner working. + */ + msg[0].addr = 0x63; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = "\x0d\x02"; + ret = i2c_transfer(&d->i2c_adap, msg, 1); + if (ret < 0) + goto err; + } + + usleep_range(10000, 50000); + + fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, + &af9035_fc0012_config[adap->id]); + break; default: fe = NULL; } @@ -945,8 +1120,8 @@ static int af9035_init(struct dvb_usb_device *d) { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, { 0x00dd0d, packet_size, 0xff }, - { 0x80f9a3, 0x00, 0x01 }, - { 0x80f9cd, 0x00, 0x01 }, + { 0x80f9a3, state->dual_mode, 0x01 }, + { 0x80f9cd, state->dual_mode, 0x01 }, { 0x80f99d, 0x00, 0x01 }, { 0x80f9a4, 0x00, 0x01 }, }; @@ -971,6 +1146,7 @@ err: return ret; } +#if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { unsigned int key; @@ -1045,6 +1221,9 @@ err: return ret; } +#else + #define af9035_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ @@ -1068,7 +1247,7 @@ static const struct dvb_usb_device_properties af9035_props = { .init = af9035_init, .get_rc_config = af9035_get_rc_config, - .num_adapters = 1, + .get_adapter_count = af9035_get_adapter_count, .adapter = { { .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 75ef1ec13fbf..29f3eec22c2c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -26,6 +26,7 @@ #include "af9033.h" #include "tua9001.h" #include "fc0011.h" +#include "fc0012.h" #include "mxl5007t.h" #include "tda18218.h" #include "fc2580.h" @@ -53,7 +54,6 @@ struct usb_req { struct state { u8 seq; /* packet sequence number */ bool dual_mode; - struct af9033_config af9033_config[2]; }; @@ -91,6 +91,7 @@ u32 clock_lut_it9135[] = { /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_2ND_DEMOD_ADDR 0x4327 #define EEPROM_IR_TYPE 0x4329 #define EEPROM_1_IFFREQ_L 0x432d #define EEPROM_1_IFFREQ_H 0x432e diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index d05c5b563dac..a20d691d0b63 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1019,6 +1019,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) return ret; } +#if IS_ENABLED(CONFIG_RC_CORE) static int anysee_rc_query(struct dvb_usb_device *d) { u8 buf[] = {CMD_GET_IR_CODE}; @@ -1054,6 +1055,9 @@ static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define anysee_get_rc_config NULL +#endif static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot, int addr) diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index d75dbf27e99e..70ec80d8be71 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -189,6 +189,7 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) return az6007_write(d, 0xbc, onoff, 0, NULL, 0); } +#if IS_ENABLED(CONFIG_RC_CORE) /* remote control stuff (does not work with my box) */ static int az6007_rc_query(struct dvb_usb_device *d) { @@ -215,6 +216,20 @@ static int az6007_rc_query(struct dvb_usb_device *d) return 0; } +static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + pr_debug("Getting az6007 Remote Control properties\n"); + + rc->allowed_protos = RC_BIT_NEC; + rc->query = az6007_rc_query; + rc->interval = 400; + + return 0; +} +#else + #define az6007_get_rc_config NULL +#endif + static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) @@ -822,17 +837,6 @@ static void az6007_usb_disconnect(struct usb_interface *intf) dvb_usbv2_disconnect(intf); } -static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - pr_debug("Getting az6007 Remote Control properties\n"); - - rc->allowed_protos = RC_BIT_NEC; - rc->query = az6007_rc_query; - rc->interval = 400; - - return 0; -} - static int az6007_download_firmware(struct dvb_usb_device *d, const struct firmware *fw) { diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 059291b892b8..3cac8bd0b116 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -347,6 +347,7 @@ struct dvb_usb_adapter { * @props: device properties * @name: device name * @rc_map: name of rc codes table + * @rc_polling_active: set when RC polling is active * @udev: pointer to the device's struct usb_device * @intf: pointer to the device's usb interface * @rc: remote controller configuration @@ -364,7 +365,7 @@ struct dvb_usb_device { const struct dvb_usb_device_properties *props; const char *name; const char *rc_map; - + bool rc_polling_active; struct usb_device *udev; struct usb_interface *intf; struct dvb_usb_rc rc; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 671b4fa232b4..086792055912 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -102,6 +102,7 @@ static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) return 0; } +#if IS_ENABLED(CONFIG_RC_CORE) static void dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, @@ -112,13 +113,16 @@ static void dvb_usb_read_remote_control(struct work_struct *work) * When the parameter has been set to 1 via sysfs while the * driver was running, or when bulk mode is enabled after IR init. */ - if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) + if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) { + d->rc_polling_active = false; return; + } ret = d->rc.query(d); if (ret < 0) { dev_err(&d->udev->dev, "%s: rc.query() failed=%d\n", KBUILD_MODNAME, ret); + d->rc_polling_active = false; return; /* stop polling */ } @@ -182,6 +186,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) d->rc.interval); schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); + d->rc_polling_active = true; } return 0; @@ -202,6 +207,10 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) return 0; } +#else + #define dvb_usbv2_remote_init(args...) 0 + #define dvb_usbv2_remote_exit(args...) +#endif static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, size_t len) @@ -959,7 +968,7 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&d->udev->dev, "%s:\n", __func__); /* stop remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) cancel_delayed_work_sync(&d->rc_query_work); for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { @@ -1006,7 +1015,7 @@ static int dvb_usbv2_resume_common(struct dvb_usb_device *d) } /* start remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 47204280b8b3..833847995c65 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -308,7 +308,7 @@ static struct i2c_algorithm it913x_i2c_algo = { }; /* Callbacks for DVB USB */ -#define IT913X_POLL 250 +#if IS_ENABLED(CONFIG_RC_CORE) static int it913x_rc_query(struct dvb_usb_device *d) { u8 ibuf[4]; @@ -334,6 +334,25 @@ static int it913x_rc_query(struct dvb_usb_device *d) return ret; } +static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + struct it913x_state *st = d->priv; + + if (st->proprietary_ir == false) { + rc->map_name = NULL; + return 0; + } + + rc->allowed_protos = RC_BIT_NEC; + rc->query = it913x_rc_query; + rc->interval = 250; + + return 0; +} +#else + #define it913x_get_rc_config NULL +#endif + /* Firmware sets raw */ static const char fw_it9135_v1[] = FW_IT9135_V1; static const char fw_it9135_v2[] = FW_IT9135_V2; @@ -643,7 +662,8 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) struct it913x_state *st = d->priv; int ret = 0; u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); - u16 ep_size = adap->stream.buf_size / 4; + u16 ep_size = (adap->pid_filtering) ? TS_BUFFER_SIZE_PID / 4 : + TS_BUFFER_SIZE_MAX / 4; u8 pkt_size = 0x80; if (d->udev->speed != USB_SPEED_HIGH) @@ -695,22 +715,6 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) } /* DVB USB Driver */ -static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - struct it913x_state *st = d->priv; - - if (st->proprietary_ir == false) { - rc->map_name = NULL; - return 0; - } - - rc->allowed_protos = RC_BIT_NEC; - rc->query = it913x_rc_query; - rc->interval = 250; - - return 0; -} - static int it913x_get_adapter_count(struct dvb_usb_device *d) { struct it913x_state *st = d->priv; @@ -779,6 +783,18 @@ static const struct usb_device_id it913x_id_table[] = { { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006, &it913x_properties, "ITE 9135(9006) Generic", RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835, + &it913x_properties, "Avermedia A835B(1835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835, + &it913x_properties, "Avermedia A835B(2835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835, + &it913x_properties, "Avermedia A835B(3835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835, + &it913x_properties, "Avermedia A835B(4835)", + RC_MAP_IT913X_V2) }, {} /* Terminating entry */ }; @@ -797,7 +813,7 @@ module_usb_driver(it913x_driver); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.32"); +MODULE_VERSION("1.33"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FW_IT9135_V1); MODULE_FIRMWARE(FW_IT9135_V2); diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 6427ac359f21..f30c58cecbba 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -81,6 +81,7 @@ #include "dvb-pll.h" #include "z0194a.h" #include "m88rs2000.h" +#include "ts2020.h" #define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw"; @@ -626,8 +627,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], gate = 5; for (i = 0; i < num; i++) { - read_o = 1 & (msg[i].flags & I2C_M_RD); - read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read_o = msg[i].flags & I2C_M_RD; + read = i + 1 < num && msg[i + 1].flags & I2C_M_RD; read |= read_o; gate = (msg[i].addr == st->i2c_tuner_addr) ? (read) ? st->i2c_tuner_gate_r @@ -640,7 +641,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], else obuf[1] = msg[i].len + read + 1; - obuf[2] = msg[i].addr; + obuf[2] = msg[i].addr << 1; + if (read) { if (read_o) len = 3; @@ -894,27 +896,27 @@ static int lme2510_kill_urb(struct usb_data_stream *stream) } static struct tda10086_config tda10086_config = { - .demod_address = 0x1c, + .demod_address = 0x0e, .invert = 0, .diseqc_tone = 1, .xtal_freq = TDA10086_XTAL_16M, }; static struct stv0288_config lme_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .min_delay_ms = 15, .inittab = s7395_inittab, }; static struct ix2505v_config lme_tuner = { - .tuner_address = 0xc0, + .tuner_address = 0x60, .min_delay_ms = 100, .tuner_gain = 0x0, .tuner_chargepump = 0x3, }; static struct stv0299_config sharp_z0194_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .inittab = sharp_z0194a_inittab, .mclk = 88000000UL, .invert = 0, @@ -943,11 +945,15 @@ static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, } static struct m88rs2000_config m88rs2000_config = { - .demod_addr = 0xd0, - .tuner_addr = 0xc0, + .demod_addr = 0x68, .set_ts_params = dm04_rs2000_set_ts_param, }; +static struct ts2020_config ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 7, +}; + static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -1049,7 +1055,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("TUN Found Frontend TDA10086"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 4; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_LG; if (st->dvb_usb_lme2510_firmware != TUNER_LG) { st->dvb_usb_lme2510_firmware = TUNER_LG; @@ -1065,7 +1071,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0299"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S0194; if (st->dvb_usb_lme2510_firmware != TUNER_S0194) { st->dvb_usb_lme2510_firmware = TUNER_S0194; @@ -1082,7 +1088,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0288"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S7395; if (st->dvb_usb_lme2510_firmware != TUNER_S7395) { st->dvb_usb_lme2510_firmware = TUNER_S7395; @@ -1097,9 +1103,11 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) if (adap->fe[0]) { info("FE Found M88RS2000"); + dvb_attach(ts2020_attach, adap->fe[0], &ts2020_config, + &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_RS2000; st->fe_set_voltage = adap->fe[0]->ops.set_voltage; @@ -1144,7 +1152,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) switch (st->tuner_config) { case TUNER_LG: - if (dvb_attach(tda826x_attach, adap->fe[0], 0xc0, + if (dvb_attach(tda826x_attach, adap->fe[0], 0x60, &d->i2c_adap, 1)) ret = st->tuner_config; break; @@ -1154,7 +1162,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) ret = st->tuner_config; break; case TUNER_S0194: - if (dvb_attach(dvb_pll_attach , adap->fe[0], 0xc0, + if (dvb_attach(dvb_pll_attach , adap->fe[0], 0x60, &d->i2c_adap, DVB_PLL_OPERA1)) ret = st->tuner_config; break; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a4c302d0aa37..4ab0dd8dd9a8 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -835,6 +835,11 @@ static struct tua9001_config rtl2832u_tua9001_config = { .i2c_addr = 0x60, }; +static const struct fc0012_config rtl2832u_fc0012_config = { + .i2c_address = 0x63, /* 0xc6 >> 1 */ + .xtal_freq = FC_XTAL_28_8_MHZ, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -847,7 +852,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) switch (priv->tuner) { case TUNER_RTL2832_FC0012: fe = dvb_attach(fc0012_attach, adap->fe[0], - &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + &d->i2c_adap, &rtl2832u_fc0012_config); /* since fc0012 includs reading the signal strength delegate * that to the tuner driver */ @@ -1120,7 +1125,7 @@ err: return ret; } - +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1203,7 +1208,11 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2831u_get_rc_config NULL +#endif +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1275,6 +1284,9 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2832u_get_rc_config NULL +#endif static const struct dvb_usb_device_properties rtl2831u_props = { .driver_name = KBUILD_MODNAME, @@ -1333,13 +1345,13 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, - &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) }, + &rtl2832u_props, "TerraTec Cinergy T Stick Black", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle (rev 2)", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, @@ -1352,6 +1364,10 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7, &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, + &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, + &rtl2832u_props, "GIGABYTE U7300", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index fa0b2931d305..c423eb80c3a5 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -267,9 +267,11 @@ config DVB_USB_DW2102 select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 receivers. diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 19b5ed2825d7..bf2a908d74cf 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -561,10 +561,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } } - if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { - err("could not acquire lock"); - return -EINTR; - } + mutex_lock(&adap->dev->usb_mutex); st->buf[0] = REQUEST_ENABLE_VIDEO; /* this bit gives a kind of command, diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 169196ec2d4e..1adf325012f7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -38,41 +38,41 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); - for (o = 0; o < adap->props.num_frontends; o++) { - struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; - /* speed - when running at FULL speed we need a HW PID filter */ - if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { - err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); - return -ENODEV; - } + for (o = 0; o < adap->props.num_frontends; o++) { + struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; + /* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); + return -ENODEV; + } - if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || - (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } else { - info("will pass the complete MPEG2 transport stream to the software demuxer."); - adap->fe_adap[o].pid_filtering = 0; - adap->fe_adap[o].max_feed_count = 255; - } + if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } else { + info("will pass the complete MPEG2 transport stream to the software demuxer."); + adap->fe_adap[o].pid_filtering = 0; + adap->fe_adap[o].max_feed_count = 255; + } - if (!adap->fe_adap[o].pid_filtering && - dvb_usb_force_pid_filter_usage && - props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { - info("pid filter enabled by module option."); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } + if (!adap->fe_adap[o].pid_filtering && + dvb_usb_force_pid_filter_usage && + props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { + info("pid filter enabled by module option."); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } - if (props->size_of_priv > 0) { - adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); - if (adap->fe_adap[o].priv == NULL) { - err("no memory for priv for adapter %d fe %d.", n, o); - return -ENOMEM; + if (props->size_of_priv > 0) { + adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); + if (adap->fe_adap[o].priv == NULL) { + err("no memory for priv for adapter %d fe %d.", n, o); + return -ENOMEM; + } } } - } if (adap->props.size_of_priv > 0) { adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 9382895b1b88..9578a6761f1b 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1,9 +1,9 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, - * TeVii S600, S630, S650, S660, S480, + * TeVii S600, S630, S650, S660, S480, S421, S632 * Prof 1100, 7500, * Geniatech SU3000 Cards - * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) + * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -22,11 +22,14 @@ #include "tda1002x.h" #include "mt312.h" #include "zl10039.h" +#include "ts2020.h" #include "ds3000.h" #include "stv0900.h" #include "stv6110.h" #include "stb6100.h" #include "stb6100_proc.h" +#include "m88rs2000.h" +#include "ts2020.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -68,6 +71,14 @@ #define USB_PID_PROF_1100 0xb012 #endif +#ifndef USB_PID_TEVII_S421 +#define USB_PID_TEVII_S421 0xd421 +#endif + +#ifndef USB_PID_TEVII_S632 +#define USB_PID_TEVII_S632 0xd632 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -80,6 +91,15 @@ #define DW2102_RC_QUERY (0x1a00) #define DW2102_LED_CTRL (0x1b00) +#define DW2101_FIRMWARE "dvb-usb-dw2101.fw" +#define DW2102_FIRMWARE "dvb-usb-dw2102.fw" +#define DW2104_FIRMWARE "dvb-usb-dw2104.fw" +#define DW3101_FIRMWARE "dvb-usb-dw3101.fw" +#define S630_FIRMWARE "dvb-usb-s630.fw" +#define S660_FIRMWARE "dvb-usb-s660.fw" +#define P1100_FIRMWARE "dvb-usb-p1100.fw" +#define P7500_FIRMWARE "dvb-usb-p7500.fw" + #define err_str "did not find the firmware file. (%s) " \ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." @@ -534,7 +554,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } /*case 0x55: cx24116 case 0x6a: stv0903 - case 0x68: ds3000, stv0903 + case 0x68: ds3000, stv0903, rs2000 case 0x60: ts2020, stv6110, stb6100 case 0xa0: eeprom */ default: { @@ -932,6 +952,17 @@ static struct ds3000_config dw2104_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dw2104_ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 1, +}; + +static struct ds3000_config s660_ds3000_config = { + .demod_address = 0x68, + .ci_mode = 1, + .set_lock_led = dw210x_led_ctrl, +}; + static struct stv0900_config dw2104a_stv0900_config = { .demod_address = 0x6a, .demod_mode = 0, @@ -981,6 +1012,30 @@ static struct stv0900_config prof_7500_stv0900_config = { static struct ds3000_config su3000_ds3000_config = { .demod_address = 0x68, .ci_mode = 1, + .set_lock_led = dw210x_led_ctrl, +}; + +static u8 m88rs2000_inittab[] = { + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0x00, 0x01, + WRITE_DELAY, 0x19, 0x00, + DEMOD_WRITE, 0x00, 0x00, + DEMOD_WRITE, 0x9a, 0xb0, + DEMOD_WRITE, 0x81, 0xc1, + DEMOD_WRITE, 0x81, 0x81, + DEMOD_WRITE, 0x86, 0xc6, + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0xf0, 0x80, + DEMOD_WRITE, 0xf1, 0xbf, + DEMOD_WRITE, 0xb0, 0x45, + DEMOD_WRITE, 0xb2, 0x01, + DEMOD_WRITE, 0x9a, 0xb0, + 0xff, 0xaa, 0xff +}; + +static struct m88rs2000_config s421_m88rs2000_config = { + .demod_addr = 0x68, + .inittab = m88rs2000_inittab, }; static int dw2104_frontend_attach(struct dvb_usb_adapter *d) @@ -1033,6 +1088,8 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe != NULL) { + dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, &d->dev->i2c_adap); d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached DS3000!\n"); return 0; @@ -1139,12 +1196,15 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; u8 obuf[] = {7, 1}; - d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &s660_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe == NULL) return -EIO; + dvb_attach(ts2020_attach, d->fe_adap[0].fe, &dw2104_ts2020_config, + &d->dev->i2c_adap); + st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; @@ -1182,6 +1242,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) err("command 0x0e transfer failed."); obuf[0] = 0xe; + obuf[1] = 0x02; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + msleep(300); + + obuf[0] = 0xe; obuf[1] = 0x83; obuf[2] = 0; @@ -1205,9 +1273,40 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; - info("Attached DS3000!\n"); + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached DS3000/TS2020!\n"); + return 0; + } - return 0; + info("Failed to attach DS3000/TS2020!\n"); + return -EIO; +} + +static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[] = { 0x51 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config, + &d->dev->i2c_adap); + + if (d->fe_adap[0].fe == NULL) + return -EIO; + + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached RS2000/TS2020!\n"); + return 0; + } + + info("Failed to attach RS2000/TS2020!\n"); + return -EIO; } static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) @@ -1447,6 +1546,8 @@ enum dw2102_table_entry { TEVII_S480_1, TEVII_S480_2, X3M_SPC1400HD, + TEVII_S421, + TEVII_S632, }; static struct usb_device_id dw2102_table[] = { @@ -1465,6 +1566,8 @@ static struct usb_device_id dw2102_table[] = { [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, + [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)}, + [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)}, { } }; @@ -1478,13 +1581,12 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset; u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; - const char *fw_2101 = "dvb-usb-dw2101.fw"; switch (dev->descriptor.idProduct) { case 0x2101: - ret = request_firmware(&fw, fw_2101, &dev->dev); + ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev); if (ret != 0) { - err(err_str, fw_2101); + err(err_str, DW2101_FIRMWARE); return ret; } break; @@ -1586,7 +1688,7 @@ static int dw2102_load_firmware(struct usb_device *dev, static struct dvb_usb_device_properties dw2102_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2102.fw", + .firmware = DW2102_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, @@ -1641,7 +1743,7 @@ static struct dvb_usb_device_properties dw2102_properties = { static struct dvb_usb_device_properties dw2104_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2104.fw", + .firmware = DW2104_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, @@ -1691,7 +1793,7 @@ static struct dvb_usb_device_properties dw2104_properties = { static struct dvb_usb_device_properties dw3101_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw3101.fw", + .firmware = DW3101_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, @@ -1739,7 +1841,7 @@ static struct dvb_usb_device_properties s6x0_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .size_of_priv = sizeof(struct s6x0_state), - .firmware = "dvb-usb-s630.fw", + .firmware = S630_FIRMWARE, .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, @@ -1814,6 +1916,19 @@ static struct dvb_usb_device_description d7500 = { {NULL}, }; +struct dvb_usb_device_properties *s421; +static struct dvb_usb_device_description d421 = { + "TeVii S421 PCI", + {&dw2102_table[TEVII_S421], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d632 = { + "TeVii S632 USB", + {&dw2102_table[TEVII_S632], NULL}, + {NULL}, +}; + static struct dvb_usb_device_properties su3000_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, @@ -1879,7 +1994,7 @@ static int dw2102_probe(struct usb_interface *intf, return -ENOMEM; /* copy default structure */ /* fill only different fields */ - p1100->firmware = "dvb-usb-p1100.fw"; + p1100->firmware = P1100_FIRMWARE; p1100->devices[0] = d1100; p1100->rc.legacy.rc_map_table = rc_map_tbs_table; p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1891,7 +2006,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(p1100); return -ENOMEM; } - s660->firmware = "dvb-usb-s660.fw"; + s660->firmware = S660_FIRMWARE; s660->num_device_descs = 3; s660->devices[0] = d660; s660->devices[1] = d480_1; @@ -1905,12 +2020,26 @@ static int dw2102_probe(struct usb_interface *intf, kfree(s660); return -ENOMEM; } - p7500->firmware = "dvb-usb-p7500.fw"; + p7500->firmware = P7500_FIRMWARE; p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; + + s421 = kmemdup(&su3000_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!s421) { + kfree(p1100); + kfree(s660); + kfree(p7500); + return -ENOMEM; + } + s421->num_device_descs = 2; + s421->devices[0] = d421; + s421->devices[1] = d632; + s421->adapter->fe[0].frontend_attach = m88rs2000_frontend_attach; + if (0 == dvb_usb_device_init(intf, &dw2102_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &dw2104_properties, @@ -1925,6 +2054,8 @@ static int dw2102_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, p7500, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, s421, + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &su3000_properties, THIS_MODULE, NULL, adapter_nr)) return 0; @@ -1943,9 +2074,17 @@ module_usb_driver(dw2102_driver); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," - " DVB-C 3101 USB2.0," - " TeVii S600, S630, S650, S660, S480," - " Prof 1100, 7500 USB2.0," - " Geniatech SU3000 devices"); + " DVB-C 3101 USB2.0," + " TeVii S600, S630, S650, S660, S480, S421, S632" + " Prof 1100, 7500 USB2.0," + " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DW2101_FIRMWARE); +MODULE_FIRMWARE(DW2102_FIRMWARE); +MODULE_FIRMWARE(DW2104_FIRMWARE); +MODULE_FIRMWARE(DW3101_FIRMWARE); +MODULE_FIRMWARE(S630_FIRMWARE); +MODULE_FIRMWARE(S660_FIRMWARE); +MODULE_FIRMWARE(P1100_FIRMWARE); +MODULE_FIRMWARE(P7500_FIRMWARE); diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c index 90a70c66a96e..d56f927fc31a 100644 --- a/drivers/media/usb/dvb-usb/friio-fe.c +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -421,11 +421,10 @@ struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) /* setup the state */ state->i2c = &d->i2c_adap; - memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + state->config = friio_fe_config; /* create dvb_frontend */ - memcpy(&state->frontend.ops, &jdvbt90502_ops, - sizeof(jdvbt90502_ops)); + state->frontend.ops = jdvbt90502_ops; state->frontend.demodulator_priv = state; if (jdvbt90502_init(&state->frontend) < 0) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 661bb75be955..92afeb20650f 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -16,6 +16,7 @@ #include "qt1010.h" #include "tda1004x.h" #include "tda827x.h" +#include "mt2060.h" #include <media/tuner.h> #include "tuner-simple.h" @@ -63,23 +64,33 @@ static inline int m920x_write(struct usb_device *udev, u8 request, return ret; } +static inline int m920x_write_seq(struct usb_device *udev, u8 request, + struct m920x_inits *seq) +{ + int ret; + while (seq->address) { + ret = m920x_write(udev, request, seq->data, seq->address); + if (ret != 0) + return ret; + + seq++; + } + + return ret; +} + static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) { int ret = 0, i, epi, flags = 0; int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ - if (d->props.rc.legacy.rc_query) { + if (d->props.rc.legacy.rc_query || d->props.rc.core.rc_query) { deb("Initialising remote control\n"); - while (rc_seq->address) { - if ((ret = m920x_write(d->udev, M9206_CORE, - rc_seq->data, - rc_seq->address)) != 0) { - deb("Initialising remote control failed\n"); - return ret; - } - - rc_seq++; + ret = m920x_write_seq(d->udev, M9206_CORE, rc_seq); + if (ret != 0) { + deb("Initialising remote control failed\n"); + return ret; } deb("Initialising remote control success\n"); @@ -130,9 +141,50 @@ static int m920x_init_ep(struct usb_interface *intf) alt->desc.bAlternateSetting); } -static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +static inline void m920x_parse_rc_state(struct dvb_usb_device *d, u8 rc_state, + int *state) { struct m920x_state *m = d->priv; + + switch (rc_state) { + case 0x80: + *state = REMOTE_NO_KEY_PRESSED; + break; + + case 0x88: /* framing error or "invalid code" */ + case 0x99: + case 0xc0: + case 0xd8: + *state = REMOTE_NO_KEY_PRESSED; + m->rep_count = 0; + break; + + case 0x93: + case 0x92: + case 0x83: /* pinnacle PCTV310e */ + case 0x82: + m->rep_count = 0; + *state = REMOTE_KEY_PRESSED; + break; + + case 0x91: + case 0x81: /* pinnacle PCTV310e */ + /* prevent immediate auto-repeat */ + if (++m->rep_count > 2) + *state = REMOTE_KEY_REPEAT; + else + *state = REMOTE_NO_KEY_PRESSED; + break; + + default: + deb("Unexpected rc state %02x\n", rc_state); + *state = REMOTE_NO_KEY_PRESSED; + break; + } +} + +static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ int i, ret = 0; u8 *rc_state; @@ -140,51 +192,22 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if (!rc_state) return -ENOMEM; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, + rc_state, 1); + if (ret != 0) goto out; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, + rc_state + 1, 1); + if (ret != 0) goto out; + m920x_parse_rc_state(d, rc_state[0], state); + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { *event = d->props.rc.legacy.rc_map_table[i].keycode; - - switch(rc_state[0]) { - case 0x80: - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - case 0x88: /* framing error or "invalid code" */ - case 0x99: - case 0xc0: - case 0xd8: - *state = REMOTE_NO_KEY_PRESSED; - m->rep_count = 0; - goto out; - - case 0x93: - case 0x92: - case 0x83: /* pinnacle PCTV310e */ - case 0x82: - m->rep_count = 0; - *state = REMOTE_KEY_PRESSED; - goto out; - - case 0x91: - case 0x81: /* pinnacle PCTV310e */ - /* prevent immediate auto-repeat */ - if (++m->rep_count > 2) - *state = REMOTE_KEY_REPEAT; - else - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - default: - deb("Unexpected rc state %02x\n", rc_state[0]); - *state = REMOTE_NO_KEY_PRESSED; - goto out; - } + goto out; } if (rc_state[1] != 0) @@ -197,6 +220,38 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return ret; } +static int m920x_rc_core_query(struct dvb_usb_device *d) +{ + int ret = 0; + u8 *rc_state; + int state; + + rc_state = kmalloc(2, GFP_KERNEL); + if (!rc_state) + return -ENOMEM; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, &rc_state[0], 1)) != 0) + goto out; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, &rc_state[1], 1)) != 0) + goto out; + + deb("state=0x%02x keycode=0x%02x\n", rc_state[0], rc_state[1]); + + m920x_parse_rc_state(d, rc_state[0], &state); + + if (state == REMOTE_NO_KEY_PRESSED) + rc_keyup(d->rc_dev); + else if (state == REMOTE_KEY_REPEAT) + rc_repeat(d->rc_dev); + else + rc_keydown(d->rc_dev, rc_state[1], 0); + +out: + kfree(rc_state); + return ret; +} + /* I2C */ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { @@ -496,6 +551,12 @@ static struct qt1010_config m920x_qt1010_config = { .i2c_address = 0x62 }; +static struct mt2060_config m920x_mt2060_config = { + .i2c_address = 0x60, /* 0xc0 */ + .clock_out = 0, +}; + + /* Callbacks for DVB USB */ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) { @@ -510,6 +571,37 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt352_frontend_attach_vp7049(struct dvb_usb_adapter *adap) +{ + struct m920x_inits vp7049_fe_init_seq[] = { + /* XXX without these commands the frontend cannot be detected, + * they must be sent BEFORE the frontend is attached */ + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x20 }, + { 0xff21, 0x60 }, + { 0xff28, 0x00 }, + { 0xff22, 0x00 }, + { 0xff20, 0x30 }, + { 0xff20, 0x20 }, + { 0xff20, 0x30 }, + { } /* terminating entry */ + }; + int ret; + + deb("%s\n", __func__); + + ret = m920x_write_seq(adap->dev->udev, M9206_CORE, vp7049_fe_init_seq); + if (ret != 0) { + deb("Initialization of vp7049 frontend failed."); + return ret; + } + + return m920x_mt352_frontend_attach(adap); +} + static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); @@ -574,6 +666,18 @@ static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt2060_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n", __func__); + + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + &m920x_mt2060_config, 1220) == NULL) + return -ENODEV; + + return 0; +} + + /* device-specific initialization */ static struct m920x_inits megasky_rc_init [] = { { M9206_RC_INIT2, 0xa8 }, @@ -591,7 +695,7 @@ static struct m920x_inits tvwalkertwin_rc_init [] = { }; static struct m920x_inits pinnacle310e_init[] = { - /* without these the tuner don't work */ + /* without these the tuner doesn't work */ { 0xff20, 0x9b }, { 0xff22, 0x70 }, @@ -602,6 +706,15 @@ static struct m920x_inits pinnacle310e_init[] = { { } /* terminating entry */ }; +static struct m920x_inits vp7049_rc_init[] = { + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x70 }, + { M9206_RC_INIT2, 0x00 }, + { M9206_RC_INIT1, 0xff }, + { } /* terminating entry */ +}; + /* ir keymaps */ static struct rc_map_table rc_map_megasky_table[] = { { 0x0012, KEY_POWER }, @@ -704,6 +817,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties; static struct dvb_usb_device_properties tvwalkertwin_properties; static struct dvb_usb_device_properties dposh_properties; static struct dvb_usb_device_properties pinnacle_pctv310e_properties; +static struct dvb_usb_device_properties vp7049_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -756,6 +870,13 @@ static int m920x_probe(struct usb_interface *intf, goto found; } + ret = dvb_usb_device_init(intf, &vp7049_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = vp7049_rc_init; + goto found; + } + return ret; } else { /* Another interface on a multi-tuner device */ @@ -787,6 +908,7 @@ static struct usb_device_id m920x_table [] = { { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, + { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_TWINHAN_VP7049) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, m920x_table); @@ -1079,6 +1201,61 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { } }; +static struct dvb_usb_device_properties vp7049_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-vp7049-0.95.fw", + .download_firmware = m920x_firmware_download, + + .rc.core = { + .rc_interval = 150, + .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, + .rc_query = m920x_rc_core_query, + .allowed_protos = RC_TYPE_UNKNOWN, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach_vp7049, + .tuner_attach = m920x_mt2060_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + } }, + } }, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "DTV-DVB UDTT7049", + { &m920x_table[7], NULL }, + { NULL }, + } + } +}; + static struct usb_driver m920x_driver = { .name = "dvb_usb_m920x", .probe = m920x_probe, diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 7a5bd61bd3bb..c754a80a8d8b 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_EM28XX depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT @@ -34,6 +34,7 @@ config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT @@ -43,7 +44,10 @@ config VIDEO_EM28XX_DVB select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF_DVB + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 619bffbab3bc..0a5aa6223adf 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -6,6 +6,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -56,10 +57,16 @@ module_param(disable_usb_speed_check, int, 0444); MODULE_PARM_DESC(disable_usb_speed_check, "override min bandwidth requirement of 480M bps"); -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); +static int usb_xfer_mode = -1; +module_param(usb_xfer_mode, int, 0444); +MODULE_PARM_DESC(usb_xfer_mode, + "USB transfer mode for frame data (-1 = auto, 0 = prefer isoc, 1 = prefer bulk)"); + + /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ static unsigned long em28xx_devused; @@ -486,7 +493,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -2905,7 +2912,7 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); - if (dev->board.ir_codes && !disable_ir) + if ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir) request_module("em28xx-rc"); #endif /* CONFIG_MODULES */ } @@ -2934,6 +2941,8 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); usb_put_dev(dev->udev); @@ -2950,9 +2959,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, struct usb_interface *interface, int minor) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; int retval; + static const char *default_chip_name = "em28xx"; + const char *chip_name = default_chip_name; dev->udev = udev; + mutex_init(&dev->vb_queue_lock); + mutex_init(&dev->vb_vbi_queue_lock); mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); @@ -2978,51 +2992,62 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, switch (dev->chip_id) { case CHIP_ID_EM2800: - em28xx_info("chip ID is em2800\n"); + chip_name = "em2800"; break; case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); + chip_name = "em2710"; break; case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); + chip_name = "em2750"; break; case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); + chip_name = "em2710/2820"; break; case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); + chip_name = "em2840"; break; case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); + chip_name = "em2860"; break; case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); + chip_name = "em2870"; dev->wait_after_write = 0; break; case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); + chip_name = "em2874"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM28174: - em28xx_info("chip ID is em28174\n"); + chip_name = "em28174"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); + chip_name = "em2882/3"; dev->wait_after_write = 0; break; case CHIP_ID_EM2884: - em28xx_info("chip ID is em2884\n"); + chip_name = "em2884"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + printk(KERN_INFO DRIVER_NAME + ": unknown em28xx chip ID (%d)\n", dev->chip_id); } } + if (chip_name != default_chip_name) + printk(KERN_INFO DRIVER_NAME + ": chip ID is %s\n", chip_name); + + /* + * For em2820/em2710, the name may change latter, after checking + * if the device has a sensor (so, it is em2710) or not. + */ + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + if (dev->is_audio_only) { retval = em28xx_audio_setup(dev); if (retval) @@ -3039,6 +3064,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); + if (dev->chip_id == CHIP_ID_EM2820) { + if (dev->board.is_webcam) + chip_name = "em2710"; + else + chip_name = "em2820"; + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + } + if (!dev->board.is_em2800) { /* Resets I2C speed */ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); @@ -3056,6 +3089,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return retval; } + v4l2_ctrl_handler_init(hdl, 4); + dev->v4l2_dev.ctrl_handler = hdl; + /* register i2c bus */ retval = em28xx_i2c_register(dev); if (retval < 0) { @@ -3081,6 +3117,18 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, __func__, retval); goto fail; } + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); + } else { + /* install the em28xx notify callback */ + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), + em28xx_ctrl_notify, dev); + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), + em28xx_ctrl_notify, dev); + } /* wake i2c devices */ em28xx_wake_i2c(dev); @@ -3110,6 +3158,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, msleep(3); } + v4l2_ctrl_handler_setup(&dev->ctrl_handler); + retval = dev->ctrl_handler.error; + if (retval) + goto fail; + retval = em28xx_register_analog_devices(dev); if (retval < 0) { goto fail; @@ -3122,6 +3175,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, fail: em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); unregister_dev: v4l2_device_unregister(&dev->v4l2_dev); @@ -3143,7 +3197,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct em28xx *dev = NULL; int retval; bool has_audio = false, has_video = false, has_dvb = false; - int i, nr; + int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3183,9 +3237,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes */ - dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * + dev->alt_max_pkt_size_isoc = + kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) * interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size == NULL) { + if (dev->alt_max_pkt_size_isoc == NULL) { em28xx_errdev("out of memory!\n"); kfree(dev); retval = -ENOMEM; @@ -3208,25 +3263,67 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (udev->speed == USB_SPEED_HIGH) size = size * hb_mult(sizedescr); - if (usb_endpoint_xfer_isoc(e) && - usb_endpoint_dir_in(e)) { + if (usb_endpoint_dir_in(e)) { switch (e->bEndpointAddress) { - case EM28XX_EP_AUDIO: - has_audio = true; - break; - case EM28XX_EP_ANALOG: + case 0x82: has_video = true; - dev->alt_max_pkt_size[i] = size; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = + e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[i] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } break; - case EM28XX_EP_DIGITAL: - has_dvb = true; - if (size > dev->dvb_max_pkt_size) { - dev->dvb_max_pkt_size = size; - dev->dvb_alt = i; + case 0x83: + if (usb_endpoint_xfer_isoc(e)) { + has_audio = true; + } else { + printk(KERN_INFO DRIVER_NAME + ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); + } + break; + case 0x84: + if (has_video && + (usb_endpoint_xfer_bulk(e))) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } else { + has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->dvb_ep_isoc = e->bEndpointAddress; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = size; + dev->dvb_alt_isoc = i; + } + } else { + dev->dvb_ep_bulk = e->bEndpointAddress; + } } break; } } + /* NOTE: + * Old logic with support for isoc transfers only was: + * 0x82 isoc => analog + * 0x83 isoc => audio + * 0x84 isoc => digital + * + * New logic with support for bulk transfers + * 0x82 isoc => analog + * 0x82 bulk => analog + * 0x83 isoc* => audio + * 0x84 isoc => digital + * 0x84 bulk => analog or digital** + * (*: audio should always be isoc) + * (**: analog, if ep 0x82 is isoc, otherwise digital) + * + * The new logic preserves backwards compatibility and + * reflects the endpoint configurations we have seen + * so far. But there might be devices for which this + * logic is not sufficient... + */ } } @@ -3261,19 +3358,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting->desc.bInterfaceNumber); - if (has_audio) - printk(KERN_INFO DRIVER_NAME - ": Audio Vendor Class interface %i found\n", - ifnum); - if (has_video) - printk(KERN_INFO DRIVER_NAME - ": Video interface %i found\n", - ifnum); - if (has_dvb) - printk(KERN_INFO DRIVER_NAME - ": DVB interface %i found\n", - ifnum); - /* * Make sure we have 480 Mbps of bandwidth, otherwise things like * video stream wouldn't likely work, since 12 Mbps is generally @@ -3287,7 +3371,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } - snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -3304,6 +3387,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } + if (has_audio) + printk(KERN_INFO DRIVER_NAME + ": Audio interface %i found %s\n", + ifnum, + dev->has_audio_class ? "(USB Audio Class)" : "(Vendor Class)"); + if (has_video) + printk(KERN_INFO DRIVER_NAME + ": Video interface %i found:%s%s\n", + ifnum, + dev->analog_ep_bulk ? " bulk" : "", + dev->analog_ep_isoc ? " isoc" : ""); + if (has_dvb) + printk(KERN_INFO DRIVER_NAME + ": DVB interface %i found:%s%s\n", + ifnum, + dev->dvb_ep_bulk ? " bulk" : "", + dev->dvb_ep_isoc ? " isoc" : ""); + dev->num_alt = interface->num_altsetting; if ((unsigned)card[nr] < em28xx_bcount) @@ -3312,6 +3413,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); + /* initialize videobuf2 stuff */ + em28xx_vb2_setup(dev); + /* allocate device struct */ mutex_init(&dev->lock); mutex_lock(&dev->lock); @@ -3320,13 +3424,46 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (usb_xfer_mode < 0) { + if (dev->board.is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = usb_xfer_mode > 0; + } + + /* Select USB transfer types to use */ + if (has_video) { + if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) + dev->analog_xfer_bulk = 1; + em28xx_info("analog set to %s mode.\n", + dev->analog_xfer_bulk ? "bulk" : "isoc"); + } if (has_dvb) { - /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size); + if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) + dev->dvb_xfer_bulk = 1; + + em28xx_info("dvb set to %s mode.\n", + dev->dvb_xfer_bulk ? "bulk" : "isoc"); + + /* pre-allocate DVB usb transfer buffers */ + if (dev->dvb_xfer_bulk) { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + 512, + EM28XX_DVB_BULK_PACKET_MULTIPLIER); + } else { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size_isoc, + EM28XX_DVB_NUM_ISOC_PACKETS); + } if (retval) { + printk(DRIVER_NAME + ": Failed to pre-allocate USB transfer buffers for DVB.\n"); goto unlock_and_free; } } @@ -3344,7 +3481,7 @@ unlock_and_free: mutex_unlock(&dev->lock); err_free: - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); err: @@ -3370,6 +3507,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) if (!dev) return; + dev->disconnected = 1; + if (dev->is_audio_only) { mutex_lock(&dev->lock); em28xx_close_extension(dev); @@ -3381,35 +3520,28 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) flush_request_modules(dev); - /* wait until all current v4l2 io is finished then deallocate - resources */ mutex_lock(&dev->lock); v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { - em28xx_warn - ("device %s is open! Deregistration and memory " - "deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); - - dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev, dev->mode); - dev->state |= DEV_DISCONNECTED; - } else { - dev->state |= DEV_DISCONNECTED; - em28xx_release_resources(dev); + em28xx_warn("device %s is open! Deregistration and memory deallocation are deferred on close.\n", + video_device_node_name(dev->vdev)); + + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); } - /* free DVB isoc buffers */ - em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + em28xx_close_extension(dev); + /* NOTE: must be called BEFORE the resources are released */ - mutex_unlock(&dev->lock); + if (!dev->users) + em28xx_release_resources(dev); - em28xx_close_extension(dev); + mutex_unlock(&dev->lock); if (!dev->users) { - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index bed07a6c33f8..80f87bbbd554 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -5,6 +5,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,7 +77,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, int ret; int pipe = usb_rcvctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if (len > URB_MAX_CTRL_SIZE) @@ -100,7 +101,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (reg_debug) printk(" failed!\n"); mutex_unlock(&dev->ctrl_urb_lock); - return ret; + return usb_translate_errors(ret); } if (len) @@ -152,7 +153,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int ret; int pipe = usb_sndctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) @@ -181,6 +182,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); + if (ret < 0) + return usb_translate_errors(ret); + if (dev->wait_after_write) msleep(dev->wait_after_write); @@ -804,21 +808,23 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } +/* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; int i; unsigned int min_pkt_size = dev->width * 2 + 4; - /* - * alt = 0 is used only for control messages, so, only values - * greater than 0 can be used for streaming. - */ - if (alt && alt < dev->num_alt) { + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + for bulk transfers, use alt=0 as default value */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); dev->alt = alt; goto set_alt; } + if (dev->analog_xfer_bulk) + goto set_alt; /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only @@ -829,22 +835,29 @@ int em28xx_set_alternate(struct em28xx *dev) for (i = 0; i < dev->num_alt; i++) { /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { dev->alt = i; break; /* otherwise make sure that we end up with the maximum bandwidth because the min_pkt_size equation might be wrong... */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) + } else if (dev->alt_max_pkt_size_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) dev->alt = i; } set_alt: if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); @@ -919,7 +932,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); ------------------------------------------------------------------*/ /* - * IRQ callback, called by URB callback + * URB completion handler for isoc/bulk transfers */ static void em28xx_irq_callback(struct urb *urb) { @@ -941,11 +954,12 @@ static void em28xx_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->slock); - dev->isoc_ctl.isoc_copy(dev, urb); + dev->usb_ctl.urb_data_copy(dev, urb); spin_unlock(&dev->slock); /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { + /* isoc only (bulk: number_of_packets = 0) */ urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } @@ -961,49 +975,50 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n", + mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (isoc_bufs->transfer_buffer[i]) { + if (usb_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - isoc_bufs->transfer_buffer[i], + usb_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - isoc_bufs->urb[i] = NULL; + usb_bufs->urb[i] = NULL; } - isoc_bufs->transfer_buffer[i] = NULL; + usb_bufs->transfer_buffer[i] = NULL; } - kfree(isoc_bufs->urb); - kfree(isoc_bufs->transfer_buffer); + kfree(usb_bufs->urb); + kfree(usb_bufs->transfer_buffer); - isoc_bufs->urb = NULL; - isoc_bufs->transfer_buffer = NULL; - isoc_bufs->num_bufs = 0; + usb_bufs->urb = NULL; + usb_bufs->transfer_buffer = NULL; + usb_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } -EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +EXPORT_SYMBOL_GPL(em28xx_uninit_usb_xfer); /* * Stop URBs @@ -1012,7 +1027,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1033,10 +1048,10 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); /* * Allocate URBs */ -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size) +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier) { - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1044,140 +1059,180 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); - if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; - else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + /* Check mode and if we have an endpoint for the selected + transfer type, select buffer */ + if (mode == EM28XX_DIGITAL_MODE) { + if ((xfer_bulk && !dev->dvb_ep_bulk) || + (!xfer_bulk && !dev->dvb_ep_isoc)) { + em28xx_errdev("no endpoint for DVB mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } + usb_bufs = &dev->usb_ctl.digital_bufs; + } else if (mode == EM28XX_ANALOG_MODE) { + if ((xfer_bulk && !dev->analog_ep_bulk) || + (!xfer_bulk && !dev->analog_ep_isoc)) { + em28xx_errdev("no endpoint for analog mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } + usb_bufs = &dev->usb_ctl.analog_bufs; + } else { + em28xx_errdev("invalid mode selected\n"); + return -EINVAL; + } /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); - isoc_bufs->num_bufs = num_bufs; + usb_bufs->num_bufs = num_bufs; - isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->urb) { + usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!usb_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->transfer_buffer) { + if (!usb_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(isoc_bufs->urb); + kfree(usb_bufs->urb); return -ENOMEM; } - isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = max_packets; - dev->isoc_ctl.vid_buf = NULL; - dev->isoc_ctl.vbi_buf = NULL; + usb_bufs->max_pkt_size = max_pkt_size; + if (xfer_bulk) + usb_bufs->num_packets = 0; + else + usb_bufs->num_packets = packet_multiplier; + dev->usb_ctl.vid_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; - sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; + sb_size = packet_multiplier * usb_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL); if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev, mode); + em28xx_err("cannot alloc usb_ctl.urb %i\n", i); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - isoc_bufs->urb[i] = urb; + usb_bufs->urb[i] = urb; - isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, + usb_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!isoc_bufs->transfer_buffer[i]) { + if (!usb_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - memset(isoc_bufs->transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, - mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); - - usb_fill_int_urb(urb, dev->udev, pipe, - isoc_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - - urb->number_of_packets = isoc_bufs->num_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < isoc_bufs->num_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - isoc_bufs->max_pkt_size; - k += isoc_bufs->max_pkt_size; + memset(usb_bufs->transfer_buffer[i], 0, sb_size); + + if (xfer_bulk) { /* bulk */ + pipe = usb_rcvbulkpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + dev->analog_ep_bulk : + dev->dvb_ep_bulk); + usb_fill_bulk_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } else { /* isoc */ + pipe = usb_rcvisocpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + dev->analog_ep_isoc : + dev->dvb_ep_isoc); + usb_fill_int_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev, 1); + urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + k = 0; + for (j = 0; j < usb_bufs->num_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usb_bufs->max_pkt_size; + k += usb_bufs->max_pkt_size; + } } + + urb->number_of_packets = usb_bufs->num_packets; } return 0; } -EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); +EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ */ -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, int num_bufs, int max_pkt_size, + int packet_multiplier, + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int rc; int alloc; - em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n", + mode); - dev->isoc_ctl.isoc_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = urb_data_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->isoc_ctl.digital_bufs; - /* no need to free/alloc isoc buffers in digital mode */ + usb_bufs = &dev->usb_ctl.digital_bufs; + /* no need to free/alloc usb buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->isoc_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, max_packets, - num_bufs, max_pkt_size); + rc = em28xx_alloc_urbs(dev, mode, xfer_bulk, num_bufs, + max_pkt_size, packet_multiplier); if (rc) return rc; } + if (xfer_bulk) { + rc = usb_clear_halt(dev->udev, usb_bufs->urb[0]->pipe); + if (rc < 0) { + em28xx_err("failed to clear USB bulk endpoint stall/halt condition (error=%i)\n", + rc); + em28xx_uninit_usb_xfer(dev, mode); + return rc; + } + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); + for (i = 0; i < usb_bufs->num_bufs; i++) { + rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return rc; } } return 0; } -EXPORT_SYMBOL_GPL(em28xx_init_isoc); +EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer); /* * em28xx_wake_i2c() diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 63f2e7070c00..a81ec2e8cc9b 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -10,6 +10,8 @@ (c) 2008 Aidan Thornton <makosoft@googlemail.com> + (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] @@ -25,7 +27,9 @@ #include "em28xx.h" #include <media/v4l2-common.h> -#include <media/videobuf-vmalloc.h> +#include <dvb_demux.h> +#include <dvb_net.h> +#include <dmxdev.h> #include <media/tuner.h> #include "tuner-simple.h" #include <linux/gpio.h> @@ -124,34 +128,47 @@ static inline void print_err_status(struct em28xx *dev, } } -static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) { - int i; + int xfer_bulk, num_packets, i; if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + xfer_bulk = usb_pipebulk(urb->pipe); - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { + if (urb->status < 0) { + print_err_status(dev, i, urb->status); + if (urb->status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } else { + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } } return 0; @@ -161,24 +178,40 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; - int max_dvb_packet_size; + int dvb_max_packet_size, packet_multiplier, dvb_alt; + + if (dev->dvb_xfer_bulk) { + if (!dev->dvb_ep_bulk) + return -ENODEV; + dvb_max_packet_size = 512; /* USB 2.0 spec */ + packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; + dvb_alt = 0; + } else { /* isoc */ + if (!dev->dvb_ep_isoc) + return -ENODEV; + dvb_max_packet_size = dev->dvb_max_pkt_size_isoc; + if (dvb_max_packet_size < 0) + return dvb_max_packet_size; + packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS; + dvb_alt = dev->dvb_alt_isoc; + } - usb_set_interface(dev->udev, 0, dev->dvb_alt); + usb_set_interface(dev->udev, 0, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size; - if (max_dvb_packet_size < 0) - return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_MAX_PACKETS, - max_dvb_packet_size); - - return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, em28xx_dvb_isoc_copy); + packet_multiplier, + dvb_max_packet_size); + + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dvb_max_packet_size, + packet_multiplier, + em28xx_dvb_urb_data_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) @@ -714,7 +747,8 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { }; static const struct tda10071_config em28xx_tda10071_config = { - .i2c_address = 0x55, /* (0xaa >> 1) */ + .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ + .tuner_i2c_addr = 0x14, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, .spec_inv = 0, @@ -1288,7 +1322,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { /* We cannot tell the device to sleep * once it has been unplugged. */ if (dvb->fe[0]) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 1683bd9d51ee..9ae8f6051d8e 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -50,15 +50,18 @@ do { \ } while (0) /* - * em2800_i2c_send_max4() - * send up to 4 bytes to the i2c device + * em2800_i2c_send_bytes() + * send up to 4 bytes to the em2800 i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; - unsigned char b2[6]; + u8 b2[6]; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -70,165 +73,212 @@ static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, if (len > 3) b2[0] = buf[3]; + /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { - em28xx_warn("writing to i2c device failed (error=%i)\n", ret); - return -EIO; + em28xx_warn("failed to trigger write to i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + /* wait for completion */ + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x80 + len - 1) + if (ret == 0x80 + len - 1) { return len; + } else if (ret == 0x94 + len - 1) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } msleep(5); } - em28xx_warn("i2c write timed out\n"); + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); return -EIO; } /* - * em2800_i2c_send_bytes() - */ -static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len) -{ - char *bufPtr = buf; - int ret; - int wrcount = 0; - int count; - int maxLen = 4; - struct em28xx *dev = (struct em28xx *)data; - while (len > 0) { - count = (len > maxLen) ? maxLen : len; - ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); - if (ret > 0) { - len -= count; - bufPtr += count; - wrcount += count; - } else - return (ret < 0) ? ret : -EFAULT; - } - return wrcount; -} - -/* - * em2800_i2c_check_for_device() - * check if there is a i2c_device at the supplied address + * em2800_i2c_recv_bytes() + * read up to 4 bytes from the em2800 i2c device */ -static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - char msg; + u8 buf2[4]; int ret; - int write_timeout; - msg = addr; - ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); - if (ret < 0) { - em28xx_warn("setting i2c device address failed (error=%i)\n", - ret); - return ret; - } - msg = 0x84; - ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); - if (ret < 0) { - em28xx_warn("preparing i2c read failed (error=%i)\n", ret); - return ret; + int read_timeout; + int i; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + + /* trigger read */ + buf2[1] = 0x84 + len - 1; + buf2[0] = addr; + ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2); + if (ret != 2) { + em28xx_warn("failed to trigger read from i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - unsigned reg = dev->em28xx_read_reg(dev, 0x5); - if (reg == 0x94) + /* wait for completion */ + for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0; + read_timeout -= 5) { + ret = dev->em28xx_read_reg(dev, 0x05); + if (ret == 0x84 + len - 1) { + break; + } else if (ret == 0x94 + len - 1) { return -ENODEV; - else if (reg == 0x84) - return 0; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } msleep(5); } - return -ENODEV; + if (ret != 0x84 + len - 1) + em28xx_warn("read from i2c device at 0x%x timed out\n", addr); + + /* get the received message */ + ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); + if (ret != len) { + em28xx_warn("reading from i2c device at 0x%x failed: " + "couldn't get the received message from the bridge " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; + } + for (i = 0; i < len; i++) + buf[i] = buf2[len - 1 - i]; + + return ret; } /* - * em2800_i2c_recv_bytes() - * read from the i2c device + * em2800_i2c_check_for_device() + * check if there is an i2c device at the supplied address */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) { + u8 buf; int ret; - /* check for the device and set i2c read address */ - ret = em2800_i2c_check_for_device(dev, addr); - if (ret) { - em28xx_warn - ("preparing read at i2c address 0x%x failed (error=%i)\n", - addr, ret); - return ret; - } - ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); - if (ret < 0) { - em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", - addr, ret); - return ret; - } - return ret; + + ret = em2800_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; } /* * em28xx_i2c_send_bytes() */ -static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len, int stop) +static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len, int stop) { - int wrcount = 0; - struct em28xx *dev = (struct em28xx *)data; int write_timeout, ret; - wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + + /* Write to i2c device */ + ret = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("writing to i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes write to i2c device at 0x%x " + "requested, but %i bytes written\n", + len, addr, ret); + return -EIO; + } + } - /* Seems to be required after a write */ - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + /* Check success of the i2c operation */ + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (!ret) - break; + if (ret == 0) { /* success */ + return len; + } else if (ret == 0x10) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); + return ret; + } msleep(5); + /* NOTE: do we really have to wait for success ? + Never seen anything else than 0x00 or 0x10 + (even with high payload) ... */ } - - return wrcount; + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); + return -EIO; } /* * em28xx_i2c_recv_bytes() * read a byte from the i2c device */ -static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + + /* Read data from i2c device */ ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes requested from i2c device at " + "0x%x, but %i bytes received\n", + len, addr, ret); + return -EIO; + } + } + + /* Check success of the i2c operation */ + ret = dev->em28xx_read_reg(dev, 0x05); if (ret < 0) { - em28xx_warn("reading i2c device failed (error=%i)\n", ret); + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); return ret; } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return ret; + if (ret > 0) { + if (ret == 0x10) { + return -ENODEV; + } else { + em28xx_warn("unknown i2c error (status=%i)\n", ret); + return -EIO; + } + } + return len; } /* * em28xx_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; + u8 buf; - ret = dev->em28xx_read_reg_req(dev, 2, addr); - if (ret < 0) { - em28xx_warn("reading from i2c device failed (error=%i)\n", ret); - return ret; - } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return 0; + ret = em28xx_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; } /* @@ -253,11 +303,11 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); - if (rc < 0) { - dprintk2(2, " no device\n"); + if (rc == -ENODEV) { + if (i2c_debug >= 2) + printk(" no device\n"); return rc; } - } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ if (dev->board.is_em2800) @@ -288,16 +338,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, msgs[i].len, i == num - 1); } - if (rc < 0) - goto err; + if (rc < 0) { + if (i2c_debug >= 2) + printk(" ERROR: %i\n", rc); + return rc; + } if (i2c_debug >= 2) printk("\n"); } return num; -err: - dprintk2(2, " ERROR: %i\n", rc); - return rc; } /* based on linux/sunrpc/svcauth.h and linux/hash.h @@ -329,7 +379,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) { unsigned char buf, *p = eedata; struct em28xx_eeprom *em_eeprom = (void *)eedata; - int i, err, size = len, block; + int i, err, size = len, block, block_max; if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174 || @@ -362,9 +412,15 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) dev->name, err); return err; } + + if (dev->board.is_em2800) + block_max = 4; + else + block_max = 64; + while (size > 0) { - if (size > 16) - block = 16; + if (size > block_max) + block = block_max; else block = size; @@ -449,7 +505,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) */ static u32 functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL; + struct em28xx *dev = adap->algo_data; + u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + if (dev->board.is_em2800) + func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; + return func_flags; } static struct i2c_algorithm em28xx_algo = { diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 660bf803c9e4..07f6030d7455 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -57,8 +57,8 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; - u8 rc_address; - u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ + + u32 scancode; }; struct em28xx_IR { @@ -72,6 +72,7 @@ struct em28xx_IR { struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; + u64 rc_type; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -236,11 +237,8 @@ static int default_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x45[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x46) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x47) */ - poll_result->rc_data[0] = msg[2]; + /* Remote Control Address/Data (Regs 0x46/0x47) */ + poll_result->scancode = msg[1] << 8 | msg[2]; return 0; } @@ -266,13 +264,35 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x51[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x52) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x53-55) */ - poll_result->rc_data[0] = msg[2]; - poll_result->rc_data[1] = msg[3]; - poll_result->rc_data[2] = msg[4]; + /* + * Remote Control Address (Reg 0x52) + * Remote Control Data (Reg 0x53-0x55) + */ + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + case RC_BIT_NEC: + if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ + poll_result->scancode = (msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + msg[4]; + else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ + poll_result->scancode = (msg[1] << 16) | + (msg[2] << 8) | + msg[3]; + else /* Normal NEC */ + poll_result->scancode = msg[1] << 8 | msg[3]; + break; + case RC_BIT_RC6_0: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + default: + poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | + (msg[3] << 8) | msg[4]; + break; + } return 0; } @@ -294,17 +314,16 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } if (unlikely(poll_result.read_count != ir->last_readcount)) { - dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + dprintk("%s: toggle: %d, count: %d, key 0x%04x\n", __func__, poll_result.toggle_bit, poll_result.read_count, - poll_result.rc_address, poll_result.rc_data[0]); + poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, - poll_result.rc_address << 8 | - poll_result.rc_data[0], + poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, - poll_result.rc_data[0], + poll_result.scancode & 0xff, poll_result.toggle_bit); if (ir->dev->chip_id == CHIP_ID_EM2874 || @@ -345,49 +364,88 @@ static void em28xx_ir_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); } -static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) { - int rc = 0; struct em28xx_IR *ir = rc_dev->priv; struct em28xx *dev = ir->dev; - u8 ir_config = EM2874_IR_RC5; - - /* Adjust xclk based o IR table for RC5/NEC tables */ + /* Adjust xclk based on IR table for RC5/NEC tables */ if (*rc_type & RC_BIT_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; *rc_type = RC_BIT_RC5; } else if (*rc_type & RC_BIT_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; - ir_config = EM2874_IR_NEC; ir->full_code = 1; *rc_type = RC_BIT_NEC; - } else if (*rc_type != RC_BIT_UNKNOWN) - rc = -EINVAL; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, + EM28XX_XCLK_IR_RC5_MODE); + + ir->rc_type = *rc_type; + + return 0; +} + +static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + u8 ir_config = EM2874_IR_RC5; + /* Adjust xclk and set type based on IR table for RC5/NEC/RC6 tables */ + if (*rc_type & RC_BIT_RC5) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_RC5; + } else if (*rc_type & RC_BIT_NEC) { + dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; + ir->full_code = 1; + *rc_type = RC_BIT_NEC; + } else if (*rc_type & RC_BIT_RC6_0) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_RC6_MODE_0; + ir->full_code = 1; + *rc_type = RC_BIT_RC6_0; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); + ir->rc_type = *rc_type; + + return 0; +} +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + /* Setup the proper handler based on the chip */ switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - ir->get_key = default_polling_getkey; - break; + return em2860_ir_change_protocol(rc_dev, rc_type); case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - break; + return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", dev->chip_id); - rc = -EINVAL; + return -EINVAL; } - - return rc; } static void em28xx_register_i2c_ir(struct em28xx *dev) @@ -410,22 +468,23 @@ static void em28xx_register_i2c_ir(struct em28xx *dev) case EM2820_BOARD_TERRATEC_CINERGY_250: dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; + dev->init_data.name = "Terratec Cinergy 200/250"; break; case EM2820_BOARD_PINNACLE_USB_2: dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + dev->init_data.name = "Pinnacle USB2"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + dev->init_data.name = "WinTV USB2"; + dev->init_data.type = RC_BIT_RC5; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + dev->init_data.name = "Winfast TV USBII Deluxe"; break; } @@ -528,6 +587,17 @@ static int em28xx_ir_init(struct em28xx *dev) int err = -ENOMEM; u64 rc_type; + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + + if (dev->board.has_ir_i2c) { + em28xx_register_i2c_ir(dev); +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("ir-kbd-i2c"); +#endif + return 0; + } + if (dev->board.ir_codes == NULL) { /* No remote control support */ em28xx_warn("Remote control support is not available for " @@ -538,7 +608,7 @@ static int em28xx_ir_init(struct em28xx *dev) ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) - goto err_out_free; + goto error; /* record handles to ourself */ ir->dev = dev; @@ -555,11 +625,28 @@ static int em28xx_ir_init(struct em28xx *dev) rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto error; + } + /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); if (err) - goto err_out_free; + goto error; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -584,22 +671,12 @@ static int em28xx_ir_init(struct em28xx *dev) /* all done */ err = rc_register_device(rc); if (err) - goto err_out_stop; - - em28xx_register_i2c_ir(dev); - -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); + goto error; return 0; - err_out_stop: +error: dev->ir = NULL; - err_out_free: rc_free_device(rc); kfree(ir); return err; diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 6ff368297f6e..885089e22bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -13,9 +13,9 @@ #define EM_GPO_3 (1 << 3) /* em28xx endpoints */ -#define EM28XX_EP_ANALOG 0x82 +/* 0x82: (always ?) analog */ #define EM28XX_EP_AUDIO 0x83 -#define EM28XX_EP_DIGITAL 0x84 +/* 0x84: digital or analog */ /* em2800 registers */ #define EM2800_R08_AUDIOSRC 0x08 @@ -177,6 +177,7 @@ /* em2874 IR config register (0x50) */ #define EM2874_IR_NEC 0x00 +#define EM2874_IR_NEC_NO_PARITY 0x01 #define EM2874_IR_RC5 0x04 #define EM2874_IR_RC6_MODE_0 0x08 #define EM2874_IR_RC6_MODE_6A 0x0b diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 2b4c9cba2d67..39f39c527c13 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -41,105 +41,72 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ -static void -free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = dev->vbi_width * dev->vbi_height * 2; -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; + if (0 == *nbuffers) + *nbuffers = 32; + if (*nbuffers < 2) + *nbuffers = 2; + if (*nbuffers > 32) + *nbuffers = 32; - *size = dev->vbi_width * dev->vbi_height * 2; + *nplanes = 1; + sizes[0] = size; - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int vbi_buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - int rc = 0; + unsigned long size; - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + size = dev->vbi_width * dev->vbi_height * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < size) { + printk(KERN_INFO "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; } + vb2_set_plane_payload(&buf->vb, 0, size); - buf->vb.state = VIDEOBUF_PREPARED; return 0; - -fail: - free_buffer(q, buf); - return rc; } static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +vbi_buffer_queue(struct vb2_buffer *vb) { + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - free_buffer(q, buf); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vbiq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -struct videobuf_queue_ops em28xx_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, + +struct vb2_ops em28xx_vbi_qops = { + .queue_setup = vbi_queue_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_vbi_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1e553d357380..2eabf2a94752 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -6,6 +6,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> Some parts based on SN9C10x PC Camera Controllers GPL driver made by Luca Risolia <luca.risolia@studio.unibo.it> @@ -39,6 +40,7 @@ #include "em28xx.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -74,9 +76,9 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); -static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); @@ -124,101 +126,50 @@ static struct em28xx_fmt format[] = { }, }; -/* supported controls */ -/* Common to all boards */ -static struct v4l2_queryctrl ac97_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - } -}; - /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ /* - * Announces that a buffer were filled and request the next + * Finish the current buffer */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static inline void finish_buffer(struct em28xx *dev, + struct em28xx_buffer *buf) { - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); - dev->isoc_ctl.vid_buf = NULL; + buf->vb.v4l2_buf.sequence = dev->field_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vbi_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } /* - * Identify the buffer header type and properly handles + * Copy picture data from USB buffer to videobuf buffer */ static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned char *usb_buf, + unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; - startread = p; + startread = usb_buf; remain = len; - if (dev->progressive) - fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } + if (dev->progressive || buf->top_field) + fieldstart = buf->vb_buf; + else /* interlaced mode, even nr. of lines */ + fieldstart = buf->vb_buf + bytesperline; - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; + linesdone = buf->pos / bytesperline; + currlinedone = buf->pos % bytesperline; if (dev->progressive) offset = linesdone * bytesperline + currlinedone; @@ -229,11 +180,12 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)startwrite + lencopy) - + ((char *)buf->vb_buf + buf->length)); + remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -243,21 +195,24 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - startwrite += lencopy + bytesperline; + if (dev->progressive) + startwrite += lencopy; + else + startwrite += lencopy + bytesperline; startread += lencopy; if (bytesperline > remain) lencopy = remain; else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + - buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - - (char *)startwrite; + ((char *)buf->vb_buf + buf->length)); + lencopy = remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; } if (lencopy <= 0) break; @@ -267,57 +222,29 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; } - dma_q->pos += len; + buf->pos += len; } +/* + * Copy VBI data from USB buffer to videobuf buffer + */ static void em28xx_copy_vbi(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + struct em28xx_buffer *buf, + unsigned char *usb_buf, + unsigned long len) { - void *startwrite, *startread; - int offset; - int bytesperline; - - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; + unsigned int offset; - if (dma_q == NULL) { - em28xx_isocdbg("dma_q is null\n"); - return; - } - if (buf == NULL) { - return; - } - if (p == NULL) { - em28xx_isocdbg("p is null\n"); - return; - } - if (outp == NULL) { - em28xx_isocdbg("outp is null\n"); - return; - } - - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - - startwrite = outp + dma_q->pos; - offset = dma_q->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; + offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } + if (buf->top_field == 0) + offset += dev->vbi_width * dev->vbi_height; - memcpy(startwrite, startread, len); - dma_q->pos += len; + memcpy(buf->vb_buf + offset, usb_buf, len); + buf->pos += len; } static inline void print_err_status(struct em28xx *dev, @@ -360,470 +287,444 @@ static inline void print_err_status(struct em28xx *dev, } /* - * video-buf generic routine to get the next available buffer + * get the next available buffer from dma queue */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - char *outp; + struct em28xx_buffer *buf; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; - *buf = NULL; - return; + return NULL; } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - + buf = list_entry(dma_q->active.next, struct em28xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); + list_del(&buf->list); + buf->pos = 0; + buf->vb_buf = buf->mem; - dev->isoc_ctl.vid_buf = *buf; - - return; + return buf; } /* - * video-buf generic routine to get the next available VBI buffer + * Finish the current buffer if completed and prepare for the next field */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; - *buf = NULL; - return; +static struct em28xx_buffer * +finish_field_prepare_next(struct em28xx *dev, + struct em28xx_buffer *buf, + struct em28xx_dmaqueue *dma_q) +{ + if (dev->progressive || dev->top_field) { /* Brand new frame */ + if (buf != NULL) + finish_buffer(dev, buf); + buf = get_next_buf(dev, dma_q); + } + if (buf != NULL) { + buf->top_field = dev->top_field; + buf->pos = 0; } - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->isoc_ctl.vbi_buf = *buf; - - return; + return buf; } /* - * Controls the isoc copy of each urb packet + * Process data packet according to the em2710/em2750/em28xx frame data format */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline void process_frame_data_em28xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) { - struct em28xx_buffer *buf; + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; + /* capture type 0 = vbi start + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ + if (data_len >= 4) { + /* NOTE: Headers are always 4 bytes and + * never split across packets */ + if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && + data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { + /* Continuation */ + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { + /* Field start (VBI mode) */ + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { + /* Field start (VBI disabled) */ + dev->capture_type = 2; + em28xx_isocdbg("VIDEO START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; } + } + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ - len = urb->iso_frame_desc[i].actual_length - 4; + if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + dev->capture_type = 1; + } - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } + if (dev->capture_type == 1) { + int vbi_size = dev->vbi_width * dev->vbi_height; + int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ? + (vbi_size - dev->vbi_read) : data_len; - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + /* Copy VBI data */ + if (vbi_buf != NULL) + em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); + dev->vbi_read += vbi_data_len; - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated - */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; + if (vbi_data_len < data_len) { + /* Continue with copying video data */ + dev->capture_type = 2; + data_pkt += vbi_data_len; + data_len -= vbi_data_len; } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - - if (buf != NULL) { - if (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } + } - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else { - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } + if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; + dev->capture_type = 3; } - return rc; + + if (dev->capture_type == 3 && buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); } -/* Version of isoc handler that takes into account a mixture of video and - VBI data */ -static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +/* Processes and copies the URB data content (video and VBI data) */ +static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { - struct em28xx_buffer *buf, *vbi_buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - int vbi_size; + int xfer_bulk, num_packets, i; + unsigned char *usb_data_pkt; + unsigned int usb_data_len; if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); + xfer_bulk = usb_pipebulk(urb->pipe); - vbi_buf = dev->isoc_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + usb_data_len = urb->actual_length; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + usb_data_pkt = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + usb_data_len = urb->iso_frame_desc[i].actual_length; + if (usb_data_len > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; - } + } - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; + usb_data_pkt = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* capture type 0 = vbi start - capture type 1 = video start - capture type 2 = video in progress */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len -= 4; + if (usb_data_len == 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ + continue; } - vbi_size = dev->vbi_width * dev->vbi_height; - - if (dev->capture_type == 0) { - if (dev->vbi_read >= vbi_size) { - /* We've already read all the VBI data, so - treat the rest as video */ - em28xx_isocdbg("dev->vbi_read > vbi_size\n"); - } else if ((dev->vbi_read + len) < vbi_size) { - /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && - (!(dev->cur_field & 1))) { - /* Brand new frame */ - if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - } - - if (dev->vbi_read == 0) { - vbi_dma_q->pos = 0; - if (vbi_buf != NULL) { - if (dev->cur_field & 1) - vbi_buf->top_field = 0; - else - vbi_buf->top_field = 1; - } - } - - dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); - } else { - /* Some of this frame is VBI data and some is - video data */ - int vbi_data_len = vbi_size - dev->vbi_read; - dev->vbi_read += vbi_data_len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, vbi_data_len); - dev->capture_type = 1; - p += vbi_data_len; - len -= vbi_data_len; - } - } + process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); + } + return 1; +} - if (dev->capture_type == 1) { - dev->capture_type = 2; - if (dev->progressive || !(dev->cur_field & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - if (buf != NULL) { - if (dev->cur_field & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } - dma_q->pos = 0; - } +static int get_ressource(enum v4l2_buf_type f_type) +{ + switch (f_type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } +/* Usage lock check functions */ +static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); - } + /* is it free? */ + if (dev->resources & res_type) { + /* no, someone else uses it */ + return -EBUSY; } - return rc; + + /* it's free, grab it */ + dev->resources |= res_type; + em28xx_videodbg("res: get %d\n", res_type); + return 0; } +static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + dev->resources &= ~res_type; + em28xx_videodbg("res: put %d\n", res_type); +} /* ------------------------------------------------------------------ - Videobuf operations + Videobuf2 operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct v4l2_frequency f; + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) - >> 3; - - if (0 == *count) - *count = EM28XX_DEF_BUF; + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - if (*count < EM28XX_MIN_BUF) - *count = EM28XX_MIN_BUF; + if (size == 0) + return -EINVAL; - /* Ask tuner to go to analog or radio mode */ - memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; - f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 == *nbuffers) + *nbuffers = 32; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + *nplanes = 1; + sizes[0] = size; return 0; } -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int +buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + unsigned long size; - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) + em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + if (vb2_plane_size(vb, 0) < size) { + em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - struct em28xx *dev = fh->dev; - int rc = 0, urb_init = 0; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct v4l2_frequency f; + int rc = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; + em28xx_videodbg("%s\n", __func__); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; + /* Make sure streaming is not already in progress for this type + of filehandle (e.g. video, vbi) */ + rc = res_get(dev, vq->type); + if (rc) + return rc; + + if (dev->streaming_users++ == 0) { + /* First active streaming user, so allocate all the URBs */ - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; + /* Allocate the USB bandwidth */ + em28xx_set_alternate(dev); + + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_wake_i2c(dev); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); + dev->capture_type = -1; + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + dev->packet_multiplier, + em28xx_urb_data_copy); if (rc < 0) goto fail; - } - if (!dev->isoc_ctl.analog_bufs.num_bufs) - urb_init = 1; + /* + * djh: it's not clear whether this code is still needed. I'm + * leaving it in here for now entirely out of concern for + * backward compatibility (the old code did it) + */ - if (urb_init) { - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); + /* Ask tuner to go to analog or radio mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) + f.type = V4L2_TUNER_RADIO; else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); - if (rc < 0) - goto fail; + f.type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - fail: - free_buffer(vq, buf); return rc; } -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static int em28xx_stop_streaming(struct vb2_queue *vq) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); + + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vidq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vidq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vid_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + return 0; } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +int em28xx_stop_vbi_streaming(struct vb2_queue *vq) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = (struct em28xx *)fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); - em28xx_isocdbg("em28xx: called buffer_release\n"); + res_free(dev, vq->type); - free_buffer(vq, buf); + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vbiq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; } -static struct videobuf_queue_ops em28xx_video_qops = { - .buf_setup = buffer_setup, +static void +buffer_queue(struct vb2_buffer *vb) +{ + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static struct vb2_ops em28xx_video_qops = { + .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; +int em28xx_vb2_setup(struct em28xx *dev) +{ + int rc; + struct vb2_queue *q; + + /* Setup Videobuf2 for Video capture */ + q = &dev->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* Setup Videobuf2 for VBI capture */ + q = &dev->vb_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + return 0; +} + /********************* v4l2 interface **************************************/ static void video_mux(struct em28xx *dev, int index) @@ -856,143 +757,54 @@ static void video_mux(struct em28xx *dev, int index) em28xx_audio_analog_set(dev); } -/* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh, unsigned int bit) -{ - struct em28xx *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - em28xx_videodbg("res: get %d\n", bit); - return 1; -} - -static int res_check(struct em28xx_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct em28xx *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct em28xx_fh *fh, unsigned int bits) +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { - struct em28xx *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - em28xx_videodbg("res: put %d\n", bits); -} - -static int get_ressource(struct em28xx_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return EM28XX_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return EM28XX_RESOURCE_VBI; - default: - BUG(); - return 0; - } -} - -/* - * ac97_queryctrl() - * return the ac97 supported controls - */ -static int ac97_queryctrl(struct v4l2_queryctrl *qc) -{ - int i; + struct em28xx *dev = priv; - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - /* Control is not ac97 related */ - return 1; -} - -/* - * ac97_get_ctrl() - * return the current values for ac97 mute and volume - */ -static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) -{ + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->mute; - return 0; + dev->mute = ctrl->val; + em28xx_audio_analog_set(dev); + break; case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - default: - /* Control is not ac97 related */ - return 1; + dev->volume = ctrl->val; + em28xx_audio_analog_set(dev); + break; } } -/* - * ac97_set_ctrl() - * set values for ac97 mute and volume - */ -static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) - if (ctrl->id == ac97_qctrl[i].id) - goto handle; - - /* Announce that hasn't handle it */ - return 1; - -handle: - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) - return -ERANGE; + struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; + dev->mute = ctrl->val; break; case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; + dev->volume = ctrl->val; break; } return em28xx_audio_analog_set(dev); } +const struct v4l2_ctrl_ops em28xx_ctrl_ops = { + .s_ctrl = em28xx_s_ctrl, +}; + static int check_dev(struct em28xx *dev) { - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_errdev("v4l2 ioctl: device not present\n"); return -ENODEV; } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } return 0; } @@ -1072,8 +884,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ + /* + * MaxPacketSize for em2800 is too small to capture at full + * resolution use half of maxw as the scaler can only scale + * to 50% + */ if (width == maxw && height == maxh) width /= 2; } else { @@ -1119,7 +934,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, /* set new image size */ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); return 0; @@ -1128,21 +942,13 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; + struct em28xx *dev = video_drvdata(file); - rc = check_dev(dev); - if (rc < 0) - return rc; + if (dev->streaming_users > 0) + return -EBUSY; vidioc_try_fmt_vid_cap(file, priv, f); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); } @@ -1153,6 +959,8 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -1168,6 +976,8 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -1184,15 +994,22 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) struct v4l2_format f; int rc; + if (dev->board.is_webcam) + return -ENOTTY; + if (*norm == dev->norm) + return 0; rc = check_dev(dev); if (rc < 0) return rc; + if (dev->streaming_users > 0) + return -EBUSY; + dev->norm = *norm; /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; + f.fmt.pix.width = 720; + f.fmt.pix.height = (*norm & V4L2_STD_525_60) ? 480 : 576; vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ @@ -1216,6 +1033,7 @@ static int vidioc_g_parm(struct file *file, void *priv, if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, g_parm, p); @@ -1233,11 +1051,12 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx *dev = fh->dev; if (!dev->board.is_webcam) - return -EINVAL; + return -ENOTTY; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); } @@ -1276,6 +1095,9 @@ static int vidioc_enum_input(struct file *file, void *priv, i->type = V4L2_INPUT_TYPE_TUNER; i->std = dev->vdev->tvnorms; + /* webcams do not have the STD API */ + if (dev->board.is_webcam) + i->capabilities = 0; return 0; } @@ -1375,131 +1197,6 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int id = qc->id; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - /* enumerate AC97 controls */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - rc = ac97_queryctrl(qc); - if (!rc) - return 0; - } - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -/* - * FIXME: This is an indirect way to check if a control exists at a - * subdev. Instead of that hack, maybe the better would be to change all - * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. - */ -static int check_subdev_ctrl(struct em28xx *dev, int id) -{ - struct v4l2_queryctrl qc; - - memset(&qc, 0, sizeof(qc)); - qc.id = id; - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); - - if (qc.type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - rc = 0; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_get_ctrl(dev, ctrl); - else - rc = 1; - - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - if (check_subdev_ctrl(dev, ctrl->id)) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } - - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_set_ctrl(dev, ctrl); - else - rc = 1; - - /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - rc = check_subdev_ctrl(dev, ctrl->id); - if (!rc) - v4l2_device_call_all(&dev->v4l2_dev, 0, - core, s_ctrl, ctrl); - /* - * In the case of non-AC97 volume controls, we still need - * to do some setups at em28xx, in order to mute/unmute - * and to adjust audio volume. However, the value ranges - * should be checked by the corresponding V4L subdriver. - */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - rc = em28xx_audio_analog_set(dev); - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - rc = em28xx_audio_analog_set(dev); - } - } - return (rc < 0) ? rc : 0; -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1515,7 +1212,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); return 0; @@ -1545,7 +1241,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 != f->tuner) + return -EINVAL; + f->frequency = dev->ctl_freq; return 0; } @@ -1564,13 +1262,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - dev->ctl_freq = f->frequency; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + dev->ctl_freq = f->frequency; return 0; } @@ -1596,6 +1290,14 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_NONE; + return 0; + } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); @@ -1704,72 +1406,10 @@ static int vidioc_cropcap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_streamon(&fh->vb_vidq); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_streamon(&fh->vb_vbiq); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - } - - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1777,20 +1417,26 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = - V4L2_CAP_SLICED_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - - if (dev->vbi_dev) - cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else if (vdev->vfl_type == VFL_TYPE_RADIO) + cap->device_caps = V4L2_CAP_RADIO; + else + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; if (dev->audio_mode.has_audio) - cap->capabilities |= V4L2_CAP_AUDIO; + cap->device_caps |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (dev->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1845,46 +1491,6 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, return 0; } -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, @@ -1900,6 +1506,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1928,6 +1535,7 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1943,100 +1551,10 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_reqbufs(&fh->vb_vidq, rb); - else - return videobuf_reqbufs(&fh->vb_vbiq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_querybuf(&fh->vb_vidq, b); - else { - /* FIXME: I'm not sure yet whether this is a bug in zvbi or - the videobuf framework, but we probably shouldn't be - returning a buffer larger than that which was asked for. - At a minimum, it causes a crash in zvbi since it does - a memcpy based on the source buffer length */ - int result = videobuf_querybuf(&fh->vb_vbiq, b); - b->length = dev->vbi_width * dev->vbi_height * 2; - - return result; - } -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_qbuf(&fh->vb_vidq, b); - else - return videobuf_qbuf(&fh->vb_vbiq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & - O_NONBLOCK); - else - return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & - O_NONBLOCK); -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2053,26 +1571,6 @@ static int radio_g_tuner(struct file *file, void *priv, return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2086,48 +1584,16 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - if (qc->id < V4L2_CID_BASE || - qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - return -EINVAL; -} - /* * em28xx_v4l2_open() * inits the device and starts isoc transfer */ static int em28xx_v4l2_open(struct file *filp) { - int errCode = 0, radio = 0; struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); enum v4l2_buf_type fh_type = 0; struct em28xx_fh *fh; - enum v4l2_field field; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -2136,9 +1602,6 @@ static int em28xx_v4l2_open(struct file *filp) case VFL_TYPE_VBI: fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; break; - case VFL_TYPE_RADIO: - radio = 1; - break; } em28xx_videodbg("open dev=%s type=%s users=%d\n", @@ -2154,14 +1617,13 @@ static int em28xx_v4l2_open(struct file *filp) mutex_unlock(&dev->lock); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; - fh->radio = radio; fh->type = fh_type; filp->private_data = fh; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); /* Needed, since GPIO might have disabled power of @@ -2170,31 +1632,18 @@ static int em28xx_v4l2_open(struct file *filp) em28xx_wake_i2c(dev); } - if (fh->radio) { + + if (vdev->vfl_type == VFL_TYPE_RADIO) { em28xx_videodbg("video_open: setting radio device\n"); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); } dev->users++; - if (dev->progressive) - field = V4L2_FIELD_NONE; - else - field = V4L2_FIELD_INTERLACED; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, field, - sizeof(struct em28xx_buffer), fh, &dev->lock); - - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct em28xx_buffer), fh, &dev->lock); mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); - return errCode; + return 0; } /* @@ -2248,25 +1697,16 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); mutex_lock(&dev->lock); - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_stop(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_stop(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } + vb2_fop_release(filp); if (dev->users == 1) { /* the device is already disconnect, free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); - kfree(fh); return 0; } @@ -2274,7 +1714,6 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ @@ -2287,129 +1726,18 @@ static int em28xx_v4l2_close(struct file *filp) } } - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); dev->users--; mutex_unlock(&dev->lock); return 0; } -/* - * em28xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - mutex_unlock(&dev->lock); - - return rc; -} - -/* - * em28xx_poll() - * will allocate buffers when called for the first time - */ -static unsigned int em28xx_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; - } -} - -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = em28xx_poll(filp, wait); - mutex_unlock(&dev->lock); - return res; -} - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - mutex_unlock(&dev->lock); - - em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - rc); - - return rc; -} - static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -2420,19 +1748,20 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_querystd = vidioc_querystd, .vidioc_s_std = vidioc_s_std, @@ -2441,15 +1770,14 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -2459,11 +1787,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, }; static const struct v4l2_file_operations radio_fops = { @@ -2474,18 +1801,13 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -2514,9 +1836,11 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + if (dev->board.is_webcam) + vfd->tvnorms = 0; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2527,7 +1851,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, int em28xx_register_analog_devices(struct em28xx *dev) { - u8 val; + u8 val; int ret; unsigned int maxw; @@ -2535,7 +1859,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->name, EM28XX_VERSION); /* set default norm */ - dev->norm = em28xx_video_template.current_norm; + dev->norm = V4L2_STD_PAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; @@ -2543,10 +1867,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->format = &format[0]; maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (dev->board.is_em2800) - maxw /= 2; + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (dev->board.is_em2800) + maxw /= 2; em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); @@ -2572,6 +1896,8 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_errdev("cannot allocate video_device.\n"); return -ENODEV; } + dev->vdev->queue = &dev->vb_vidq; + dev->vdev->queue->lock = &dev->vb_queue_lock; /* register v4l2 video video_device */ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, @@ -2587,6 +1913,9 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + dev->vbi_dev->queue = &dev->vb_vbiq; + dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; + /* register v4l2 vbi video_device */ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 86e90d86da6d..2aa4b8472a62 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -4,6 +4,7 @@ Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com> Ludovico Cavedon <cavedon@sssup.it> Mauro Carvalho Chehab <mchehab@infradead.org> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de> @@ -30,13 +31,12 @@ #include <linux/mutex.h> #include <linux/videodev2.h> -#include <media/videobuf-vmalloc.h> +#include <media/videobuf2-vmalloc.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> #include <media/ir-kbd-i2c.h> #include <media/rc-core.h> -#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) -#include <media/videobuf-dvb.h> -#endif #include "tuner-xc2028.h" #include "xc5000.h" #include "em28xx-reg.h" @@ -157,12 +157,18 @@ #define EM28XX_NUM_BUFS 5 #define EM28XX_DVB_NUM_BUFS 5 -/* number of packets for each buffer +/* isoc transfers: number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 64 -#define EM28XX_DVB_MAX_PACKETS 64 +#define EM28XX_NUM_ISOC_PACKETS 64 +#define EM28XX_DVB_NUM_ISOC_PACKETS 64 + +/* bulk transfers: transfer buffer size = packet size * packet multiplier + USB 2.0 spec says bulk packet size is always 512 bytes + */ +#define EM28XX_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 #define EM28XX_INTERLACED_DEFAULT 1 @@ -187,12 +193,8 @@ Interval: 125us */ -/* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT \ - msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) - /* time in msecs to wait for i2c writes to finish */ -#define EM2800_I2C_WRITE_TIMEOUT 20 +#define EM2800_I2C_XFER_TIMEOUT 20 enum em28xx_mode { EM28XX_SUSPEND, @@ -203,7 +205,7 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_bufs { +struct em28xx_usb_bufs { /* max packet size of isoc transaction */ int max_pkt_size; @@ -213,26 +215,26 @@ struct em28xx_usb_isoc_bufs { /* number of allocated urbs */ int num_bufs; - /* urb for isoc transfers */ + /* urb for isoc/bulk transfers */ struct urb **urb; - /* transfer buffers for isoc transfer */ + /* transfer buffers for isoc/bulk transfer */ char **transfer_buffer; }; -struct em28xx_usb_isoc_ctl { - /* isoc transfer buffers for analog mode */ - struct em28xx_usb_isoc_bufs analog_bufs; +struct em28xx_usb_ctl { + /* isoc/bulk transfer buffers for analog mode */ + struct em28xx_usb_bufs analog_bufs; - /* isoc transfer buffers for digital mode */ - struct em28xx_usb_isoc_bufs digital_bufs; + /* isoc/bulk transfer buffers for digital mode */ + struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* isoc urb callback */ - int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + /* copy data from URB */ + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb); }; @@ -247,19 +249,26 @@ struct em28xx_fmt { /* buffer for one video frame */ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head list; - struct list_head frame; + void *mem; + unsigned int length; int top_field; + + /* counter to control buffer fill */ + unsigned int pos; + /* NOTE; in interlaced mode, this value is reset to zero at + * the start of each new field (not frame !) */ + + /* pointer to vmalloc memory address in vb */ + char *vb_buf; }; struct em28xx_dmaqueue { struct list_head active; wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; }; /* inputs */ @@ -430,13 +439,6 @@ struct em28xx_eeprom { u8 string_idx_table; }; -/* device states */ -enum em28xx_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - #define EM28XX_AUDIO_BUFS 5 #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ @@ -469,12 +471,8 @@ struct em28xx_audio { struct em28xx; struct em28xx_fh { + struct v4l2_fh fh; struct em28xx *dev; - int radio; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -487,9 +485,14 @@ struct em28xx { int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; + unsigned char disconnected:1; /* device has been diconnected */ + int audio_ifnum; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + /* provides ac97 mute and volume overrides */ + struct v4l2_ctrl_handler ac97_ctrl_handler; struct em28xx_board board; /* Webcam specific fields */ @@ -497,7 +500,7 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; - /* Allows progressive (e. g. non-interlaced) mode */ + /* Progressive (non-interlaced) mode */ int progressive; /* Vinmode/Vinctl used at the driver */ @@ -532,6 +535,7 @@ struct em28xx { struct i2c_client i2c_client; /* video for linux */ int users; /* user count for exclusive use */ + int streaming_users; /* Number of actively streaming users */ struct video_device *vdev; /* video for linux device struct */ v4l2_std_id norm; /* selected tv norm */ int ctl_freq; /* selected frequency */ @@ -554,13 +558,10 @@ struct em28xx { struct em28xx_audio adev; - /* states */ - enum em28xx_dev_state state; - - /* vbi related state tracking */ + /* capture state tracking */ int capture_type; + unsigned char top_field:1; int vbi_read; - unsigned char cur_field; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ @@ -574,6 +575,12 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* Videobuf2 */ + struct vb2_queue vb_vidq; + struct vb2_queue vb_vbiq; + struct mutex vb_queue_lock; + struct mutex vb_vbi_queue_lock; + /* resources in use */ unsigned int resources; @@ -582,17 +589,31 @@ struct em28xx { /* Isoc control struct */ struct em28xx_dmaqueue vidq; struct em28xx_dmaqueue vbiq; - struct em28xx_usb_isoc_ctl isoc_ctl; + struct em28xx_usb_ctl usb_ctl; spinlock_t slock; + unsigned int field_count; + unsigned int vbi_field_count; + /* usb transfer */ struct usb_device *udev; /* the usb device */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - int dvb_alt; /* alternate for DVB */ - unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ + u8 analog_ep_isoc; /* address of isoc endpoint for analog */ + u8 analog_ep_bulk; /* address of bulk endpoint for analog */ + u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ + u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */ + int alt; /* alternate setting */ + int max_pkt_size; /* max packet size of the selected ep at alt */ + int packet_multiplier; /* multiplier for wMaxPacketSize, used for + URB buffer size definition */ + int num_alt; /* number of alternative settings */ + unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc + transfers for analog */ + int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ + unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the + selected DVB ep at dvb_alt */ + unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc + transfers for DVB */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ @@ -666,12 +687,14 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size); -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier); +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier, + int (*urb_data_copy) + (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); @@ -683,8 +706,13 @@ void em28xx_init_extension(struct em28xx *dev); void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ +int em28xx_vb2_setup(struct em28xx *dev); int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); +int em28xx_stop_vbi_streaming(struct vb2_queue *vq); +extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); @@ -695,7 +723,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-vbi.c */ -extern struct videobuf_queue_ops em28xx_vbi_qops; +extern struct vb2_ops em28xx_vbi_qops; /* printk macros */ diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 62ba80d9b998..fdaeeb14453f 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -536,20 +536,4 @@ static struct usb_driver sd_driver = { #endif }; -/* -- module insert / remove -- */ -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index d236d1791f78..1f253dfebe47 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -55,6 +55,11 @@ MODULE_LICENSE("GPL"); #define PAC207_AUTOGAIN_DEADZONE 30 +/* global parameters */ +static int led_invert; +module_param(led_invert, int, 0644); +MODULE_PARM_DESC(led_invert, "Invert led"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -187,10 +192,14 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; + pac207_write_reg(gspca_dev, 0x41, mode); pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ return gspca_dev->usb_err; @@ -303,7 +312,11 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x02, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ - mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x00; + else + mode = 0x02; if (gspca_dev->width == 176) { /* 176x144 */ mode |= 0x01; PDEBUG(D_STREAM, "pac207_start mode 176x144"); @@ -325,8 +338,15 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ - pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index 748e1421d6d8..e95fa8997d22 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -52,9 +52,13 @@ static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_HFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_hflip(gspca_dev, ctrl->val); break; case V4L2_CID_VFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_vflip(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: @@ -94,11 +98,14 @@ static int vv6410_init_controls(struct sd *sd) { struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_handler_init(hdl, 2); + /* Disable the hardware VFLIP and HFLIP as we currently lack a + mechanism to adjust the image offset in such a way that + we don't need to renegotiate the announced format */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index 8bc6c3ceec2c..b92d4ef2de6e 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -494,7 +494,7 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - PDEBUG(D_CONF, "Gamma: %d", sd->gamma); + PDEBUG(D_CONF, "Gamma: %d", val); reg_w_ixbuf(gspca_dev, 0x90, gamma_table[val], sizeof gamma_table[0]); } diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 031cf024304c..6c5054f3f674 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -217,8 +217,7 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) hdpvr_activate_ir(dev); - memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, - sizeof(struct i2c_adapter)); + dev->i2c_adapter = hdpvr_i2c_adapter_template; dev->i2c_adapter.dev.parent = &dev->udev->dev; i2c_set_adapdata(&dev->i2c_adapter, dev); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c index e046fdaec5ae..f7702aeeda3f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -422,8 +422,7 @@ int pvr2_encoder_adjust(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Error from cx2341x module code=%d",ret); } else { - memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_state = hdw->enc_ctl_state; hdw->enc_cur_valid = !0; } return ret; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 9ab596c78a4e..b5e929f1bf82 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -649,8 +649,8 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) } // Configure the adapter and set up everything else related to it. - memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); - memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); + hdw->i2c_adap = pvr2_i2c_adap_template; + hdw->i2c_algo = pvr2_i2c_algo_template; strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; hdw->i2c_adap.algo = &hdw->i2c_algo; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 6930676051e7..34c3b6e80e86 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1339,7 +1339,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); + dip->devbase = vdev_template; dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; { diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5210239cbaee..5ec15cb1ed26 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -316,7 +316,8 @@ static void pwc_isoc_handler(struct urb *urb) struct pwc_frame_buf *fbuf = pdev->fill_buf; if (pdev->vsync == 1) { - do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp( + &fbuf->vb.v4l2_buf.timestamp); pdev->vsync = 2; } @@ -1007,7 +1008,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } /* Init video_device structure */ - memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); + pdev->vdev = pwc_template; strcpy(pdev->vdev.name, name); pdev->vdev.queue = &pdev->vb_queue; pdev->vdev.queue->lock = &pdev->vb_queue_lock; diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 8ebec0d7bf59..498c57ea5d32 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -593,7 +593,7 @@ static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); @@ -629,7 +629,6 @@ static void s2255_fillbuff(struct s2255_channel *channel, struct s2255_buffer *buf, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -674,8 +673,7 @@ static void s2255_fillbuff(struct s2255_channel *channel, /* tell v4l buffer was filled */ buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 73605864fffa..c957e9aa6077 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -173,7 +173,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, cam->frame[i].buf.sequence = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; + cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } return cam->nbuffers; @@ -773,7 +773,8 @@ end_of_frame: img); if ((*f)->buf.bytesused == 0) - do_gettimeofday(&(*f)->buf.timestamp); + v4l2_get_timestamp( + &(*f)->buf.timestamp); (*f)->buf.bytesused += img; @@ -2826,7 +2827,7 @@ sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) b.index >= cam->nbuffers || cam->io != IO_MMAP) return -EINVAL; - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + b = cam->frame[b.index].buf; if (cam->frame[b.index].vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; @@ -2929,7 +2930,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, f->state = F_UNUSED; - memcpy(&b, &f->buf, sizeof(b)); + b = f->buf; if (f->vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index fa3671de02aa..39f1aae209bc 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -78,7 +78,7 @@ struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev) unsigned long flags = 0; /* Current buffer must be NULL when this functions gets called */ - BUG_ON(dev->isoc_ctl.buf); + WARN_ON(dev->isoc_ctl.buf); spin_lock_irqsave(&dev->buf_lock, flags); if (!list_empty(&dev->avail_bufs)) { @@ -101,7 +101,7 @@ void stk1160_buffer_done(struct stk1160 *dev) buf->vb.v4l2_buf.sequence = dev->field_count >> 1; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.bytesused = buf->bytesused; - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 5d3c032d733c..4cbab085e348 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -28,6 +28,7 @@ #include <linux/errno.h> #include <linux/slab.h> +#include <linux/dmi.h> #include <linux/usb.h> #include <linux/mm.h> #include <linux/vmalloc.h> @@ -38,12 +39,12 @@ #include "stk-webcam.h" -static bool hflip; -module_param(hflip, bool, 0444); +static int hflip = -1; +module_param(hflip, int, 0444); MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip; -module_param(vflip, bool, 0444); +static int vflip = -1; +module_param(vflip, int, 0444); MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; @@ -62,6 +63,19 @@ static struct usb_device_id stkwebcam_table[] = { }; MODULE_DEVICE_TABLE(usb, stkwebcam_table); +/* The stk webcam laptop module is mounted upside down in some laptops :( */ +static const struct dmi_system_id stk_upside_down_dmi_table[] = { + { + .ident = "ASUS G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G1") + } + }, + {} +}; + + /* * Basic stuff */ @@ -466,6 +480,7 @@ static int stk_setup_siobuf(struct stk_camera *dev, int index) buf->dev = dev; buf->v4lbuf.index = index; buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->v4lbuf.field = V4L2_FIELD_NONE; buf->v4lbuf.memory = V4L2_MEMORY_MMAP; buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; @@ -816,10 +831,16 @@ static int stk_vidioc_g_ctrl(struct file *filp, c->value = dev->vsettings.brightness; break; case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.hflip; + else + c->value = dev->vsettings.hflip; break; case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.vflip; + else + c->value = dev->vsettings.vflip; break; default: return -EINVAL; @@ -836,10 +857,16 @@ static int stk_vidioc_s_ctrl(struct file *filp, dev->vsettings.brightness = c->value; return stk_sensor_set_brightness(dev, c->value >> 8); case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = !c->value; + else + dev->vsettings.hflip = c->value; return 0; case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = !c->value; + else + dev->vsettings.vflip = c->value; return 0; default: return -EINVAL; @@ -1113,7 +1140,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); + v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); *buf = sbuf->v4lbuf; return 0; @@ -1275,8 +1302,18 @@ static int stk_camera_probe(struct usb_interface *interface, dev->interface = interface; usb_get_intf(interface); - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; + if (hflip != -1) + dev->vsettings.hflip = hflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = 1; + else + dev->vsettings.hflip = 0; + if (vflip != -1) + dev->vsettings.vflip = vflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = 1; + else + dev->vsettings.vflip = 0; dev->n_sbufs = 0; set_present(dev); diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 3082bfa9b2c5..21723378bb8f 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -212,7 +212,7 @@ static void submit_frame(struct front_face *front) front->curr_frame = NULL; vb->state = VIDEOBUF_DONE; vb->field_count++; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); wake_up(&vb->done); } diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index e1f3f66e1e63..9fc1e940a82b 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -360,8 +360,8 @@ dvb_dmx_err: dvb_dmx_release(&dvb->demux); frontend_err: if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } adapter_err: dvb_unregister_adapter(&dvb->adapter); @@ -384,8 +384,8 @@ static void unregister_dvb(struct tm6000_core *dev) /* mutex_lock(&tm6000_driver.open_close_mutex); */ if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } dvb_dmxdev_release(&dvb->dmxdev); diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f656fd7a39a2..e3c567c27918 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -49,12 +49,15 @@ #define TM6000_MIN_BUF 4 #define TM6000_DEF_BUF 8 +#define TM6000_NUM_URB_BUF 8 + #define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static bool keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; @@ -191,7 +194,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -538,6 +541,71 @@ static void tm6000_irq_callback(struct urb *urb) } /* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer != NULL) + return 0; + + dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->urb_buffer) { + tm6000_err("cannot allocate memory for urb buffers\n"); + return -ENOMEM; + } + + dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL); + if (!dev->urb_dma) { + tm6000_err("cannot allocate memory for urb dma pointers\n"); + return -ENOMEM; + } + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (dev->urb_buffer == NULL) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + +/* * Stop and Deallocate URBs */ static void tm6000_uninit_isoc(struct tm6000_core *dev) @@ -551,18 +619,15 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } + if (!keep_urb) + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); kfree(dev->isoc_ctl.transfer_buffer); @@ -572,12 +637,13 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) } /* - * Allocate URBs and start IRQ + * Assign URBs and start IRQ */ static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; struct urb *urb; /* De-allocates all pending stuff */ @@ -605,6 +671,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; + dev->urb_size = sb_size; dev->isoc_ctl.num_bufs = num_bufs; @@ -627,6 +694,17 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets, num_bufs, sb_size, dev->isoc_in.maxsize, size); + + if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); @@ -638,17 +716,8 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) } dev->isoc_ctl.urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, @@ -1826,6 +1895,9 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) video_unregister_device(dev->radio_dev); @@ -1851,3 +1923,5 @@ MODULE_PARM_DESC(debug, "activates debug info"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 6df418658c9c..173dcd7a7284 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -264,6 +264,11 @@ struct tm6000_core { spinlock_t slock; + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + unsigned long quirks; }; diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index c9b2042f8bdf..816b1cffab7d 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1169,7 +1169,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - do_gettimeofday(&(frame->timestamp)); + v4l2_get_timestamp(&(frame->timestamp)); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index 89fec029e924..ba262a32bd3a 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -189,8 +189,7 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) if (usbvision->registered_i2c) return 0; - memcpy(&usbvision->i2c_adap, &i2c_adap_template, - sizeof(struct i2c_adapter)); + usbvision->i2c_adap = i2c_adap_template; sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name, usbvision->dev->bus->busnum, usbvision->dev->devpath); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index ad7f7448072e..cd1fe78a5532 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -761,7 +761,7 @@ static int vidioc_querybuf(struct file *file, if (vb->index >= usbvision->num_frames) return -EINVAL; /* Updating the corresponding frame state */ - vb->flags = 0; + vb->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; frame = &usbvision->frame[vb->index]; if (frame->grabstate >= frame_state_ready) vb->flags |= V4L2_BUF_FLAG_QUEUED; @@ -843,7 +843,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) vb->memory = V4L2_MEMORY_MMAP; vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE; + V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; vb->timestamp = f->timestamp; diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 39edd4442932..74d56df3347f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -501,7 +501,6 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -530,8 +529,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } @@ -559,7 +557,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 65875c3aba1b..976d029e9925 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -82,3 +82,14 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + +config VIDEO_V4L2_INT_DEVICE + tristate "V4L2 int device (DEPRECATED)" + depends on VIDEO_V4L2 + ---help--- + An early framework for a hardware-independent interface for + image sensors and bridges etc. Currently used by omap24xxcam and + tcm825x drivers that should be converted to V4L2 subdev. + + Do not use for new developments. + diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c2d61d4f03d1..a9d355230e8e 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -10,7 +10,8 @@ ifeq ($(CONFIG_COMPAT),y) videodev-objs += v4l2-compat-ioctl32.o endif -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o +obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 380ddd89fa4c..614316f9b7a4 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -978,3 +978,13 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( return best; } EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); + +void v4l2_get_timestamp(struct timeval *tv) +{ + struct timespec ts; + + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; +} +EXPORT_SYMBOL_GPL(v4l2_get_timestamp); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ee201d9347..7b486ac3f4d9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -577,8 +577,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_GAIN: return "Gain"; case V4L2_CID_HFLIP: return "Horizontal Flip"; case V4L2_CID_VFLIP: return "Vertical Flip"; - case V4L2_CID_HCENTER: return "Horizontal Center"; - case V4L2_CID_VCENTER: return "Vertical Center"; case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; @@ -1204,6 +1202,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, send_event(fh, ctrl, (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + if (ctrl->call_notify && changed && ctrl->handler->notify) + ctrl->handler->notify(ctrl, ctrl->handler->notify_priv); } } @@ -2725,6 +2725,22 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) +{ + if (ctrl == NULL) + return; + if (notify == NULL) { + ctrl->call_notify = 0; + return; + } + if (WARN_ON(ctrl->handler->notify && ctrl->handler->notify != notify)) + return; + ctrl->handler->notify = notify; + ctrl->handler->notify_priv = priv; + ctrl->call_notify = 1; +} +EXPORT_SYMBOL(v4l2_ctrl_notify); + static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 513969fa695d..98a7f5e9cb1b 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -159,31 +159,21 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->v4l2_dev = v4l2_dev; if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); - if (err) { - module_put(sd->owner); - return err; - } + if (err) + goto error_module; } /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); - if (err) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err) + goto error_unregister; #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { err = media_device_register_entity(v4l2_dev->mdev, entity); - if (err < 0) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err < 0) + goto error_unregister; } #endif @@ -192,6 +182,14 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, spin_unlock(&v4l2_dev->lock); return 0; + +error_unregister: + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); +error_module: + module_put(sd->owner); + sd->v4l2_dev = NULL; + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 438ea45d1074..da99cf727162 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -62,7 +62,7 @@ struct v4l2_m2m_dev { struct list_head job_queue; spinlock_t job_spinlock; - struct v4l2_m2m_ops *m2m_ops; + const struct v4l2_m2m_ops *m2m_ops; }; static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, @@ -519,7 +519,7 @@ EXPORT_SYMBOL(v4l2_m2m_mmap); * * Usually called from driver's probe() function. */ -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 5449e8aa984a..fb5ee5dd8fe9 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -340,7 +340,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - b->flags = 0; + b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (vb->map) b->flags |= V4L2_BUF_FLAG_MAPPED; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index e02c4797b1c6..d09be38dd377 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -40,9 +40,10 @@ module_param(debug, int, 0644); #define call_qop(q, op, args...) \ (((q)->ops->op) ? ((q)->ops->op(args)) : 0) -#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ +#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED) + V4L2_BUF_FLAG_PREPARED | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -401,7 +402,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) /* * Clear any buffer state related flags. */ - b->flags &= ~V4L2_BUFFER_STATE_FLAGS; + b->flags &= ~V4L2_BUFFER_MASK_FLAGS; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -941,7 +943,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; + vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; } /** |