diff options
Diffstat (limited to 'drivers')
312 files changed, 23875 insertions, 5277 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eb2ee11b6412..ccca9ac34dca 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2070,6 +2070,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_BEATPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MASTERKIT, USB_DEVICE_ID_MASTERKIT_MA901RADIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4dfa605e2d14..f39be08c1358 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -551,6 +551,9 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_VENDOR_ID_MASTERKIT 0x16c0 +#define USB_DEVICE_ID_MASTERKIT_MA901RADIO 0x05df + #define USB_VENDOR_ID_MCC 0x09db #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 #define USB_DEVICE_ID_MCC_PMD1208LS 0x007a 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; } /** diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 427218b8b10f..ae0abc350e34 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -23,6 +23,8 @@ source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/cxd2099/Kconfig" +source "drivers/staging/media/davinci_vpfe/Kconfig" + source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index aec6eb963940..2b97cae99499 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c index d2a4bce89623..4a2bbd766655 100644 --- a/drivers/staging/media/as102/as10x_cmd_cfg.c +++ b/drivers/staging/media/as102/as10x_cmd_cfg.c @@ -197,7 +197,7 @@ out: * @prsp: pointer to AS10x command response buffer * @proc_id: id of the command * - * Since the contex command reponse does not follow the common + * Since the contex command response does not follow the common * response, a specific parse function is required. * Return 0 on success or negative value in case of error. */ diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 0ff19724992f..822c487592a4 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -66,8 +66,9 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); + dev_err(&adapter->dev, + "Failed to write to I2C register %02x@%02x!\n", + reg, adr); return -1; } return 0; @@ -79,7 +80,7 @@ static int i2c_write(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); + dev_err(&adapter->dev, "Failed to write to I2C!\n"); return -1; } return 0; @@ -94,7 +95,7 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, .buf = val, .len = 1} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); + dev_err(&adapter->dev, "error in i2c_read_reg\n"); return -1; } return 0; @@ -109,7 +110,7 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); + dev_err(&adapter->dev, "error in i2c_read\n"); return -1; } return 0; @@ -277,7 +278,7 @@ static void cam_mode(struct cxd *ci, int mode) #ifdef BUFFER_MODE if (!ci->en.read_data) return; - printk(KERN_INFO "enable cam buffer mode\n"); + dev_info(&ci->i2c->dev, "enable cam buffer mode\n"); /* write_reg(ci, 0x0d, 0x00); */ /* write_reg(ci, 0x0e, 0x01); */ write_regm(ci, 0x08, 0x40, 0x40); @@ -524,7 +525,7 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) msleep(10); #if 0 read_reg(ci, 0x06, &val); - printk(KERN_INFO "%d:%02x\n", i, val); + dev_info(&ci->i2c->dev, "%d:%02x\n", i, val); if (!(val&0x10)) break; #else @@ -542,7 +543,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - printk(KERN_INFO "slot_shutdown\n"); + dev_info(&ci->i2c->dev, "slot_shutdown\n"); mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ @@ -578,10 +579,10 @@ static int campoll(struct cxd *ci) if (istat&0x40) { ci->dr = 1; - printk(KERN_INFO "DR\n"); + dev_info(&ci->i2c->dev, "DR\n"); } if (istat&0x20) - printk(KERN_INFO "WC\n"); + dev_info(&ci->i2c->dev, "WC\n"); if (istat&2) { u8 slotstat; @@ -597,7 +598,7 @@ static int campoll(struct cxd *ci) if (ci->slot_stat) { ci->slot_stat = 0; write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); + dev_info(&ci->i2c->dev, "NO CAM\n"); ci->ready = 0; } } @@ -634,7 +635,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - printk(KERN_INFO "read_data\n"); + dev_info(&ci->i2c->dev, "read_data\n"); if (!ci->dr) return 0; @@ -687,7 +688,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, u8 val; if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); + dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr); return NULL; } @@ -705,7 +706,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, ci->en = en_templ; ci->en.data = ci; init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); + dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr); return &ci->en; } EXPORT_SYMBOL(cxd2099_attach); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index 19c588a59588..0eb607c5b423 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -43,7 +43,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig new file mode 100644 index 000000000000..2e4a28b018e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_DM365_VPFE + tristate "DM365 VPFE Media Controller Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_VPFE_CAPTURE + select VIDEOBUF2_DMA_CONTIG + help + Support for DM365 VPFE based Media Controller Capture driver. + + To compile this driver as a module, choose M here: the + module will be called vpfe-mc-capture. diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile new file mode 100644 index 000000000000..c64515c644cd --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_DM365_VPFE) += \ + dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \ + dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO new file mode 100644 index 000000000000..7015ab35ded5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/TODO @@ -0,0 +1,37 @@ +TODO (general): +================================== + +- User space interface refinement + - Controls should be used when possible rather than private ioctl + - No enums should be used + - Use of MC and V4L2 subdev APIs when applicable + - Single interface header might suffice + - Current interface forces to configure everything at once +- Get rid of the dm365_ipipe_hw.[ch] layer +- Active external sub-devices defined by link configuration; no strcmp + needed +- More generic platform data (i2c adapters) +- The driver should have no knowledge of possible external subdevs; see + struct vpfe_subdev_id +- Some of the hardware control should be refactorede +- Check proper serialisation (through mutexes and spinlocks) +- Names that are visible in kernel global namespace should have a common + prefix (or a few) +- While replacing the older driver in media folder, provide a compatibility + layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD + approach) there is no regression for the users using the older driver. + +Building of uImage and Applications: +================================== + +As of now since the interface will undergo few changes all the include +files are present in staging itself, to build for dm365 follow below steps, + +- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to + include/media/davinci/ folder for building the uImage. +- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required + for building application). +- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required + for building application). diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt new file mode 100644 index 000000000000..1dbd56418704 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt @@ -0,0 +1,154 @@ +Davinci Video processing Front End (VPFE) driver + +Copyright (C) 2012 Texas Instruments Inc + +Contacts: Manjunath Hadli <manjunath.hadli@ti.com> + Prabhakar Lad <prabhakar.lad@ti.com> + + +Introduction +============ + +This file documents the Texas Instruments Davinci Video processing Front End +(VPFE) driver located under drivers/media/platform/davinci. The original driver +exists for Davinci VPFE, which is now being changed to Media Controller +Framework. + +Currently the driver has been successfully used on the following +version of Davinci: + + DM365/DM368 + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor, +lens and flash drivers using the v4l2_subdev interface in the kernel are +supported. + + +Split to subdevs +================ + +The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE +having one subdev to represent it. Each of the subdevs provide a V4L2 subdev +interface to userspace. + + DAVINCI ISIF + DAVINCI IPIPEIF + DAVINCI IPIPE + DAVINCI CROP RESIZER + DAVINCI RESIZER A + DAVINCI RESIZER B + +Each possible link in the VPFE is modeled by a link in the Media controller +interface. For an example program see [1]. + + +ISIF, IPIPE, and RESIZER block IOCTLs +====================================== + +The Davinci Video processing Front End (VPFE) driver supports standard V4L2 +IOCTLs and controls where possible and practical. Much of the functions provided +by the VPFE, however, does not fall under the standard IOCTL's. + +In general, there is a private ioctl for configuring each of the blocks +containing hardware-dependent functions. + +The following private IOCTLs are supported: + + VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS + VIDIOC_VPFE_IPIPE_[S/G]_CONFIG + VIDIOC_VPFE_RSZ_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/davinci_vpfe.h. + +The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and +VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in +the isif, ipipe and resizer blocks respectively. These IOCTL's control several +functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL +accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly +VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And +VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its +argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG +and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in +the isif, ipipe and resizer blocks respectively. + +The detailed functions of the VPFE itself related to a given VPFE block is +described in the Technical Reference Manuals (TRMs) --- see the end of the +document for those. + + +IPIPEIF block IOCTLs +====================================== + +The following private IOCTLs are supported: + + VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/dm365_ipipeif.h + +The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif +hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and +VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params +as its argument. + + +VPFE Operating Modes +========================================== + +a: Continuous Modes +------------------------ + +1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM + +2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +a: Single Shot Modes +------------------------ + +1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<------------------<---------------<--| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + +2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<---------------<---| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + + +Technical reference manuals (TRMs) and other documentation +========================================================== + +Davinci DM365 TRM: +<URL:http://www.ti.com/lit/ds/sprs457e/sprs457e.pdf> +Referenced MARCH 2009-REVISED JUNE 2011 + +Davinci DM368 TRM: +<URL:http://www.ti.com/lit/ds/sprs668c/sprs668c.pdf> +Referenced APRIL 2010-REVISED JUNE 2011 + +Davinci Video Processing Front End (VPFE) DM36x +<URL:http://www.ti.com/lit/ug/sprufg8c/sprufg8c.pdf> + + +References +========== + +[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h new file mode 100644 index 000000000000..7b7e7b26c1e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h @@ -0,0 +1,1290 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_USER_H +#define _DAVINCI_VPFE_USER_H + +#include <linux/types.h> +#include <linux/videodev2.h> + +/* + * Private IOCTL + * + * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif + * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif + * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration + * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration + * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration + * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration + */ + +#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \ + _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_IPIPE_S_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_IPIPE_G_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_RSZ_S_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config) +#define VIDIOC_VPFE_RSZ_G_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config) + +/* + * Private Control's for ISIF + */ +#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001) +#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002) +#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003) +#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004) +#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005) + +/* + * Private Control's for ISIF and IPIPEIF + */ +#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006) + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum vpfe_isif_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + VPFE_ISIF_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + VPFE_ISIF_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define VPFE_ISIF_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum vpfe_isif_vdfc_shift { + /* No Shift */ + VPFE_ISIF_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + VPFE_ISIF_VDFC_SHIFT_1, + /* Shift by 2 bit */ + VPFE_ISIF_VDFC_SHIFT_2, + /* Shift by 3 bit */ + VPFE_ISIF_VDFC_SHIFT_3, + /* Shift by 4 bit */ + VPFE_ISIF_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct vpfe_isif_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct vpfe_isif_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum vpfe_isif_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum vpfe_isif_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum vpfe_isif_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + VPFE_ISIF_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum vpfe_isif_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + VPFE_ISIF_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + VPFE_ISIF_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum vpfe_isif_horz_bc_sz_h { + VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum vpfe_isif_horz_bc_sz_v { + VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct vpfe_isif_horz_bclamp { + /* horizontal clamp mode */ + enum vpfe_isif_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum vpfe_isif_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + VPFE_ISIF_VERT_BC_NO_UPDATE +}; + +enum vpfe_isif_vert_bc_sz_h { + VPFE_ISIF_VERT_BC_SZ_H_2PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_4PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_8PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_16PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_32PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vertical Black Clamp configuration params + */ +struct vpfe_isif_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct vpfe_isif_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vertical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct vpfe_isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct vpfe_isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define VPFE_ISIF_CSC_NUM_COEFF 16 + +struct float_8_bit { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +struct float_16_bit { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct vpfe_isif_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF]; +}; + +enum vpfe_isif_datasft { + /* No Shift */ + VPFE_ISIF_NO_SHIFT, + /* 1 bit Shift */ + VPFE_ISIF_1BIT_SHIFT, + /* 2 bit Shift */ + VPFE_ISIF_2BIT_SHIFT, + /* 3 bit Shift */ + VPFE_ISIF_3BIT_SHIFT, + /* 4 bit Shift */ + VPFE_ISIF_4BIT_SHIFT, + /* 5 bit Shift */ + VPFE_ISIF_5BIT_SHIFT, + /* 6 bit Shift */ + VPFE_ISIF_6BIT_SHIFT +}; + +#define VPFE_ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct vpfe_isif_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum vpfe_isif_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct float_16_bit scale_fact; + /* Size of the linear table */ + unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE]; +}; + +/************************************************************************* +** ISIF Raw configuration parameters +*************************************************************************/ +enum vpfe_isif_fmt_mode { + VPFE_ISIF_SPLIT, + VPFE_ISIF_COMBINE +}; + +enum vpfe_isif_lnum { + VPFE_ISIF_1LINE, + VPFE_ISIF_2LINES, + VPFE_ISIF_3LINES, + VPFE_ISIF_4LINES +}; + +enum vpfe_isif_line { + VPFE_ISIF_1STLINE, + VPFE_ISIF_2NDLINE, + VPFE_ISIF_3RDLINE, + VPFE_ISIF_4THLINE +}; + +struct vpfe_isif_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen3; +}; + +struct vpfe_isif_fmt_cfg { + /* Split or combine or line alternate */ + enum vpfe_isif_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum vpfe_isif_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct vpfe_isif_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum vpfe_isif_line out_line; +}; + +struct vpfe_isif_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct vpfe_isif_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct vpfe_isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct vpfe_isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct vpfe_isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct vpfe_isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct vpfe_isif_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct vpfe_isif_gain_offsets_adj { + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct vpfe_isif_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct vpfe_isif_raw_config { + /* Linearization parameters for image sensor data input */ + struct vpfe_isif_linearize linearize; + /* Data formatter or CSC */ + struct vpfe_isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct vpfe_isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct vpfe_isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct vpfe_isif_gain_offsets_adj gain_offset; + /* Culling */ + struct vpfe_isif_cul culling; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; +}; + +/********************************************************************** + IPIPE API Structures +**********************************************************************/ + +/* IPIPE module configurations */ + +/* IPIPE input configuration */ +#define VPFE_IPIPE_INPUT_CONFIG (1 << 0) +/* LUT based Defect Pixel Correction */ +#define VPFE_IPIPE_LUTDPC (1 << 1) +/* On the fly (OTF) Defect Pixel Correction */ +#define VPFE_IPIPE_OTFDPC (1 << 2) +/* Noise Filter - 1 */ +#define VPFE_IPIPE_NF1 (1 << 3) +/* Noise Filter - 2 */ +#define VPFE_IPIPE_NF2 (1 << 4) +/* White Balance. Also a control ID */ +#define VPFE_IPIPE_WB (1 << 5) +/* 1st RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_1 (1 << 6) +/* 2nd RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_2 (1 << 7) +/* Gamma Correction */ +#define VPFE_IPIPE_GAMMA (1 << 8) +/* 3D LUT color conversion */ +#define VPFE_IPIPE_3D_LUT (1 << 9) +/* RGB to YCbCr module */ +#define VPFE_IPIPE_RGB2YUV (1 << 10) +/* YUV 422 conversion module */ +#define VPFE_IPIPE_YUV422_CONV (1 << 11) +/* Edge Enhancement */ +#define VPFE_IPIPE_YEE (1 << 12) +/* Green Imbalance Correction */ +#define VPFE_IPIPE_GIC (1 << 13) +/* CFA Interpolation */ +#define VPFE_IPIPE_CFA (1 << 14) +/* Chroma Artifact Reduction */ +#define VPFE_IPIPE_CAR (1 << 15) +/* Chroma Gain Suppression */ +#define VPFE_IPIPE_CGS (1 << 16) +/* Global brightness and contrast control */ +#define VPFE_IPIPE_GBCE (1 << 17) + +#define VPFE_IPIPE_MAX_MODULES 18 + +struct ipipe_float_u16 { + unsigned short integer; + unsigned short decimal; +}; + +struct ipipe_float_s16 { + short integer; + unsigned short decimal; +}; + +struct ipipe_float_u8 { + unsigned char integer; + unsigned char decimal; +}; + +/* Copy method selection for vertical correction + * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT + */ +enum vpfe_ipipe_dpc_corr_meth { + /* replace by black or white dot specified by repl_white */ + VPFE_IPIPE_DPC_REPL_BY_DOT = 0, + /* Copy from left */ + VPFE_IPIPE_DPC_CL = 1, + /* Copy from right */ + VPFE_IPIPE_DPC_CR = 2, + /* Horizontal interpolation */ + VPFE_IPIPE_DPC_H_INTP = 3, + /* Vertical interpolation */ + VPFE_IPIPE_DPC_V_INTP = 4, + /* Copy from top */ + VPFE_IPIPE_DPC_CT = 5, + /* Copy from bottom */ + VPFE_IPIPE_DPC_CB = 6, + /* 2D interpolation */ + VPFE_IPIPE_DPC_2D_INTP = 7, +}; + +struct vpfe_ipipe_lutdpc_entry { + /* Horizontal position */ + unsigned short horz_pos; + /* vertical position */ + unsigned short vert_pos; + enum vpfe_ipipe_dpc_corr_meth method; +}; + +#define VPFE_IPIPE_MAX_SIZE_DPC 256 + +/* Structure for configuring DPC module */ +struct vpfe_ipipe_lutdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - replace with black dot, 1 - white dot when correction + * method is IPIPE_DFC_REPL_BY_DOT=0, + */ + unsigned char repl_white; + /* number of entries in the correction table. Currently only + * support up-to 256 entries. infinite mode is not supported + */ + unsigned short dpc_size; + struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC]; +}; + +enum vpfe_ipipe_otfdpc_det_meth { + VPFE_IPIPE_DPC_OTF_MIN_MAX, + VPFE_IPIPE_DPC_OTF_MIN_MAX2 +}; + +struct vpfe_ipipe_otfdpc_thr { + unsigned short r; + unsigned short gr; + unsigned short gb; + unsigned short b; +}; + +enum vpfe_ipipe_otfdpc_alg { + VPFE_IPIPE_OTFDPC_2_0, + VPFE_IPIPE_OTFDPC_3_0 +}; + +struct vpfe_ipipe_otfdpc_2_0_cfg { + /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */ + struct vpfe_ipipe_otfdpc_thr det_thr; + /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or + * maximum value for MIN_MAX method + */ + struct vpfe_ipipe_otfdpc_thr corr_thr; +}; + +struct vpfe_ipipe_otfdpc_3_0_cfg { + /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf) + */ + unsigned char act_adj_shf; + /* DPC3.0 detection threshold, THR */ + unsigned short det_thr; + /* DPC3.0 detection threshold slope, SLP */ + unsigned short det_slp; + /* DPC3.0 detection threshold min, MIN */ + unsigned short det_thr_min; + /* DPC3.0 detection threshold max, MAX */ + unsigned short det_thr_max; + /* DPC3.0 correction threshold, THR */ + unsigned short corr_thr; + /* DPC3.0 correction threshold slope, SLP */ + unsigned short corr_slp; + /* DPC3.0 correction threshold min, MIN */ + unsigned short corr_thr_min; + /* DPC3.0 correction threshold max, MAX */ + unsigned short corr_thr_max; +}; + +struct vpfe_ipipe_otfdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + union { + /* if alg is IPIPE_OTFDPC_2_0 */ + struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0; + /* if alg is IPIPE_OTFDPC_3_0 */ + struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0; + } alg_cfg; +}; + +/* Threshold values table size */ +#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8 +/* Intensity values table size */ +#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8 + +/* NF, sampling method for green pixels */ +enum vpfe_ipipe_nf_sampl_meth { + /* Same as R or B */ + VPFE_IPIPE_NF_BOX, + /* Diamond mode */ + VPFE_IPIPE_NF_DIAMOND +}; + +/* Structure for configuring NF module */ +struct vpfe_ipipe_nf { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* Sampling method for green pixels */ + enum vpfe_ipipe_nf_sampl_meth gr_sample_meth; + /* Down shift value in LUT reference address + */ + unsigned char shft_val; + /* Spread value in NF algorithm + */ + unsigned char spread_val; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF + */ + unsigned char apply_lsc_gain; + /* Threshold values table */ + unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE]; + /* intensity values table */ + unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE]; + /* Edge detection minimum threshold */ + unsigned short edge_det_min_thr; + /* Edge detection maximum threshold */ + unsigned short edge_det_max_thr; +}; + +enum vpfe_ipipe_gic_alg { + VPFE_IPIPE_GIC_ALG_CONST_GAIN, + VPFE_IPIPE_GIC_ALG_ADAPT_GAIN +}; + +enum vpfe_ipipe_gic_thr_sel { + VPFE_IPIPE_GIC_THR_REG, + VPFE_IPIPE_GIC_THR_NF +}; + +enum vpfe_ipipe_gic_wt_fn_type { + /* Use difference as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_DIF, + /* Use weight function as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL +}; + +/* structure for Green Imbalance Correction */ +struct vpfe_ipipe_gic { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - Constant gain , 1 - Adaptive gain algorithm */ + enum vpfe_ipipe_gic_alg gic_alg; + /* GIC gain or weight. Used for Constant gain and Adaptive algorithms + */ + unsigned short gain; + /* Threshold selection. GIC register values or NF2 thr table */ + enum vpfe_ipipe_gic_thr_sel thr_sel; + /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */ + unsigned short thr; + /* this value is used for thr2-thr1, thr3-thr2 or + * thr4-thr3 when wt_fn_type is index. Otherwise it + * is the + */ + unsigned short slope; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG + */ + unsigned char apply_lsc_gain; + /* Multiply Nf2 threshold by this gain. Use this when thr_sel + * is IPIPE_GIC_THR_NF + */ + struct ipipe_float_u8 nf2_thr_gain; + /* Weight function uses difference as index or high pass value. + * Used for adaptive gain algorithm + */ + enum vpfe_ipipe_gic_wt_fn_type wt_fn_type; +}; + +/* Structure for configuring WB module */ +struct vpfe_ipipe_wb { + /* Offset (S12) for R */ + short ofst_r; + /* Offset (S12) for Gr */ + short ofst_gr; + /* Offset (S12) for Gb */ + short ofst_gb; + /* Offset (S12) for B */ + short ofst_b; + /* Gain (U13Q9) for Red */ + struct ipipe_float_u16 gain_r; + /* Gain (U13Q9) for Gr */ + struct ipipe_float_u16 gain_gr; + /* Gain (U13Q9) for Gb */ + struct ipipe_float_u16 gain_gb; + /* Gain (U13Q9) for Blue */ + struct ipipe_float_u16 gain_b; +}; + +enum vpfe_ipipe_cfa_alg { + /* Algorithm is 2DirAC */ + VPFE_IPIPE_CFA_ALG_2DIRAC, + /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */ + VPFE_IPIPE_CFA_ALG_2DIRAC_DAA, + /* Algorithm is DAA */ + VPFE_IPIPE_CFA_ALG_DAA +}; + +/* Structure for CFA Interpolation */ +struct vpfe_ipipe_cfa { + /* 2DirAC or 2DirAC + DAA */ + enum vpfe_ipipe_cfa_alg alg; + /* 2Dir CFA HP value Low Threshold */ + unsigned short hpf_thr_2dir; + /* 2Dir CFA HP value slope */ + unsigned short hpf_slp_2dir; + /* 2Dir CFA HP mix threshold */ + unsigned short hp_mix_thr_2dir; + /* 2Dir CFA HP mix slope */ + unsigned short hp_mix_slope_2dir; + /* 2Dir Direction threshold */ + unsigned short dir_thr_2dir; + /* 2Dir Direction slope */ + unsigned short dir_slope_2dir; + /* 2Dir Non Directional Weight */ + unsigned short nd_wt_2dir; + /* DAA Mono Hue Fraction */ + unsigned short hue_fract_daa; + /* DAA Mono Edge threshold */ + unsigned short edge_thr_daa; + /* DAA Mono threshold minimum */ + unsigned short thr_min_daa; + /* DAA Mono threshold slope */ + unsigned short thr_slope_daa; + /* DAA Mono slope minimum */ + unsigned short slope_min_daa; + /* DAA Mono slope slope */ + unsigned short slope_slope_daa; + /* DAA Mono LP wight */ + unsigned short lp_wt_daa; +}; + +/* Struct for configuring RGB2RGB blending module */ +struct vpfe_ipipe_rgb2rgb { + /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */ + struct ipipe_float_s16 coef_rr; + /* Matrix coefficient for GR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gr; + /* Matrix coefficient for BR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_br; + /* Matrix coefficient for RG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rg; + /* Matrix coefficient for GG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gg; + /* Matrix coefficient for BG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bg; + /* Matrix coefficient for RB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rb; + /* Matrix coefficient for GB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gb; + /* Matrix coefficient for BB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bb; + /* Output offset for R S13/S11 */ + int out_ofst_r; + /* Output offset for G S13/S11 */ + int out_ofst_g; + /* Output offset for B S13/S11 */ + int out_ofst_b; +}; + +#define VPFE_IPIPE_MAX_SIZE_GAMMA 512 + +enum vpfe_ipipe_gamma_tbl_size { + VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64, + VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128, + VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256, + VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512, +}; + +enum vpfe_ipipe_gamma_tbl_sel { + VPFE_IPIPE_GAMMA_TBL_RAM = 0, + VPFE_IPIPE_GAMMA_TBL_ROM = 1, +}; + +struct vpfe_ipipe_gamma_entry { + /* 10 bit slope */ + short slope; + /* 10 bit offset */ + unsigned short offset; +}; + +/* Structure for configuring Gamma correction module */ +struct vpfe_ipipe_gamma { + /* 0 - Enable Gamma correction for Red + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_r; + /* 0 - Enable Gamma correction for Blue + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_b; + /* 0 - Enable Gamma correction for Green + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_g; + /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */ + enum vpfe_ipipe_gamma_tbl_sel tbl_sel; + /* Table size for RAM gamma table. + */ + enum vpfe_ipipe_gamma_tbl_size tbl_size; + /* R table */ + struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Blue table */ + struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Green table */ + struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA]; +}; + +#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729 + +struct vpfe_ipipe_3d_lut_entry { + /* 10 bit entry for red */ + unsigned short r; + /* 10 bit entry for green */ + unsigned short g; + /* 10 bit entry for blue */ + unsigned short b; +}; + +/* structure for 3D-LUT */ +struct vpfe_ipipe_3d_lut { + /* enable/disable 3D lut */ + unsigned char en; + /* 3D - LUT table entry */ + struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT]; +}; + +/* Struct for configuring rgb2ycbcr module */ +struct vpfe_ipipe_rgb2yuv { + /* Matrix coefficient for RY S12Q8 */ + struct ipipe_float_s16 coef_ry; + /* Matrix coefficient for GY S12Q8 */ + struct ipipe_float_s16 coef_gy; + /* Matrix coefficient for BY S12Q8 */ + struct ipipe_float_s16 coef_by; + /* Matrix coefficient for RCb S12Q8 */ + struct ipipe_float_s16 coef_rcb; + /* Matrix coefficient for GCb S12Q8 */ + struct ipipe_float_s16 coef_gcb; + /* Matrix coefficient for BCb S12Q8 */ + struct ipipe_float_s16 coef_bcb; + /* Matrix coefficient for RCr S12Q8 */ + struct ipipe_float_s16 coef_rcr; + /* Matrix coefficient for GCr S12Q8 */ + struct ipipe_float_s16 coef_gcr; + /* Matrix coefficient for BCr S12Q8 */ + struct ipipe_float_s16 coef_bcr; + /* Output offset for R S11 */ + int out_ofst_y; + /* Output offset for Cb S11 */ + int out_ofst_cb; + /* Output offset for Cr S11 */ + int out_ofst_cr; +}; + +enum vpfe_ipipe_gbce_type { + VPFE_IPIPE_GBCE_Y_VAL_TBL = 0, + VPFE_IPIPE_GBCE_GAIN_TBL = 1, +}; + +#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024 + +/* structure for Global brightness and Contrast */ +struct vpfe_ipipe_gbce { + /* enable/disable GBCE */ + unsigned char en; + /* Y - value table or Gain table */ + enum vpfe_ipipe_gbce_type type; + /* ptr to LUT for GBCE with 1024 entries */ + unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT]; +}; + +/* Chrominance position. Applicable only for YCbCr input + * Applied after edge enhancement + */ +enum vpfe_chr_pos { + /* Co-siting, same position with luminance */ + VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0, + /* Centering, In the middle of luminance */ + VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1, +}; + +/* Structure for configuring yuv422 conversion module */ +struct vpfe_ipipe_yuv422_conv { + /* Max Chrominance value */ + unsigned char en_chrom_lpf; + /* 1 - enable LPF for chrminance, 0 - disable */ + enum vpfe_chr_pos chrom_pos; +}; + +#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024 + +enum vpfe_ipipe_yee_merge_meth { + VPFE_IPIPE_YEE_ABS_MAX = 0, + VPFE_IPIPE_YEE_EE_ES = 1, +}; + +/* Structure for configuring YUV Edge Enhancement module */ +struct vpfe_ipipe_yee { + /* 1 - enable enhancement, 0 - disable */ + unsigned char en; + /* enable/disable halo reduction in edge sharpner */ + unsigned char en_halo_red; + /* Merge method between Edge Enhancer and Edge sharpner */ + enum vpfe_ipipe_yee_merge_meth merge_meth; + /* HPF Shift length */ + unsigned char hpf_shft; + /* HPF Coefficient 00, S10 */ + short hpf_coef_00; + /* HPF Coefficient 01, S10 */ + short hpf_coef_01; + /* HPF Coefficient 02, S10 */ + short hpf_coef_02; + /* HPF Coefficient 10, S10 */ + short hpf_coef_10; + /* HPF Coefficient 11, S10 */ + short hpf_coef_11; + /* HPF Coefficient 12, S10 */ + short hpf_coef_12; + /* HPF Coefficient 20, S10 */ + short hpf_coef_20; + /* HPF Coefficient 21, S10 */ + short hpf_coef_21; + /* HPF Coefficient 22, S10 */ + short hpf_coef_22; + /* Lower threshold before referring to LUT */ + unsigned short yee_thr; + /* Edge sharpener Gain */ + unsigned short es_gain; + /* Edge sharpener lower threshold */ + unsigned short es_thr1; + /* Edge sharpener upper threshold */ + unsigned short es_thr2; + /* Edge sharpener gain on gradient */ + unsigned short es_gain_grad; + /* Edge sharpener offset on gradient */ + unsigned short es_ofst_grad; + /* Ptr to EE table. Must have 1024 entries */ + short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT]; +}; + +enum vpfe_ipipe_car_meth { + /* Chromatic Gain Control */ + VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0, + /* Dynamic switching between CHR_GAIN_CTRL + * and MED_FLTR + */ + VPFE_IPIPE_CAR_DYN_SWITCH = 1, + /* Median Filter */ + VPFE_IPIPE_CAR_MED_FLTR = 2, +}; + +enum vpfe_ipipe_car_hpf_type { + VPFE_IPIPE_CAR_HPF_Y = 0, + VPFE_IPIPE_CAR_HPF_H = 1, + VPFE_IPIPE_CAR_HPF_V = 2, + VPFE_IPIPE_CAR_HPF_2D = 3, + /* 2D HPF from YUV Edge Enhancement */ + VPFE_IPIPE_CAR_HPF_2D_YEE = 4, +}; + +struct vpfe_ipipe_car_gain { + /* csup_gain */ + unsigned char gain; + /* csup_shf. */ + unsigned char shft; + /* gain minimum */ + unsigned short gain_min; +}; + +/* Structure for Chromatic Artifact Reduction */ +struct vpfe_ipipe_car { + /* enable/disable */ + unsigned char en; + /* Gain control or Dynamic switching */ + enum vpfe_ipipe_car_meth meth; + /* Gain1 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain1; + /* Gain2 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain2; + /* HPF type used for CAR */ + enum vpfe_ipipe_car_hpf_type hpf; + /* csup_thr: HPF threshold for Gain control */ + unsigned char hpf_thr; + /* Down shift value for hpf. 2 bits */ + unsigned char hpf_shft; + /* switch limit for median filter */ + unsigned char sw0; + /* switch coefficient for Gain control */ + unsigned char sw1; +}; + +/* structure for Chromatic Gain Suppression */ +struct vpfe_ipipe_cgs { + /* enable/disable */ + unsigned char en; + /* gain1 bright side threshold */ + unsigned char h_thr; + /* gain1 bright side slope */ + unsigned char h_slope; + /* gain1 down shift value for bright side */ + unsigned char h_shft; + /* gain1 bright side minimum gain */ + unsigned char h_min; +}; + +/* Max pixels allowed in the input. If above this either decimation + * or frame division mode to be enabled + */ +#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600 + +struct vpfe_ipipe_input_config { + unsigned int vst; + unsigned int hst; +}; + +/** + * struct vpfe_ipipe_config - IPIPE engine configuration (user) + * @input_config: Pointer to structure for ipipe configuration. + * @flag: Specifies which ISP IPIPE functions should be enabled. + * @lutdpc: Pointer to luma enhancement structure. + * @otfdpc: Pointer to structure for defect correction. + * @nf1: Pointer to structure for Noise Filter. + * @nf2: Pointer to structure for Noise Filter. + * @gic: Pointer to structure for Green Imbalance. + * @wbal: Pointer to structure for White Balance. + * @cfa: Pointer to structure containing the CFA interpolation. + * @rgb2rgb1: Pointer to structure for RGB to RGB Blending. + * @rgb2rgb2: Pointer to structure for RGB to RGB Blending. + * @gamma: Pointer to gamma structure. + * @lut: Pointer to structure for 3D LUT. + * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion. + * @gbce: Pointer to structure for Global Brightness,Contrast Control. + * @yuv422_conv: Pointer to structure for YUV 422 conversion. + * @yee: Pointer to structure for Edge Enhancer. + * @car: Pointer to structure for Chromatic Artifact Reduction. + * @cgs: Pointer to structure for Chromatic Gain Suppression. + */ +struct vpfe_ipipe_config { + __u32 flag; + struct vpfe_ipipe_input_config __user *input_config; + struct vpfe_ipipe_lutdpc __user *lutdpc; + struct vpfe_ipipe_otfdpc __user *otfdpc; + struct vpfe_ipipe_nf __user *nf1; + struct vpfe_ipipe_nf __user *nf2; + struct vpfe_ipipe_gic __user *gic; + struct vpfe_ipipe_wb __user *wbal; + struct vpfe_ipipe_cfa __user *cfa; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2; + struct vpfe_ipipe_gamma __user *gamma; + struct vpfe_ipipe_3d_lut __user *lut; + struct vpfe_ipipe_rgb2yuv __user *rgb2yuv; + struct vpfe_ipipe_gbce __user *gbce; + struct vpfe_ipipe_yuv422_conv __user *yuv422_conv; + struct vpfe_ipipe_yee __user *yee; + struct vpfe_ipipe_car __user *car; + struct vpfe_ipipe_cgs __user *cgs; +}; + +/******************************************************************* +** Resizer API structures +*******************************************************************/ +/* Interpolation types used for horizontal rescale */ +enum vpfe_rsz_intp_t { + VPFE_RSZ_INTP_CUBIC, + VPFE_RSZ_INTP_LINEAR +}; + +/* Horizontal LPF intensity selection */ +enum vpfe_rsz_h_lpf_lse_t { + VPFE_RSZ_H_LPF_LSE_INTERN, + VPFE_RSZ_H_LPF_LSE_USER_VAL +}; + +enum vpfe_rsz_down_scale_ave_sz { + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + VPFE_IPIPE_DWN_SCALE_1_OVER_4, + VPFE_IPIPE_DWN_SCALE_1_OVER_8, + VPFE_IPIPE_DWN_SCALE_1_OVER_16, + VPFE_IPIPE_DWN_SCALE_1_OVER_32, + VPFE_IPIPE_DWN_SCALE_1_OVER_64, + VPFE_IPIPE_DWN_SCALE_1_OVER_128, + VPFE_IPIPE_DWN_SCALE_1_OVER_256 +}; + +struct vpfe_rsz_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* line start offset for y. */ + unsigned int vst_y; + /* line start offset for c. Only for 420 */ + unsigned int vst_c; + /* vertical rescale interpolation type, YCbCr or Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* vertical rescale interpolation type for Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* horizontal rescale interpolation types, Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + /* Use down scale mode for scale down */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +struct vpfe_rsz_config_params { + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* output spec of the image data coming out of resizer - 0(UYVY). + */ + struct vpfe_rsz_output_spec output1; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + struct vpfe_rsz_output_spec output2; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char chroma_sample_even; + unsigned char frame_div_mode_en; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum vpfe_chr_pos out_chr_pos; + unsigned char bypass; +}; + +/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */ +struct vpfe_rsz_config { + struct vpfe_rsz_config_params *config; +}; + +#endif /* _DAVINCI_VPFE_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c new file mode 100644 index 000000000000..92853539cc0a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -0,0 +1,1863 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * IPIPE allows fine tuning of the input image using different + * tuning modules in IPIPE. Some examples :- Noise filter, Defect + * pixel correction etc. It essentially operate on Bayer Raw data + * or YUV raw data. To do image tuning, application call, + * + */ + +#include <linux/slab.h> + +#include "dm365_ipipe.h" +#include "dm365_ipipe_hw.h" +#include "vpfe_mc_capture.h" + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* ipipe input format's */ +static const unsigned int ipipe_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +/* ipipe output format's */ +static const unsigned int ipipe_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc) +{ + int i; + + if (lutdpc->en > 1 || lutdpc->repl_white > 1 || + lutdpc->dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (lutdpc->en && !lutdpc->table) + return -EINVAL; + + for (i = 0; i < lutdpc->dpc_size; i++) + if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK || + lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + struct vpfe_ipipe_lutdpc *dpc_param; + struct device *dev; + + if (!param) { + memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + dpc_param = (struct vpfe_ipipe_lutdpc *)param; + lutdpc->en = dpc_param->en; + lutdpc->repl_white = dpc_param->repl_white; + lutdpc->dpc_size = dpc_param->dpc_size; + memcpy(&lutdpc->table, &dpc_param->table, + (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + if (ipipe_validate_lutdpc_params(lutdpc) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc); + + return 0; +} + +static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param; + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + + lut_param->en = lutdpc->en; + lut_param->repl_white = lutdpc->repl_white; + lut_param->dpc_size = lutdpc->dpc_size; + memcpy(&lut_param->table, &lutdpc->table, + (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + + return 0; +} + +static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + memset(config, 0, sizeof(struct vpfe_ipipe_input_config)); + else + memcpy(config, param, sizeof(struct vpfe_ipipe_input_config)); + return 0; +} + +static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + return -EINVAL; + + memcpy(param, config, sizeof(struct vpfe_ipipe_input_config)); + + return 0; +} + +static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + + if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + struct device *dev; + + if (!param) { + memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0)); + goto success; + } + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc)); + if (ipipe_validate_otfdpc_params(otfdpc) < 0) { + dev_err(dev, "Invalid otfdpc params\n"); + return -EINVAL; + } + +success: + ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc); + + return 0; +} + +static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + + memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc)); + return 0; +} + +static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param) +{ + int i; + + if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + struct device *dev; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + if (!nf_param) { + memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf)); + if (ipipe_validate_nf_params(nf) < 0) { + dev_err(dev, "Invalid nf params\n"); + return -EINVAL; + } + +success: + ipipe_set_d2f_regs(ipipe->base_addr, id, nf); + + return 0; +} + +static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf)); + + return 0; +} + +static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic) +{ + if (gic->en > 1 || gic->gain > GIC_GAIN_MASK || + gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK || + gic->apply_lsc_gain > 1 || + gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + if (!gic_param) { + memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic)); + goto success; + } + + memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic)); + if (ipipe_validate_gic_params(gic) < 0) { + dev_err(dev, "Invalid gic params\n"); + return -EINVAL; + } + +success: + ipipe_set_gic_regs(ipipe->base_addr, gic); + + return 0; +} + +static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic)); + + return 0; +} + +static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal) +{ + if (wbal->ofst_r > WB_OFFSET_MASK || + wbal->ofst_gr > WB_OFFSET_MASK || + wbal->ofst_gb > WB_OFFSET_MASK || + wbal->ofst_b > WB_OFFSET_MASK || + wbal->gain_r.integer > WB_GAIN_INT_MASK || + wbal->gain_r.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gr.integer > WB_GAIN_INT_MASK || + wbal->gain_gr.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gb.integer > WB_GAIN_INT_MASK || + wbal->gain_gb.decimal > WB_GAIN_DECI_MASK || + wbal->gain_b.integer > WB_GAIN_INT_MASK || + wbal->gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + if (!wb_param) { + const struct vpfe_ipipe_wb wb_defaults = { + .gain_r = {2, 0x0}, + .gain_gr = {2, 0x0}, + .gain_gb = {2, 0x0}, + .gain_b = {2, 0x0} + }; + memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb)); + goto success; + } + + memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb)); + if (ipipe_validate_wb_params(wbal) < 0) + return -EINVAL; + +success: + ipipe_set_wb_regs(ipipe->base_addr, wbal); + + return 0; +} + +static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb)); + return 0; +} + +static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa) +{ + if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK || + cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK || + cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK || + cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK || + cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + if (!cfa_param) { + memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa)); + cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC; + goto success; + } + + memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa)); + if (ipipe_validate_cfa_params(cfa) < 0) + return -EINVAL; + +success: + ipipe_set_cfa_regs(ipipe->base_addr, cfa); + + return 0; +} + +static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa)); + return 0; +} + +static int +ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb, + unsigned int id) +{ + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id == IPIPE_RGB2RGB_2) { + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + + return 0; +} + +static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + if (!rgb2rgb_param) { + const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ + }; + /* Copy defaults for rgb2rgb conversion */ + memcpy(rgb2rgb, &rgb2rgb_defaults, + sizeof(struct vpfe_ipipe_rgb2rgb)); + goto success; + } + + memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb)); + if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) { + dev_err(dev, "Invalid rgb2rgb params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb); + + return 0; +} + +static int +ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb)); + + return 0; +} + +static int +ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int +ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev) +{ + int table_size; + int err; + + if (gamma->bypass_r > 1 || + gamma->bypass_b > 1 || + gamma->bypass_g > 1) + return -EINVAL; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + if (!gamma->bypass_r) { + err = ipipe_validate_gamma_entry(gamma->table_r, table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_b) { + err = ipipe_validate_gamma_entry(gamma->table_b, table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_g) { + err = ipipe_validate_gamma_entry(gamma->table_g, table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + + return 0; +} + +static int +ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + if (!gamma_param) { + memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma)); + gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM; + goto success; + } + + gamma->bypass_r = gamma_param->bypass_r; + gamma->bypass_b = gamma_param->bypass_b; + gamma->bypass_g = gamma_param->bypass_g; + gamma->tbl_sel = gamma_param->tbl_sel; + gamma->tbl_size = gamma_param->tbl_size; + + if (ipipe_validate_gamma_params(gamma, dev) < 0) + return -EINVAL; + + if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = gamma->tbl_size; + if (!gamma_param->bypass_r) + memcpy(&gamma->table_r, &gamma_param->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_b) + memcpy(&gamma->table_b, &gamma_param->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_g) + memcpy(&gamma->table_g, &gamma_param->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + +success: + ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma); + + return 0; +} + +static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + gamma_param->bypass_r = gamma->bypass_r; + gamma_param->bypass_g = gamma->bypass_g; + gamma_param->bypass_b = gamma->bypass_b; + gamma_param->tbl_sel = gamma->tbl_sel; + gamma_param->tbl_size = gamma->tbl_size; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && !gamma_param->table_r) { + dev_err(dev, + "ipipe_get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + memcpy(gamma_param->table_r, gamma->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_g && !gamma_param->table_g) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + memcpy(gamma_param->table_g, gamma->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_b && !gamma_param->table_b) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + memcpy(gamma_param->table_b, gamma->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + return 0; +} + +static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut) +{ + int i; + + if (!lut->en) + return 0; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) + if (lut->table[i].r > D3_LUT_ENTRY_MASK || + lut->table[i].g > D3_LUT_ENTRY_MASK || + lut->table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + lut_param->en = lut->en; + if (!lut_param->table) { + dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(lut_param->table, &lut->table, + (VPFE_IPIPE_MAX_SIZE_3D_LUT * + sizeof(struct vpfe_ipipe_3d_lut_entry))); + + return 0; +} + +static int +ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!lut_param) { + memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut)); + goto success; + } + + memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut)); + if (ipipe_validate_3d_lut_params(lut) < 0) { + dev_err(dev, "Invalid 3D-LUT Params\n"); + return -EINVAL; + } + +success: + ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut); + + return 0; +} + +static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv) +{ + if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + if (!rgb2yuv_param) { + /* Defaults for rgb2yuv conversion */ + const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80, + }; + /* Copy defaults for rgb2yuv conversion */ + memcpy(rgb2yuv, &rgb2yuv_defaults, + sizeof(struct vpfe_ipipe_rgb2yuv)); + goto success; + } + + memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv)); + if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) { + dev_err(dev, "Invalid rgb2yuv params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv); + + return 0; +} + +static int +ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv)); + return 0; +} + +static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!gbce->en) + return 0; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++) + if (gbce->table[i] > max) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!gbce_param) { + memset(gbce, 0 , sizeof(struct vpfe_ipipe_gbce)); + } else { + memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce)); + if (ipipe_validate_gbce_params(gbce) < 0) { + dev_err(dev, "Invalid gbce params\n"); + return -EINVAL; + } + } + + ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce); + + return 0; +} + +static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + gbce_param->en = gbce->en; + gbce_param->type = gbce->type; + if (!gbce_param->table) { + dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(gbce_param->table, gbce->table, + (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short))); + + return 0; +} + +static int +ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv) +{ + if (yuv422_conv->en_chrom_lpf > 1) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + if (!yuv422_conv_param) { + memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv)); + yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE; + } else { + memcpy(yuv422_conv, yuv422_conv_param, + sizeof(struct vpfe_ipipe_yuv422_conv)); + if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) { + dev_err(dev, "Invalid yuv422 params\n"); + return -EINVAL; + } + } + + ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv); + + return 0; +} + +static int +ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + memcpy(yuv422_conv_param, yuv422_conv, + sizeof(struct vpfe_ipipe_yuv422_conv)); + + return 0; +} + +static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee) +{ + int i; + + if (yee->en > 1 || + yee->en_halo_red > 1 || + yee->hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (yee->hpf_coef_00 > YEE_COEF_MASK || + yee->hpf_coef_01 > YEE_COEF_MASK || + yee->hpf_coef_02 > YEE_COEF_MASK || + yee->hpf_coef_10 > YEE_COEF_MASK || + yee->hpf_coef_11 > YEE_COEF_MASK || + yee->hpf_coef_12 > YEE_COEF_MASK || + yee->hpf_coef_20 > YEE_COEF_MASK || + yee->hpf_coef_21 > YEE_COEF_MASK || + yee->hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (yee->yee_thr > YEE_THR_MASK || + yee->es_gain > YEE_ES_GAIN_MASK || + yee->es_thr1 > YEE_ES_THR1_MASK || + yee->es_thr2 > YEE_THR_MASK || + yee->es_gain_grad > YEE_THR_MASK || + yee->es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++) + if (yee->table[i] > YEE_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + if (!yee_param) { + memset(yee, 0, sizeof(struct vpfe_ipipe_yee)); + } else { + memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee)); + if (ipipe_validate_yee_params(yee) < 0) { + dev_err(dev, "Invalid yee params\n"); + return -EINVAL; + } + } + + ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee); + + return 0; +} + +static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + yee_param->en = yee->en; + yee_param->en_halo_red = yee->en_halo_red; + yee_param->merge_meth = yee->merge_meth; + yee_param->hpf_shft = yee->hpf_shft; + yee_param->hpf_coef_00 = yee->hpf_coef_00; + yee_param->hpf_coef_01 = yee->hpf_coef_01; + yee_param->hpf_coef_02 = yee->hpf_coef_02; + yee_param->hpf_coef_10 = yee->hpf_coef_10; + yee_param->hpf_coef_11 = yee->hpf_coef_11; + yee_param->hpf_coef_12 = yee->hpf_coef_12; + yee_param->hpf_coef_20 = yee->hpf_coef_20; + yee_param->hpf_coef_21 = yee->hpf_coef_21; + yee_param->hpf_coef_22 = yee->hpf_coef_22; + yee_param->yee_thr = yee->yee_thr; + yee_param->es_gain = yee->es_gain; + yee_param->es_thr1 = yee->es_thr1; + yee_param->es_thr2 = yee->es_thr2; + yee_param->es_gain_grad = yee->es_gain_grad; + yee_param->es_ofst_grad = yee->es_ofst_grad; + memcpy(yee_param->table, &yee->table, + (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short))); + + return 0; +} + +static int ipipe_validate_car_params(struct vpfe_ipipe_car *car) +{ + if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK || + car->gain1.shft > CAR_GAIN1_SHFT_MASK || + car->gain1.gain_min > CAR_GAIN_MIN_MASK || + car->gain2.shft > CAR_GAIN2_SHFT_MASK || + car->gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + if (!car_param) { + memset(car , 0, sizeof(struct vpfe_ipipe_car)); + } else { + memcpy(car, car_param, sizeof(struct vpfe_ipipe_car)); + if (ipipe_validate_car_params(car) < 0) { + dev_err(dev, "Invalid car params\n"); + return -EINVAL; + } + } + + ipipe_set_car_regs(ipipe->base_addr, car); + + return 0; +} + +static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + memcpy(car_param, car, sizeof(struct vpfe_ipipe_car)); + return 0; +} + +static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs) +{ + if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + if (!cgs_param) { + memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs)); + } else { + memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs)); + if (ipipe_validate_cgs_params(cgs) < 0) { + dev_err(dev, "Invalid cgs params\n"); + return -EINVAL; + } + } + + ipipe_set_cgs_regs(ipipe->base_addr, cgs); + + return 0; +} + +static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs)); + + return 0; +} + +static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = { + /* VPFE_IPIPE_INPUT_CONFIG */ { + offsetof(struct ipipe_module_params, input_config), + FIELD_SIZEOF(struct ipipe_module_params, input_config), + offsetof(struct vpfe_ipipe_config, input_config), + ipipe_set_input_config, + ipipe_get_input_config, + }, /* VPFE_IPIPE_LUTDPC */ { + offsetof(struct ipipe_module_params, lutdpc), + FIELD_SIZEOF(struct ipipe_module_params, lutdpc), + offsetof(struct vpfe_ipipe_config, lutdpc), + ipipe_set_lutdpc_params, + ipipe_get_lutdpc_params, + }, /* VPFE_IPIPE_OTFDPC */ { + offsetof(struct ipipe_module_params, otfdpc), + FIELD_SIZEOF(struct ipipe_module_params, otfdpc), + offsetof(struct vpfe_ipipe_config, otfdpc), + ipipe_set_otfdpc_params, + ipipe_get_otfdpc_params, + }, /* VPFE_IPIPE_NF1 */ { + offsetof(struct ipipe_module_params, nf1), + FIELD_SIZEOF(struct ipipe_module_params, nf1), + offsetof(struct vpfe_ipipe_config, nf1), + ipipe_set_nf1_params, + ipipe_get_nf1_params, + }, /* VPFE_IPIPE_NF2 */ { + offsetof(struct ipipe_module_params, nf2), + FIELD_SIZEOF(struct ipipe_module_params, nf2), + offsetof(struct vpfe_ipipe_config, nf2), + ipipe_set_nf2_params, + ipipe_get_nf2_params, + }, /* VPFE_IPIPE_WB */ { + offsetof(struct ipipe_module_params, wbal), + FIELD_SIZEOF(struct ipipe_module_params, wbal), + offsetof(struct vpfe_ipipe_config, wbal), + ipipe_set_wb_params, + ipipe_get_wb_params, + }, /* VPFE_IPIPE_RGB2RGB_1 */ { + offsetof(struct ipipe_module_params, rgb2rgb1), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1), + offsetof(struct vpfe_ipipe_config, rgb2rgb1), + ipipe_set_rgb2rgb_1_params, + ipipe_get_rgb2rgb_1_params, + }, /* VPFE_IPIPE_RGB2RGB_2 */ { + offsetof(struct ipipe_module_params, rgb2rgb2), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2), + offsetof(struct vpfe_ipipe_config, rgb2rgb2), + ipipe_set_rgb2rgb_2_params, + ipipe_get_rgb2rgb_2_params, + }, /* VPFE_IPIPE_GAMMA */ { + offsetof(struct ipipe_module_params, gamma), + FIELD_SIZEOF(struct ipipe_module_params, gamma), + offsetof(struct vpfe_ipipe_config, gamma), + ipipe_set_gamma_params, + ipipe_get_gamma_params, + }, /* VPFE_IPIPE_3D_LUT */ { + offsetof(struct ipipe_module_params, lut), + FIELD_SIZEOF(struct ipipe_module_params, lut), + offsetof(struct vpfe_ipipe_config, lut), + ipipe_set_3d_lut_params, + ipipe_get_3d_lut_params, + }, /* VPFE_IPIPE_RGB2YUV */ { + offsetof(struct ipipe_module_params, rgb2yuv), + FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv), + offsetof(struct vpfe_ipipe_config, rgb2yuv), + ipipe_set_rgb2yuv_params, + ipipe_get_rgb2yuv_params, + }, /* VPFE_IPIPE_YUV422_CONV */ { + offsetof(struct ipipe_module_params, yuv422_conv), + FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv), + offsetof(struct vpfe_ipipe_config, yuv422_conv), + ipipe_set_yuv422_conv_params, + ipipe_get_yuv422_conv_params, + }, /* VPFE_IPIPE_YEE */ { + offsetof(struct ipipe_module_params, yee), + FIELD_SIZEOF(struct ipipe_module_params, yee), + offsetof(struct vpfe_ipipe_config, yee), + ipipe_set_yee_params, + ipipe_get_yee_params, + }, /* VPFE_IPIPE_GIC */ { + offsetof(struct ipipe_module_params, gic), + FIELD_SIZEOF(struct ipipe_module_params, gic), + offsetof(struct vpfe_ipipe_config, gic), + ipipe_set_gic_params, + ipipe_get_gic_params, + }, /* VPFE_IPIPE_CFA */ { + offsetof(struct ipipe_module_params, cfa), + FIELD_SIZEOF(struct ipipe_module_params, cfa), + offsetof(struct vpfe_ipipe_config, cfa), + ipipe_set_cfa_params, + ipipe_get_cfa_params, + }, /* VPFE_IPIPE_CAR */ { + offsetof(struct ipipe_module_params, car), + FIELD_SIZEOF(struct ipipe_module_params, car), + offsetof(struct vpfe_ipipe_config, car), + ipipe_set_car_params, + ipipe_get_car_params, + }, /* VPFE_IPIPE_CGS */ { + offsetof(struct ipipe_module_params, cgs), + FIELD_SIZEOF(struct ipipe_module_params, cgs), + offsetof(struct vpfe_ipipe_config, cgs), + ipipe_set_cgs_params, + ipipe_get_cgs_params, + }, /* VPFE_IPIPE_GBCE */ { + offsetof(struct ipipe_module_params, gbce), + FIELD_SIZEOF(struct ipipe_module_params, gbce), + offsetof(struct vpfe_ipipe_config, gbce), + ipipe_set_gbce_params, + ipipe_get_gbce_params, + }, +}; + +static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *from = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *to; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + to = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + if (copy_from_user(to, from, size)) { + rval = -EFAULT; + break; + } + rval = module_if->set(ipipe, to); + if (rval) + goto error; + } else if (to && !from && size) { + rval = module_if->set(ipipe, NULL); + if (rval) + goto error; + } + kfree(params); + } + } +error: + return rval; +} + +static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *to = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *from; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + from = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + rval = module_if->get(ipipe, from); + if (rval) + goto error; + if (copy_to_user(to, from, size)) { + rval = -EFAULT; + break; + } + } + kfree(params); + } + } +error: + return rval; +} + +/* + * ipipe_ioctl() - Handle ipipe module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch (cmd) { + case VIDIOC_VPFE_IPIPE_S_CONFIG: + ret = ipipe_s_config(sd, arg); + break; + + case VIDIOC_VPFE_IPIPE_G_CONFIG: + ret = ipipe_g_config(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe; + unsigned char val; + + if (ipipe->input == IPIPE_INPUT_NONE) + return; + + /* ipipe is set to single shot */ + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) { + /* for single-shot mode, need to wait for h/w to + * reset many register bits + */ + do { + val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr, + IPIPE_SRC_EN); + } while (val); + } + regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN); +} + +/* + * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + + if (enable && ipipe->input != IPIPE_INPUT_NONE && + ipipe->output != IPIPE_OUTPUT_NONE) { + if (config_ipipe_hw(ipipe) < 0) + return -EINVAL; + } + + vpfe_ipipe_enable(vpfe_dev, enable); + + return 0; +} + +/* + * __ipipe_get_format() - helper function for getting ipipe format + * @ipipe: pointer to ipipe private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipe->formats[pad]; +} + +/* + * ipipe_try_format() - Handle try format by pad subdev method + * @ipipe: VPFE ipipe device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipe_try_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPE_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++) + if (fmt->code == ipipe_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipe_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPE_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++) + if (fmt->code == ipipe_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipe_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +/* + * ipipe_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPE_PAD_SINK && + (ipipe->input == IPIPE_INPUT_CCDC || + ipipe->input == IPIPE_INPUT_MEMORY)) + ipipe->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPE_PAD_SOURCE && + ipipe->output == IPIPE_OUTPUT_RESIZER) + ipipe->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +/* + * ipipe_get_format() - Handle get format by pads subdev method. + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure. + */ +static int +ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipe->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +/* + * ipipe_enum_frame_size() - enum frame sizes on pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fse: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int +ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipe_enum_mbus_code() - enum mbus codes for pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPE_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipe_input_fmts)) + return -EINVAL; + code->code = ipipe_input_fmts[code->index]; + break; + + case IPIPE_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipe_output_fmts)) + return -EINVAL; + code->code = ipipe_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipe_device *ipipe = + container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls); + struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + lum_adj->brightness = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + case V4L2_CID_CONTRAST: + lum_adj->contrast = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_init_formats() - Initialize formats on all pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = { + .ioctl = ipipe_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipe_ctrl_ops = { + .s_ctrl = ipipe_s_ctrl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { + .open = ipipe_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { + .s_stream = ipipe_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { + .enum_mbus_code = ipipe_enum_mbus_code, + .enum_frame_size = ipipe_enum_frame_size, + .get_fmt = ipipe_get_format, + .set_fmt = ipipe_set_format, +}; + +/* v4l2 subdev operation */ +static const struct v4l2_subdev_ops ipipe_v4l2_ops = { + .core = &ipipe_v4l2_core_ops, + .video = &ipipe_v4l2_video_ops, + .pad = &ipipe_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ipipe_link_setup() - Setup ipipe connections + * @entity: ipipe media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipe->input = IPIPE_INPUT_NONE; + break; + } + if (ipipe->input != IPIPE_INPUT_NONE) + return -EBUSY; + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + ipipe->input = IPIPE_INPUT_MEMORY; + else + ipipe->input = IPIPE_INPUT_CCDC; + break; + + case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* out to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) + ipipe->output = IPIPE_OUTPUT_RESIZER; + else + ipipe->output = IPIPE_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipe_media_ops = { + .link_setup = ipipe_link_setup, +}; + +/* + * vpfe_ipipe_unregister_entities() - ipipe unregister entity + * @vpfe_ipipe: pointer to ipipe subdevice structure. + */ +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe) +{ + /* cleanup entity */ + media_entity_cleanup(&vpfe_ipipe->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_ipipe->subdev); +} + +/* + * vpfe_ipipe_register_entities() - ipipe register entity + * @ipipe: pointer to ipipe subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int +vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipe->subdev); + if (ret) { + pr_err("Failed to register ipipe as v4l2 subdevice\n"); + return ret; + } + + return ret; +} + +#define IPIPE_CONTRAST_HIGH 0xff +#define IPIPE_BRIGHT_HIGH 0xff + +/* + * vpfe_ipipe_init() - ipipe module initialization. + * @ipipe: pointer to ipipe subdevice structure. + * @pdev: platform device pointer. + */ +int +vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) +{ + struct media_pad *pads = &ipipe->pads[0]; + struct v4l2_subdev *sd = &ipipe->subdev; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + ipipe->base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->base_addr) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 6); + if (!res) + return -ENOENT; + ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->isp5_base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &ipipe_v4l2_ops); + sd->internal_ops = &ipipe_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ipipe); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ipipe->input = IPIPE_INPUT_NONE; + ipipe->output = IPIPE_OUTPUT_NONE; + + me->ops = &ipipe_media_ops; + v4l2_ctrl_handler_init(&ipipe->ctrls, 2); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, + IPIPE_BRIGHT_HIGH, 1, 16); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_CONTRAST, 0, + IPIPE_CONTRAST_HIGH, 1, 16); + + + v4l2_ctrl_handler_setup(&ipipe->ctrls); + sd->ctrl_handler = &ipipe->ctrls; + + return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); +} + +/* + * vpfe_ipipe_cleanup() - ipipe subdevice cleanup. + * @ipipe: pointer to ipipe subdevice + * @dev: pointer to platform device + */ +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipe->ctrls); + + iounmap(ipipe->base_addr); + iounmap(ipipe->isp5_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (res) + release_mem_region(res->start, res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h new file mode 100644 index 000000000000..cf4204603eb8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_H +#define _DAVINCI_VPFE_DM365_IPIPE_H + +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "davinci_vpfe_user.h" +#include "vpfe_video.h" + +#define CEIL(a, b) (((a) + (b-1)) / (b)) + +enum ipipe_noise_filter { + IPIPE_D2F_1ST = 0, + IPIPE_D2F_2ND = 1, +}; + +/* Used for driver storage */ +struct ipipe_otfdpc_2_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0; +}; + +struct ipipe_otfdpc_3_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0; +}; + +/* Structure for configuring Luminance Adjustment module */ +struct ipipe_lum_adj { + /* Brightness adjustments */ + unsigned char brightness; + /* contrast adjustments */ + unsigned char contrast; +}; + +enum ipipe_rgb2rgb { + IPIPE_RGB2RGB_1 = 0, + IPIPE_RGB2RGB_2 = 1, +}; + +struct ipipe_module_params { + __u32 flag; + struct vpfe_ipipe_input_config input_config; + struct vpfe_ipipe_lutdpc lutdpc; + struct vpfe_ipipe_otfdpc otfdpc; + struct vpfe_ipipe_nf nf1; + struct vpfe_ipipe_nf nf2; + struct vpfe_ipipe_gic gic; + struct vpfe_ipipe_wb wbal; + struct vpfe_ipipe_cfa cfa; + struct vpfe_ipipe_rgb2rgb rgb2rgb1; + struct vpfe_ipipe_rgb2rgb rgb2rgb2; + struct vpfe_ipipe_gamma gamma; + struct vpfe_ipipe_3d_lut lut; + struct vpfe_ipipe_rgb2yuv rgb2yuv; + struct vpfe_ipipe_gbce gbce; + struct vpfe_ipipe_yuv422_conv yuv422_conv; + struct vpfe_ipipe_yee yee; + struct vpfe_ipipe_car car; + struct vpfe_ipipe_cgs cgs; + struct ipipe_lum_adj lum_adj; +}; + +#define IPIPE_PAD_SINK 0 +#define IPIPE_PAD_SOURCE 1 + +#define IPIPE_PADS_NUM 2 + +#define IPIPE_OUTPUT_NONE 0 +#define IPIPE_OUTPUT_RESIZER (1 << 0) + +enum ipipe_input_entity { + IPIPE_INPUT_NONE = 0, + IPIPE_INPUT_MEMORY = 1, + IPIPE_INPUT_CCDC = 2, +}; + + +struct vpfe_ipipe_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPE_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM]; + enum ipipe_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + void *__iomem base_addr; + void *__iomem isp5_base_addr; + struct ipipe_module_params config; +}; + +struct ipipe_module_if { + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + int (*set)(struct vpfe_ipipe_device *ipipe, void *param); + int (*get)(struct vpfe_ipipe_device *ipipe, void *param); +}; + +/* data paths */ +enum ipipe_data_paths { + IPIPE_RAW2YUV, + /* Bayer RAW input to YCbCr output */ + IPIPE_RAW2RAW, + /* Bayer Raw to Bayer output */ + IPIPE_RAW2BOX, + /* Bayer Raw to Boxcar output */ + IPIPE_YUV2YUV + /* YUV Raw to YUV Raw output */ +}; + +#define IPIPE_COLPTN_R_Ye 0x0 +#define IPIPE_COLPTN_Gr_Cy 0x1 +#define IPIPE_COLPTN_Gb_G 0x2 +#define IPIPE_COLPTN_B_Mg 0x3 + +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +#define ipipe_sgrbg_pattern \ + (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT) + +#define ipipe_srggb_pattern \ + (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT) + +int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *v4l2_dev); +int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe); +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c new file mode 100644 index 000000000000..e027b92b54ef --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_ipipe_hw.h" + +#define IPIPE_MODE_CONTINUOUS 0 +#define IPIPE_MODE_SINGLE_SHOT 1 + +static void ipipe_clock_enable(void *__iomem base_addr) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); +} + +static void +rsz_set_common_params(void *__iomem rsz_base, struct resizer_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + regw_rsz(rsz_base, val, RSZ_SRC_FMT0); + + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + regw_rsz(rsz_base, val, RSZ_SRC_FMT1); + + regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS); +} + +static void +rsz_set_rsz_regs(void *__iomem rsz_base, unsigned int rsz_id, + struct resizer_params *params) +{ + struct resizer_scale_param *rsc_params; + struct rsz_ext_mem_param *ext_mem; + struct resizer_rgb *rgb; + u32 reg_base; + u32 val; + + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(rsz_base, val, RSZ_SEQ); + + regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE); + + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(rsz_base, val, reg_base + RSZ_420); + + regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK, + reg_base + RSZ_I_VPS); + regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK, + reg_base + RSZ_I_HPS); + regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK, + reg_base + RSZ_O_VSZ); + regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK, + reg_base + RSZ_O_HSZ); + regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_Y); + regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_C); + + /* keep this additional adjustment to zero for now */ + regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK, + reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | + ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF); + + regw_rsz(rsz_base, rsc_params->h_phs & + RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + + regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsz_base, rsc_params->h_dif & + RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + + val = (rsc_params->h_typ_y & 1) | + ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP); + + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF); + + regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + + val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) | + ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN); + + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP); + + regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y, + reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y, + reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c, + reg_base + RSZ_SDR_C_PTR_S); + regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1), + reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void +ipipe_setup_resizer(void *__iomem rsz_base, struct resizer_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(rsz_base, 1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(rsz_base, 0, RSZ_GCK_SDR); + else + regw_rsz(rsz_base, 1, RSZ_GCK_SDR); + + rsz_set_common_params(rsz_base, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A); + + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(rsz_base, RSZ_A, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B); + + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(rsz_base, RSZ_B, params); +} + +static u32 ipipe_get_color_pat(enum v4l2_mbus_pixelcode pix) +{ + switch (pix) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ipipe_sgrbg_pattern; + + default: + return ipipe_srggb_pattern; + } +} + +static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (ipipe->formats[IPIPE_PAD_SINK].code) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + temp_pix_fmt = V4L2_MBUS_FMT_SGRBG12_1X12; + break; + + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (ipipe->formats[IPIPE_PAD_SOURCE].code == + V4L2_MBUS_FMT_SGRBG12_1X12) + return IPIPE_RAW2RAW; + return IPIPE_RAW2YUV; + } + + return IPIPE_YUV2YUV; +} + +static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + return IPIPE_MODE_SINGLE_SHOT; + else if (ipipeif_sink == IPIPEIF_INPUT_ISIF) + return IPIPE_MODE_CONTINUOUS; + + return -EINVAL; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + void __iomem *ipipe_base = ipipe->base_addr; + struct v4l2_mbus_framefmt *outformat; + u32 color_pat; + u32 ipipe_mode; + u32 data_path; + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + if (ipipe->input == IPIPE_INPUT_NONE) { + regw_ip(ipipe_base, 0, IPIPE_SRC_EN); + return 0; + } + + ipipe_mode = get_ipipe_mode(ipipe); + if (ipipe < 0) { + pr_err("Failed to get ipipe mode"); + return -EINVAL; + } + regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE); + + data_path = ipipe_get_data_path(ipipe); + regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT); + + regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + + outformat = &ipipe->formats[IPIPE_PAD_SOURCE]; + regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK, + IPIPE_SRC_VSZ); + regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK, + IPIPE_SRC_HSZ); + + if (data_path == IPIPE_RAW2YUV || + data_path == IPIPE_RAW2RAW) { + color_pat = + ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code); + regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL); + } + + return 0; +} + +/* + * config_rsz_hw() - Performs hardware setup of resizer. + */ +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + void *__iomem ipipe_base = vpfe_dev->vpfe_ipipe.base_addr; + void *__iomem rsz_base = vpfe_dev->vpfe_resizer.base_addr; + + /* enable VPSS clock */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + ipipe_setup_resizer(rsz_base, config); + + return 0; +} + +static void +rsz_set_y_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H); +} + +static void +rsz_set_c_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H); +} + +/* + * resizer_set_outaddr() - set the address for given resize_no + * @rsz_base: resizer base address + * @params: pointer to ipipe_params structure + * @resize_no: 0 - Resizer-A, 1 - Resizer B + * @address: the address to set + */ +int +resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address) +{ + struct resizer_scale_param *rsc_param; + struct rsz_ext_mem_param *mem_param; + struct rsz_common_params *rsz_common; + unsigned int rsz_start_add; + unsigned int val; + + if (resize_no != RSZ_A && resize_no != RSZ_B) + return -EINVAL; + + mem_param = ¶ms->ext_mem_param[resize_no]; + rsc_param = ¶ms->rsz_rsc_param[resize_no]; + rsz_common = ¶ms->rsz_common; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(rsz_base, val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset + + mem_param->flip_ofst_c + + mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(rsz_base, val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add); + + return 0; +} + +void +ipipe_set_lutdpc_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, dpc->en, DPC_LUT_EN); + + if (dpc->en != 1) + return; + + val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1); + regw_ip(base_addr, val, DPC_LUT_SEL); + regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) | + ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method << + LUT_DPC_CORR_METH_SHIFT); + w_ip_table(isp5_base_addr, val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void +set_dpc_thresholds(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr) +{ + regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_R); + regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GR); + regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GB); + regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_B); + regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_R); + regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GR); + regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GB); + regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN); + if (!otfdpc->en) + return; + + /* dpc enabled */ + val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg; + regw_ip(base_addr, val, DPC_OTF_TYP); + + if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + regw_ip(base_addr, dpc_3_0->act_adj_shf & + OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip(base_addr, dpc_3_0->det_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP); + regw_ip(base_addr, dpc_3_0->det_thr_min & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN); + regw_ip(base_addr, dpc_3_0->det_thr_max & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip(base_addr, dpc_3_0->corr_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP); + regw_ip(base_addr, dpc_3_0->corr_thr_min & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN); + regw_ip(base_addr, dpc_3_0->corr_thr_max & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void +ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + if (id == IPIPE_D2F_2ND) + offset = D2F_2ND; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN); + if (!noise_filter->en) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val & + D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + regw_ip(base_addr, val, offset + D2F_TYP); + + /* edge detection minimum */ + regw_ip(base_addr, noise_filter->edge_det_min_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN); + + /* edge detection maximum */ + regw_ip(base_addr, noise_filter->edge_det_max_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX); + + for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++) + regw_ip(base_addr, + (noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++) + regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = (gic->wt_fn_type << GIC_TYP_SHIFT) | + (gic->thr_sel << GIC_THR_SEL_SHIFT) | + ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT); + regw_ip(base_addr, val, GIC_TYP); + + regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(base_addr, GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) { + regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(base_addr, val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb) +{ + u32 val; + + ipipe_clock_enable(base_addr); + /* Ofsets. S12 */ + regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(base_addr, val, WB2_WGN_R); + + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(base_addr, val, WB2_WGN_GR); + + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(base_addr, val, WB2_WGN_GB); + + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(base_addr, val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa) +{ + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, cfa->alg, CFA_MODE); + regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, + CFA_2DIR_HPF_THR); + regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, + CFA_2DIR_HPF_SLP); + regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, + CFA_2DIR_DIR_THR); + regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, + CFA_2DIR_DIR_SLP); + regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, + CFA_2DIR_NDWT); + regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, + CFA_MONO_HUE_FRA); + regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, + CFA_MONO_EDG_THR); + regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, + CFA_MONO_THR_MIN); + regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, + CFA_MONO_THR_SLP); + regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, + CFA_MONO_SLP_MIN); + regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, + CFA_MONO_SLP_SLP); + regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, + CFA_MONO_LPWT); +} + +void +ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(base_addr); + + if (id == IPIPE_RGB2RGB_2) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void +ipipe_update_gamma_tbl(void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma_entry *table, int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(isp5_base_addr, val, (addr + (count * 4))); + } +} + +void +ipipe_set_gamma_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(base_addr); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(base_addr, val, GMM_CFG); + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r, + table_size, GAMMA_R_START_ADDR); + if (!gamma->bypass_b && gamma->table_b != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b, + table_size, GAMMA_B_START_ADDR); + if (!gamma->bypass_g && gamma->table_g != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g, + table_size, GAMMA_G_START_ADDR); +} + +void +ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_3d_lut *lut_3d) +{ + struct vpfe_ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void +ipipe_set_lum_adj_regs(void *__iomem base_addr, struct ipipe_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(base_addr, val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) + +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(base_addr); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(base_addr, val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(base_addr, val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(base_addr, val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(base_addr, val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(base_addr, val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(base_addr, val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(base_addr, val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(base_addr, val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(base_addr, val, YUV_MUL_BCR); + regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void +ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(base_addr, val, YUV_PHS); +} + +void +ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gbce *gbce) +{ + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(base_addr, gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2) + w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) << + GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask), + ((count/2) << 2) + GBCE_TB_START_ADDR); +} + +void +ipipe_set_ee_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_yee *ee) +{ + unsigned int count; + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(base_addr, val, YEE_TYP); + + regw_ip(base_addr, ee->hpf_shft, YEE_SHF); + regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2) + w_ip_table(isp5_base_addr, ((ee->table[count + 1] & + YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) | + (ee->table[count] & YEE_ENTRY_MASK), + ((count/2) << 2) + YEE_TB_START_ADDR); +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void *__iomem base_addr) +{ + /* typ to dynamic switch */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(base_addr, CAR_MF_THR, CAR_SW); +} + +static void +ipipe_set_gain_ctrl(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(base_addr, car->hpf, CAR_HPF_TYP); + regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR); + regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN); + regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN1_MIN); + regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN); + regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN2_MIN); +} + +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case VPFE_IPIPE_CAR_MED_FLTR: + ipipe_set_mf(base_addr); + break; + + case VPFE_IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(base_addr, car); + break; + + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(base_addr); + ipipe_set_gain_ctrl(base_addr, car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(base_addr, val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs) +{ + ipipe_clock_enable(base_addr); + regw_ip(base_addr, cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR); + regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(void *__iomem rsz_base, int enable) +{ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); +} + +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(rsz_base, enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(rsz_base, enable, RSZ_EN_B); + } else { + BUG(); + } + + return 0; +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h new file mode 100644 index 000000000000..010fdb247faf --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H +#define _DAVINCI_VPFE_DM365_IPIPE_HW_H + +#include "vpfe_mc_capture.h" + +#define SET_LOW_ADDR 0x0000ffff +#define SET_HIGH_ADDR 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void regw_ip(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); +} + +static inline u32 w_ip_table(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +static inline u32 regr_rsz(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline u32 regw_rsz(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe); +int resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address); +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable); +void rsz_src_enable(void *__iomem rsz_base, int enable); +void rsz_set_in_pix_format(unsigned char y_c); +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config); +void ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter); +void ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb); +void ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv); +void ipipe_set_lum_adj_regs(void *__iomem base_addr, + struct ipipe_lum_adj *lum_adj); +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv); +void ipipe_set_lutdpc_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc); +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc); +void ipipe_set_3d_lut_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d); +void ipipe_set_gamma_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gamma *gamma); +void ipipe_set_ee_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_yee *ee); +void ipipe_set_gbce_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gbce *gbce); +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic); +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa); +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car); +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs); +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c new file mode 100644 index 000000000000..c8cae51d3d00 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_ipipeif.h" +#include "vpfe_mc_capture.h" + +static const unsigned int ipipeif_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, +}; + +static const unsigned int ipipeif_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +static int +ipipeif_get_pack_mode(enum v4l2_mbus_pixelcode in_pix_fmt) +{ + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_PACK_8_BIT; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + return IPIPEIF_5_1_PACK_8_BIT_A_LAW; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_PACK_16_BIT; + + case V4L2_MBUS_FMT_SBGGR12_1X12: + return IPIPEIF_5_1_PACK_12_BIT; + + default: + return IPIPEIF_5_1_PACK_16_BIT; + } +} + +static inline u32 ipipeif_read(void *addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void ipipeif_write(u32 val, void *addr, u32 offset) +{ + writel(val, addr + offset); +} + +static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + ipipeif_write(val, addr, IPIPEIF_DPC2); +} + +#define IPIPEIF_MODE_CONTINUOUS 0 +#define IPIPEIF_MODE_ONE_SHOT 1 + +static int get_oneshot_mode(enum ipipeif_input_entity input) +{ + if (input == IPIPEIF_INPUT_MEMORY) + return IPIPEIF_MODE_ONE_SHOT; + else if (input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_MODE_CONTINUOUS; + + return -EINVAL; +} + +static int +ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && + (informat->code == V4L2_MBUS_FMT_Y8_1X8 || + informat->code == V4L2_MBUS_FMT_UV8_1X8)) + return IPIPEIF_CCDC; + + return IPIPEIF_SRC1_PARALLEL_PORT; +} + +static int +ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + + switch (informat->code) { + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_BITS11_0; + + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_BITS11_0; + + default: + return IPIPEIF_5_1_BITS7_0; + } +} + +static enum ipipeif_input_source +ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_CCDC; + + if (informat->code == V4L2_MBUS_FMT_UYVY8_2X8) + return IPIPEIF_SDRAM_YUV; + + return IPIPEIF_SDRAM_RAW; +} + +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif) +{ + struct vpfe_video_device *video_in = &ipipeif->video_in; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + spin_lock(&video_in->dma_queue_lock); + vpfe_video_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); +} + +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.decimation; +} + +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.rsz; +} + +#define RD_DATA_15_2 0x7 + +/* + * ipipeif_hw_setup() - This function sets up IPIPEIF + * @sd: pointer to v4l2 subdev structure + * return -EINVAL or zero on success + */ +static int ipipeif_hw_setup(struct v4l2_subdev *sd) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *informat, *outformat; + struct ipipeif_params params = ipipeif->config; + enum ipipeif_input_source ipipeif_source; + enum v4l2_mbus_pixelcode isif_port_if; + void *ipipeif_base_addr; + unsigned int val; + int data_shift; + int pack_mode; + int source1; + + ipipeif_base_addr = ipipeif->ipipeif_base_addr; + + /* Enable clock to IPIPEIF and IPIPE */ + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE]; + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = get_oneshot_mode(ipipeif->input); + if (val < 0) { + pr_err("ipipeif: links setup required"); + return -EINVAL; + } + val = val << ONESHOT_SHIFT; + + ipipeif_source = ipipeif_get_source(ipipeif); + val |= ipipeif_source << INPSRC_SHIFT; + + val |= params.clock_select << CLKSEL_SHIFT; + val |= params.avg_filter << AVGFILT_SHIFT; + val |= params.decimation << DECIM_SHIFT; + + pack_mode = ipipeif_get_pack_mode(informat->code); + val |= pack_mode << PACK8IN_SHIFT; + + source1 = ipipeif_get_cfg_src1(ipipeif); + val |= source1 << INPSRC1_SHIFT; + + data_shift = ipipeif_get_data_shift(ipipeif); + if (ipipeif_source != IPIPEIF_SDRAM_YUV) + val |= data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1); + + switch (ipipeif_source) { + case IPIPEIF_CCDC: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + break; + + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= data_shift << DATASFT_SHIFT; + ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN); + ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR); + ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM); + ipipeif_write(informat->height, + ipipeif_base_addr, IPIPEIF_VNUM); + break; + + default: + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params.decimation) + ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ); + + /* Setup sync alignment and initial rsz position */ + val = params.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ); + isif_port_if = informat->code; + + if (isif_port_if == V4L2_MBUS_FMT_Y8_1X8) + isif_port_if = V4L2_MBUS_FMT_YUYV8_1X16; + else if (isif_port_if == V4L2_MBUS_FMT_UV8_1X8) + isif_port_if = V4L2_MBUS_FMT_SGRBG12_1X12; + + /* Enable DPCM decompression */ + switch (ipipeif_source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (outformat->code == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) { + val = 1; + val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (ipipeif->dpcm_predictor & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + ipipeif_write(params.if_5_1.clip, + ipipeif_base_addr, IPIPEIF_OCLIP); + + /* fall through for SDRAM YUV mode */ + /* configure CFG2 */ + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + } + + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params.clock_select == IPIPEIF_SDRAM_CLK) { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV); + val |= (params.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params.if_5_1.clk_div.n - 1); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV); + } + break; + + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params.if_5_1.df_gain_en) { + val = params.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH); + val = (params.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL); + /* configure CFG2 */ + val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT; + val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT; + break; + + default: + /* Bayer */ + ipipeif_write(params.if_5_1.clip, ipipeif_base_addr, + IPIPEIF_OCLIP); + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int +ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!config) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + ipipeif->config.clock_select = config->clock_select; + ipipeif->config.ppln = config->ppln; + ipipeif->config.lpfr = config->lpfr; + ipipeif->config.rsz = config->rsz; + ipipeif->config.decimation = config->decimation; + if (ipipeif->config.decimation && + (ipipeif->config.rsz < IPIPEIF_RSZ_MIN || + ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + ipipeif->config.avg_filter = config->avg_filter; + + ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr; + ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain; + ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en; + + ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start; + ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync; + ipipeif->config.if_5_1.clip = config->if_5_1.clip; + + ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en; + ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr; + + ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m; + ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n; + + return 0; +} + +static int +ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct ipipeif_params *config = (struct ipipeif_params *)arg; + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!arg) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + config->clock_select = ipipeif->config.clock_select; + config->ppln = ipipeif->config.ppln; + config->lpfr = ipipeif->config.lpfr; + config->rsz = ipipeif->config.rsz; + config->decimation = ipipeif->config.decimation; + config->avg_filter = ipipeif->config.avg_filter; + + config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr; + config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain; + config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en; + + config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start; + config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync; + config->if_5_1.clip = ipipeif->config.if_5_1.clip; + + config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en; + config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr; + + config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m; + config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n; + + return 0; +} + +/* + * ipipeif_ioctl() - Handle ipipeif module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipeif_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct ipipeif_params *config = (struct ipipeif_params *)arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_VPFE_IPIPEIF_S_CONFIG: + ret = ipipeif_set_config(sd, config); + break; + + case VIDIOC_VPFE_IPIPEIF_G_CONFIG: + ret = ipipeif_get_config(sd, arg); + break; + } + return ret; +} + +/* + * ipipeif_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipeif_device *ipipeif = + container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls); + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + ipipeif->dpcm_predictor = ctrl->val; + break; + + case V4L2_CID_GAIN: + ipipeif->gain = ctrl->val; + break; + + default: + return -EINVAL; + } + + return 0; +} + +#define ENABLE_IPIPEIF 0x1 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned char val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + do { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE); + } while (val & 0x1); + + ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE); +} + +/* + * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + int ret = 0; + + if (!enable) + return ret; + + ret = ipipeif_hw_setup(sd); + if (!ret) + vpfe_ipipeif_enable(vpfe_dev); + + return ret; +} + +/* + * ipipeif_enum_mbus_code() - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPEIF_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipeif_input_fmts)) + return -EINVAL; + + code->code = ipipeif_input_fmts[code->index]; + break; + + case IPIPEIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipeif_output_fmts)) + return -EINVAL; + + code->code = ipipeif_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipeif_get_format() - Handle get format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static int +ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipeif->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* + * ipipeif_try_format() - Handle try format by pad subdev method + * @ipipeif: VPFE ipipeif device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPEIF_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++) + if (fmt->code == ipipeif_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipeif_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPEIF_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++) + if (fmt->code == ipipeif_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipeif_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +static int +ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * __ipipeif_get_format() - helper function for getting ipipeif format + * @ipipeif: pointer to ipipeif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipeif->formats[pad]; +} + +/* + * ipipeif_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPEIF_PAD_SINK && + ipipeif->input != IPIPEIF_INPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPEIF_PAD_SOURCE && + ipipeif->output != IPIPEIF_OUTPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 + + const struct ipipeif_params ipipeif_defaults = { + .clock_select = IPIPEIF_SDRAM_CLK, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .if_5_1 = { + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .clip = 4095, + }, + }; + memset(&ipipeif->config, 0, sizeof(struct ipipeif_params)); + memcpy(&ipipeif->config, &ipipeif_defaults, + sizeof(struct ipipeif_params)); +} + +/* + * ipipeif_init_formats() - Initialize formats on all pads + * @sd: VPFE ipipeif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + ipipeif_set_default_config(ipipeif); + + return 0; +} + +/* + * ipipeif_video_in_queue() - ipipeif video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static int +ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned int adofs; + u32 val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return -EINVAL; + + switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) { + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width; + break; + + default: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1; + break; + } + + /* adjust the line len to be a multiple of 32 */ + adofs += 31; + adofs &= ~0x1f; + val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = { + .ioctl = ipipeif_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = { + .s_ctrl = ipipeif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = { + .ops = &ipipeif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = { + .open = ipipeif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = { + .s_stream = ipipeif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = { + .enum_mbus_code = ipipeif_enum_mbus_code, + .enum_frame_size = ipipeif_enum_frame_size, + .get_fmt = ipipeif_get_format, + .set_fmt = ipipeif_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops ipipeif_v4l2_ops = { + .core = &ipipeif_v4l2_core_ops, + .video = &ipipeif_v4l2_video_ops, + .pad = &ipipeif_v4l2_pad_ops, +}; + +static const struct vpfe_video_operations video_in_ops = { + .queue = ipipeif_video_in_queue, +}; + +static int +ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Single shot mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + ipipeif->input = IPIPEIF_INPUT_MEMORY; + break; + + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from isif */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + if (ipipeif->input != IPIPEIF_INPUT_NONE) + return -EBUSY; + + ipipeif->input = IPIPEIF_INPUT_ISIF; + break; + + case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->output = IPIPEIF_OUTPUT_NONE; + break; + } + if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity) + /* connencted to ipipe */ + ipipeif->output = IPIPEIF_OUTPUT_IPIPE; + else if (remote->entity == &vpfe->vpfe_resizer. + crop_resizer.subdev.entity) + /* connected to resizer */ + ipipeif->output = IPIPEIF_OUTPUT_RESIZER; + else + return -EINVAL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipeif_media_ops = { + .link_setup = ipipeif_link_setup, +}; + +/* + * vpfe_ipipeif_unregister_entities() - Unregister entity + * @ipipeif - pointer to ipipeif subdevice structure. + */ +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif) +{ + /* unregister video device */ + vpfe_video_unregister(&ipipeif->video_in); + + /* cleanup entity */ + media_entity_cleanup(&ipipeif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ipipeif->subdev); +} + +int +vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev); + if (ret < 0) + return ret; + + ret = vpfe_video_register(&ipipeif->video_in, vdev); + if (ret) { + pr_err("Failed to register ipipeif video-in device\n"); + goto fail; + } + ipipeif->video_in.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + &ipipeif->subdev.entity, 0, flags); + if (ret < 0) + goto fail; + + return 0; +fail: + v4l2_device_unregister_subdev(&ipipeif->subdev); + + return ret; +} + +#define IPIPEIF_GAIN_HIGH 0x3ff +#define IPIPEIF_DEFAULT_GAIN 0x200 + +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ipipeif->subdev; + struct media_pad *pads = &ipipeif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif->ipipeif_base_addr) { + ret = -EBUSY; + goto fail; + } + + v4l2_subdev_init(sd, &ipipeif_v4l2_ops); + + sd->internal_ops = &ipipeif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + + v4l2_set_subdevdata(sd, ipipeif); + + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ipipeif->input = IPIPEIF_INPUT_NONE; + ipipeif->output = IPIPEIF_OUTPUT_NONE; + me->ops = &ipipeif_media_ops; + + ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + if (ret) + goto fail; + + v4l2_ctrl_handler_init(&ipipeif->ctrls, 2); + v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops, + V4L2_CID_GAIN, 0, + IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN); + v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL); + v4l2_ctrl_handler_setup(&ipipeif->ctrls); + sd->ctrl_handler = &ipipeif->ctrls; + + ipipeif->video_in.ops = &video_in_ops; + ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF"); + if (ret) { + pr_err("Failed to init IPIPEIF video-in device\n"); + goto fail; + } + ipipeif_set_default_config(ipipeif); + return 0; +fail: + release_mem_region(res->start, res_len); + return ret; +} + +void +vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipeif->ctrls); + iounmap(ipipeif->ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h new file mode 100644 index 000000000000..608701fc5fed --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_H + +#include <linux/platform_device.h> + +#include <media/davinci/vpss.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "dm365_ipipeif_user.h" +#include "vpfe_video.h" + +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2 = 0, + IPIPEIF_BITS14_1 = 1, + IPIPEIF_BITS13_0 = 2, + IPIPEIF_BITS12_0 = 3, + IPIPEIF_BITS11_0 = 4, + IPIPEIF_BITS10_0 = 5, + IPIPEIF_BITS9_0 = 6, +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF = 0, + IPIPEIF_DIVIDE_THIRD = 1, + IPIPEIF_DIVIDE_FOURTH = 2, + IPIPEIF_DIVIDE_FIFTH = 3, + IPIPEIF_DIVIDE_SIXTH = 4, + IPIPEIF_DIVIDE_EIGHTH = 5, + IPIPEIF_DIVIDE_SIXTEENTH = 6, + IPIPEIF_DIVIDE_THIRTY = 7, +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT = 0, + IPIPEIF_PACK_8_BIT = 1, +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT = 0, + IPIPEIF_5_1_PACK_8_BIT = 1, + IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2, + IPIPEIF_5_1_PACK_12_BIT = 3 +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC = 0, + IPIPEIF_SDRAM_RAW = 1, + IPIPEIF_CCDC_DARKFM = 2, + IPIPEIF_SDRAM_YUV = 3, +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF = 0, + IPIPEIF_ALAW_ON = 1, +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT = 0, + IPIPEIF_SRC1_SDRAM_RAW = 1, + IPIPEIF_SRC1_ISIF_DARKFM = 2, + IPIPEIF_SRC1_SDRAM_YUV = 3, +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM = 0, + IPIPEIF_SDRAM_MINUS_PORT = 1, +}; + +enum ipipeif_chroma_phase { + IPIPEIF_CBCR_Y = 0, + IPIPEIF_Y_CBCR = 1, +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT = 0, + IPIPEIF_DPCM_8BIT_12BIT = 1, +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0 = 0, + IPIPEIF_5_1_BITS10_0 = 1, + IPIPEIF_5_1_BITS9_0 = 2, + IPIPEIF_5_1_BITS8_0 = 3, + IPIPEIF_5_1_BITS7_0 = 4, + IPIPEIF_5_1_BITS15_4 = 5, +}; + +#define IPIPEIF_PAD_SINK 0 +#define IPIPEIF_PAD_SOURCE 1 + +#define IPIPEIF_NUM_PADS 2 + +enum ipipeif_input_entity { + IPIPEIF_INPUT_NONE = 0, + IPIPEIF_INPUT_ISIF = 1, + IPIPEIF_INPUT_MEMORY = 2, +}; + +enum ipipeif_output_entity { + IPIPEIF_OUTPUT_NONE = 0, + IPIPEIF_OUTPUT_IPIPE = 1, + IPIPEIF_OUTPUT_RESIZER = 2, +}; + +struct vpfe_ipipeif_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPEIF_NUM_PADS]; + struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS]; + enum ipipeif_input_entity input; + unsigned int output; + struct vpfe_video_device video_in; + struct v4l2_ctrl_handler ctrls; + void *__iomem ipipeif_base_addr; + struct ipipeif_params config; + int dpcm_predictor; + int gain; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif); +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev); +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev); +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif); + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h new file mode 100644 index 000000000000..e2a69b59554a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +struct ipipeif_5_1 { + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +struct ipipeif_params { + enum ipipeif_clock clock_select; + unsigned int ppln; + unsigned int lpfr; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + /* IPIPE 5.1 */ + struct ipipeif_5_1 if_5_1; +}; + +/* + * Private IOCTL + * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration + * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration + */ +#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params) +#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params) + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c new file mode 100644 index 000000000000..ebeea72e176a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include "dm365_isif.h" +#include "vpfe_mc_capture.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int isif_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +}; + +#define ISIF_COLPTN_R_Ye 0x0 +#define ISIF_COLPTN_Gr_Cy 0x1 +#define ISIF_COLPTN_Gb_G 0x2 +#define ISIF_COLPTN_B_Mg 0x3 + +#define ISIF_CCOLP_CP01_0 0 +#define ISIF_CCOLP_CP03_2 2 +#define ISIF_CCOLP_CP05_4 4 +#define ISIF_CCOLP_CP07_6 6 +#define ISIF_CCOLP_CP11_0 8 +#define ISIF_CCOLP_CP13_2 10 +#define ISIF_CCOLP_CP15_4 12 +#define ISIF_CCOLP_CP17_6 14 + +static const u32 isif_sgrbg_pattern = + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6; + +static const u32 isif_srggb_pattern = + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6; + +static inline u32 isif_read(void *__iomem base_addr, u32 offset) +{ + return readl(base_addr + offset); +} + +static inline void isif_write(void *__iomem base_addr, u32 val, u32 offset) +{ + writel(val, base_addr + offset); +} + +static inline u32 isif_merge(void *__iomem base_addr, u32 mask, u32 val, + u32 offset) +{ + u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask); + + isif_write(base_addr, new_val, offset); + + return new_val; +} + +static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en) +{ + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK, + en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static inline void +isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i) +{ + if (!i) + writel(val, isif->isif_cfg.linear_tbl0_addr + offset); + else + writel(val, isif->isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(struct vpfe_isif_device *isif) +{ + /* disable BC */ + isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG); + /* disable vdfc */ + isif_write(isif->isif_cfg.base_addr, 0, DFCCTL); + /* disable CSC */ + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + /* disable linearization */ + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); +} + +static void isif_enable(struct vpfe_isif_device *isif, int en) +{ + if (!en) + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(isif); + + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK, + en, SYNCEN); +} + +/* + * ISIF helper functions + */ + +#define DM365_ISIF_MDFS_OFFSET 15 +#define DM365_ISIF_MDFS_MASK 0x1 + +/* get field id in isif hardware */ +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + u32 field_status; + + field_status = isif_read(isif->isif_cfg.base_addr, MODESET); + field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) & + DM365_ISIF_MDFS_MASK; + return field_status; +} + +static int +isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + isif->isif_cfg.data_pack = ISIF_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + else + return -EINVAL; + + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + else + return -EINVAL; + + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static int +isif_set_frame_format(struct vpfe_isif_device *isif, + enum isif_frmfmt frm_fmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif->isif_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static int isif_set_image_window(struct vpfe_isif_device *isif) +{ + struct v4l2_rect *win = &isif->crop; + + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + isif->isif_cfg.bayer.win.top = win->top; + isif->isif_cfg.bayer.win.left = win->left; + isif->isif_cfg.bayer.win.width = win->width; + isif->isif_cfg.bayer.win.height = win->height; + return 0; + } + isif->isif_cfg.ycbcr.win.top = win->top; + isif->isif_cfg.ycbcr.win.left = win->left; + isif->isif_cfg.ycbcr.win.width = win->width; + isif->isif_cfg.ycbcr.win.height = win->height; + + return 0; +} + +static int +isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.buf_type = buf_type; + else + isif->isif_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +/* configure format in isif hardware */ +static int +isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad) +{ + struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif; + enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format); + + if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Failed to set pixel format in isif\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_isif->crop.left = 0; + vpfe_isif->crop.top = 0; + vpfe_isif->crop.width = format.width; + vpfe_isif->crop.height = format.height; + + /* configure the image window */ + isif_set_image_window(vpfe_isif); + + switch (vpfe_dev->vpfe_isif.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = isif_set_frame_format(vpfe_isif, frm_fmt); + + return ret; +} + +/* + * isif_try_format() - Try video format on a pad + * @isif: VPFE isif device + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static void +isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) { + if (fmt->format.code == isif_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(isif_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == ISIF_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct vpfe_video_device *video = &isif->video_out; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the ISIF memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_video_schedule_bottom_field(video); + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_video_device *video = &isif->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + + isif_write(isif->isif_cfg.base_addr, (addr >> 21) & + ISIF_CADU_BITS, CADU); + isif_write(isif->isif_cfg.base_addr, (addr >> 5) & + ISIF_CADL_BITS, CADL); + + return 0; +} + +static const struct vpfe_video_operations isif_video_ops = { + .queue = isif_video_queue, +}; + +/* + * V4L2 subdev operations + */ + +/* Parameter operations */ +static int isif_get_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return -EINVAL; + memcpy(params, &isif->isif_cfg.bayer.config_params, + sizeof(isif->isif_cfg.bayer.config_params)); + return 0; +} + +static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc) +{ + struct vpfe_isif_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) + if (csc->coeff[i].integer > + ISIF_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + ISIF_CSC_COEF_DECIMAL_MASK) { + pr_err("Invalid CSC coefficients\n"); + return err; + } + } + } + if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) { + pr_err("Invalid df_csc start pix value\n"); + return err; + } + + if (df_csc->num_pixels > ISIF_DF_NUMPIX) { + pr_err("Invalid df_csc num pixels value\n"); + return err; + } + + if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) { + pr_err("Invalid df_csc start_line value\n"); + return err; + } + + if (df_csc->num_lines > ISIF_DF_NUMLINES) { + pr_err("Invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +#define DM365_ISIF_MAX_VDFLSFT 4 +#define DM365_ISIF_MAX_VDFSLV 4095 +#define DM365_ISIF_MAX_DFCMEM0 0x1fff +#define DM365_ISIF_MAX_DFCMEM1 0x1fff + +static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + pr_err("Invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) { + pr_err("Invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) { + pr_err("Invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || + dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) { + pr_err("Invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) { + pr_err("Invalid pos_vert value\n"); + return err; + } + if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) { + pr_err("Invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +#define DM365_ISIF_MAX_CLVRV 0xfff +#define DM365_ISIF_MAX_CLDC 0x1fff +#define DM365_ISIF_MAX_CLHSH 0x1fff +#define DM365_ISIF_MAX_CLHSV 0x1fff +#define DM365_ISIF_MAX_CLVSH 0x1fff +#define DM365_ISIF_MAX_CLVSV 0x1fff +#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff + +static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) { + pr_err("Invalid bclamp dc_offset value\n"); + return err; + } + if (!bclamp->en) + return 0; + if (bclamp->horz.clamp_pix_limit > 1) { + pr_err("Invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + pr_err("Invalid bclamp horz win_count_calc value\n"); + return err; + } + if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) { + pr_err("Invalid bclamp reset_clamp_val value\n"); + return err; + } + if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) { + pr_err("Invalid bclamp ob_v_sz_calc value\n"); + return err; + } + if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + return 0; +} + +static int +isif_validate_raw_params(struct vpfe_isif_raw_config *params) +{ + int ret; + + ret = isif_validate_df_csc_params(¶ms->df_csc); + if (ret) + return ret; + ret = isif_validate_dfc_params(¶ms->dfc); + if (ret) + return ret; + ret = isif_validate_bclamp_params(¶ms->bclamp); + return ret; +} + +static int isif_set_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_isif_raw_config isif_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return ret; + + memcpy(&isif_raw_params, params, sizeof(isif_raw_params)); + if (!isif_validate_raw_params(&isif_raw_params)) { + memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params, + sizeof(isif_raw_params)); + ret = 0; + } + return ret; +} +/* + * isif_ioctl() - isif module private ioctl's + * @sd: VPFE isif V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret; + + switch (cmd) { + case VIDIOC_VPFE_ISIF_S_RAW_PARAMS: + ret = isif_set_params(sd, arg); + break; + + case VIDIOC_VPFE_ISIF_G_RAW_PARAMS: + ret = isif_get_params(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void isif_config_gain_offset(struct vpfe_isif_device *isif) +{ + struct vpfe_isif_gain_offsets_adj *gain_off_ptr = + &isif->isif_cfg.bayer.config_params.gain_offset; + void *__iomem base = isif->isif_cfg.base_addr; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK, + COFSTA); + +} + +static void isif_config_bclamp(struct vpfe_isif_device *isif, + struct vpfe_isif_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) << + ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) << + ISIF_HORZ_BC_MODE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG); + + if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + ISIF_HORZ_BC_WIN_H_SIZE_MASK) << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + ISIF_HORZ_BC_WIN_V_SIZE_MASK) << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLHWIN0); + + val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN1); + + val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) << + ISIF_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLSV); +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void +isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win, + enum isif_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + isif_write(isif->isif_cfg.base_addr, + horz_start & START_PX_HOR_MASK, SPH); + isif_write(isif->isif_cfg.base_addr, + horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == ISIF_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1); + } + + if (!mode) + isif_write(isif->isif_cfg.base_addr, 0, VDINT0); + else + isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_ONE_MASK, SLV0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_TWO_MASK, SLV1); + isif_write(isif->isif_cfg.base_addr, + vert_nr_lines & NUM_LINES_VER, LNV); +} + +#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1 +#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2 + +static void +isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) << + ISIF_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) << + ISIF_VDFC_LEVEL_SHFT_SHIFT; + + isif_write(isif->isif_cfg.base_addr, val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK; + isif_write(isif->isif_cfg.base_addr, val, VDFSATLV); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (isif_read(isif->isif_cfg.base_addr, + DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + } + if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_MAX_DFCMEM1, DFCMEM1); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL); + } + /* enable VDFC */ + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (1 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (0 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && + (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2)) + count--; + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL); + } +} + +static void +isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + return; + } + /* initialize all bits to 0 */ + val1 = 0; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + isif_write(isif->isif_cfg.base_addr, val2, + (CSCM0 + ((i-1) << 1))); + } + } + /* program the active area */ + isif_write(isif->isif_cfg.base_addr, df_csc->start_pix & + ISIF_DF_CSC_SPH_MASK, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels & + ISIF_DF_CSC_SPH_MASK, FMTLNH); + isif_write(isif->isif_cfg.base_addr, df_csc->start_line & + ISIF_DF_CSC_SPH_MASK, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_lines & + ISIF_DF_CSC_SPH_MASK, FMTLNV); + /* Enable CSC */ + isif_write(isif->isif_cfg.base_addr, 1, CSCCTL); +} + +static void +isif_config_linearization(struct vpfe_isif_device *isif, + struct vpfe_isif_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); + return; + } + /* shift value for correction */ + val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) << + ISIF_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, LINCFG0); + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, LINCFG1); + + for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & ISIF_LIN_ENTRY_MASK; + if (i%2) + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1); + else + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0); + } +} + +static void +isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + isif_write(isif->isif_cfg.base_addr, val, CULH); + /* vertical pattern */ + isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV); + /* LPF */ + isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static int isif_get_pix_fmt(u32 mbus_code) +{ + switch (mbus_code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ISIF_PIXFMT_RAW; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + return ISIF_PIXFMT_YCBCR_8BIT; + + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return ISIF_PIXFMT_YCBCR_16BIT; + + default: + break; + } + return -EINVAL; +} + +#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d +#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d +#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000 +#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000 + +static int isif_config_raw(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_params_raw *params = &isif->isif_cfg.bayer; + struct vpfe_isif_raw_config *module_params = + &isif->isif_cfg.bayer.config_params; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 val; + + format = &isif->formats[ISIF_PAD_SINK]; + + /* In case of user has set BT656IF earlier, it should be reset + * when configuring for raw input. + */ + isif_write(isif->isif_cfg.base_addr, 0, REC656IF); + /* Configure CCDCFG register + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack & + ISIF_DATA_PACK_MASK); + isif_write(isif->isif_cfg.base_addr, val, CCDCFG); + + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) << + ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) << + ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL & + ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE & + ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt & + ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt & + ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT); + + /* currently only V4L2_MBUS_FMT_SGRBG12_1X12 is + * supported. shift appropriately depending on + * different MBUS fmt's added + */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val |= ((VPFE_ISIF_NO_SHIFT & + ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, MODESET); + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) << + ISIF_GAMMAWD_CFA_SHIFT; + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | ISIF_ALAW_ENABLE; + + val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) << + ISIF_ALAW_GAMA_WD_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD); + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << ISIF_DPCM_EN_SHIFT; + val |= (params->dpcm_predictor & + ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT; + } + isif_write(isif->isif_cfg.base_addr, val, MISC); + /* Configure Gain & Offset */ + isif_config_gain_offset(isif); + /* Configure Color pattern */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val = isif_sgrbg_pattern; + else + /* default set to rggb */ + val = isif_srggb_pattern; + + isif_write(isif->isif_cfg.base_addr, val, CCOLP); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) << + ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK; + else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & ISIF_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + ISIF_LINEOFST_MASK; + isif_write(isif->isif_cfg.base_addr, val, HSIZE); + /* Configure SDOFST register */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) { + if (params->image_invert_en) + /* For interlace inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_INVERSE_MODE, SDOFST); + else + /* For interlace non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST); + } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST); + else + /* For progessive non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST); + } + /* Configure video window */ + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + /* Configure Black Clamp */ + isif_config_bclamp(isif, &module_params->bclamp); + /* Configure Vertical Defection Pixel Correction */ + isif_config_dfc(isif, &module_params->dfc); + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(isif, &module_params->df_csc); + + isif_config_linearization(isif, &module_params->linearize); + /* Configure Culling */ + isif_config_culling(isif, &module_params->culling); + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAHOFST); + + val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAVOFST); + + return 0; +} + +#define DM365_ISIF_HSIZE_MASK 0xffffffe0 +#define DM365_ISIF_SDOFST_2_LINES 0x00000249 + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 modeset; + u32 ccdcfg; + + format = &isif->formats[ISIF_PAD_SINK]; + /* + * first reset the ISIF + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + /* start with all bits zero */ + ccdcfg = modeset = 0; + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* configure pixel format or input mode */ + modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) << + ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) << + ISIF_FRM_FMT_SHIFT) | (((params->fid_pol & + ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) | + (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) | + (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT)); + /* pack the data to 8-bit CCDCCFG */ + switch (format->code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT); + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR; + break; + + case V4L2_MBUS_FMT_YUYV10_2X10: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= ISIF_PACK_8BIT; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + case V4L2_MBUS_FMT_YUYV8_1X16: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + default: + /* should never come here */ + pr_debug("Invalid interface type\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, modeset, MODESET); + /* Set up pix order */ + ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) << + ISIF_PIX_ORDER_SHIFT; + isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG); + /* configure video window */ + if (format->code == V4L2_MBUS_FMT_YUYV10_1X20 || + format->code == V4L2_MBUS_FMT_YUYV8_1X16) + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + else + isif_setwin(isif, ¶ms->win, params->frm_fmt, 2, mode); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + isif_write(isif->isif_cfg.base_addr, + ((((params->win.width * 2) + 31) & + DM365_ISIF_HSIZE_MASK) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED && + params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_SDOFST_2_LINES, SDOFST); + return 0; +} + +static int isif_configure(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = &isif->formats[ISIF_PAD_SINK]; + + switch (format->code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return isif_config_raw(sd, mode); + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return isif_config_ycbcr(sd, mode); + + default: + break; + } + return -EINVAL; +} + +/* + * isif_set_stream() - Enable/Disable streaming on the ISIF module + * @sd: VPFE ISIF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int isif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = isif_configure(sd, + (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + if (isif->output == ISIF_OUTPUT_MEMORY) + isif_enable_output_to_sdram(isif, 1); + isif_enable(isif, 1); + } else { + isif_enable(isif, 0); + isif_enable_output_to_sdram(isif, 0); + } + + return 0; +} + +/* + * __isif_get_format() - helper function for getting isif format + * @isif: pointer to isif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + */ +static struct v4l2_mbus_framefmt * +__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } + return &isif->formats[pad]; +} + +/* +* isif_set_format() - set format on pad +* @sd : VPFE ISIF device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int +isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + isif_try_format(isif, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == ISIF_PAD_SOURCE) + return isif_config_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * isif_get_format() - Retrieve the video format on a pad + * @sd: VPFE ISIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int +isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * isif_enum_frame_size() - enum frame sizes on pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int +isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * isif_enum_mbus_code() - enum mbus codes for pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case ISIF_PAD_SINK: + case ISIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(isif_fmts)) + return -EINVAL; + code->code = isif_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * isif_pad_set_crop() - set crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_isif->formats[ISIF_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_isif->formats[ISIF_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + vpfe_isif->crop = crop->rect; + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + isif_set_image_window(vpfe_isif); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(rect, &vpfe_isif->crop, sizeof(*rect)); + } + return 0; +} + +/* + * isif_pad_get_crop() - get crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_isif->crop; + } + + return 0; +} + +/* + * isif_init_formats() - Initialize formats on all pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +isif_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = ISIF_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + isif_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = { + .ioctl = isif_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = { + .open = isif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = { + .s_stream = isif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { + .enum_mbus_code = isif_enum_mbus_code, + .enum_frame_size = isif_enum_frame_size, + .get_fmt = isif_get_format, + .set_fmt = isif_set_format, + .set_crop = isif_pad_set_crop, + .get_crop = isif_pad_get_crop, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops isif_v4l2_ops = { + .core = &isif_v4l2_core_ops, + .video = &isif_v4l2_video_ops, + .pad = &isif_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * isif_link_setup() - Setup isif connections + * @entity: isif media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +isif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + isif->input = ISIF_INPUT_NONE; + break; + } + if (isif->input != ISIF_INPUT_NONE) + return -EBUSY; + isif->input = ISIF_INPUT_PARALLEL; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_MEMORY; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_IPIPEIF; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations isif_media_ops = { + .link_setup = isif_link_setup, +}; + +/* + * vpfe_isif_unregister_entities() - isif unregister entity + * @isif - pointer to isif subdevice structure. + */ +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif) +{ + vpfe_video_unregister(&isif->video_out); + /* cleanup entity */ + media_entity_cleanup(&isif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&isif->subdev); +} + +static void isif_restore_defaults(struct vpfe_isif_device *isif) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memset(&isif->isif_cfg.bayer.config_params, 0, + sizeof(struct vpfe_isif_raw_config)); + + isif->isif_cfg.bayer.config_params.linearize.corr_shft = + VPFE_ISIF_NO_SHIFT; + isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1; + isif->isif_cfg.bayer.config_params.culling.hcpat_odd = + ISIF_CULLING_HCAPT_ODD; + isif->isif_cfg.bayer.config_params.culling.hcpat_even = + ISIF_CULLING_HCAPT_EVEN; + isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + isif_write(isif->isif_cfg.base_addr, 0, i); + /* no culling support */ + isif_write(isif->isif_cfg.base_addr, 0xffff, CULH); + isif_write(isif->isif_cfg.base_addr, 0xff, CULV); + + /* Set default offset and gain */ + isif_config_gain_offset(isif); + vpss_select_ccdc_source(source); +} + +/* + * vpfe_isif_register_entities() - isif register entity + * @isif - pointer to isif subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &isif->subdev); + if (ret < 0) + return ret; + + isif_restore_defaults(isif); + ret = vpfe_video_register(&isif->video_out, vdev); + if (ret) { + pr_err("Failed to register isif video out device\n"); + goto out_video_register; + } + isif->video_out.vpfe_dev = vpfe_dev; + flags = 0; + /* connect isif to video node */ + ret = media_entity_create_link(&isif->subdev.entity, 1, + &isif->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + return 0; +out_create_link: + vpfe_video_unregister(&isif->video_out); +out_video_register: + v4l2_device_unregister_subdev(&isif->subdev); + return ret; +} + +/* ------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_isif_device *isif = + container_of(ctrl->handler, struct vpfe_isif_device, ctrls); + struct isif_oper_config *config = &isif->isif_cfg; + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + config->bayer.dpcm_predictor = ctrl->val; + break; + + case VPFE_ISIF_CID_CRGAIN: + config->isif_gain_params.cr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGRGAIN: + config->isif_gain_params.cgr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGBGAIN: + config->isif_gain_params.cgb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CBGAIN: + config->isif_gain_params.cb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_GAIN_OFFSET: + config->isif_gain_params.offset = ctrl->val; + break; + + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = { + .s_ctrl = vpfe_isif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_crgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CRGAIN, + .name = "CRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgrgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGRGAIN, + .name = "CGRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGBGAIN, + .name = "CGBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CBGAIN, + .name = "CBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_gain_offset = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_GAIN_OFFSET, + .name = "Gain Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static void isif_remove(struct vpfe_isif_device *isif, + struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif->isif_cfg.base_addr); + iounmap(isif->isif_cfg.linear_tbl0_addr); + iounmap(isif->isif_cfg.linear_tbl1_addr); + + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} + +static void isif_config_defaults(struct vpfe_isif_device *isif) +{ + isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY; + isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT; + isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED; + isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED; + + isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8; + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC; + isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11; + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; +} +/* + * vpfe_isif_init() - Initialize V4L2 subdev and media entity + * @isif: VPFE isif module + * @pdev: Pointer to platform device structure. + * Return 0 on success and a negative error code on failure. + */ +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &isif->subdev; + struct media_pad *pads = &isif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i = 0; + + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif->isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + /* queue ops */ + isif->video_out.ops = &isif_video_ops; + v4l2_subdev_init(sd, &isif_v4l2_ops); + sd->internal_ops = &isif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, isif); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + isif->input = ISIF_INPUT_NONE; + isif->output = ISIF_OUTPUT_NONE; + me->ops = &isif_media_ops; + status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + if (status) + goto isif_fail; + isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + status = vpfe_video_init(&isif->video_out, "ISIF"); + if (status) { + pr_err("Failed to init isif-out video device\n"); + goto isif_fail; + } + v4l2_ctrl_handler_init(&isif->ctrls, 6); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL); + + v4l2_ctrl_handler_setup(&isif->ctrls); + sd->ctrl_handler = &isif->ctrls; + isif_config_defaults(isif); + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (isif->isif_cfg.base_addr) + iounmap(isif->isif_cfg.base_addr); + if (isif->isif_cfg.linear_tbl0_addr) + iounmap(isif->isif_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + return status; +isif_fail: + v4l2_ctrl_handler_free(&isif->ctrls); + isif_remove(isif, pdev); + return status; +} + +/* + * vpfe_isif_cleanup - isif module cleanup + * @isif: pointer to isif subdevice + * @dev: pointer to platform device structure + */ +void +vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + isif_remove(isif, pdev); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h new file mode 100644 index 000000000000..473fd2cfe350 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_H +#define _DAVINCI_VPFE_DM365_ISIF_H + +#include <linux/platform_device.h> + +#include <mach/mux.h> + +#include <media/davinci/vpfe_types.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#include "davinci_vpfe_user.h" +#include "dm365_isif_regs.h" +#include "vpfe_video.h" + +#define ISIF_CULLING_HCAPT_ODD 0xff +#define ISIF_CULLING_HCAPT_EVEN 0xff +#define ISIF_CULLING_VCAPT 0xff + +#define ISIF_CADU_BITS 0x07ff +#define ISIF_CADL_BITS 0x0ffff + +enum isif_pixfmt { + ISIF_PIXFMT_RAW = 0, + ISIF_PIXFMT_YCBCR_16BIT = 1, + ISIF_PIXFMT_YCBCR_8BIT = 2, +}; + +enum isif_frmfmt { + ISIF_FRMFMT_PROGRESSIVE = 0, + ISIF_FRMFMT_INTERLACED = 1, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum isif_pixorder { + ISIF_PIXORDER_YCBYCR = 0, + ISIF_PIXORDER_CBYCRY = 1, +}; + +enum isif_buftype { + ISIF_BUFTYPE_FLD_INTERLEAVED = 0, + ISIF_BUFTYPE_FLD_SEPARATED = 1, +}; + +struct isif_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* isif crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum isif_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum isif_buftype buf_type; +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC = 0, + ISIF_CFA_PAT_STRIPE = 1, +}; + +enum isif_data_msb { + /* MSB b15 */ + ISIF_BIT_MSB_15 = 0, + /* MSB b14 */ + ISIF_BIT_MSB_14 = 1, + /* MSB b13 */ + ISIF_BIT_MSB_13 = 2, + /* MSB b12 */ + ISIF_BIT_MSB_12 = 3, + /* MSB b11 */ + ISIF_BIT_MSB_11 = 4, + /* MSB b10 */ + ISIF_BIT_MSB_10 = 5, + /* MSB b9 */ + ISIF_BIT_MSB_9 = 6, + /* MSB b8 */ + ISIF_BIT_MSB_8 = 7, + /* MSB b7 */ + ISIF_BIT_MSB_7 = 8, +}; + +struct isif_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum isif_buftype buf_type; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + unsigned char dpcm_predictor; + struct vpfe_isif_raw_config config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT = 0, + ISIF_PACK_12BIT = 1, + ISIF_PACK_8BIT = 2, +}; + +struct isif_gain_values { + unsigned int cr_gain; + unsigned int cgr_gain; + unsigned int cgb_gain; + unsigned int cb_gain; + unsigned int offset; +}; + +struct isif_oper_config { + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + struct isif_gain_values isif_gain_params; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +#define ISIF_PAD_SINK 0 +#define ISIF_PAD_SOURCE 1 + +#define ISIF_PADS_NUM 2 + +enum isif_input_entity { + ISIF_INPUT_NONE = 0, + ISIF_INPUT_PARALLEL = 1, +}; + +#define ISIF_OUTPUT_NONE (0) +#define ISIF_OUTPUT_MEMORY (1 << 0) +#define ISIF_OUTPUT_IPIPEIF (1 << 1) + +struct vpfe_isif_device { + struct v4l2_subdev subdev; + struct media_pad pads[ISIF_PADS_NUM]; + struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM]; + enum isif_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + struct v4l2_rect crop; + struct isif_oper_config isif_cfg; + struct vpfe_video_device video_out; +}; + +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev); +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif); +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *dev); +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev); +void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif, + struct platform_device *pdev); +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif); +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif); + +#endif /* _DAVINCI_VPFE_DM365_ISIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h new file mode 100644 index 000000000000..8aceabb43f8e --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H +#define _DAVINCI_VPFE_DM365_ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 0x03 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_PIX_ORDER_MASK 0x01 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_MASK 0x03 +#define ISIF_INPUT_SHIFT 12 +#define ISIF_FID_POL_MASK 0x01 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_MASK 0x01 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_MASK 0x01 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0x00 +#define ISIF_DATAPOL_MASK 0x01 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0x00 +#define ISIF_EXWEN_MASK 0x01 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_MASK 0x01 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_MASK 0x07 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMA_WD_MASK 0xf +#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 0x01 +#define ISIF_GAMMAWD_CFA_MASK 0x01 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 0x01 +#define ISIF_HSIZE_FLIP_SHIFT 12 +#define ISIF_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 +#define ISIF_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define ISIF_BC_DCOFFSET_MASK 0x1fff +#define ISIF_BC_MODE_COLOR_MASK 1 +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_MASK 3 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff +#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff +#define ISIF_VERT_BC_OB_H_SZ_MASK 7 +#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff +#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define ISIF_VERT_BC_RST_VAL_MASK 0xfff +#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_MASK 3 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_MASK 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff +#define ISIF_VDFC_POS_MASK 0x1fff +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1fff +#define ISIF_DF_CSC_LNH_MASK 0x1fff +#define ISIF_DF_CSC_SLV_MASK 0x1fff +#define ISIF_DF_CSC_LNV_MASK 0x1fff +#define ISIF_DF_NUMLINES 0x7fff +#define ISIF_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1fff +#define ISIF_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define ISIF_LIN_CORRSFT_MASK 7 +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define ISIF_LIN_ENTRY_MASK 0x3ff + +/* masks and shifts*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c new file mode 100644 index 000000000000..9cb0262b9b33 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * Resizer allows upscaling or downscaling a image to a desired + * resolution. There are 2 resizer modules. both operating on the + * same input image, but can have different output resolution. + */ + +#include "dm365_ipipe_hw.h" +#include "dm365_resizer.h" + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH 4095 +#define MAX_IN_HEIGHT 4095 +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 + +static const unsigned int resizer_input_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +static const unsigned int resizer_output_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +/* resizer_calculate_line_length() - This function calculates the line length of + * various image planes at the input and + * output. + */ +static void +resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || + pix == V4L2_MBUS_FMT_SGRBG12_1X12) { + *line_len = width << 1; + } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || + pix == V4L2_MBUS_FMT_UV8_1X8) { + *line_len = width; + *line_len_c = width; + } else { + /* YUV 420 */ + /* round width to upper 32 byte boundary */ + *line_len = width; + *line_len_c = width; + } + /* adjust the line len to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + *line_len_c += 31; + *line_len_c &= ~0x1f; +} + +static inline int +resizer_validate_output_image_format(struct device *dev, + struct v4l2_mbus_framefmt *format, + int *in_line_len, int *in_line_len_c) +{ + if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 && + format->code != V4L2_MBUS_FMT_Y8_1X8 && + format->code != V4L2_MBUS_FMT_UV8_1X8 && + format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 && + format->code != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, "Invalid Mbus format, %d\n", format->code); + return -EINVAL; + } + if (!format->width || !format->height) { + dev_err(dev, "invalid width or height\n"); + return -EINVAL; + } + resizer_calculate_line_length(format->code, format->width, + format->height, in_line_len, in_line_len_c); + return 0; +} + +static void +resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass) +{ + struct resizer_params *param = &resizer->config; + + param->rsz_rsc_param[RSZ_A].cen = DISABLE; + param->rsz_rsc_param[RSZ_A].yen = DISABLE; + param->rsz_rsc_param[RSZ_A].v_phs_y = 0; + param->rsz_rsc_param[RSZ_A].v_phs_c = 0; + param->rsz_rsc_param[RSZ_A].v_dif = 256; + param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].h_phs = 0; + param->rsz_rsc_param[RSZ_A].h_dif = 256; + param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; + param->rsz2rgb[RSZ_A].rgb_en = DISABLE; + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_en[RSZ_B] = DISABLE; + if (bypass) { + param->rsz_rsc_param[RSZ_A].i_vps = 0; + param->rsz_rsc_param[RSZ_A].i_hps = 0; + /* Raw Bypass */ + param->rsz_common.passthrough = BYPASS_ON; + } +} + +static void +configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, + void *output_spec, unsigned char partial, + unsigned flag) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + struct vpfe_rsz_output_spec *output; + + if (index == RSZ_A && + resizer->resizer_a.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + if (index == RSZ_B && + resizer->resizer_b.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + output = (struct vpfe_rsz_output_spec *)output_spec; + param->rsz_en[index] = ENABLE; + if (partial) { + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = + output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = + output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = + output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = + output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = + output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = + output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = + output->v_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; + return; + } + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + param->rsz_rsc_param[index].o_vsz = outformat->height - 1; + param->rsz_rsc_param[index].o_hsz = outformat->width - 1; + param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y; + param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height; + + if (!flag) + return; + /* update common parameters */ + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; +} + +/* + * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer + * A or B. This is called after setting + * the input size or output size. + * @resizer: Pointer to VPFE resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +void +resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + if (outformat->field != V4L2_FIELD_INTERLACED) + param->rsz_rsc_param[index].v_dif = + ((informat->height) * 256) / (outformat->height); + else + param->rsz_rsc_param[index].v_dif = + ((informat->height >> 1) * 256) / (outformat->height); + param->rsz_rsc_param[index].h_dif = + ((informat->width) * 256) / (outformat->width); +} + +void +static resizer_enable_422_420_conversion(struct resizer_params *param, + int index, bool en) +{ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +/* resizer_calculate_sdram_offsets() - This function calculates the offsets from + * start of buffer for the C plane when + * output format is YUV420SP. It also + * calculates the offsets from the start of + * the buffer when the image is flipped + * vertically or horizontally for ycbcr/y/c + * planes. + * @resizer: Pointer to resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +static int +resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + int bytesperpixel = 2; + int image_height; + int image_width; + int yuv_420 = 0; + int offset = 0; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + image_height = outformat->height + 1; + image_width = outformat->width + 1; + param->ext_mem_param[index].c_offset = 0; + param->ext_mem_param[index].flip_ofst_y = 0; + param->ext_mem_param[index].flip_ofst_c = 0; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) { + /* YUV 420 */ + yuv_420 = 1; + bytesperpixel = 1; + } + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = (image_width * bytesperpixel) - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (image_height - 1) * + param->ext_mem_param[index].rsz_sdr_oft_y; + param->ext_mem_param[index].flip_ofst_y = offset; + if (!yuv_420) + return 0; + offset = 0; + /* half height for c-plane */ + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = image_width - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (((image_height >> 1) - 1) * + param->ext_mem_param[index].rsz_sdr_oft_c); + param->ext_mem_param[index].flip_ofst_c = offset; + param->ext_mem_param[index].c_offset = + param->ext_mem_param[index].rsz_sdr_oft_y * image_height; + return 0; +} + +int resizer_configure_output_win(struct vpfe_resizer_device *resizer) +{ + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_output_spec output_specs; + struct v4l2_mbus_framefmt *outformat; + int line_len_c; + int line_len; + int ret; + + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + + output_specs.vst_y = param->user_config.vst; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + output_specs.vst_c = param->user_config.vst; + + configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0); + resizer_calculate_line_length(outformat->code, + param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, + &line_len, &line_len_c); + param->ext_mem_param[0].rsz_sdr_oft_y = line_len; + param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; + resizer_calculate_resize_ratios(resizer, RSZ_A); + if (param->rsz_en[RSZ_B]) + resizer_calculate_resize_ratios(resizer, RSZ_B); + + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, RSZ_A, DISABLE); + + ret = resizer_calculate_sdram_offsets(resizer, RSZ_A); + if (!ret && param->rsz_en[RSZ_B]) + ret = resizer_calculate_sdram_offsets(resizer, RSZ_B); + + if (ret) + pr_err("Error in calculating sdram offsets\n"); + return ret; +} + +static int +resizer_calculate_down_scale_f_div_param(struct device *dev, + int input_width, int output_width, + struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int two_power; + unsigned int upper_h1; + unsigned int upper_h2; + unsigned int val1; + unsigned int val; + unsigned int rsz; + unsigned int h1; + unsigned int h2; + unsigned int o; + unsigned int n; + + upper_h1 = input_width >> 1; + n = param->h_dscale_ave_sz; + /* 2 ^ (scale+1) */ + two_power = 1 << (n + 1); + upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); + upper_h2 = input_width - upper_h1; + if (upper_h2 % two_power) { + dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); + return -EINVAL; + } + two_power = 1 << n; + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val = ((upper_h1 << 8) / val) + 1; + if (!(val % 2)) { + h1 = val; + } else { + val = upper_h1 << 8; + val >>= n + 1; + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + h1 = val; + } + o = 10 + (two_power << 2); + if (((input_width << 7) / rsz) % 2) + o += (((CEIL(rsz, 1024)) << 1) << n); + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = upper_h1 + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = 10 + (val1 * two_power); + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = upper_h1 - o; + param->f_div.pass[1].src_hsz = upper_h2 + o; + + return 0; +} + +static int +resizer_configure_common_in_params(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *user_config; + struct v4l2_mbus_framefmt *informat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + user_config = &resizer->config.user_config; + param->rsz_common.vps = param->user_config.vst; + param->rsz_common.hps = param->user_config.hst; + + if (vpfe_ipipeif_decimation_enabled(vpfe_dev)) + param->rsz_common.hsz = (((informat->width - 1) * + IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev)); + else + param->rsz_common.hsz = informat->width - 1; + + if (informat->field == V4L2_FIELD_INTERLACED) + param->rsz_common.vsz = (informat->height - 1) >> 1; + else + param->rsz_common.vsz = informat->height - 1; + + param->rsz_common.raw_flip = 0; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF) + param->rsz_common.source = IPIPEIF_DATA; + else + param->rsz_common.source = IPIPE_DATA; + + switch (informat->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 0; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 1; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + param->rsz_common.raw_flip = 1; + break; + + default: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + } + + param->rsz_common.yuv_y_min = user_config->yuv_y_min; + param->rsz_common.yuv_y_max = user_config->yuv_y_max; + param->rsz_common.yuv_c_min = user_config->yuv_c_min; + param->rsz_common.yuv_c_max = user_config->yuv_c_max; + param->rsz_common.out_chr_pos = user_config->out_chr_pos; + param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even; + + return 0; +} +static int +resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *cont_config; + int line_len_c; + int line_len; + int ret; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) { + dev_err(dev, "enable resizer - Resizer-A\n"); + return -EINVAL; + } + + cont_config = &resizer->config.user_config; + param->rsz_en[RSZ_A] = ENABLE; + configure_resizer_out_params(resizer, RSZ_A, + &cont_config->output1, 1, 0); + param->rsz_en[RSZ_B] = DISABLE; + param->oper_mode = RESIZER_MODE_CONTINIOUS; + + if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + struct v4l2_mbus_framefmt *outformat2; + + param->rsz_en[RSZ_B] = ENABLE; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + &cont_config->output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + resizer_configure_common_in_params(resizer); + ret = resizer_configure_output_win(resizer); + if (ret) + return ret; + + param->rsz_common.passthrough = cont_config->bypass; + if (cont_config->bypass) + resizer_configure_passthru(resizer, 1); + + return 0; +} + +static inline int +resizer_validate_input_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, int height, int *line_len) +{ + int val; + + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, + "resizer validate output: pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (!width || !height) { + dev_err(dev, + "resizer validate input: invalid width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UV8_1X8) + resizer_calculate_line_length(pix, width, + height, &val, line_len); + else + resizer_calculate_line_length(pix, width, + height, line_len, &val); + + return 0; +} + +static int +resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en, + unsigned char rsz, unsigned char frame_div_mode_en, + int width) +{ + if (dec_en && frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en can not enabled simultaneously\n"); + return -EINVAL; + } + + if (frame_div_mode_en) { + dev_err(dev, "frame_div_mode mode not supported\n"); + return -EINVAL; + } + + if (!dec_en) + return 0; + + if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) { + dev_err(dev, + "image width to be more than %d for decimation\n", + VPFE_IPIPE_MAX_INPUT_WIDTH); + return -EINVAL; + } + + if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + return 0; +} + +/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame + * division parameters for resizer. + * in normal mode. + */ +static int +resizer_calculate_normal_f_div_param(struct device *dev, int input_width, + int output_width, struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int val1; + unsigned int rsz; + unsigned int val; + unsigned int h1; + unsigned int h2; + unsigned int o; + + if (output_width > input_width) { + dev_err(dev, "frame div mode is used for scale down only\n"); + return -EINVAL; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val = ((input_width << 8) / val) + 1; + o = 14; + if (!(val % 2)) { + h1 = val; + } else { + val = (input_width << 7); + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val; + } + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((input_width >> 1) - o) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = (input_width >> 2) + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = val1; + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = (input_width >> 2) - o; + param->f_div.pass[1].src_hsz = (input_width >> 2) + o; + + return 0; +} + +static int +resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer) +{ + struct vpfe_rsz_config_params *config = &resizer->config.user_config; + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct v4l2_mbus_framefmt *outformat1, *outformat2; + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat; + int decimation; + int line_len_c; + int line_len; + int rsz; + int ret; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev); + rsz = vpfe_ipipeif_get_rsz(vpfe_dev); + if (decimation && param->user_config.frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en cannot enabled simultaneously\n"); + return -EINVAL; + } + + ret = resizer_validate_decimation(dev, decimation, rsz, + param->user_config.frame_div_mode_en, informat->width); + if (ret) + return -EINVAL; + + ret = resizer_validate_input_image_format(dev, informat->code, + informat->width, informat->height, &line_len); + if (ret) + return -EINVAL; + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_A] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat1, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_A, + ¶m->user_config.output1, 0, 1); + + if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_A, DISABLE); + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_B] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + ¶m->user_config.output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + + resizer_configure_common_in_params(resizer); + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_A); + resizer_calculate_sdram_offsets(resizer, RSZ_A); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_A].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_A].o_vsz + 1); + } + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_B); + resizer_calculate_sdram_offsets(resizer, RSZ_B); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_B].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_B].o_vsz + 1); + } + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_A]) { + if (!param->rsz_rsc_param[RSZ_A].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + if (ret) + return -EINVAL; + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_B]) { + if (!param->rsz_rsc_param[RSZ_B].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + if (ret) + return -EINVAL; + } + param->rsz_common.passthrough = config->bypass; + if (config->bypass) + resizer_configure_passthru(resizer, 1); + return 0; +} + +static void +resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + const struct resizer_params rsz_default_config = { + .oper_mode = RESIZER_MODE_ONE_SHOT, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + .rsz_rsc_param = { + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE, + .user_config = { + .output1 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + }; + memset(&resizer->config, 0, sizeof(struct resizer_params)); + memcpy(&resizer->config, &rsz_default_config, + sizeof(struct resizer_params)); +} + +/* + * resizer_set_configuration() - set resizer config + * @resizer: vpfe resizer device pointer. + * @chan_config: resizer channel configuration. + */ +static int +resizer_set_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + if (!chan_config->config) + resizer_set_defualt_configuration(resizer); + else + if (copy_from_user(&resizer->config.user_config, + chan_config->config, sizeof(struct vpfe_rsz_config_params))) + return -EFAULT; + + return 0; +} + +/* + * resizer_get_configuration() - get resizer config + * @resizer: vpfe resizer device pointer. + * @channel: image processor logical channel. + * @chan_config: resizer channel configuration. + */ +static int +resizer_get_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + + if (!chan_config->config) { + dev_err(dev, "Resizer channel invalid pointer\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)&resizer->config.user_config, + sizeof(struct vpfe_rsz_config_params))) { + dev_err(dev, "resizer_get_configuration: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * VPFE video operations + */ + +/* + * resizer_a_video_out_queue() - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_A, addr); +} + +/* + * resizer_b_video_out_queue() - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_B, addr); +} + +static const struct vpfe_video_operations resizer_a_video_ops = { + .queue = resizer_a_video_out_queue, +}; + +static const struct vpfe_video_operations resizer_b_video_ops = { + .queue = resizer_b_video_out_queue, +}; + +static void resizer_enable(struct vpfe_resizer_device *resizer, int en) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + unsigned char val; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF && + ipipeif_sink == IPIPEIF_INPUT_MEMORY) { + do { + val = regr_rsz(resizer->base_addr, RSZ_SRC_EN); + } while (val); + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_A); + } while (val); + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_B); + } while (val); + } + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_A, en); + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_B, en); +} + + +/* + * resizer_ss_isr() - resizer module single-shot buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +static void resizer_ss_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u32 val; + + if (ipipeif_sink != IPIPEIF_INPUT_MEMORY) + return; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->resizer_b.output == + RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (vpfe_video_is_pipe_ready(pipe) && + resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + resizer_enable(resizer, 1); + vpfe_ipipe_enable(vpfe_dev, 1); + vpfe_ipipeif_enable(vpfe_dev); + } +} + +/* + * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + rsz_src_enable(resizer->base_addr, 1); + } else { + rsz_src_enable(resizer->base_addr, 0); + } + return; + } + + /* handle interlaced frame capture */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * vpfe_resizer_dma_isr() - resizer module dma isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + int schedule_capture = 0; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + resizer_ss_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = vpfe_isif_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl() - Handle resizer module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_rsz_config *user_config; + int ret = -ENOIOCTLCMD; + + if (&resizer->crop_resizer.subdev != sd) + return ret; + + switch (cmd) { + case VIDIOC_VPFE_RSZ_S_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + ret = resizer_set_configuration(resizer, user_config); + break; + + case VIDIOC_VPFE_RSZ_G_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + if (!user_config->config) { + dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); + return -EINVAL; + } + ret = resizer_get_configuration(resizer, user_config); + break; + } + return ret; +} + +static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + struct resizer_params *param = &resizer->config; + int ret = 0; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY || + resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY && + ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + ret = resizer_configure_in_single_shot_mode(resizer); + else + ret = resizer_configure_in_continious_mode(resizer); + if (ret) + return ret; + ret = config_rsz_hw(resizer, param); + } + return ret; +} + +/* + * resizer_set_stream() - Enable/Disable streaming on resizer subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (&resizer->crop_resizer.subdev != sd) + return 0; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (resizer_do_hw_setup(resizer) < 0) + return -EINVAL; + resizer_enable(resizer, enable); + break; + + case 0: + resizer_enable(resizer, enable); + break; + } + + return 0; +} + +/* + * __resizer_get_format() - helper function for getting resizer format + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad number. + * @which: wanted subdev format. + * Retun wanted mbus frame format. + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + if (&resizer->crop_resizer.subdev == sd) + return &resizer->crop_resizer.formats[pad]; + if (&resizer->resizer_a.subdev == sd) + return &resizer->resizer_a.formats[pad]; + if (&resizer->resizer_b.subdev == sd) + return &resizer->resizer_b.formats[pad]; + return NULL; +} + +/* + * resizer_try_format() - Handle try format by pad subdev method + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which: wanted subdev format. + */ +static void +resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->crop_resizer.subdev == sd && + (pad == RESIZER_CROP_PAD_SOURCE || + pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) { + for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) { + if (fmt->code == resizer_input_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_input_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + MAX_IN_WIDTH); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + } else if (&resizer->resizer_a.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (&resizer->resizer_b.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* + * resizer_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (&resizer->crop_resizer.subdev == sd) { + if (fmt->pad == RESIZER_CROP_PAD_SINK) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE && + resizer->crop_resizer.output == RESIZER_A) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 && + resizer->crop_resizer.output2 == RESIZER_B) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE] = fmt->format; + } else { + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else if (&resizer->resizer_b.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format() - Retrieve the video format on a pad + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size() - enum frame sizes on pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * @code: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code() - enum mbus codes for pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resizer_input_formats)) + return -EINVAL; + + code->code = resizer_input_formats[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resizer_output_formats)) + return -EINVAL; + + code->code = resizer_output_formats[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats() - Initialize formats on all pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (&resizer->crop_resizer.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE2; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_a.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_b.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; + resizer_set_format(sd, fh, &format); + } + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup() - Setup resizer connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad array + * @remote: Pointer to remote pad array + * @flags: Link flags + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + + if (&resizer->crop_resizer.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_NONE; + break; + } + + if (resizer->crop_resizer.input != + RESIZER_CROP_INPUT_NONE) + return -EBUSY; + if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPEIF; + else if (ipipe_source == IPIPE_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPE; + else + return -EINVAL; + break; + + case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output = RESIZER_A; + break; + + case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output2 = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output2 != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output2 = RESIZER_B; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_a.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_a.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_b.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_b.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_b.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities() - Unregister entity + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out); + vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev); +} + +/* + * vpfe_resizer_register_entities() - Register entity + * @resizer - pointer to resizer devive. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags = 0; + int ret; + + /* Register the crop resizer subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev); + if (ret < 0) { + pr_err("Failed to register crop resizer as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-A subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-a as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-B subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-b as v4l2-subdev\n"); + return ret; + } + /* Register video-out device for resizer-a */ + ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-A video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_a.video_out.vpfe_dev = vpfe_dev; + + /* Register video-out device for resizer-b */ + ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-B video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; + + /* create link between Resizer Crop----> Resizer A*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + &resizer->resizer_a.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer Crop----> Resizer B*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + &resizer->resizer_b.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer A ----> video out */ + ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + &resizer->resizer_a.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer B ----> video out */ + ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + &resizer->resizer_b.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->resizer_b.video_out); +out_video_out2_register: + vpfe_video_unregister(&resizer->resizer_a.video_out); + media_entity_cleanup(&resizer->crop_resizer.subdev.entity); + media_entity_cleanup(&resizer->resizer_a.subdev.entity); + media_entity_cleanup(&resizer->resizer_b.subdev.entity); + v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_a.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_b.subdev); + return ret; +} + +/* + * vpfe_resizer_init() - resizer device initialization. + * @vpfe_rsz - pointer to resizer device + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev; + struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len); + if (!vpfe_rsz->base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE; + vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_a.subdev; + pads = &vpfe_rsz->resizer_a.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_b.subdev; + pads = &vpfe_rsz->resizer_b.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops; + vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A"); + if (ret) { + pr_err("Failed to init RSZ video-out device\n"); + return ret; + } + vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops; + vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B"); + if (ret) { + pr_err("Failed to init RSZ video-out2 device\n"); + return ret; + } + memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params)); + + return 0; +} + +void +vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct resource *res; + + iounmap(vpfe_rsz->base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h new file mode 100644 index 000000000000..59a79422b914 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_DM365_RESIZER_H +#define _DAVINCI_VPFE_DM365_RESIZER_H + +enum resizer_oper_mode { + RESIZER_MODE_CONTINIOUS = 0, + RESIZER_MODE_ONE_SHOT = 1, +}; + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +#define MAX_PASSES 2 + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[MAX_PASSES]; +}; + +/* Resizer Rescale Parameters*/ +struct resizer_scale_param { + bool h_flip; + bool v_flip; + bool cen; + bool yen; + unsigned short i_vps; + unsigned short i_hps; + unsigned short o_vsz; + unsigned short o_hsz; + unsigned short v_phs_y; + unsigned short v_phs_c; + unsigned short v_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + unsigned short h_phs; + unsigned short h_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + bool dscale_en; + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* store the calculated frame division parameter */ + struct f_div_param f_div; +}; + +enum resizer_rgb_t { + OUTPUT_32BIT, + OUTPUT_16BIT +}; + +enum resizer_rgb_msk_t { + NOMASK = 0, + MASKLAST2 = 1, +}; + +/* Resizer RGB Conversion Parameters */ +struct resizer_rgb { + bool rgb_en; + enum resizer_rgb_t rgb_typ; + enum resizer_rgb_msk_t rgb_msk0; + enum resizer_rgb_msk_t rgb_msk1; + unsigned int rgb_alpha_val; +}; + +/* Resizer External Memory Parameters */ +struct rsz_ext_mem_param { + unsigned int rsz_sdr_oft_y; + unsigned int rsz_sdr_ptr_s_y; + unsigned int rsz_sdr_ptr_e_y; + unsigned int rsz_sdr_oft_c; + unsigned int rsz_sdr_ptr_s_c; + unsigned int rsz_sdr_ptr_e_c; + /* offset to be added to buffer start when flipping for y/ycbcr */ + unsigned int flip_ofst_y; + /* offset to be added to buffer start when flipping for c */ + unsigned int flip_ofst_c; + /* c offset for YUV 420SP */ + unsigned int c_offset; + /* User Defined Y offset for YUV 420SP or YUV420ILE data */ + unsigned int user_y_ofst; + /* User Defined C offset for YUV 420SP data */ + unsigned int user_c_ofst; +}; + +enum rsz_data_source { + IPIPE_DATA, + IPIPEIF_DATA +}; + +enum rsz_src_img_fmt { + RSZ_IMG_422, + RSZ_IMG_420 +}; + +enum rsz_dpaths_bypass_t { + BYPASS_OFF = 0, + BYPASS_ON = 1, +}; + +struct rsz_common_params { + unsigned int vps; + unsigned int vsz; + unsigned int hps; + unsigned int hsz; + /* 420 or 422 */ + enum rsz_src_img_fmt src_img_fmt; + /* Y or C when src_fmt is 420, 0 - y, 1 - c */ + unsigned char y_c; + /* flip raw or ycbcr */ + unsigned char raw_flip; + /* IPIPE or IPIPEIF data */ + enum rsz_data_source source; + enum rsz_dpaths_bypass_t passthrough; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + bool rsz_seq_crv; + enum vpfe_chr_pos out_chr_pos; +}; + +struct resizer_params { + enum resizer_oper_mode oper_mode; + struct rsz_common_params rsz_common; + struct resizer_scale_param rsz_rsc_param[2]; + struct resizer_rgb rsz2rgb[2]; + struct rsz_ext_mem_param ext_mem_param[2]; + bool rsz_en[2]; + struct vpfe_rsz_config_params user_config; +}; + +#define ENABLE 1 +#define DISABLE (!ENABLE) + +#define RESIZER_CROP_PAD_SINK 0 +#define RESIZER_CROP_PAD_SOURCE 1 +#define RESIZER_CROP_PAD_SOURCE2 2 + +#define RESIZER_CROP_PADS_NUM 3 + +enum resizer_crop_input_entity { + RESIZER_CROP_INPUT_NONE = 0, + RESIZER_CROP_INPUT_IPIPEIF = 1, + RESIZER_CROP_INPUT_IPIPE = 2, +}; + +enum resizer_crop_output_entity { + RESIZER_CROP_OUTPUT_NONE, + RESIZER_A, + RESIZER_B, +}; + +struct dm365_crop_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_CROP_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM]; + enum resizer_crop_input_entity input; + enum resizer_crop_output_entity output; + enum resizer_crop_output_entity output2; + struct vpfe_resizer_device *rsz_device; +}; + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 + +#define RESIZER_PADS_NUM 2 + +enum resizer_input_entity { + RESIZER_INPUT_NONE = 0, + RESIZER_INPUT_CROP_RESIZER = 1, +}; + +enum resizer_output_entity { + RESIZER_OUTPUT_NONE = 0, + RESIZER_OUPUT_MEMORY = 1, +}; + +struct dm365_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + enum resizer_output_entity output; + struct vpfe_video_device video_out; + struct vpfe_resizer_device *rsz_device; +}; + +struct vpfe_resizer_device { + struct dm365_crop_resizer_device crop_resizer; + struct dm365_resizer_device resizer_a; + struct dm365_resizer_device resizer_b; + struct resizer_params config; + void *__iomem base_addr; +}; + +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer); + +#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h new file mode 100644 index 000000000000..0587bc52a840 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include <linux/v4l2-subdev.h> +#include <linux/clk.h> +#include <linux/i2c.h> + +#include <media/davinci/vpfe_types.h> + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c new file mode 100644 index 000000000000..7b351717bcbb --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + * + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +static bool debug; +static bool interface; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + + default: + pr_err("Invalid mbus code set\n"); + } + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif); + vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif); + return IRQ_HANDLED; +} + +/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif); + vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/* + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = + clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_prepare_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) + if (vpfe_dev->clks[i]) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n"); + kzfree(vpfe_dev->clks); + + return ret; +} + +/* + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/* + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting IMP IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/* + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = + kzalloc(sizeof(struct v4l2_subdev *)*num_subdevs, GFP_KERNEL); + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /* + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, + "could not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/* + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_isif_register; + + ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipeif_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipe_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_isif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipeif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipe.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_ipipe_register: + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); +out_ipipeif_register: + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); +out_isif_register: + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + + return ret; +} + +/* + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +} + +/* + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); + vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev); +} + +/* + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev); + if (ret) + return ret; + + ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev); + if (ret) + goto out_isif_init; + + ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev); + if (ret) + goto out_ipipeif_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_ipipe_init; + + return 0; + +out_ipipe_init: + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); +out_ipipeif_init: + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); +out_isif_init: + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + + return ret; +} + +/* + * vpfe_probe() : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + int ret = -ENOMEM; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + return 0; + +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h new file mode 100644 index 000000000000..68f6fe43a5b5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_MC_CAPTURE_H +#define _DAVINCI_VPFE_MC_CAPTURE_H + +#include "dm365_ipipe.h" +#include "dm365_ipipeif.h" +#include "dm365_isif.h" +#include "dm365_resizer.h" +#include "vpfe_video.h" + +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_isif_device vpfe_isif; + /* ipipeif subdevice */ + struct vpfe_ipipeif_device vpfe_ipipeif; + /* ipipe subdevice */ + struct vpfe_ipipe_device vpfe_ipipe; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; +}; + +/* File handle structure */ +struct vpfe_fh { + struct v4l2_fh vfh; + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c new file mode 100644 index 000000000000..99ccbebea598 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> + +#include <media/v4l2-ioctl.h> + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +/* minimum number of buffers needed in cont-mode */ +#define MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return NULL; + } + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + vpfe_cfg = vpfe_dev->pdev->platform_data; + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to isif/ccdc */ + if (i == vpfe_cfg->num_subdevs) { + pr_err("Invalid media chain connection to isif/ccdc\n"); + return -EINVAL; + } + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + if (pad) + *pad = remote->index; + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at output pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + pr_err("Invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* + * Should not matter if it is output[0] or 1 as + * the general ideas is to traverse backwards and + * the fact that the out video node always has the + * format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + return 0; +} + +/* + * vpfe_pipeline_enable() - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + return ret; +} + +/* + * vpfe_pipeline_disable() - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/* + * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + return 1; +} + +/* + * vpfe_open() - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file handle + * + * Return 0 if successful, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *handle; + + /* Allocate memory for the file handle object */ + handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video_dev); + v4l2_fh_add(&handle->vfh); + + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device users counter */ + video->usrs++; + /* Set io_allowed member to false */ + handle->io_allowed = 0; + v4l2_prio_open(&video->prio, &handle->prio); + handle->video = video; + file->private_data = &handle->vfh; + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +static unsigned long +vpfe_video_get_next_buffer(struct vpfe_video_device *video) +{ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + addr += video->field_off; + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp); + vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture() - stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release() - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + vb2_streamoff(&video->buffer_queue, + video->buffer_queue.type); + } + video->io_usrs = 0; + /* Free buffers allocated */ + vb2_queue_release(&video->buffer_queue); + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + } + /* Decrement device users counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + v4l2_fh_del(vfh); + kzfree(fh); + return 0; +} + +/* + * vpfe_mmap() - It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + return vb2_mmap(&video->buffer_queue, vma); +} + +/* + * vpfe_poll() - It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + if (video->started) + return vb2_poll(&video->buffer_queue, file, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap() - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = cap->capabilities; + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt() - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + return 0; +} + +/* + * vpfe_enum_fmt() - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote pad for video node\n"); + return -EINVAL; + } + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return -EINVAL; + } + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt() - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + *fmt = format; + video->fmt = *fmt; + return 0; +} + +/* + * vpfe_try_fmt() - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + return 0; +} + +/* + * vpfe_enum_input() - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + return -EINVAL; +} + +/* + * vpfe_g_input() - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + return 0; +} + +/* + * vpfe_s_input() - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + video->current_input = index; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd() - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_s_std() - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + video->stdid = V4L2_STD_UNKNOWN; + goto unlock_out; + } + video->stdid = *std_id; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + *tvnorm = video->stdid; + return 0; +} + +/* + * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_enum_dv_timings structure + * + * enum dv_timings's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n"); + return v4l2_subdev_call(subdev, video, enum_dv_timings, timings); +} + +/* + * vpfe_query_dv_timings() - query the dv_timings which is being input + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_timings which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n"); + return v4l2_subdev_call(subdev, video, query_dv_timings, timings); +} + +/* + * vpfe_s_dv_timings() - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * set dv_timings pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n"); + + video->stdid = V4L2_STD_UNKNOWN; + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_timings, timings); +} + +/* + * vpfe_g_dv_timings() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n"); + return v4l2_subdev_call(subdev, video, g_dv_timings, timings); +} + +/* + * Videobuf operations + */ +/* + * vpfe_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer nbuffers and buffer size + */ +static int +vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long size; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n"); + size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (size * *nbuffers > vpfe_dev->video_limit) + (*nbuffers)--; + } + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*nbuffers < MIN_NUM_BUFFERS) + *nbuffers = MIN_NUM_BUFFERS; + } + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = video->alloc_ctx; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "nbuffers=%d, size=%lu\n", *nbuffers, size); + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + if (vb->state != VB2_BUF_STATE_ACTIVE && + vb->state != VB2_BUF_STATE_PREPARED) + return 0; + + /* Initialize buffer */ + vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + return 0; +} + +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + /* add the buffer to the DMA queue */ + list_add_tail(&buf->list, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_video_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (vpfe_video_is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +/* vpfe_start_capture() - start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + if (vpfe_video_is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->list); + /* Mark state of the current frame to active */ + video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; + + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type); + return 0; +} + +static int vpfe_buffer_init(struct vb2_buffer *vb) +{ + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +/* abort streaming and wait for last buffer */ +static int vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + + if (!vb2_is_streaming(vq)) + return 0; + /* release all active buffers */ + while (!list_empty(&video->dma_queue)) { + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&video->next_frm->list); + vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static void vpfe_buf_cleanup(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n"); + if (vb->state == VB2_BUF_STATE_ACTIVE) + list_del_init(&buf->list); +} + +static struct vb2_ops video_qops = { + .queue_setup = vpfe_buffer_queue_setup, + .buf_init = vpfe_buffer_init, + .buf_prepare = vpfe_buffer_prepare, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, + .buf_cleanup = vpfe_buf_cleanup, + .buf_queue = vpfe_buffer_queue, +}; + +/* + * vpfe_reqbufs() - supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + struct vb2_queue *q; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + video->memory = req_buf->memory; + + /* Initialize videobuf2 queue as per the buffer type */ + video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev); + if (IS_ERR(video->alloc_ctx)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n"); + return PTR_ERR(video->alloc_ctx); + } + + q = &video->buffer_queue; + q->type = req_buf->type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fh; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + + ret = vb2_queue_init(q); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev); + return ret; + } + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = vb2_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf() - query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call vb2_querybuf to get information */ + return vb2_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf() - queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return vb2_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf() - deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return vb2_dqbuf(&video->buffer_queue, + buf, (file->f_flags & O_NONBLOCK)); +} + +/* + * vpfe_streamon() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + sdinfo = video->current_ext_subdev; + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.queued_list)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + /* Call vb2_streamon to start streaming */ + return vb2_streamon(&video->buffer_queue, buf_type); +} + +/* + * vpfe_streamoff() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + ret = vb2_streamoff(&video->buffer_queue, buf_type); + mutex_unlock(&video->lock); + + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_enum_dv_timings = vpfe_enum_dv_timings, + .vidioc_query_dv_timings = vpfe_query_dv_timings, + .vidioc_s_dv_timings = vpfe_s_dv_timings, + .vidioc_g_dv_timings = vpfe_g_dv_timings, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + + default: + return -EINVAL; + } + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + pr_err("%s: could not register video device (%d)\n", + __func__, ret); + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h new file mode 100644 index 000000000000..bf8af01d4a1b --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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. + * + * 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 + * + * Contributors: + * Manjunath Hadli <manjunath.hadli@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> + */ + +#ifndef _DAVINCI_VPFE_VIDEO_H +#define _DAVINCI_VPFE_VIDEO_H + +#include <media/videobuf2-dma-contig.h> + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + int(*queue) (struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED = 0, + VPFE_PIPELINE_STREAM_CONTINUOUS = 1, + VPFE_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continuous, + * single-shot or stopped + */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipeline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current vpfe_cap_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next vpfe_cap_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video); + +#endif /* _DAVINCI_VPFE_VIDEO_H */ diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 479c643da2f6..e33b7f55d84e 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -785,7 +785,7 @@ dt3155_init_board(struct pci_dev *pdev) } write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ - /* select chanel 1 for input and set sync level */ + /* select channel 1 for input and set sync level */ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index ece2dd146487..c9dfc75d1d2c 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -201,7 +201,8 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + dev_info(&adapter->dev, + "go7007: probing for module i2c:%s failed\n", type); return -1; } @@ -217,7 +218,7 @@ int go7007_register_encoder(struct go7007 *go) { int i, ret; - printk(KERN_INFO "go7007: registering new %s\n", go->name); + dev_info(go->dev, "go7007: registering new %s\n", go->name); mutex_lock(&go->hw_lock); ret = go7007_init_encoder(go); diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 6bc82aaeef11..39456a36b2c6 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -60,10 +60,10 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, #ifdef GO7007_I2C_DEBUG if (read) - printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", + dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", command, addr); else - printk(KERN_DEBUG + dev_dbg(go->dev, "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", *data, command, addr); #endif @@ -85,7 +85,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -119,7 +119,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -216,7 +216,7 @@ int go7007_i2c_init(struct go7007 *go) go->i2c_adapter.dev.parent = go->dev; i2c_set_adapdata(&go->i2c_adapter, go); if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR + dev_err(go->dev, "go7007-i2c: error: i2c_add_adapter failed\n"); return -1; } diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index a78133b67de2..0d3713dceba5 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1811,8 +1811,8 @@ int go7007_v4l2_init(struct go7007 *go) } video_set_drvdata(go->video_dev, go); ++go->ref_count; - printk(KERN_INFO "%s: registered device %s [v4l2]\n", - go->video_dev->name, video_device_node_name(go->video_dev)); + dev_info(go->dev, "registered device %s [v4l2]\n", + video_device_node_name(go->video_dev)); return 0; } diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index b3974100c6cd..d60e06596375 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -103,8 +103,7 @@ static u16 vid_regs_fp[] = { }; /* PAL specific values */ -static u16 vid_regs_fp_pal[] = -{ +static u16 vid_regs_fp_pal[] = { 0x120, 0x017, 0x121, 0xd22, 0x122, 0x122, @@ -174,7 +173,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -213,7 +212,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -231,13 +230,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) val_read = (buf[2] << 8) + buf[3]; kfree(buf); if (val_read != val) { - printk(KERN_INFO "invalid fp write %x %x\n", - val_read, val); + dev_info(&client->dev, "invalid fp write %x %x\n", + val_read, val); return -EFAULT; } if (subaddr != addr) { - printk(KERN_INFO "invalid fp write addr %x %x\n", - subaddr, addr); + dev_info(&client->dev, "invalid fp write addr %x %x\n", + subaddr, addr); return -EFAULT; } } else { @@ -275,7 +274,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) memset(buf, 0xcd, 6); usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -299,7 +298,7 @@ static int write_regs(struct i2c_client *client, u8 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed\n"); + dev_info(&client->dev, "failed\n"); return -1; } } @@ -312,7 +311,7 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed fp\n"); + dev_info(&client->dev, "failed fp\n"); return -1; } } @@ -606,23 +605,20 @@ static int s2250_probe(struct i2c_client *client, /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing audio\n"); + dev_err(&client->dev, "error initializing audio\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs(client, vid_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f1bd159ac195..f57eb3beb341 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -55,16 +55,16 @@ static int s2250loader_probe(struct usb_interface *interface, usbdev = usb_get_dev(interface_to_usbdev(interface)); if (!usbdev) { - printk(KERN_ERR "Enter s2250loader_probe failed\n"); + dev_err(&interface->dev, "Enter s2250loader_probe failed\n"); return -1; } - printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); - printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); + dev_info(&interface->dev, "Enter s2250loader_probe 2.6 kernel\n"); + dev_info(&interface->dev, "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); if (usbdev->descriptor.bNumConfigurations != 1) { - printk(KERN_ERR "can't handle multiple config\n"); + dev_err(&interface->dev, "can't handle multiple config\n"); return -1; } mutex_lock(&s2250_dev_table_mutex); @@ -75,31 +75,32 @@ static int s2250loader_probe(struct usb_interface *interface, } if (minor < 0 || minor >= MAX_DEVICES) { - printk(KERN_ERR "Invalid minor: %d\n", minor); + dev_err(&interface->dev, "Invalid minor: %d\n", minor); goto failed; } /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); if (s == NULL) { - printk(KERN_ERR "Out of memory\n"); + dev_err(&interface->dev, "Out of memory\n"); goto failed; } s2250_dev_table[minor] = s; - printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); + dev_info(&interface->dev, + "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); memset(s, 0, sizeof(device_extension_t)); s->usbdev = usbdev; - printk(KERN_INFO "loading 2250 loader\n"); + dev_info(&interface->dev, "loading 2250 loader\n"); kref_init(&(s->kref)); mutex_unlock(&s2250_dev_table_mutex); if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_LOADER_FIRMWARE); goto failed2; @@ -107,12 +108,12 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "loader download failed\n"); + dev_err(&interface->dev, "loader download failed\n"); goto failed2; } if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_FIRMWARE); goto failed2; @@ -120,7 +121,7 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "firmware_s2250 download failed\n"); + dev_err(&interface->dev, "firmware_s2250 download failed\n"); goto failed2; } @@ -133,14 +134,14 @@ failed2: if (s) kref_put(&(s->kref), s2250loader_delete); - printk(KERN_ERR "probe failed\n"); + dev_err(&interface->dev, "probe failed\n"); return -1; } static void s2250loader_disconnect(struct usb_interface *interface) { pdevice_extension_t s; - printk(KERN_INFO "s2250: disconnect\n"); + dev_info(&interface->dev, "s2250: disconnect\n"); s = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); kref_put(&(s->kref), s2250loader_delete); diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c index 1291ab79d2af..5d7ff8c81d6d 100644 --- a/drivers/staging/media/go7007/wis-sony-tuner.c +++ b/drivers/staging/media/go7007/wis-sony-tuner.c @@ -95,8 +95,8 @@ static int set_freq(struct i2c_client *client, int freq) band_name = "UHF"; band_select = tun->UHF; } - printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); + dev_dbg(&client->dev, "tuning to frequency %d.%04d (%s)\n", + freq / 16, (freq % 16) * 625, band_name); n = freq + tun->IFPCoff; buffer[0] = n >> 8; @@ -288,16 +288,16 @@ static int mpx_setup(struct i2c_client *client) u8 buf1[3], buf2[2]; struct i2c_msg msgs[2]; - printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " - "%04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); + dev_dbg(&client->dev, + "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[t->mpxmode].modus, + source, + mpx_audio_modes[t->mpxmode].acb, + mpx_audio_modes[t->mpxmode].fm_prescale, + mpx_audio_modes[t->mpxmode].nicam_prescale, + mpx_audio_modes[t->mpxmode].scart_prescale, + mpx_audio_modes[t->mpxmode].system, + mpx_audio_modes[t->mpxmode].volume); buf1[0] = 0x11; buf1[1] = 0x00; buf1[2] = 0x7e; @@ -310,14 +310,14 @@ static int mpx_setup(struct i2c_client *client) msgs[1].len = 2; msgs[1].buf = buf2; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX system: %02x%02x\n", + buf2[0], buf2[1]); buf1[0] = 0x11; buf1[1] = 0x02; buf1[2] = 0x00; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX status: %02x%02x\n", + buf2[0], buf2[1]); } #endif return 0; @@ -375,8 +375,7 @@ static int set_if(struct i2c_client *client) t->mpxmode = force_mpx_mode; else t->mpxmode = default_mpx_mode; - printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", - t->mpxmode); + dev_dbg(&client->dev, "setting MPX to mode %d\n", t->mpxmode); mpx_setup(client); return 0; @@ -401,8 +400,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (t->type >= 0) { if (t->type != *type) - printk(KERN_ERR "wis-sony-tuner: type already " - "set to %d, ignoring request for %d\n", + dev_err(&client->dev, + "type already set to %d, ignoring request for %d\n", t->type, *type); break; } @@ -414,28 +413,28 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) case 'B': case 'g': case 'G': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-B/G bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-B/G bands\n"); force_band = V4L2_STD_PAL_BG; break; case 'i': case 'I': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-I band\n"); + dev_info(&client->dev, + "forcing tuner to PAL-I band\n"); force_band = V4L2_STD_PAL_I; break; case 'd': case 'D': case 'k': case 'K': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-D/K bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-D/K bands\n"); force_band = V4L2_STD_PAL_I; break; case 'l': case 'L': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to SECAM-L band\n"); + dev_info(&client->dev, + "forcing tuner to SECAM-L band\n"); force_band = V4L2_STD_SECAM_L; break; default: @@ -455,14 +454,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) t->std = V4L2_STD_NTSC_M; break; default: - printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " - "supported by this module\n", *type); + dev_err(&client->dev, + "tuner type %d is not supported by this module\n", + *type); break; } if (type >= 0) - printk(KERN_INFO - "wis-sony-tuner: type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); + dev_info(&clinet->dev, + "type set to %d (%s)\n", + t->type, sony_tuners[t->type - 200].name); break; } #endif @@ -544,9 +544,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (force_band && (*std & force_band) != *std && *std != V4L2_STD_PAL && *std != V4L2_STD_SECAM) { - printk(KERN_DEBUG "wis-sony-tuner: ignoring " - "requested TV standard in " - "favor of force_band value\n"); + dev_dbg(&client->dev, + "ignoring requested TV standard in favor of force_band value\n"); t->std = force_band; } else if (*std & V4L2_STD_PAL_BG) { /* default */ t->std = V4L2_STD_PAL_BG; @@ -557,8 +556,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } else if (*std & V4L2_STD_SECAM_L) { t->std = V4L2_STD_SECAM_L; } else { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ break; } @@ -567,15 +566,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case TUNER_SONY_BTF_PK467Z: if (!(*std & V4L2_STD_NTSC_M_JP)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; case TUNER_SONY_BTF_PB463Z: if (!(*std & V4L2_STD_NTSC_M)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; @@ -673,8 +672,7 @@ static int wis_sony_tuner_probe(struct i2c_client *client, t->audmode = V4L2_TUNER_MODE_STEREO; i2c_set_clientdata(client, t); - printk(KERN_DEBUG - "wis-sony-tuner: initializing tuner at address %d on %s\n", + dev_dbg(&client->dev, "initializing tuner at address %d on %s\n", client->addr, adapter->name); return 0; diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index d6410ee01be8..290fd8c7bfef 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -128,30 +128,32 @@ static int wis_tw2804_command(struct i2c_client *client, int *input = arg; if (*input < 0 || *input > 3) { - printk(KERN_ERR "wis-tw2804: channel %d is not " - "between 0 and 3!\n", *input); + dev_err(&client->dev, + "channel %d is not between 0 and 3!\n", *input); return 0; } dec->channel = *input; - printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " - "channel %d\n", dec->channel); + dev_dbg(&client->dev, "initializing TW2804 channel %d\n", + dec->channel); if (dec->channel == 0 && write_regs(client, global_registers, 0) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 global registers\n"); + dev_err(&client->dev, + "error initializing TW2804 global registers\n"); return 0; } if (write_regs(client, channel_registers, dec->channel) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 channel %d\n", dec->channel); + dev_err(&client->dev, + "error initializing TW2804 channel %d\n", + dec->channel); return 0; } return 0; } if (dec->channel < 0) { - printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " - "channel number is set\n", cmd); + dev_dbg(&client->dev, + "ignoring command %08x until channel number is set\n", + cmd); return 0; } @@ -311,7 +313,7 @@ static int wis_tw2804_probe(struct i2c_client *client, dec->hue = 128; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", + dev_dbg(&client->dev, "creating TW2804 at address %d on %s\n", client->addr, adapter->name); return 0; diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 94071def3bb4..684ca37f0382 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -31,8 +31,7 @@ struct wis_tw9903 { int hue; }; -static u8 initial_registers[] = -{ +static u8 initial_registers[] = { 0x02, 0x44, /* input 1, composite */ 0x03, 0x92, /* correct digital format */ 0x04, 0x00, @@ -128,8 +127,8 @@ static int wis_tw9903_command(struct i2c_client *client, 0x06, 0xc0, /* reset device */ 0, 0, }; - printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", - vscale, hscale); + dev_dbg(&client->dev, "vscale is %04x, hscale is %04x\n", + vscale, hscale); /*write_regs(client, regs);*/ break; } @@ -288,12 +287,11 @@ static int wis_tw9903_probe(struct i2c_client *client, dec->hue = 0; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG - "wis-tw9903: initializing TW9903 at address %d on %s\n", + dev_dbg(&client->dev, "initializing TW9903 at address %d on %s\n", client->addr, adapter->name); if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); + dev_err(&client->dev, "error initializing TW9903\n"); kfree(dec); return -ENODEV; } diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c index 05ac798f35f7..582ea120a531 100644 --- a/drivers/staging/media/go7007/wis-uda1342.c +++ b/drivers/staging/media/go7007/wis-uda1342.c @@ -47,8 +47,8 @@ static int wis_uda1342_command(struct i2c_client *client, write_reg(client, 0x00, 0x1241); /* select input 1 */ break; default: - printk(KERN_ERR "wis-uda1342: input %d not supported\n", - *inp); + dev_err(&client->dev, "input %d not supported\n", + *inp); break; } break; @@ -67,8 +67,7 @@ static int wis_uda1342_probe(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - printk(KERN_DEBUG - "wis-uda1342: initializing UDA1342 at address %d on %s\n", + dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", client->addr, adapter->name); write_reg(client, 0x00, 0x8000); /* reset registers */ diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c index 951007a3fc96..fa31ee7dd6a9 100644 --- a/drivers/staging/media/lirc/lirc_bt829.c +++ b/drivers/staging/media/lirc/lirc_bt829.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/threads.h> @@ -72,20 +74,19 @@ static struct pci_dev *do_pci_probe(void) my_dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT, NULL); if (my_dev) { - printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", - pci_name(my_dev)); + pr_err("Using device: %s\n", pci_name(my_dev)); pci_addr_phys = 0; if (my_dev->resource[0].flags & IORESOURCE_MEM) { pci_addr_phys = my_dev->resource[0].start; - printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + pr_info("memory at 0x%08X\n", (unsigned int)pci_addr_phys); } if (pci_addr_phys == 0) { - printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + pr_err("no memory resource ?\n"); return NULL; } } else { - printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + pr_err("pci_probe failed\n"); return NULL; } return my_dev; @@ -140,7 +141,7 @@ int init_module(void) atir_minor = lirc_register_driver(&atir_driver); if (atir_minor < 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + pr_err("failed to register driver!\n"); return atir_minor; } dprintk("driver is registered on minor %d\n", atir_minor); @@ -159,7 +160,7 @@ static int atir_init_start(void) { pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); if (pci_addr_lin == 0) { - printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + pr_info("pci mem must be mapped\n"); return 0; } return 1; diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c index 939a801c23e4..2faa391006db 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -223,8 +223,8 @@ static int unregister_from_lirc(struct igorplug *ir) int devnum; if (!ir) { - printk(KERN_ERR "%s: called with NULL device struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL device struct!\n", __func__); return -EINVAL; } @@ -232,8 +232,8 @@ static int unregister_from_lirc(struct igorplug *ir) d = ir->d; if (!d) { - printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL lirc driver struct!\n", __func__); return -EINVAL; } @@ -347,8 +347,8 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) if (ir->buf_in[2] == 0) send_fragment(ir, buf, DEVICE_HEADERLEN, ret); else { - printk(KERN_WARNING DRIVER_NAME - "[%d]: Device buffer overrun.\n", ir->devnum); + dev_warn(&ir->usbdev->dev, + "[%d]: Device buffer overrun.\n", ir->devnum); /* HHHNNNNNNNNNNNOOOOOOOO H = header <---[2]---> N = newer <---------ret--------> O = older */ diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 2944fde89f44..343c622f9387 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> @@ -205,12 +207,12 @@ static void deregister_from_lirc(struct imon_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR KBUILD_MODNAME - ": %s: unable to deregister from lirc(%d)", - __func__, retval); + dev_err(&context->usbdev->dev, + ": %s: unable to deregister from lirc(%d)", + __func__, retval); else - printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " - "(minor:%d)\n", minor); + dev_info(&context->usbdev->dev, + "Deregistered iMON driver (minor:%d)\n", minor); } @@ -231,8 +233,7 @@ static int display_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&imon_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -282,8 +283,7 @@ static int display_close(struct inode *inode, struct file *file) context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -391,8 +391,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -521,8 +520,7 @@ static void ir_close(void *data) context = (struct imon_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -1009,8 +1007,8 @@ static void imon_disconnect(struct usb_interface *interface) mutex_unlock(&driver_lock); - printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", - __func__, ifnum); + dev_info(&interface->dev, "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); } static int imon_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index ec14bc81851b..41d110f8bc02 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /*** Includes ***/ #include <linux/module.h> @@ -115,8 +117,7 @@ static void out(int offset, int value) parport_write_control(pport, value); break; case LIRC_LP_STATUS: - printk(KERN_INFO "%s: attempt to write to status register\n", - LIRC_DRIVER_NAME); + pr_info("attempt to write to status register\n"); break; } } @@ -166,27 +167,23 @@ static unsigned int init_lirc_timer(void) if (default_timer == 0) { /* autodetect timer */ newtimer = (1000000*count)/timeelapsed; - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; } else { newtimer = (1000000*count)/timeelapsed; if (abs(newtimer - default_timer) > default_timer/10) { /* bad timer */ - printk(KERN_NOTICE "%s: bad timer: %u Hz\n", - LIRC_DRIVER_NAME, newtimer); - printk(KERN_NOTICE "%s: using default timer: " - "%u Hz\n", - LIRC_DRIVER_NAME, default_timer); + pr_notice("bad timer: %u Hz\n", newtimer); + pr_notice("using default timer: %u Hz\n", + default_timer); return default_timer; } else { - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; /* use detected value */ } } } else { - printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + pr_notice("no timer detected\n"); return 0; } } @@ -194,13 +191,10 @@ static unsigned int init_lirc_timer(void) static int lirc_claim(void) { if (parport_claim(ppdevice) != 0) { - printk(KERN_WARNING "%s: could not claim port\n", - LIRC_DRIVER_NAME); - printk(KERN_WARNING "%s: waiting for port becoming available" - "\n", LIRC_DRIVER_NAME); + pr_warn("could not claim port\n"); + pr_warn("waiting for port becoming available\n"); if (parport_claim_or_block(ppdevice) < 0) { - printk(KERN_NOTICE "%s: could not claim port, giving" - " up\n", LIRC_DRIVER_NAME); + pr_notice("could not claim port, giving up\n"); return 0; } } @@ -219,7 +213,7 @@ static void rbuf_write(int signal) if (nwptr == rptr) { /* no new signals will be accepted */ lost_irqs++; - printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + pr_notice("buffer overrun\n"); return; } rbuf[wptr] = signal; @@ -290,7 +284,7 @@ static void irq_handler(void *blah) if (signal > timeout || (check_pselecd && (in(1) & LP_PSELECD))) { signal = 0; - printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + pr_notice("timeout\n"); break; } } while (lirc_get_signal()); @@ -644,8 +638,7 @@ static int __init lirc_parallel_init(void) result = platform_driver_register(&lirc_parallel_driver); if (result) { - printk(KERN_NOTICE "platform_driver_register" - " returned %d\n", result); + pr_notice("platform_driver_register returned %d\n", result); return result; } @@ -661,8 +654,7 @@ static int __init lirc_parallel_init(void) pport = parport_find_base(io); if (pport == NULL) { - printk(KERN_NOTICE "%s: no port at %x found\n", - LIRC_DRIVER_NAME, io); + pr_notice("no port at %x found\n", io); result = -ENXIO; goto exit_device_put; } @@ -670,8 +662,7 @@ static int __init lirc_parallel_init(void) pf, kf, irq_handler, 0, NULL); parport_put_port(pport); if (ppdevice == NULL) { - printk(KERN_NOTICE "%s: parport_register_device() failed\n", - LIRC_DRIVER_NAME); + pr_notice("parport_register_device() failed\n"); result = -ENXIO; goto exit_device_put; } @@ -706,14 +697,12 @@ static int __init lirc_parallel_init(void) driver.dev = &lirc_parallel_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_NOTICE "%s: register_chrdev() failed\n", - LIRC_DRIVER_NAME); + pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } - printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", - LIRC_DRIVER_NAME, io, irq); + pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; exit_device_put: diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index f4e4d9003f38..b3fe21e7a90d 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -34,6 +34,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> @@ -171,7 +173,7 @@ static void delete_context(struct sasem_context *context) kfree(context); if (debug) - printk(KERN_INFO "%s: context deleted\n", __func__); + pr_info("%s: context deleted\n", __func__); } static void deregister_from_lirc(struct sasem_context *context) @@ -181,11 +183,10 @@ static void deregister_from_lirc(struct sasem_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR "%s: unable to deregister from lirc (%d)\n", - __func__, retval); + pr_err("%s: unable to deregister from lirc (%d)\n", + __func__, retval); else - printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", - minor); + pr_info("Deregistered Sasem driver (minor:%d)\n", minor); } @@ -206,8 +207,7 @@ static int vfd_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&sasem_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -252,8 +252,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -266,7 +265,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context->vfd_contrast = (unsigned int)arg; break; default: - printk(KERN_INFO "Unknown IOCTL command\n"); + pr_info("Unknown IOCTL command\n"); mutex_unlock(&context->ctx_lock); return -ENOIOCTLCMD; /* not supported */ } @@ -287,8 +286,7 @@ static int vfd_close(struct inode *inode, struct file *file) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -299,7 +297,7 @@ static int vfd_close(struct inode *inode, struct file *file) retval = -EIO; } else { context->vfd_isopen = 0; - printk(KERN_INFO "VFD port closed\n"); + dev_info(&context->dev->dev, "VFD port closed\n"); if (!context->dev_present && !context->ir_isopen) { /* Device disconnected before close and IR port is @@ -373,16 +371,14 @@ static ssize_t vfd_write(struct file *file, const char *buf, context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } mutex_lock(&context->ctx_lock); if (!context->dev_present) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no Sasem device present\n", __func__); + pr_err("%s: no Sasem device present\n", __func__); retval = -ENODEV; goto exit; } @@ -519,7 +515,7 @@ static int ir_open(void *data) __func__, retval); else { context->ir_isopen = 1; - printk(KERN_INFO "IR port opened\n"); + dev_info(&context->dev->dev, "IR port opened\n"); } exit: @@ -538,8 +534,7 @@ static void ir_close(void *data) context = (struct sasem_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -547,7 +542,7 @@ static void ir_close(void *data) usb_kill_urb(context->rx_urb); context->ir_isopen = 0; - printk(KERN_INFO "IR port closed\n"); + pr_info("IR port closed\n"); if (!context->dev_present) { @@ -584,8 +579,9 @@ static void incoming_packet(struct sasem_context *context, int i; if (len != 8) { - printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", - __func__, len); + dev_warn(&context->dev->dev, + "%s: invalid incoming packet size (%d)\n", + __func__, len); return; } @@ -663,7 +659,7 @@ static void usb_rx_callback(struct urb *urb) break; default: - printk(KERN_WARNING "%s: status (%d): ignored", + dev_warn(&urb->dev->dev, "%s: status (%d): ignored", __func__, urb->status); break; } @@ -830,8 +826,9 @@ static int sasem_probe(struct usb_interface *interface, retval = lirc_minor; goto unlock; } else - printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", - __func__, lirc_minor); + dev_info(&interface->dev, + "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); /* Needed while unregistering! */ driver->minor = lirc_minor; @@ -852,15 +849,18 @@ static int sasem_probe(struct usb_interface *interface, if (vfd_ep_found) { if (debug) - printk(KERN_INFO "Registering VFD with sysfs\n"); + dev_info(&interface->dev, + "Registering VFD with sysfs\n"); if (usb_register_dev(interface, &sasem_class)) /* Not a fatal error, so ignore */ - printk(KERN_INFO "%s: could not get a minor number " - "for VFD\n", __func__); + dev_info(&interface->dev, + "%s: could not get a minor number for VFD\n", + __func__); } - printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", - __func__, dev->bus->busnum, dev->devnum); + dev_info(&interface->dev, + "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); unlock: mutex_unlock(&context->ctx_lock); @@ -891,7 +891,7 @@ exit: } /** - * Callback function for USB core API: disonnect + * Callback function for USB core API: disconnect */ static void sasem_disconnect(struct usb_interface *interface) { @@ -903,7 +903,8 @@ static void sasem_disconnect(struct usb_interface *interface) context = usb_get_intfdata(interface); mutex_lock(&context->ctx_lock); - printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + dev_info(&interface->dev, "%s: Sasem device disconnected\n", + __func__); usb_set_intfdata(interface, NULL); context->dev_present = 0; diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index b5d0088f3102..af08e677b60f 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -48,6 +48,8 @@ * Steve Davies <steve@daviesfam.org> July 2001 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> @@ -667,8 +669,7 @@ static irqreturn_t irq_handler(int i, void *blah) counter++; status = sinp(UART_MSR); if (counter > RS_ISR_PASS_LIMIT) { - printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " - "We're caught!\n"); + pr_warn("AIEEEE: We're caught!\n"); break; } if ((status & hardware[type].signal_pin_change) @@ -703,11 +704,10 @@ static irqreturn_t irq_handler(int i, void *blah) dcd = (status & hardware[type].signal_pin) ? 1 : 0; if (dcd == last_dcd) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": ignoring spike: %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); continue; } @@ -715,25 +715,20 @@ static irqreturn_t irq_handler(int i, void *blah) if (tv.tv_sec < lasttv.tv_sec || (tv.tv_sec == lasttv.tv_sec && tv.tv_usec < lasttv.tv_usec)) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: your clock just jumped " - "backwards\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: your clock just jumped backwards\n"); + pr_warn("%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); data = PULSE_MASK; } else if (deltv > 15) { data = PULSE_MASK; /* really long time */ if (!(dcd^sense)) { /* sanity check */ - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: " - "%d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); /* * detecting pulse while this * MUST be a space! @@ -776,8 +771,7 @@ static int hardware_init_port(void) soutp(UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0f) { /* we fail, there's nothing here */ - printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " - "failed, cannot continue\n"); + pr_err("port existence test failed, cannot continue\n"); return -ENODEV; } @@ -850,11 +844,9 @@ static int lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { if (result == -EBUSY) - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", - irq); + dev_err(&dev->dev, "IRQ %d busy\n", irq); else if (result == -EINVAL) - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); + dev_err(&dev->dev, "Bad irq number or handler\n"); return result; } @@ -869,14 +861,11 @@ static int lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME) == NULL)) || ((iommap == 0) && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": port %04x already in use\n", io); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": use 'setserial /dev/ttySX uart none'\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": or compile the serial port driver as module and\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": make sure this module is loaded first\n"); + dev_err(&dev->dev, "port %04x already in use\n", io); + dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); + dev_warn(&dev->dev, + "or compile the serial port driver as module and\n"); + dev_warn(&dev->dev, "make sure this module is loaded first\n"); result = -EBUSY; goto exit_free_irq; } @@ -907,11 +896,11 @@ static int lirc_serial_probe(struct platform_device *dev) msleep(40); } sense = (nlow >= nhigh ? 1 : 0); - printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "auto-detected active %s receiver\n", + sense ? "low" : "high"); } else - printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "Manually using active %s receiver\n", + sense ? "low" : "high"); dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; @@ -1251,8 +1240,7 @@ static int __init lirc_serial_init_module(void) driver.dev = &lirc_serial_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": register_chrdev failed!\n"); + pr_err("register_chrdev failed!\n"); lirc_serial_exit(); return driver.minor; } diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index a45799874a21..63a554c36f75 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -33,6 +33,8 @@ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/sched.h> #include <linux/errno.h> @@ -495,7 +497,7 @@ static int init_chrdev(void) driver.dev = &lirc_sir_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + pr_err("init_chrdev() failed.\n"); return -EIO; } return 0; @@ -604,7 +606,7 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) } if (status & UTSR0_TFS) - printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + pr_err("transmit fifo not full, shouldn't happen\n"); /* We must clear certain bits. */ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); @@ -787,7 +789,7 @@ static int init_hardware(void) #ifdef LIRC_ON_SA1100 #ifdef CONFIG_SA1100_BITSY if (machine_is_bitsy()) { - printk(KERN_INFO "Power on IR module\n"); + pr_info("Power on IR module\n"); set_bitsy_egpio(EGPIO_BITSY_IR_ON); } #endif @@ -885,8 +887,7 @@ static int init_hardware(void) udelay(1500); /* read previous control byte */ - printk(KERN_INFO LIRC_DRIVER_NAME - ": 0x%02x\n", sinp(UART_RX)); + pr_info("0x%02x\n", sinp(UART_RX)); /* Set DLAB 1. */ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); @@ -964,8 +965,7 @@ static int init_port(void) /* get I/O port access and IRQ line */ #ifndef LIRC_ON_SA1100 if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); + pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } #endif @@ -975,15 +975,11 @@ static int init_port(void) # ifndef LIRC_ON_SA1100 release_region(io, 8); # endif - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); + pr_err("IRQ %d already in use.\n", irq); return retval; } #ifndef LIRC_ON_SA1100 - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", - io, irq); + pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); #endif init_timer(&timerlist); @@ -1213,8 +1209,7 @@ static int init_lirc_sir(void) if (retval < 0) return retval; init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME - ": Installed.\n"); + pr_info("Installed.\n"); return 0; } @@ -1243,23 +1238,20 @@ static int __init lirc_sir_init(void) retval = platform_driver_register(&lirc_sir_driver); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register " - "failed!\n"); + pr_err("Platform driver register failed!\n"); return -ENODEV; } lirc_sir_dev = platform_device_alloc("lirc_dev", 0); if (!lirc_sir_dev) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc " - "failed!\n"); + pr_err("Platform device alloc failed!\n"); retval = -ENOMEM; goto pdev_alloc_fail; } retval = platform_device_add(lirc_sir_dev); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add " - "failed!\n"); + pr_err("Platform device add failed!\n"); retval = -ENODEV; goto pdev_add_fail; } @@ -1292,7 +1284,7 @@ static void __exit lirc_sir_exit(void) drop_port(); platform_device_unregister(lirc_sir_dev); platform_driver_unregister(&lirc_sir_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); + pr_info("Uninstalled.\n"); } module_init(lirc_sir_init); diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c index 56210f0fc5ec..58ab61b1f1d9 100644 --- a/drivers/staging/media/solo6x10/p2m.c +++ b/drivers/staging/media/solo6x10/p2m.c @@ -231,15 +231,15 @@ static void run_p2m_test(struct solo_dev *solo_dev) u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); int i, d; - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6X10_NAME, size); + dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", + size); for (i = 0; i < size; i += TEST_CHUNK_SIZE) for (d = 0; d < 4; d++) errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6X10_NAME, errs); + dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n", + errs); return; } diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c index f8f0da952288..4977e869d5b7 100644 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ b/drivers/staging/media/solo6x10/v4l2-enc.c @@ -1619,6 +1619,8 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; if (!err) err = solo_osd_print(solo_enc); + else + err = -EFAULT; } break; default: @@ -1654,6 +1656,8 @@ static int solo_g_ext_ctrls(struct file *file, void *priv, err = copy_to_user(ctrl->string, solo_enc->osd_text, OSD_TEXT_MAX); + if (err) + err = -EFAULT; } break; default: |