diff options
author | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2014-10-09 14:00:54 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2014-10-09 14:00:54 -0300 |
commit | a66d05d504a24894a8fdf11e4569752f313e5764 (patch) | |
tree | 2bb40c1f6c301b231d7aeaad08c190aa5a82ba1f /drivers/media | |
parent | bfe01a5ba2490f299e1d2d5508cbbbadd897bbe9 (diff) | |
parent | 5563caaf8b8cd22e35997d5d74cb3609df86b223 (diff) | |
download | op-kernel-dev-a66d05d504a24894a8fdf11e4569752f313e5764.zip op-kernel-dev-a66d05d504a24894a8fdf11e4569752f313e5764.tar.gz |
Merge branch 'patchwork' into v4l_for_linus
* patchwork: (544 commits)
[media] ir-hix5hd2: fix build on c6x arch
[media] pt3: fix DTV FE I2C driver load error paths
Revert "[media] media: em28xx - remove reset_resume interface"
[media] exynos4-is: fix some warnings when compiling on arm64
[media] usb drivers: use %zu instead of %zd
[media] pci drivers: use %zu instead of %zd
[media] dvb-frontends: use %zu instead of %zd
[media] s5p-mfc: Fix several printk warnings
[media] s5p_mfc_opr: Fix warnings
[media] ti-vpe: Fix typecast
[media] s3c-camif: fix dma_addr_t printks
[media] s5p_mfc_opr_v6: get rid of warnings when compiled with 64 bits
[media] s5p_mfc_opr_v5: Fix lots of warnings on x86_64
[media] em28xx: Fix identation
[media] drxd: remove a dead code
[media] saa7146: remove return after BUG()
[media] cx88: remove return after BUG()
[media] cx88: fix cards table CodingStyle
[media] radio-sf16fmr2: declare some structs as static
[media] radio-sf16fmi: declare pnp_attached as static
...
Conflicts:
Documentation/DocBook/media/v4l/compat.xml
Diffstat (limited to 'drivers/media')
448 files changed, 37490 insertions, 12268 deletions
diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h index 897b10c..8942bda 100644 --- a/drivers/media/common/b2c2/flexcop.h +++ b/drivers/media/common/b2c2/flexcop.h @@ -4,7 +4,7 @@ * see flexcop.c for copyright information */ #ifndef __FLEXCOP_H__ -#define __FLEXCOP_H___ +#define __FLEXCOP_H__ #define FC_LOG_PREFIX "b2c2-flexcop" #include "flexcop-common.h" diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index 6c47f3f..b7d6393 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -311,7 +311,6 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) } default: BUG(); - return 0; } if (mutex_lock_interruptible(vdev->lock)) @@ -399,7 +398,6 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof return -EINVAL; default: BUG(); - return 0; } } @@ -423,7 +421,6 @@ static ssize_t fops_write(struct file *file, const char __user *data, size_t cou return -EINVAL; default: BUG(); - return -EINVAL; } } diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c index 8276999..82c7a12 100644 --- a/drivers/media/common/siano/sms-cards.c +++ b/drivers/media/common/siano/sms-cards.c @@ -157,6 +157,12 @@ static struct sms_board sms_boards[] = { .type = SMS_DENVER_2160, .default_mode = DEVICE_MODE_DAB_TDMB, }, + [SMS1XXX_BOARD_PCTV_77E] = { + .name = "Hauppauge microStick 77e", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0, + .default_mode = DEVICE_MODE_DVBT_BDA, + }, }; struct sms_board *sms_get_board(unsigned id) diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h index c63b544..4c4cadd 100644 --- a/drivers/media/common/siano/sms-cards.h +++ b/drivers/media/common/siano/sms-cards.h @@ -45,6 +45,7 @@ #define SMS1XXX_BOARD_SIANO_RIO 18 #define SMS1XXX_BOARD_SIANO_DENVER_1530 19 #define SMS1XXX_BOARD_SIANO_DENVER_2160 20 +#define SMS1XXX_BOARD_PCTV_77E 21 struct sms_board_gpio_cfg { int lna_vhf_exist; diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index 050984c..a367743 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -2129,8 +2129,6 @@ int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num, static int __init smscore_module_init(void) { - int rc = 0; - INIT_LIST_HEAD(&g_smscore_notifyees); INIT_LIST_HEAD(&g_smscore_devices); kmutex_init(&g_smscore_deviceslock); @@ -2138,7 +2136,7 @@ static int __init smscore_module_init(void) INIT_LIST_HEAD(&g_smscore_registry); kmutex_init(&g_smscore_registrylock); - return rc; + return 0; } static void __exit smscore_module_exit(void) diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index c0363f1..abff803 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -1087,8 +1087,8 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait) struct dmxdev_filter *dmxdevfilter = file->private_data; unsigned int mask = 0; - if (!dmxdevfilter) - return -EINVAL; + if ((!dmxdevfilter) || dmxdevfilter->dev->exit) + return POLLERR; poll_wait(file, &dmxdevfilter->buffer.queue, wait); @@ -1181,6 +1181,9 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait) dprintk("function : %s\n", __func__); + if (dmxdev->exit) + return POLLERR; + poll_wait(file, &dmxdev->dvr_buffer.queue, wait); if ((file->f_flags & O_ACCMODE) == O_RDONLY) { diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 12ce19c..e07a84e 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -144,6 +144,7 @@ #define USB_PID_ITETECH_IT9135 0x9135 #define USB_PID_ITETECH_IT9135_9005 0x9005 #define USB_PID_ITETECH_IT9135_9006 0x9006 +#define USB_PID_ITETECH_IT9303 0x9306 #define USB_PID_KWORLD_399U 0xe399 #define USB_PID_KWORLD_399U_2 0xe400 #define USB_PID_KWORLD_395U 0xe396 @@ -244,6 +245,7 @@ #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d +#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI 0x3012 #define USB_PID_TECHNOTREND_TVSTICK_CT2_4400 0x3014 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index c2a6a0a..b8579ee 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1934,15 +1934,13 @@ static int dvb_frontend_ioctl_properties(struct file *file, struct dtv_frontend_properties *c = &fe->dtv_property_cache; int err = 0; - struct dtv_properties *tvps = NULL; + struct dtv_properties *tvps = parg; struct dtv_property *tvp = NULL; int i; dev_dbg(fe->dvb->device, "%s:\n", __func__); - if(cmd == FE_SET_PROPERTY) { - tvps = (struct dtv_properties __user *)parg; - + if (cmd == FE_SET_PROPERTY) { dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num); dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props); @@ -1957,7 +1955,8 @@ static int dvb_frontend_ioctl_properties(struct file *file, goto out; } - if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + if (copy_from_user(tvp, (void __user *)tvps->props, + tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; goto out; } @@ -1972,10 +1971,7 @@ static int dvb_frontend_ioctl_properties(struct file *file, if (c->state == DTV_TUNE) dev_dbg(fe->dvb->device, "%s: Property cache is full, tuning\n", __func__); - } else - if(cmd == FE_GET_PROPERTY) { - tvps = (struct dtv_properties __user *)parg; - + } else if (cmd == FE_GET_PROPERTY) { dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num); dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props); @@ -1990,7 +1986,8 @@ static int dvb_frontend_ioctl_properties(struct file *file, goto out; } - if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + if (copy_from_user(tvp, (void __user *)tvps->props, + tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; goto out; } @@ -2012,7 +2009,8 @@ static int dvb_frontend_ioctl_properties(struct file *file, (tvp + i)->result = err; } - if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { + if (copy_to_user((void __user *)tvps->props, tvp, + tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; goto out; } @@ -2072,6 +2070,23 @@ static int dtv_set_frontend(struct dvb_frontend *fe) case SYS_DVBC_ANNEX_C: rolloff = 113; break; + case SYS_DVBS: + case SYS_TURBO: + rolloff = 135; + break; + case SYS_DVBS2: + switch (c->rolloff) { + case ROLLOFF_20: + rolloff = 120; + break; + case ROLLOFF_25: + rolloff = 125; + break; + default: + case ROLLOFF_35: + rolloff = 135; + } + break; default: break; } @@ -2550,7 +2565,9 @@ int dvb_frontend_suspend(struct dvb_frontend *fe) dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, fe->id); - if (fe->ops.tuner_ops.sleep) + if (fe->ops.tuner_ops.suspend) + ret = fe->ops.tuner_ops.suspend(fe); + else if (fe->ops.tuner_ops.sleep) ret = fe->ops.tuner_ops.sleep(fe); if (fe->ops.sleep) @@ -2572,7 +2589,9 @@ int dvb_frontend_resume(struct dvb_frontend *fe) if (fe->ops.init) ret = fe->ops.init(fe); - if (fe->ops.tuner_ops.init) + if (fe->ops.tuner_ops.resume) + ret = fe->ops.tuner_ops.resume(fe); + else if (fe->ops.tuner_ops.init) ret = fe->ops.tuner_ops.init(fe); fe->exit = DVB_FE_NO_EXIT; diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index d398de4..816269e 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -201,6 +201,8 @@ struct dvb_tuner_ops { int (*release)(struct dvb_frontend *fe); int (*init)(struct dvb_frontend *fe); int (*sleep)(struct dvb_frontend *fe); + int (*suspend)(struct dvb_frontend *fe); + int (*resume)(struct dvb_frontend *fe); /** This is for simple PLLs - set all parameters in one go. */ int (*set_params)(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c index a5712cd..1100e98 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb-core/dvb_ringbuffer.c @@ -166,6 +166,31 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t return len; } +ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, + const u8 __user *buf, size_t len) +{ + int status; + size_t todo = len; + size_t split; + + split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; + + if (split > 0) { + status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split); + if (status) + return len - todo; + buf += split; + todo -= split; + rbuf->pwrite = 0; + } + status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); + if (status) + return len - todo; + rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; + + return len; +} + ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) { int status; @@ -297,3 +322,4 @@ EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); EXPORT_SYMBOL(dvb_ringbuffer_read_user); EXPORT_SYMBOL(dvb_ringbuffer_read); EXPORT_SYMBOL(dvb_ringbuffer_write); +EXPORT_SYMBOL(dvb_ringbuffer_write_user); diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h index 41f04da..9e1e11b 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.h +++ b/drivers/media/dvb-core/dvb_ringbuffer.h @@ -133,6 +133,8 @@ extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, */ extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len); +extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, + const u8 __user *buf, size_t len); /** diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index fe0ddcc..5a13454 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -471,6 +471,11 @@ config DVB_SI2168 help Say Y when you want to support this frontend. +config DVB_AS102_FE + tristate + depends on DVB_CORE + default DVB_AS102 + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -643,6 +648,14 @@ config DVB_MB86A20S A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. +config DVB_TC90522 + tristate "Toshiba TC90522" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Toshiba TC90522 2xISDB-T + 2xISDB-S demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE @@ -720,6 +733,13 @@ config DVB_A8293 depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT +config DVB_SP2 + tristate "CIMaX SP2" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + CIMaX SP2/SP2HF Common Interface module. + config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index edf103d..ba59df6 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -107,10 +107,12 @@ obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_SI2165) += si2165.o obj-$(CONFIG_DVB_A8293) += a8293.o +obj-$(CONFIG_DVB_SP2) += sp2.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_RTL2832) += rtl2832.o obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o - +obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_TC90522) += tc90522.o diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index ecf6388..8001690 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -683,7 +683,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe) switch (c->transmission_mode) { case TRANSMISSION_MODE_AUTO: - auto_mode = 1; + auto_mode = true; break; case TRANSMISSION_MODE_2K: break; @@ -693,12 +693,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe) default: dev_dbg(&state->i2c->dev, "%s: invalid transmission_mode\n", __func__); - auto_mode = 1; + auto_mode = true; } switch (c->guard_interval) { case GUARD_INTERVAL_AUTO: - auto_mode = 1; + auto_mode = true; break; case GUARD_INTERVAL_1_32: break; @@ -714,12 +714,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe) default: dev_dbg(&state->i2c->dev, "%s: invalid guard_interval\n", __func__); - auto_mode = 1; + auto_mode = true; } switch (c->hierarchy) { case HIERARCHY_AUTO: - auto_mode = 1; + auto_mode = true; break; case HIERARCHY_NONE: break; @@ -734,12 +734,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe) break; default: dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__); - auto_mode = 1; + auto_mode = true; } switch (c->modulation) { case QAM_AUTO: - auto_mode = 1; + auto_mode = true; break; case QPSK: break; @@ -751,7 +751,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe) break; default: dev_dbg(&state->i2c->dev, "%s: invalid modulation\n", __func__); - auto_mode = 1; + auto_mode = true; } /* Use HP. How and which case we can switch to LP? */ @@ -759,7 +759,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe) switch (c->code_rate_HP) { case FEC_AUTO: - auto_mode = 1; + auto_mode = true; break; case FEC_1_2: break; @@ -778,12 +778,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe) default: dev_dbg(&state->i2c->dev, "%s: invalid code_rate_HP\n", __func__); - auto_mode = 1; + auto_mode = true; } switch (c->code_rate_LP) { case FEC_AUTO: - auto_mode = 1; + auto_mode = true; break; case FEC_1_2: break; @@ -804,7 +804,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe) default: dev_dbg(&state->i2c->dev, "%s: invalid code_rate_LP\n", __func__); - auto_mode = 1; + auto_mode = true; } switch (c->bandwidth_hz) { diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 5c90ea6..63a89c1 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -24,29 +24,35 @@ /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 64 -struct af9033_state { - struct i2c_adapter *i2c; +struct af9033_dev { + struct i2c_client *client; struct dvb_frontend fe; struct af9033_config cfg; + bool is_af9035; + bool is_it9135; u32 bandwidth_hz; bool ts_mode_parallel; bool ts_mode_serial; - u32 ber; - u32 ucb; - unsigned long last_stat_check; + fe_status_t fe_status; + u64 post_bit_error_prev; /* for old read_ber we return (curr - prev) */ + u64 post_bit_error; + u64 post_bit_count; + u64 error_block_count; + u64 total_block_count; + struct delayed_work stat_work; }; /* write multiple registers */ -static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, +static int af9033_wr_regs(struct af9033_dev *dev, u32 reg, const u8 *val, int len) { int ret; u8 buf[MAX_XFER_SIZE]; struct i2c_msg msg[1] = { { - .addr = state->cfg.i2c_addr, + .addr = dev->client->addr, .flags = 0, .len = 3 + len, .buf = buf, @@ -54,9 +60,9 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, }; if (3 + len > sizeof(buf)) { - dev_warn(&state->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); + dev_warn(&dev->client->dev, + "i2c wr reg=%04x: len=%d is too big!\n", + reg, len); return -EINVAL; } @@ -65,12 +71,12 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, buf[2] = (reg >> 0) & 0xff; memcpy(&buf[3], val, len); - ret = i2c_transfer(state->i2c, msg, 1); + ret = i2c_transfer(dev->client->adapter, msg, 1); if (ret == 1) { ret = 0; } else { - dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%06x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); + dev_warn(&dev->client->dev, "i2c wr failed=%d reg=%06x len=%d\n", + ret, reg, len); ret = -EREMOTEIO; } @@ -78,31 +84,31 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, } /* read multiple registers */ -static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) +static int af9033_rd_regs(struct af9033_dev *dev, u32 reg, u8 *val, int len) { int ret; u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff, (reg >> 0) & 0xff }; struct i2c_msg msg[2] = { { - .addr = state->cfg.i2c_addr, + .addr = dev->client->addr, .flags = 0, .len = sizeof(buf), .buf = buf }, { - .addr = state->cfg.i2c_addr, + .addr = dev->client->addr, .flags = I2C_M_RD, .len = len, .buf = val } }; - ret = i2c_transfer(state->i2c, msg, 2); + ret = i2c_transfer(dev->client->adapter, msg, 2); if (ret == 2) { ret = 0; } else { - dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%06x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); + dev_warn(&dev->client->dev, "i2c rd failed=%d reg=%06x len=%d\n", + ret, reg, len); ret = -EREMOTEIO; } @@ -111,19 +117,19 @@ static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) /* write single register */ -static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val) +static int af9033_wr_reg(struct af9033_dev *dev, u32 reg, u8 val) { - return af9033_wr_regs(state, reg, &val, 1); + return af9033_wr_regs(dev, reg, &val, 1); } /* read single register */ -static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val) +static int af9033_rd_reg(struct af9033_dev *dev, u32 reg, u8 *val) { - return af9033_rd_regs(state, reg, val, 1); + return af9033_rd_regs(dev, reg, val, 1); } /* write single register with mask */ -static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, +static int af9033_wr_reg_mask(struct af9033_dev *dev, u32 reg, u8 val, u8 mask) { int ret; @@ -131,7 +137,7 @@ static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = af9033_rd_regs(state, reg, &tmp, 1); + ret = af9033_rd_regs(dev, reg, &tmp, 1); if (ret) return ret; @@ -140,17 +146,17 @@ static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, val |= tmp; } - return af9033_wr_regs(state, reg, &val, 1); + return af9033_wr_regs(dev, reg, &val, 1); } /* read single register with mask */ -static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, +static int af9033_rd_reg_mask(struct af9033_dev *dev, u32 reg, u8 *val, u8 mask) { int ret, i; u8 tmp; - ret = af9033_rd_regs(state, reg, &tmp, 1); + ret = af9033_rd_regs(dev, reg, &tmp, 1); if (ret) return ret; @@ -167,18 +173,17 @@ static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, } /* write reg val table using reg addr auto increment */ -static int af9033_wr_reg_val_tab(struct af9033_state *state, +static int af9033_wr_reg_val_tab(struct af9033_dev *dev, const struct reg_val *tab, int tab_len) { #define MAX_TAB_LEN 212 int ret, i, j; u8 buf[1 + MAX_TAB_LEN]; - dev_dbg(&state->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); + dev_dbg(&dev->client->dev, "tab_len=%d\n", tab_len); if (tab_len > sizeof(buf)) { - dev_warn(&state->i2c->dev, "%s: tab len %d is too big\n", - KBUILD_MODNAME, tab_len); + dev_warn(&dev->client->dev, "tab len %d is too big\n", tab_len); return -EINVAL; } @@ -186,7 +191,7 @@ static int af9033_wr_reg_val_tab(struct af9033_state *state, buf[j] = tab[i].val; if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1) { - ret = af9033_wr_regs(state, tab[i].reg - j, buf, j + 1); + ret = af9033_wr_regs(dev, tab[i].reg - j, buf, j + 1); if (ret < 0) goto err; @@ -199,16 +204,16 @@ static int af9033_wr_reg_val_tab(struct af9033_state *state, return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } -static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x) +static u32 af9033_div(struct af9033_dev *dev, u32 a, u32 b, u32 x) { u32 r = 0, c = 0, i; - dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x); + dev_dbg(&dev->client->dev, "a=%d b=%d x=%d\n", a, b, x); if (a > b) { c = a / b; @@ -225,22 +230,15 @@ static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x) } r = (c << (u32)x) + r; - dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n", - __func__, a, b, x, r, r); + dev_dbg(&dev->client->dev, "a=%d b=%d x=%d r=%d r=%x\n", a, b, x, r, r); return r; } -static void af9033_release(struct dvb_frontend *fe) -{ - struct af9033_state *state = fe->demodulator_priv; - - kfree(state); -} - static int af9033_init(struct dvb_frontend *fe) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, len; const struct reg_val *init; u8 buf[4]; @@ -248,7 +246,7 @@ static int af9033_init(struct dvb_frontend *fe) struct reg_val_mask tab[] = { { 0x80fb24, 0x00, 0x08 }, { 0x80004c, 0x00, 0xff }, - { 0x00f641, state->cfg.tuner, 0xff }, + { 0x00f641, dev->cfg.tuner, 0xff }, { 0x80f5ca, 0x01, 0x01 }, { 0x80f715, 0x01, 0x01 }, { 0x00f41f, 0x04, 0x04 }, @@ -267,88 +265,82 @@ static int af9033_init(struct dvb_frontend *fe) { 0x00d830, 0x01, 0xff }, { 0x00d831, 0x00, 0xff }, { 0x00d832, 0x00, 0xff }, - { 0x80f985, state->ts_mode_serial, 0x01 }, - { 0x80f986, state->ts_mode_parallel, 0x01 }, + { 0x80f985, dev->ts_mode_serial, 0x01 }, + { 0x80f986, dev->ts_mode_parallel, 0x01 }, { 0x00d827, 0x00, 0xff }, { 0x00d829, 0x00, 0xff }, - { 0x800045, state->cfg.adc_multiplier, 0xff }, + { 0x800045, dev->cfg.adc_multiplier, 0xff }, }; /* program clock control */ - clock_cw = af9033_div(state, state->cfg.clock, 1000000ul, 19ul); + clock_cw = af9033_div(dev, dev->cfg.clock, 1000000ul, 19ul); buf[0] = (clock_cw >> 0) & 0xff; buf[1] = (clock_cw >> 8) & 0xff; buf[2] = (clock_cw >> 16) & 0xff; buf[3] = (clock_cw >> 24) & 0xff; - dev_dbg(&state->i2c->dev, "%s: clock=%d clock_cw=%08x\n", - __func__, state->cfg.clock, clock_cw); + dev_dbg(&dev->client->dev, "clock=%d clock_cw=%08x\n", + dev->cfg.clock, clock_cw); - ret = af9033_wr_regs(state, 0x800025, buf, 4); + ret = af9033_wr_regs(dev, 0x800025, buf, 4); if (ret < 0) goto err; /* program ADC control */ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { - if (clock_adc_lut[i].clock == state->cfg.clock) + if (clock_adc_lut[i].clock == dev->cfg.clock) break; } - adc_cw = af9033_div(state, clock_adc_lut[i].adc, 1000000ul, 19ul); + adc_cw = af9033_div(dev, clock_adc_lut[i].adc, 1000000ul, 19ul); buf[0] = (adc_cw >> 0) & 0xff; buf[1] = (adc_cw >> 8) & 0xff; buf[2] = (adc_cw >> 16) & 0xff; - dev_dbg(&state->i2c->dev, "%s: adc=%d adc_cw=%06x\n", - __func__, clock_adc_lut[i].adc, adc_cw); + dev_dbg(&dev->client->dev, "adc=%d adc_cw=%06x\n", + clock_adc_lut[i].adc, adc_cw); - ret = af9033_wr_regs(state, 0x80f1cd, buf, 3); + ret = af9033_wr_regs(dev, 0x80f1cd, buf, 3); if (ret < 0) goto err; /* program register table */ for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val, + ret = af9033_wr_reg_mask(dev, tab[i].reg, tab[i].val, tab[i].mask); if (ret < 0) goto err; } - /* feed clock to RF tuner */ - switch (state->cfg.tuner) { - case AF9033_TUNER_IT9135_38: - case AF9033_TUNER_IT9135_51: - case AF9033_TUNER_IT9135_52: - case AF9033_TUNER_IT9135_60: - case AF9033_TUNER_IT9135_61: - case AF9033_TUNER_IT9135_62: - ret = af9033_wr_reg(state, 0x80fba8, 0x00); + /* clock output */ + if (dev->cfg.dyn0_clk) { + ret = af9033_wr_reg(dev, 0x80fba8, 0x00); if (ret < 0) goto err; } /* settings for TS interface */ - if (state->cfg.ts_mode == AF9033_TS_MODE_USB) { - ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01); + if (dev->cfg.ts_mode == AF9033_TS_MODE_USB) { + ret = af9033_wr_reg_mask(dev, 0x80f9a5, 0x00, 0x01); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01); + ret = af9033_wr_reg_mask(dev, 0x80f9b5, 0x01, 0x01); if (ret < 0) goto err; } else { - ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x80f990, 0x00, 0x01); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x80f9b5, 0x00, 0x01); if (ret < 0) goto err; } /* load OFSM settings */ - dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__); - switch (state->cfg.tuner) { + dev_dbg(&dev->client->dev, "load ofsm settings\n"); + switch (dev->cfg.tuner) { case AF9033_TUNER_IT9135_38: case AF9033_TUNER_IT9135_51: case AF9033_TUNER_IT9135_52: @@ -367,14 +359,13 @@ static int af9033_init(struct dvb_frontend *fe) break; } - ret = af9033_wr_reg_val_tab(state, init, len); + ret = af9033_wr_reg_val_tab(dev, init, len); if (ret < 0) goto err; /* load tuner specific settings */ - dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n", - __func__); - switch (state->cfg.tuner) { + dev_dbg(&dev->client->dev, "load tuner specific settings\n"); + switch (dev->cfg.tuner) { case AF9033_TUNER_TUA9001: len = ARRAY_SIZE(tuner_init_tua9001); init = tuner_init_tua9001; @@ -424,90 +415,108 @@ static int af9033_init(struct dvb_frontend *fe) init = tuner_init_it9135_62; break; default: - dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", - __func__, state->cfg.tuner); + dev_dbg(&dev->client->dev, "unsupported tuner ID=%d\n", + dev->cfg.tuner); ret = -ENODEV; goto err; } - ret = af9033_wr_reg_val_tab(state, init, len); + ret = af9033_wr_reg_val_tab(dev, init, len); if (ret < 0) goto err; - if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { - ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); + if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + ret = af9033_wr_reg_mask(dev, 0x00d91c, 0x01, 0x01); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x00d917, 0x00, 0x01); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x00d916, 0x00, 0x01); if (ret < 0) goto err; } - switch (state->cfg.tuner) { + switch (dev->cfg.tuner) { case AF9033_TUNER_IT9135_60: case AF9033_TUNER_IT9135_61: case AF9033_TUNER_IT9135_62: - ret = af9033_wr_reg(state, 0x800000, 0x01); + ret = af9033_wr_reg(dev, 0x800000, 0x01); if (ret < 0) goto err; } - state->bandwidth_hz = 0; /* force to program all parameters */ + dev->bandwidth_hz = 0; /* force to program all parameters */ + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* start statistics polling */ + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_sleep(struct dvb_frontend *fe) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret, i; u8 tmp; - ret = af9033_wr_reg(state, 0x80004c, 1); + /* stop statistics polling */ + cancel_delayed_work_sync(&dev->stat_work); + + ret = af9033_wr_reg(dev, 0x80004c, 1); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x800000, 0); + ret = af9033_wr_reg(dev, 0x800000, 0); if (ret < 0) goto err; for (i = 100, tmp = 1; i && tmp; i--) { - ret = af9033_rd_reg(state, 0x80004c, &tmp); + ret = af9033_rd_reg(dev, 0x80004c, &tmp); if (ret < 0) goto err; usleep_range(200, 10000); } - dev_dbg(&state->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&dev->client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; goto err; } - ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08); + ret = af9033_wr_reg_mask(dev, 0x80fb24, 0x08, 0x08); if (ret < 0) goto err; /* prevent current leak (?) */ - if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { /* enable parallel TS */ - ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x00d917, 0x00, 0x01); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01); + ret = af9033_wr_reg_mask(dev, 0x00d916, 0x01, 0x01); if (ret < 0) goto err; } @@ -515,7 +524,7 @@ static int af9033_sleep(struct dvb_frontend *fe) return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } @@ -533,14 +542,14 @@ static int af9033_get_tune_settings(struct dvb_frontend *fe, static int af9033_set_frontend(struct dvb_frontend *fe) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, spec_inv, sampling_freq; u8 tmp, buf[3], bandwidth_reg_val; u32 if_frequency, freq_cw, adc_freq; - dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", - __func__, c->frequency, c->bandwidth_hz); + dev_dbg(&dev->client->dev, "frequency=%d bandwidth_hz=%d\n", + c->frequency, c->bandwidth_hz); /* check bandwidth */ switch (c->bandwidth_hz) { @@ -554,8 +563,7 @@ static int af9033_set_frontend(struct dvb_frontend *fe) bandwidth_reg_val = 0x02; break; default: - dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n", - __func__); + dev_dbg(&dev->client->dev, "invalid bandwidth_hz\n"); ret = -EINVAL; goto err; } @@ -565,23 +573,23 @@ static int af9033_set_frontend(struct dvb_frontend *fe) fe->ops.tuner_ops.set_params(fe); /* program CFOE coefficients */ - if (c->bandwidth_hz != state->bandwidth_hz) { + if (c->bandwidth_hz != dev->bandwidth_hz) { for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { - if (coeff_lut[i].clock == state->cfg.clock && + if (coeff_lut[i].clock == dev->cfg.clock && coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { break; } } - ret = af9033_wr_regs(state, 0x800001, + ret = af9033_wr_regs(dev, 0x800001, coeff_lut[i].val, sizeof(coeff_lut[i].val)); } /* program frequency control */ - if (c->bandwidth_hz != state->bandwidth_hz) { - spec_inv = state->cfg.spec_inv ? -1 : 1; + if (c->bandwidth_hz != dev->bandwidth_hz) { + spec_inv = dev->cfg.spec_inv ? -1 : 1; for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { - if (clock_adc_lut[i].clock == state->cfg.clock) + if (clock_adc_lut[i].clock == dev->cfg.clock) break; } adc_freq = clock_adc_lut[i].adc; @@ -602,12 +610,12 @@ static int af9033_set_frontend(struct dvb_frontend *fe) else sampling_freq *= -1; - freq_cw = af9033_div(state, sampling_freq, adc_freq, 23ul); + freq_cw = af9033_div(dev, sampling_freq, adc_freq, 23ul); if (spec_inv == -1) freq_cw = 0x800000 - freq_cw; - if (state->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X) + if (dev->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X) freq_cw /= 2; buf[0] = (freq_cw >> 0) & 0xff; @@ -618,26 +626,26 @@ static int af9033_set_frontend(struct dvb_frontend *fe) if (if_frequency == 0) buf[2] = 0; - ret = af9033_wr_regs(state, 0x800029, buf, 3); + ret = af9033_wr_regs(dev, 0x800029, buf, 3); if (ret < 0) goto err; - state->bandwidth_hz = c->bandwidth_hz; + dev->bandwidth_hz = c->bandwidth_hz; } - ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03); + ret = af9033_wr_reg_mask(dev, 0x80f904, bandwidth_reg_val, 0x03); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x800040, 0x00); + ret = af9033_wr_reg(dev, 0x800040, 0x00); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x800047, 0x00); + ret = af9033_wr_reg(dev, 0x800047, 0x00); if (ret < 0) goto err; - ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01); + ret = af9033_wr_reg_mask(dev, 0x80f999, 0x00, 0x01); if (ret < 0) goto err; @@ -646,33 +654,33 @@ static int af9033_set_frontend(struct dvb_frontend *fe) else tmp = 0x01; /* UHF */ - ret = af9033_wr_reg(state, 0x80004b, tmp); + ret = af9033_wr_reg(dev, 0x80004b, tmp); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x800000, 0x00); + ret = af9033_wr_reg(dev, 0x800000, 0x00); if (ret < 0) goto err; return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_get_frontend(struct dvb_frontend *fe) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[8]; - dev_dbg(&state->i2c->dev, "%s:\n", __func__); + dev_dbg(&dev->client->dev, "\n"); /* read all needed registers */ - ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); + ret = af9033_rd_regs(dev, 0x80f900, buf, sizeof(buf)); if (ret < 0) goto err; @@ -784,21 +792,21 @@ static int af9033_get_frontend(struct dvb_frontend *fe) return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret; u8 tmp; *status = 0; /* radio channel status, 0=no result, 1=has signal, 2=no signal */ - ret = af9033_rd_reg(state, 0x800047, &tmp); + ret = af9033_rd_reg(dev, 0x800047, &tmp); if (ret < 0) goto err; @@ -808,7 +816,7 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) if (tmp != 0x02) { /* TPS lock */ - ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01); + ret = af9033_rd_reg_mask(dev, 0x80f5a9, &tmp, 0x01); if (ret < 0) goto err; @@ -817,7 +825,7 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) FE_HAS_VITERBI; /* full lock */ - ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01); + ret = af9033_rd_reg_mask(dev, 0x80f999, &tmp, 0x01); if (ret < 0) goto err; @@ -827,76 +835,38 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) FE_HAS_LOCK; } + dev->fe_status = *status; + return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct af9033_state *state = fe->demodulator_priv; - int ret, i, len; - u8 buf[3], tmp; - u32 snr_val; - const struct val_snr *uninitialized_var(snr_lut); - - /* read value */ - ret = af9033_rd_regs(state, 0x80002c, buf, 3); - if (ret < 0) - goto err; + struct af9033_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; - snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; - - /* read current modulation */ - ret = af9033_rd_reg(state, 0x80f903, &tmp); - if (ret < 0) - goto err; - - switch ((tmp >> 0) & 3) { - case 0: - len = ARRAY_SIZE(qpsk_snr_lut); - snr_lut = qpsk_snr_lut; - break; - case 1: - len = ARRAY_SIZE(qam16_snr_lut); - snr_lut = qam16_snr_lut; - break; - case 2: - len = ARRAY_SIZE(qam64_snr_lut); - snr_lut = qam64_snr_lut; - break; - default: - goto err; - } - - for (i = 0; i < len; i++) { - tmp = snr_lut[i].snr; - - if (snr_val < snr_lut[i].val) - break; - } - - *snr = tmp * 10; /* dB/10 */ + /* use DVBv5 CNR */ + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); /* 1000x => 10x */ + else + *snr = 0; return 0; - -err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); - - return ret; } static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret; u8 strength2; /* read signal strength of 0-100 scale */ - ret = af9033_rd_reg(state, 0x800048, &strength2); + ret = af9033_rd_reg(dev, 0x800048, &strength2); if (ret < 0) goto err; @@ -906,244 +876,225 @@ static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); - - return ret; -} - -static int af9033_update_ch_stat(struct af9033_state *state) -{ - int ret = 0; - u32 err_cnt, bit_cnt; - u16 abort_cnt; - u8 buf[7]; - - /* only update data every half second */ - if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) { - ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf)); - if (ret < 0) - goto err; - /* in 8 byte packets? */ - abort_cnt = (buf[1] << 8) + buf[0]; - /* in bits */ - err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2]; - /* in 8 byte packets? always(?) 0x2710 = 10000 */ - bit_cnt = (buf[6] << 8) + buf[5]; - - if (bit_cnt < abort_cnt) { - abort_cnt = 1000; - state->ber = 0xffffffff; - } else { - /* 8 byte packets, that have not been rejected already */ - bit_cnt -= (u32)abort_cnt; - if (bit_cnt == 0) { - state->ber = 0xffffffff; - } else { - err_cnt -= (u32)abort_cnt * 8 * 8; - bit_cnt *= 8 * 8; - state->ber = err_cnt * (0xffffffff / bit_cnt); - } - } - state->ucb += abort_cnt; - state->last_stat_check = jiffies; - } - - return 0; -err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) { - struct af9033_state *state = fe->demodulator_priv; - int ret; - - ret = af9033_update_ch_stat(state); - if (ret < 0) - return ret; + struct af9033_dev *dev = fe->demodulator_priv; - *ber = state->ber; + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; return 0; } static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct af9033_state *state = fe->demodulator_priv; - int ret; - - ret = af9033_update_ch_stat(state); - if (ret < 0) - return ret; - - *ucblocks = state->ucb; + struct af9033_dev *dev = fe->demodulator_priv; + *ucblocks = dev->error_block_count; return 0; } static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret; - dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable); + dev_dbg(&dev->client->dev, "enable=%d\n", enable); - ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01); + ret = af9033_wr_reg_mask(dev, 0x00fa04, enable, 0x01); if (ret < 0) goto err; return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret; - dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff); + dev_dbg(&dev->client->dev, "onoff=%d\n", onoff); - ret = af9033_wr_reg_mask(state, 0x80f993, onoff, 0x01); + ret = af9033_wr_reg_mask(dev, 0x80f993, onoff, 0x01); if (ret < 0) goto err; return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } -static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, int onoff) +static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, + int onoff) { - struct af9033_state *state = fe->demodulator_priv; + struct af9033_dev *dev = fe->demodulator_priv; int ret; u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff}; - dev_dbg(&state->i2c->dev, "%s: index=%d pid=%04x onoff=%d\n", - __func__, index, pid, onoff); + dev_dbg(&dev->client->dev, "index=%d pid=%04x onoff=%d\n", + index, pid, onoff); if (pid > 0x1fff) return 0; - ret = af9033_wr_regs(state, 0x80f996, wbuf, 2); + ret = af9033_wr_regs(dev, 0x80f996, wbuf, 2); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x80f994, onoff); + ret = af9033_wr_reg(dev, 0x80f994, onoff); if (ret < 0) goto err; - ret = af9033_wr_reg(state, 0x80f995, index); + ret = af9033_wr_reg(dev, 0x80f995, index); if (ret < 0) goto err; return 0; err: - dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } -static struct dvb_frontend_ops af9033_ops; - -struct dvb_frontend *af9033_attach(const struct af9033_config *config, - struct i2c_adapter *i2c, - struct af9033_ops *ops) +static void af9033_stat_work(struct work_struct *work) { - int ret; - struct af9033_state *state; - u8 buf[8]; + struct af9033_dev *dev = container_of(work, struct af9033_dev, stat_work.work); + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, tmp, i, len; + u8 u8tmp, buf[7]; + + dev_dbg(&dev->client->dev, "\n"); + + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + if (dev->is_af9035) { + ret = af9033_rd_reg(dev, 0x80004a, &u8tmp); + tmp = -u8tmp * 1000; + } else { + ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp); + tmp = (u8tmp - 100) * 1000; + } + if (ret) + goto err; - dev_dbg(&i2c->dev, "%s:\n", __func__); + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = tmp; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL); - if (state == NULL) - goto err; + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + u32 snr_val; + const struct val_snr *snr_lut; - /* setup the state */ - state->i2c = i2c; - memcpy(&state->cfg, config, sizeof(struct af9033_config)); + /* read value */ + ret = af9033_rd_regs(dev, 0x80002c, buf, 3); + if (ret) + goto err; - if (state->cfg.clock != 12000000) { - dev_err(&state->i2c->dev, "%s: af9033: unsupported clock=%d, " \ - "only 12000000 Hz is supported currently\n", - KBUILD_MODNAME, state->cfg.clock); - goto err; - } + snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); - /* firmware version */ - ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); - if (ret < 0) - goto err; + /* read current modulation */ + ret = af9033_rd_reg(dev, 0x80f903, &u8tmp); + if (ret) + goto err; - ret = af9033_rd_regs(state, 0x804191, &buf[4], 4); - if (ret < 0) - goto err; + switch ((u8tmp >> 0) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_lut); + snr_lut = qpsk_snr_lut; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_lut); + snr_lut = qam16_snr_lut; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_lut); + snr_lut = qam64_snr_lut; + break; + default: + goto err_schedule_delayed_work; + } - dev_info(&state->i2c->dev, "%s: firmware version: LINK=%d.%d.%d.%d " \ - "OFDM=%d.%d.%d.%d\n", KBUILD_MODNAME, buf[0], buf[1], - buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + for (i = 0; i < len; i++) { + tmp = snr_lut[i].snr * 1000; + if (snr_val < snr_lut[i].val) + break; + } - /* sleep */ - switch (state->cfg.tuner) { - case AF9033_TUNER_IT9135_38: - case AF9033_TUNER_IT9135_51: - case AF9033_TUNER_IT9135_52: - case AF9033_TUNER_IT9135_60: - case AF9033_TUNER_IT9135_61: - case AF9033_TUNER_IT9135_62: - /* IT9135 did not like to sleep at that early */ - break; - default: - ret = af9033_wr_reg(state, 0x80004c, 1); - if (ret < 0) - goto err; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = tmp; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - ret = af9033_wr_reg(state, 0x800000, 0); - if (ret < 0) + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* outer FEC, 204 byte packets */ + u16 abort_packet_count, rsd_packet_count; + /* inner FEC, bits */ + u32 rsd_bit_err_count; + + /* + * Packet count used for measurement is 10000 + * (rsd_packet_count). Maybe it should be increased? + */ + + ret = af9033_rd_regs(dev, 0x800032, buf, 7); + if (ret) goto err; - } - /* configure internal TS mode */ - switch (state->cfg.ts_mode) { - case AF9033_TS_MODE_PARALLEL: - state->ts_mode_parallel = true; - break; - case AF9033_TS_MODE_SERIAL: - state->ts_mode_serial = true; - break; - case AF9033_TS_MODE_USB: - /* usb mode for AF9035 */ - default: - break; - } + abort_packet_count = (buf[1] << 8) | (buf[0] << 0); + rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2]; + rsd_packet_count = (buf[6] << 8) | (buf[5] << 0); - /* create dvb_frontend */ - memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); - state->fe.demodulator_priv = state; + dev->error_block_count += abort_packet_count; + dev->total_block_count += rsd_packet_count; + dev->post_bit_error += rsd_bit_err_count; + dev->post_bit_count += rsd_packet_count * 204 * 8; - if (ops) { - ops->pid_filter = af9033_pid_filter; - ops->pid_filter_ctrl = af9033_pid_filter_ctrl; - } + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue = dev->total_block_count; + + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->error_block_count; + + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; - return &state->fe; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + } +err_schedule_delayed_work: + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + return; err: - kfree(state); - return NULL; + dev_dbg(&dev->client->dev, "failed=%d\n", ret); } -EXPORT_SYMBOL(af9033_attach); static struct dvb_frontend_ops af9033_ops = { .delsys = { SYS_DVBT }, @@ -1170,8 +1121,6 @@ static struct dvb_frontend_ops af9033_ops = { FE_CAN_MUTE_TS }, - .release = af9033_release, - .init = af9033_init, .sleep = af9033_sleep, @@ -1188,6 +1137,150 @@ static struct dvb_frontend_ops af9033_ops = { .i2c_gate_ctrl = af9033_i2c_gate_ctrl, }; +static int af9033_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct af9033_config *cfg = client->dev.platform_data; + struct af9033_dev *dev; + int ret; + u8 buf[8]; + u32 reg; + + /* allocate memory for the internal state */ + dev = kzalloc(sizeof(struct af9033_dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "Could not allocate memory for state\n"); + goto err; + } + + /* setup the state */ + dev->client = client; + INIT_DELAYED_WORK(&dev->stat_work, af9033_stat_work); + memcpy(&dev->cfg, cfg, sizeof(struct af9033_config)); + + if (dev->cfg.clock != 12000000) { + ret = -ENODEV; + dev_err(&dev->client->dev, + "unsupported clock %d Hz, only 12000000 Hz is supported currently\n", + dev->cfg.clock); + goto err_kfree; + } + + /* firmware version */ + switch (dev->cfg.tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + dev->is_it9135 = true; + reg = 0x004bfc; + break; + default: + dev->is_af9035 = true; + reg = 0x0083e9; + break; + } + + ret = af9033_rd_regs(dev, reg, &buf[0], 4); + if (ret < 0) + goto err_kfree; + + ret = af9033_rd_regs(dev, 0x804191, &buf[4], 4); + if (ret < 0) + goto err_kfree; + + dev_info(&dev->client->dev, + "firmware version: LINK %d.%d.%d.%d - OFDM %d.%d.%d.%d\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], + buf[7]); + + /* sleep */ + switch (dev->cfg.tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + /* IT9135 did not like to sleep at that early */ + break; + default: + ret = af9033_wr_reg(dev, 0x80004c, 1); + if (ret < 0) + goto err_kfree; + + ret = af9033_wr_reg(dev, 0x800000, 0); + if (ret < 0) + goto err_kfree; + } + + /* configure internal TS mode */ + switch (dev->cfg.ts_mode) { + case AF9033_TS_MODE_PARALLEL: + dev->ts_mode_parallel = true; + break; + case AF9033_TS_MODE_SERIAL: + dev->ts_mode_serial = true; + break; + case AF9033_TS_MODE_USB: + /* usb mode for AF9035 */ + default: + break; + } + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + *cfg->fe = &dev->fe; + if (cfg->ops) { + cfg->ops->pid_filter = af9033_pid_filter; + cfg->ops->pid_filter_ctrl = af9033_pid_filter_ctrl; + } + i2c_set_clientdata(client, dev); + + dev_info(&dev->client->dev, "Afatech AF9033 successfully attached\n"); + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_remove(struct i2c_client *client) +{ + struct af9033_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&dev->client->dev, "\n"); + + dev->fe.ops.release = NULL; + dev->fe.demodulator_priv = NULL; + kfree(dev); + + return 0; +} + +static const struct i2c_device_id af9033_id_table[] = { + {"af9033", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, af9033_id_table); + +static struct i2c_driver af9033_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "af9033", + }, + .probe = af9033_probe, + .remove = af9033_remove, + .id_table = af9033_id_table, +}; + +module_i2c_driver(af9033_driver); + MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index 539f4db..6ad22b6 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -24,13 +24,12 @@ #include <linux/kconfig.h> +/* + * I2C address (TODO: are these in 8-bit format?) + * 0x38, 0x3a, 0x3c, 0x3e + */ struct af9033_config { /* - * I2C address - */ - u8 i2c_addr; - - /* * clock Hz * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, * 30000000, 36000000, 20480000, 16384000 @@ -75,8 +74,23 @@ struct af9033_config { * input spectrum inversion */ bool spec_inv; -}; + /* + * + */ + bool dyn0_clk; + + /* + * PID filter ops + */ + struct af9033_ops *ops; + + /* + * frontend + * returned by that driver + */ + struct dvb_frontend **fe; +}; struct af9033_ops { int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff); @@ -84,36 +98,4 @@ struct af9033_ops { int onoff); }; - -#if IS_ENABLED(CONFIG_DVB_AF9033) -extern -struct dvb_frontend *af9033_attach(const struct af9033_config *config, - struct i2c_adapter *i2c, - struct af9033_ops *ops); - -#else -static inline -struct dvb_frontend *af9033_attach(const struct af9033_config *config, - struct i2c_adapter *i2c, - struct af9033_ops *ops) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, - int onoff) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -#endif - #endif /* AF9033_H */ diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index ded7b67..c12c92c 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -24,6 +24,7 @@ #include "dvb_frontend.h" #include "af9033.h" +#include <linux/math64.h> struct reg_val { u32 reg; diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c new file mode 100644 index 0000000..4936658 --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe.c @@ -0,0 +1,480 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ + +#include <dvb_frontend.h> + +#include "as102_fe.h" + +struct as102_state { + struct dvb_frontend frontend; + struct as10x_demod_stats demod_stats; + + const struct as102_fe_ops *ops; + void *priv; + uint8_t elna_cfg; + + /* signal strength */ + uint16_t signal_strength; + /* bit error rate */ + uint32_t ber; +}; + +static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg) +{ + uint8_t c; + + switch (arg) { + case FEC_1_2: + c = CODE_RATE_1_2; + break; + case FEC_2_3: + c = CODE_RATE_2_3; + break; + case FEC_3_4: + c = CODE_RATE_3_4; + break; + case FEC_5_6: + c = CODE_RATE_5_6; + break; + case FEC_7_8: + c = CODE_RATE_7_8; + break; + default: + c = CODE_RATE_UNKNOWN; + break; + } + + return c; +} + +static int as102_fe_set_frontend(struct dvb_frontend *fe) +{ + struct as102_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct as10x_tune_args tune_args = { 0 }; + + /* set frequency */ + tune_args.freq = c->frequency / 1000; + + /* fix interleaving_mode */ + tune_args.interleaving_mode = INTLV_NATIVE; + + switch (c->bandwidth_hz) { + case 8000000: + tune_args.bandwidth = BW_8_MHZ; + break; + case 7000000: + tune_args.bandwidth = BW_7_MHZ; + break; + case 6000000: + tune_args.bandwidth = BW_6_MHZ; + break; + default: + tune_args.bandwidth = BW_8_MHZ; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_32: + tune_args.guard_interval = GUARD_INT_1_32; + break; + case GUARD_INTERVAL_1_16: + tune_args.guard_interval = GUARD_INT_1_16; + break; + case GUARD_INTERVAL_1_8: + tune_args.guard_interval = GUARD_INT_1_8; + break; + case GUARD_INTERVAL_1_4: + tune_args.guard_interval = GUARD_INT_1_4; + break; + case GUARD_INTERVAL_AUTO: + default: + tune_args.guard_interval = GUARD_UNKNOWN; + break; + } + + switch (c->modulation) { + case QPSK: + tune_args.modulation = CONST_QPSK; + break; + case QAM_16: + tune_args.modulation = CONST_QAM16; + break; + case QAM_64: + tune_args.modulation = CONST_QAM64; + break; + default: + tune_args.modulation = CONST_UNKNOWN; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + tune_args.transmission_mode = TRANS_MODE_2K; + break; + case TRANSMISSION_MODE_8K: + tune_args.transmission_mode = TRANS_MODE_8K; + break; + default: + tune_args.transmission_mode = TRANS_MODE_UNKNOWN; + } + + switch (c->hierarchy) { + case HIERARCHY_NONE: + tune_args.hierarchy = HIER_NONE; + break; + case HIERARCHY_1: + tune_args.hierarchy = HIER_ALPHA_1; + break; + case HIERARCHY_2: + tune_args.hierarchy = HIER_ALPHA_2; + break; + case HIERARCHY_4: + tune_args.hierarchy = HIER_ALPHA_4; + break; + case HIERARCHY_AUTO: + tune_args.hierarchy = HIER_UNKNOWN; + break; + } + + pr_debug("as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n", + c->frequency, + tune_args.bandwidth, + tune_args.guard_interval); + + /* + * Detect a hierarchy selection + * if HP/LP are both set to FEC_NONE, HP will be selected. + */ + if ((tune_args.hierarchy != HIER_NONE) && + ((c->code_rate_LP == FEC_NONE) || + (c->code_rate_HP == FEC_NONE))) { + + if (c->code_rate_LP == FEC_NONE) { + tune_args.hier_select = HIER_HIGH_PRIORITY; + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_HP); + } + + if (c->code_rate_HP == FEC_NONE) { + tune_args.hier_select = HIER_LOW_PRIORITY; + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_LP); + } + + pr_debug("as102: \thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n", + tune_args.hierarchy, + tune_args.hier_select == HIER_HIGH_PRIORITY ? + "HP" : "LP", + tune_args.hier_select == HIER_HIGH_PRIORITY ? + "HP" : "LP", + tune_args.code_rate); + } else { + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_HP); + } + + /* Set frontend arguments */ + return state->ops->set_tune(state->priv, &tune_args); +} + +static int as102_fe_get_frontend(struct dvb_frontend *fe) +{ + struct as102_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret = 0; + struct as10x_tps tps = { 0 }; + + /* send abilis command: GET_TPS */ + ret = state->ops->get_tps(state->priv, &tps); + if (ret < 0) + return ret; + + /* extract constellation */ + switch (tps.modulation) { + case CONST_QPSK: + c->modulation = QPSK; + break; + case CONST_QAM16: + c->modulation = QAM_16; + break; + case CONST_QAM64: + c->modulation = QAM_64; + break; + } + + /* extract hierarchy */ + switch (tps.hierarchy) { + case HIER_NONE: + c->hierarchy = HIERARCHY_NONE; + break; + case HIER_ALPHA_1: + c->hierarchy = HIERARCHY_1; + break; + case HIER_ALPHA_2: + c->hierarchy = HIERARCHY_2; + break; + case HIER_ALPHA_4: + c->hierarchy = HIERARCHY_4; + break; + } + + /* extract code rate HP */ + switch (tps.code_rate_HP) { + case CODE_RATE_1_2: + c->code_rate_HP = FEC_1_2; + break; + case CODE_RATE_2_3: + c->code_rate_HP = FEC_2_3; + break; + case CODE_RATE_3_4: + c->code_rate_HP = FEC_3_4; + break; + case CODE_RATE_5_6: + c->code_rate_HP = FEC_5_6; + break; + case CODE_RATE_7_8: + c->code_rate_HP = FEC_7_8; + break; + } + + /* extract code rate LP */ + switch (tps.code_rate_LP) { + case CODE_RATE_1_2: + c->code_rate_LP = FEC_1_2; + break; + case CODE_RATE_2_3: + c->code_rate_LP = FEC_2_3; + break; + case CODE_RATE_3_4: + c->code_rate_LP = FEC_3_4; + break; + case CODE_RATE_5_6: + c->code_rate_LP = FEC_5_6; + break; + case CODE_RATE_7_8: + c->code_rate_LP = FEC_7_8; + break; + } + + /* extract guard interval */ + switch (tps.guard_interval) { + case GUARD_INT_1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case GUARD_INT_1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case GUARD_INT_1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case GUARD_INT_1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + /* extract transmission mode */ + switch (tps.transmission_mode) { + case TRANS_MODE_2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case TRANS_MODE_8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + return 0; +} + +static int as102_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) { + + settings->min_delay_ms = 1000; + + return 0; +} + +static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + int ret = 0; + struct as102_state *state = fe->demodulator_priv; + struct as10x_tune_status tstate = { 0 }; + + /* send abilis command: GET_TUNE_STATUS */ + ret = state->ops->get_status(state->priv, &tstate); + if (ret < 0) + return ret; + + state->signal_strength = tstate.signal_strength; + state->ber = tstate.BER; + + switch (tstate.tune_state) { + case TUNE_STATUS_SIGNAL_DVB_OK: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + break; + case TUNE_STATUS_STREAM_DETECTED: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | + FE_HAS_VITERBI; + break; + case TUNE_STATUS_STREAM_TUNED: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | + FE_HAS_LOCK | FE_HAS_VITERBI; + break; + default: + *status = TUNE_STATUS_NOT_TUNED; + } + + pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n", + tstate.tune_state, tstate.signal_strength, + tstate.PER, tstate.BER); + + if (!(*status & FE_HAS_LOCK)) { + memset(&state->demod_stats, 0, sizeof(state->demod_stats)); + return 0; + } + + ret = state->ops->get_stats(state->priv, &state->demod_stats); + if (ret < 0) + memset(&state->demod_stats, 0, sizeof(state->demod_stats)); + + return ret; +} + +/* + * Note: + * - in AS102 SNR=MER + * - the SNR will be returned in linear terms, i.e. not in dB + * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB + * - the accuracy is >2dB for SNR values outside this range + */ +static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct as102_state *state = fe->demodulator_priv; + + *snr = state->demod_stats.mer; + + return 0; +} + +static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct as102_state *state = fe->demodulator_priv; + + *ber = state->ber; + + return 0; +} + +static int as102_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct as102_state *state = fe->demodulator_priv; + + *strength = (((0xffff * 400) * state->signal_strength + 41000) * 2); + + return 0; +} + +static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct as102_state *state = fe->demodulator_priv; + + if (state->demod_stats.has_started) + *ucblocks = state->demod_stats.bad_frame_count; + else + *ucblocks = 0; + + return 0; +} + +static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct as102_state *state = fe->demodulator_priv; + + return state->ops->stream_ctrl(state->priv, acquire, + state->elna_cfg); +} + +static void as102_fe_release(struct dvb_frontend *fe) +{ + struct as102_state *state = fe->demodulator_priv; + + kfree(state); +} + + +static struct dvb_frontend_ops as102_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Abilis AS102 DVB-T", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_INVERSION_AUTO + | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 + | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO + | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK + | FE_CAN_QAM_AUTO + | FE_CAN_TRANSMISSION_MODE_AUTO + | FE_CAN_GUARD_INTERVAL_AUTO + | FE_CAN_HIERARCHY_AUTO + | FE_CAN_RECOVER + | FE_CAN_MUTE_TS + }, + + .set_frontend = as102_fe_set_frontend, + .get_frontend = as102_fe_get_frontend, + .get_tune_settings = as102_fe_get_tune_settings, + + .read_status = as102_fe_read_status, + .read_snr = as102_fe_read_snr, + .read_ber = as102_fe_read_ber, + .read_signal_strength = as102_fe_read_signal_strength, + .read_ucblocks = as102_fe_read_ucblocks, + .ts_bus_ctrl = as102_fe_ts_bus_ctrl, + .release = as102_fe_release, +}; + +struct dvb_frontend *as102_attach(const char *name, + const struct as102_fe_ops *ops, + void *priv, + uint8_t elna_cfg) +{ + struct as102_state *state; + struct dvb_frontend *fe; + + state = kzalloc(sizeof(struct as102_state), GFP_KERNEL); + if (state == NULL) { + pr_err("%s: unable to allocate memory for state\n", __func__); + return NULL; + } + fe = &state->frontend; + fe->demodulator_priv = state; + state->ops = ops; + state->priv = priv; + state->elna_cfg = elna_cfg; + + /* init frontend callback ops */ + memcpy(&fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops)); + strncpy(fe->ops.info.name, name, sizeof(fe->ops.info.name)); + + return fe; + +} +EXPORT_SYMBOL_GPL(as102_attach); + +MODULE_DESCRIPTION("as102-fe"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h new file mode 100644 index 0000000..a7c9143 --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe.h @@ -0,0 +1,29 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2014 Mauro Carvalho Chehab <m.chehab@samsung.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, 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. + */ + +#include "as102_fe_types.h" + +struct as102_fe_ops { + int (*set_tune)(void *priv, struct as10x_tune_args *tune_args); + int (*get_tps)(void *priv, struct as10x_tps *tps); + int (*get_status)(void *priv, struct as10x_tune_status *tstate); + int (*get_stats)(void *priv, struct as10x_demod_stats *demod_stats); + int (*stream_ctrl)(void *priv, int acquire, uint32_t elna_cfg); +}; + +struct dvb_frontend *as102_attach(const char *name, + const struct as102_fe_ops *ops, + void *priv, + uint8_t elna_cfg); diff --git a/drivers/media/dvb-frontends/as102_fe_types.h b/drivers/media/dvb-frontends/as102_fe_types.h new file mode 100644 index 0000000..80a5398 --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe_types.h @@ -0,0 +1,188 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ +#ifndef _AS10X_TYPES_H_ +#define _AS10X_TYPES_H_ + +/*********************************/ +/* MACRO DEFINITIONS */ +/*********************************/ + +/* bandwidth constant values */ +#define BW_5_MHZ 0x00 +#define BW_6_MHZ 0x01 +#define BW_7_MHZ 0x02 +#define BW_8_MHZ 0x03 + +/* hierarchy priority selection values */ +#define HIER_NO_PRIORITY 0x00 +#define HIER_LOW_PRIORITY 0x01 +#define HIER_HIGH_PRIORITY 0x02 + +/* constellation available values */ +#define CONST_QPSK 0x00 +#define CONST_QAM16 0x01 +#define CONST_QAM64 0x02 +#define CONST_UNKNOWN 0xFF + +/* hierarchy available values */ +#define HIER_NONE 0x00 +#define HIER_ALPHA_1 0x01 +#define HIER_ALPHA_2 0x02 +#define HIER_ALPHA_4 0x03 +#define HIER_UNKNOWN 0xFF + +/* interleaving available values */ +#define INTLV_NATIVE 0x00 +#define INTLV_IN_DEPTH 0x01 +#define INTLV_UNKNOWN 0xFF + +/* code rate available values */ +#define CODE_RATE_1_2 0x00 +#define CODE_RATE_2_3 0x01 +#define CODE_RATE_3_4 0x02 +#define CODE_RATE_5_6 0x03 +#define CODE_RATE_7_8 0x04 +#define CODE_RATE_UNKNOWN 0xFF + +/* guard interval available values */ +#define GUARD_INT_1_32 0x00 +#define GUARD_INT_1_16 0x01 +#define GUARD_INT_1_8 0x02 +#define GUARD_INT_1_4 0x03 +#define GUARD_UNKNOWN 0xFF + +/* transmission mode available values */ +#define TRANS_MODE_2K 0x00 +#define TRANS_MODE_8K 0x01 +#define TRANS_MODE_4K 0x02 +#define TRANS_MODE_UNKNOWN 0xFF + +/* DVBH signalling available values */ +#define TIMESLICING_PRESENT 0x01 +#define MPE_FEC_PRESENT 0x02 + +/* tune state available */ +#define TUNE_STATUS_NOT_TUNED 0x00 +#define TUNE_STATUS_IDLE 0x01 +#define TUNE_STATUS_LOCKING 0x02 +#define TUNE_STATUS_SIGNAL_DVB_OK 0x03 +#define TUNE_STATUS_STREAM_DETECTED 0x04 +#define TUNE_STATUS_STREAM_TUNED 0x05 +#define TUNE_STATUS_ERROR 0xFF + +/* available TS FID filter types */ +#define TS_PID_TYPE_TS 0 +#define TS_PID_TYPE_PSI_SI 1 +#define TS_PID_TYPE_MPE 2 + +/* number of echos available */ +#define MAX_ECHOS 15 + +/* Context types */ +#define CONTEXT_LNA 1010 +#define CONTEXT_ELNA_HYSTERESIS 4003 +#define CONTEXT_ELNA_GAIN 4004 +#define CONTEXT_MER_THRESHOLD 5005 +#define CONTEXT_MER_OFFSET 5006 +#define CONTEXT_IR_STATE 7000 +#define CONTEXT_TSOUT_MSB_FIRST 7004 +#define CONTEXT_TSOUT_FALLING_EDGE 7005 + +/* Configuration modes */ +#define CFG_MODE_ON 0 +#define CFG_MODE_OFF 1 +#define CFG_MODE_AUTO 2 + +struct as10x_tps { + uint8_t modulation; + uint8_t hierarchy; + uint8_t interleaving_mode; + uint8_t code_rate_HP; + uint8_t code_rate_LP; + uint8_t guard_interval; + uint8_t transmission_mode; + uint8_t DVBH_mask_HP; + uint8_t DVBH_mask_LP; + uint16_t cell_ID; +} __packed; + +struct as10x_tune_args { + /* frequency */ + uint32_t freq; + /* bandwidth */ + uint8_t bandwidth; + /* hierarchy selection */ + uint8_t hier_select; + /* constellation */ + uint8_t modulation; + /* hierarchy */ + uint8_t hierarchy; + /* interleaving mode */ + uint8_t interleaving_mode; + /* code rate */ + uint8_t code_rate; + /* guard interval */ + uint8_t guard_interval; + /* transmission mode */ + uint8_t transmission_mode; +} __packed; + +struct as10x_tune_status { + /* tune status */ + uint8_t tune_state; + /* signal strength */ + int16_t signal_strength; + /* packet error rate 10^-4 */ + uint16_t PER; + /* bit error rate 10^-4 */ + uint16_t BER; +} __packed; + +struct as10x_demod_stats { + /* frame counter */ + uint32_t frame_count; + /* Bad frame counter */ + uint32_t bad_frame_count; + /* Number of wrong bytes fixed by Reed-Solomon */ + uint32_t bytes_fixed_by_rs; + /* Averaged MER */ + uint16_t mer; + /* statistics calculation state indicator (started or not) */ + uint8_t has_started; +} __packed; + +struct as10x_ts_filter { + uint16_t pid; /* valid PID value 0x00 : 0x2000 */ + uint8_t type; /* Red TS_PID_TYPE_<N> values */ + uint8_t idx; /* index in filtering table */ +} __packed; + +struct as10x_register_value { + uint8_t mode; + union { + uint8_t value8; /* 8 bit value */ + uint16_t value16; /* 16 bit value */ + uint32_t value32; /* 32 bit value */ + } __packed u; +} __packed; + +struct as10x_register_addr { + /* register addr */ + uint32_t addr; + /* register mode access */ + uint8_t mode; +}; + +#endif diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 39a29dd..638c7aa 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -639,12 +639,12 @@ static int bcm3510_download_firmware(struct dvb_frontend* fe) err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); return ret; } - deb_info("got firmware: %zd\n",fw->size); + deb_info("got firmware: %zu\n", fw->size); b = fw->data; for (i = 0; i < fw->size;) { - addr = le16_to_cpu( *( (u16 *)&b[i] ) ); - len = le16_to_cpu( *( (u16 *)&b[i+2] ) ); + addr = le16_to_cpu(*((__le16 *)&b[i])); + len = le16_to_cpu(*((__le16 *)&b[i+2])); deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { err("firmware download failed: %d\n",ret); diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c index 0f4657e..149fdca 100644 --- a/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -65,7 +65,7 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) } priv->delivery_system = SYS_DVBC_ANNEX_A; - priv->ber_running = 0; /* tune stops BER counter */ + priv->ber_running = false; /* tune stops BER counter */ /* program IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) { @@ -168,7 +168,7 @@ int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber) start_ber = 1; } } else { - priv->ber_running = 1; + priv->ber_running = true; start_ber = 1; } diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 03930d5..cd2af3e 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -564,10 +564,10 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) /* check if we have a valid signal */ if (status & FE_HAS_LOCK) { - priv->last_tune_failed = 0; + priv->last_tune_failed = false; return DVBFE_ALGO_SEARCH_SUCCESS; } else { - priv->last_tune_failed = 1; + priv->last_tune_failed = true; return DVBFE_ALGO_SEARCH_AGAIN; } diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index 9b5a45b..51401d0 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -89,7 +89,7 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) } priv->delivery_system = SYS_DVBT; - priv->ber_running = 0; /* tune stops BER counter */ + priv->ber_running = false; /* tune stops BER counter */ /* program IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) { @@ -272,7 +272,7 @@ int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber) start_ber = 1; } } else { - priv->ber_running = 1; + priv->ber_running = true; start_ber = 1; } diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 661760d..589134e 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2559,7 +2559,7 @@ static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode) dib7000p_write_word(state, 1288, reg_1288); } -int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) +static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) { struct dib7000p_state *state = fe->demodulator_priv; u16 reg_1287; diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 7ca7a21..5ec221f 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2174,7 +2174,7 @@ int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr, u32 addr, u32 *data, u32 flags) { - u8 buf[sizeof(*data)]; + u8 buf[sizeof(*data)] = { 0 }; int rc = -EIO; u32 word = 0; @@ -4193,7 +4193,7 @@ int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr, u32 addr, u16 *data, u32 flags) { - u8 buf[2]; + u8 buf[2] = { 0 }; int rc = -EIO; u16 word = 0; @@ -10667,7 +10667,7 @@ ctrl_sig_quality(struct drx_demod_instance *demod, enum drx_standard standard = ext_attr->standard; int rc; u32 ber, cnt, err, pkt; - u16 mer, strength; + u16 mer, strength = 0; rc = get_sig_strength(demod, &strength); if (rc < 0) { @@ -11602,7 +11602,7 @@ static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words) u32 carry = 0; while (i < nr_words) { - crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data)); + crc_word |= (u32)be16_to_cpu(*(__be16 *)(block_data)); for (j = 0; j < 16; j++) { crc_word <<= 1; if (carry != 0) @@ -11629,7 +11629,7 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, int i; unsigned count = 2 * sizeof(u16); u32 mc_dev_type, mc_version, mc_base_version; - u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16))); + u16 mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data + sizeof(u16))); /* * Scan microcode blocks first for version info @@ -11647,13 +11647,13 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, goto eof; /* Process block header */ - block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count)); + block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data + count)); count += sizeof(u32); - block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count)); + block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data + count)); count += sizeof(u16); - block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count)); + block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data + count)); count += sizeof(u16); - block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count)); + block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data + count)); count += sizeof(u16); pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", @@ -11667,7 +11667,7 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, if (block_hdr.addr + sizeof(u16) > size) goto eof; - auxtype = be16_to_cpu(*(u32 *)(auxblk)); + auxtype = be16_to_cpu(*(__be16 *)(auxblk)); /* Aux block. Check type */ if (DRX_ISMCVERTYPE(auxtype)) { @@ -11675,11 +11675,11 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, goto eof; auxblk += sizeof(u16); - mc_dev_type = be32_to_cpu(*(u32 *)(auxblk)); + mc_dev_type = be32_to_cpu(*(__be32 *)(auxblk)); auxblk += sizeof(u32); - mc_version = be32_to_cpu(*(u32 *)(auxblk)); + mc_version = be32_to_cpu(*(__be32 *)(auxblk)); auxblk += sizeof(u32); - mc_base_version = be32_to_cpu(*(u32 *)(auxblk)); + mc_base_version = be32_to_cpu(*(__be32 *)(auxblk)); DRX_ATTR_MCRECORD(demod).aux_type = auxtype; DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type; @@ -11765,9 +11765,9 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, mc_data = (void *)mc_data_init; /* Check data */ - mc_magic_word = be16_to_cpu(*(u32 *)(mc_data)); + mc_magic_word = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); - mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data)); + mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) { @@ -11791,13 +11791,13 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, u16 mc_block_nr_bytes = 0; /* Process block header */ - block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data)); + block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data)); mc_data += sizeof(u32); - block_hdr.size = be16_to_cpu(*(u32 *)(mc_data)); + block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); - block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data)); + block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); - block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data)); + block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index ae2276d..687e893 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2628,10 +2628,11 @@ static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size) break; /* Apply I2c address patch to B1 */ - if (!state->type_A && state->m_HiI2cPatch != NULL) + if (!state->type_A && state->m_HiI2cPatch != NULL) { status = WriteTable(state, state->m_HiI2cPatch); if (status < 0) break; + } if (state->type_A) { /* HI firmware patch for UIO readout, @@ -2830,14 +2831,8 @@ static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status) static int drxd_init(struct dvb_frontend *fe) { struct drxd_state *state = fe->demodulator_priv; - int err = 0; -/* if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */ return DRXD_init(state, NULL, 0); - - err = DRXD_init(state, state->fw->data, state->fw->size); - release_firmware(state->fw); - return err; } static int drxd_config_i2c(struct dvb_frontend *fe, int onoff) diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index cce94a7..6721951 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1028,7 +1028,7 @@ static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result) ((state->m_hi_cfg_ctrl) & SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) == SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ); - if (powerdown_cmd == false) { + if (!powerdown_cmd) { /* Wait until command rdy */ u32 retry_count = 0; u16 wait_cmd; @@ -1129,7 +1129,7 @@ static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable) if (status < 0) goto error; - if (mpeg_enable == false) { + if (!mpeg_enable) { /* Set MPEG TS pads to inputmode */ status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000); if (status < 0) @@ -1190,7 +1190,7 @@ static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable) if (status < 0) goto error; - if (state->m_enable_parallel == true) { + if (state->m_enable_parallel) { /* parallel -> enable MD1 to MD7 */ status = write16(state, SIO_PDR_MD1_CFG__A, sio_pdr_mdx_cfg); @@ -1392,7 +1392,7 @@ static int dvbt_enable_ofdm_token_ring(struct drxk_state *state, bool enable) dprintk(1, "\n"); - if (enable == false) { + if (!enable) { desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF; desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN; } @@ -2012,7 +2012,7 @@ static int mpegts_dto_setup(struct drxk_state *state, goto error; fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M); fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M); - if (state->m_insert_rs_byte == true) { + if (state->m_insert_rs_byte) { /* enable parity symbol forward */ fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M; /* MVAL disable during parity bytes */ @@ -2023,7 +2023,7 @@ static int mpegts_dto_setup(struct drxk_state *state, /* Check serial or parallel output */ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); - if (state->m_enable_parallel == false) { + if (!state->m_enable_parallel) { /* MPEG data output is serial -> set ipr_mode[0] */ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M; } @@ -2136,19 +2136,19 @@ static int mpegts_configure_polarity(struct drxk_state *state) /* Control selective inversion of output bits */ fec_oc_reg_ipr_invert &= (~(invert_data_mask)); - if (state->m_invert_data == true) + if (state->m_invert_data) fec_oc_reg_ipr_invert |= invert_data_mask; fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M)); - if (state->m_invert_err == true) + if (state->m_invert_err) fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M; fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M)); - if (state->m_invert_str == true) + if (state->m_invert_str) fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M; fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M)); - if (state->m_invert_val == true) + if (state->m_invert_val) fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M; fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M)); - if (state->m_invert_clk == true) + if (state->m_invert_clk) fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M; return write16(state, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert); @@ -2220,12 +2220,13 @@ static int set_agc_rf(struct drxk_state *state, } /* Set TOP, only if IF-AGC is in AUTO mode */ - if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO) + if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO) { status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, p_agc_cfg->top); if (status < 0) goto error; + } /* Cut-Off current */ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, @@ -3352,7 +3353,7 @@ static int dvbt_ctrl_set_inc_enable(struct drxk_state *state, bool *enabled) int status; dprintk(1, "\n"); - if (*enabled == true) + if (*enabled) status = write16(state, IQM_CF_BYPASSDET__A, 0); else status = write16(state, IQM_CF_BYPASSDET__A, 1); @@ -3368,7 +3369,7 @@ static int dvbt_ctrl_set_fr_enable(struct drxk_state *state, bool *enabled) int status; dprintk(1, "\n"); - if (*enabled == true) { + if (*enabled) { /* write mask to 1 */ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, DEFAULT_FR_THRES_8K); @@ -6794,11 +6795,11 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->enable_merr_cfg = config->enable_merr_cfg; if (config->dynamic_clk) { - state->m_dvbt_static_clk = 0; - state->m_dvbc_static_clk = 0; + state->m_dvbt_static_clk = false; + state->m_dvbc_static_clk = false; } else { - state->m_dvbt_static_clk = 1; - state->m_dvbc_static_clk = 1; + state->m_dvbt_static_clk = true; + state->m_dvbc_static_clk = true; } diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index dfe0c2f..81657e9 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -159,6 +159,7 @@ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv, { int ret, i, j; u8 buf[83]; + dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); if (tab_len > 83) { @@ -247,8 +248,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8 u8tmp, u8tmp1, u8tmp2; u8 buf[2]; u16 u16tmp, divide_ratio; - u32 tuner_frequency, target_mclk, ts_clk; + u32 tuner_frequency, target_mclk; s32 s32tmp; + dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", __func__, c->delivery_system, @@ -316,9 +318,6 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) target_mclk = 144000; break; case M88DS3103_TS_PARALLEL: - case M88DS3103_TS_PARALLEL_12: - case M88DS3103_TS_PARALLEL_16: - case M88DS3103_TS_PARALLEL_19_2: case M88DS3103_TS_CI: if (c->symbol_rate < 18000000) target_mclk = 96000; @@ -352,33 +351,17 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) switch (priv->cfg->ts_mode) { case M88DS3103_TS_SERIAL: u8tmp1 = 0x00; - ts_clk = 0; - u8tmp = 0x46; + u8tmp = 0x06; break; case M88DS3103_TS_SERIAL_D7: u8tmp1 = 0x20; - ts_clk = 0; - u8tmp = 0x46; + u8tmp = 0x06; break; case M88DS3103_TS_PARALLEL: - ts_clk = 24000; - u8tmp = 0x42; - break; - case M88DS3103_TS_PARALLEL_12: - ts_clk = 12000; - u8tmp = 0x42; - break; - case M88DS3103_TS_PARALLEL_16: - ts_clk = 16000; - u8tmp = 0x42; - break; - case M88DS3103_TS_PARALLEL_19_2: - ts_clk = 19200; - u8tmp = 0x42; + u8tmp = 0x02; break; case M88DS3103_TS_CI: - ts_clk = 6000; - u8tmp = 0x43; + u8tmp = 0x03; break; default: dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); @@ -386,6 +369,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; } + if (priv->cfg->ts_clk_pol) + u8tmp |= 0x40; + /* TS mode */ ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp); if (ret) @@ -399,8 +385,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; } - if (ts_clk) { - divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk); + if (priv->cfg->ts_clk) { + divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk); u8tmp1 = divide_ratio / 2; u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); } else { @@ -411,7 +397,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) dev_dbg(&priv->i2c->dev, "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n", - __func__, target_mclk, ts_clk, divide_ratio); + __func__, target_mclk, priv->cfg->ts_clk, divide_ratio); u8tmp1--; u8tmp2--; @@ -536,6 +522,7 @@ static int m88ds3103_init(struct dvb_frontend *fe) const struct firmware *fw = NULL; u8 *fw_file = M88DS3103_FIRMWARE; u8 u8tmp; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); /* set cold state by default */ @@ -648,6 +635,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) { struct m88ds3103_priv *priv = fe->demodulator_priv; int ret; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); priv->delivery_system = SYS_UNDEFINED; @@ -682,6 +670,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[3]; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { @@ -857,6 +846,7 @@ static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) u8 buf[3]; u16 noise, signal; u32 noise_tot, signal_tot; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); /* reports SNR in resolution of 0.1 dB */ @@ -933,6 +923,7 @@ static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber) int ret; unsigned int utmp; u8 buf[3], u8tmp; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); switch (c->delivery_system) { @@ -1013,6 +1004,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, struct m88ds3103_priv *priv = fe->demodulator_priv; int ret; u8 u8tmp, tone, reg_a1_mask; + dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__, fe_sec_tone_mode); @@ -1053,12 +1045,64 @@ err: return ret; } +static int m88ds3103_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t fe_sec_voltage) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret; + u8 u8tmp; + bool voltage_sel, voltage_dis; + + dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, + fe_sec_voltage); + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_18: + voltage_sel = true; + voltage_dis = false; + break; + case SEC_VOLTAGE_13: + voltage_sel = false; + voltage_dis = false; + break; + case SEC_VOLTAGE_OFF: + voltage_sel = false; + voltage_dis = true; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", + __func__); + ret = -EINVAL; + goto err; + } + + /* output pin polarity */ + voltage_sel ^= priv->cfg->lnb_hv_pol; + voltage_dis ^= priv->cfg->lnb_en_pol; + + u8tmp = voltage_dis << 1 | voltage_sel << 0; + ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0x03); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *diseqc_cmd) { struct m88ds3103_priv *priv = fe->demodulator_priv; int ret, i; u8 u8tmp; + dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__, diseqc_cmd->msg_len, diseqc_cmd->msg); @@ -1130,6 +1174,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, struct m88ds3103_priv *priv = fe->demodulator_priv; int ret, i; u8 u8tmp, burst; + dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, fe_sec_mini_cmd); @@ -1202,6 +1247,7 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, static void m88ds3103_release(struct dvb_frontend *fe) { struct m88ds3103_priv *priv = fe->demodulator_priv; + i2c_del_mux_adapter(priv->i2c_adapter); kfree(priv); } @@ -1370,6 +1416,7 @@ static struct dvb_frontend_ops m88ds3103_ops = { .diseqc_send_burst = m88ds3103_diseqc_send_burst, .set_tone = m88ds3103_set_tone, + .set_voltage = m88ds3103_set_voltage, }; MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h index bbb7e3a..9b3b496 100644 --- a/drivers/media/dvb-frontends/m88ds3103.h +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -47,14 +47,23 @@ struct m88ds3103_config { */ #define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */ #define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */ -#define M88DS3103_TS_PARALLEL 2 /* 24 MHz, normal */ -#define M88DS3103_TS_PARALLEL_12 3 /* 12 MHz */ -#define M88DS3103_TS_PARALLEL_16 4 /* 16 MHz */ -#define M88DS3103_TS_PARALLEL_19_2 5 /* 19.2 MHz */ -#define M88DS3103_TS_CI 6 /* 6 MHz */ +#define M88DS3103_TS_PARALLEL 2 /* TS Parallel mode */ +#define M88DS3103_TS_CI 3 /* TS CI Mode */ u8 ts_mode; /* + * TS clk in KHz + * Default: 0. + */ + u32 ts_clk; + + /* + * TS clk polarity. + * Default: 0. 1-active at falling edge; 0-active at rising edge. + */ + u8 ts_clk_pol:1; + + /* * spectrum inversion * Default: 0 */ @@ -86,6 +95,22 @@ struct m88ds3103_config { * Default: none, must set */ u8 agc; + + /* + * LNB H/V pin polarity + * Default: 0. + * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18. + * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13. + */ + u8 lnb_hv_pol:1; + + /* + * LNB enable pin polarity + * Default: 0. + * 1: pin high to enable, pin low to disable. + * 0: pin high to disable, pin low to enable. + */ + u8 lnb_en_pol:1; }; /* diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 9ae40ab..3ddea44 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -28,7 +28,7 @@ #include "mb86a16.h" #include "mb86a16_priv.h" -unsigned int verbose = 5; +static unsigned int verbose = 5; module_param(verbose, int, 0644); #define ABS(x) ((x) < 0 ? (-x) : (x)) @@ -115,9 +115,11 @@ static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val) }; ret = i2c_transfer(state->i2c_adap, msg, 2); if (ret != 2) { - dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=0x%i)", + dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=%i)", reg, ret); + if (ret < 0) + return ret; return -EREMOTEIO; } *val = b1[0]; diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index b931179..e6f165a 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -33,7 +33,7 @@ enum mb86a20s_bandwidth { MB86A20S_3SEG = 3, }; -u8 mb86a20s_subchannel[] = { +static u8 mb86a20s_subchannel[] = { 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x00, 0x10, 0x20, }; @@ -1228,7 +1228,7 @@ struct linear_segments { * All tables below return a dB/1000 measurement */ -static struct linear_segments cnr_to_db_table[] = { +static const struct linear_segments cnr_to_db_table[] = { { 19648, 0}, { 18187, 1000}, { 16534, 2000}, @@ -1262,7 +1262,7 @@ static struct linear_segments cnr_to_db_table[] = { { 788, 30000}, }; -static struct linear_segments cnr_64qam_table[] = { +static const struct linear_segments cnr_64qam_table[] = { { 3922688, 0}, { 3920384, 1000}, { 3902720, 2000}, @@ -1296,7 +1296,7 @@ static struct linear_segments cnr_64qam_table[] = { { 388864, 30000}, }; -static struct linear_segments cnr_16qam_table[] = { +static const struct linear_segments cnr_16qam_table[] = { { 5314816, 0}, { 5219072, 1000}, { 5118720, 2000}, @@ -1330,7 +1330,7 @@ static struct linear_segments cnr_16qam_table[] = { { 95744, 30000}, }; -struct linear_segments cnr_qpsk_table[] = { +static const struct linear_segments cnr_qpsk_table[] = { { 2834176, 0}, { 2683648, 1000}, { 2536960, 2000}, @@ -1364,7 +1364,7 @@ struct linear_segments cnr_qpsk_table[] = { { 11520, 30000}, }; -static u32 interpolate_value(u32 value, struct linear_segments *segments, +static u32 interpolate_value(u32 value, const struct linear_segments *segments, unsigned len) { u64 tmp64; @@ -1448,7 +1448,7 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; u32 mer, cnr; int rc, val, layer; - struct linear_segments *segs; + const struct linear_segments *segs; unsigned segs_len; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index a74ac0d..2163490 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -103,7 +103,7 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, if (1 + count > sizeof(buf)) { printk(KERN_WARNING - "mt312: write: len=%zd is too big!\n", count); + "mt312: write: len=%zu is too big!\n", count); return -EINVAL; } diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index 10cfc05..873ea1d 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -111,7 +111,7 @@ static int or51211_load_firmware (struct dvb_frontend* fe, u8 tudata[585]; int i; - dprintk("Firmware is %zd bytes\n",fw->size); + dprintk("Firmware is %zu bytes\n", fw->size); /* Get eprom data */ tudata[0] = 17; diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index fdbed35..eb737cf 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -936,7 +936,7 @@ static void rtl2832_i2c_gate_work(struct work_struct *work) if (ret != 1) goto err; - priv->i2c_gate_state = 0; + priv->i2c_gate_state = false; return; err: diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 023e0f4..7bf98cf 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -329,7 +329,7 @@ static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg, static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( struct rtl2832_sdr_state *s) { - unsigned long flags = 0; + unsigned long flags; struct rtl2832_sdr_frame_buf *buf = NULL; spin_lock_irqsave(&s->queued_bufs_lock, flags); @@ -365,17 +365,19 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, dst_len = 0; } - /* calculate samping rate and output it in 10 seconds intervals */ + /* calculate sample rate and output it in 10 seconds intervals */ if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL + #define MSECS 10000UL + unsigned int msecs = jiffies_to_msecs(jiffies - + s->jiffies_next + msecs_to_jiffies(MSECS)); unsigned int samples = s->sample - s->sample_measured; s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample_measured = s->sample; dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); + "slen=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); } /* total number of I+Q pairs */ @@ -394,8 +396,8 @@ static void rtl2832_sdr_urb_complete(struct urb *urb) struct rtl2832_sdr_frame_buf *fbuf; dev_dbg_ratelimited(&s->udev->dev, - "%s: status=%d length=%d/%d errors=%d\n", - __func__, urb->status, urb->actual_length, + "status=%d length=%d/%d errors=%d\n", + urb->status, urb->actual_length, urb->transfer_buffer_length, urb->error_count); switch (urb->status) { @@ -443,7 +445,7 @@ static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s) int i; for (i = s->urbs_submitted - 1; i >= 0; i--) { - dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); + dev_dbg(&s->udev->dev, "kill urb=%d\n", i); /* stop the URB */ usb_kill_urb(s->urb_list[i]); } @@ -457,7 +459,7 @@ static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s) int i, ret; for (i = 0; i < s->urbs_initialized; i++) { - dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); + dev_dbg(&s->udev->dev, "submit urb=%d\n", i); ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); if (ret) { dev_err(&s->udev->dev, @@ -477,8 +479,7 @@ static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s) if (s->flags & USB_STATE_URB_BUF) { while (s->buf_num) { s->buf_num--; - dev_dbg(&s->udev->dev, "%s: free buf=%d\n", - __func__, s->buf_num); + dev_dbg(&s->udev->dev, "free buf=%d\n", s->buf_num); usb_free_coherent(s->udev, s->buf_size, s->buf_list[s->buf_num], s->dma_addr[s->buf_num]); @@ -494,24 +495,22 @@ static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s) s->buf_num = 0; s->buf_size = BULK_BUFFER_SIZE; - dev_dbg(&s->udev->dev, - "%s: all in all I will use %u bytes for streaming\n", - __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); + dev_dbg(&s->udev->dev, "all in all I will use %u bytes for streaming\n", + MAX_BULK_BUFS * BULK_BUFFER_SIZE); for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, BULK_BUFFER_SIZE, GFP_ATOMIC, &s->dma_addr[s->buf_num]); if (!s->buf_list[s->buf_num]) { - dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", - __func__, s->buf_num); + dev_dbg(&s->udev->dev, "alloc buf=%d failed\n", + s->buf_num); rtl2832_sdr_free_stream_bufs(s); return -ENOMEM; } - dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", - __func__, s->buf_num, - s->buf_list[s->buf_num], + dev_dbg(&s->udev->dev, "alloc buf=%d %p (dma %llu)\n", + s->buf_num, s->buf_list[s->buf_num], (long long)s->dma_addr[s->buf_num]); s->flags |= USB_STATE_URB_BUF; } @@ -527,8 +526,7 @@ static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s) for (i = s->urbs_initialized - 1; i >= 0; i--) { if (s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: free urb=%d\n", - __func__, i); + dev_dbg(&s->udev->dev, "free urb=%d\n", i); /* free the URBs */ usb_free_urb(s->urb_list[i]); } @@ -544,10 +542,10 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { - dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); + dev_dbg(&s->udev->dev, "alloc urb=%d\n", i); s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); if (!s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: failed\n", __func__); + dev_dbg(&s->udev->dev, "failed\n"); for (j = 0; j < i; j++) usb_free_urb(s->urb_list[j]); return -ENOMEM; @@ -570,9 +568,9 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) /* Must be called with vb_queue_lock hold */ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) { - unsigned long flags = 0; + unsigned long flags; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); spin_lock_irqsave(&s->queued_bufs_lock, flags); while (!list_empty(&s->queued_bufs)) { @@ -591,7 +589,7 @@ static void rtl2832_sdr_release_sec(struct dvb_frontend *fe) { struct rtl2832_sdr_state *s = fe->sec_priv; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); mutex_lock(&s->vb_queue_lock); mutex_lock(&s->v4l2_lock); @@ -613,7 +611,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); @@ -631,15 +629,15 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + dev_dbg(&s->udev->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ if (vq->num_buffers + *nbuffers < 8) *nbuffers = 8 - vq->num_buffers; *nplanes = 1; sizes[0] = PAGE_ALIGN(s->buffersize); - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); + dev_dbg(&s->udev->dev, "nbuffers=%d sizes[0]=%d\n", + *nbuffers, sizes[0]); return 0; } @@ -659,7 +657,7 @@ static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); struct rtl2832_sdr_frame_buf *buf = container_of(vb, struct rtl2832_sdr_frame_buf, vb); - unsigned long flags = 0; + unsigned long flags; /* Check the device has not disconnected between prep and queuing */ if (!s->udev) { @@ -681,7 +679,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) u64 u64tmp; u32 u32tmp; - dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc); + dev_dbg(&s->udev->dev, "f_adc=%u\n", s->f_adc); if (!test_bit(POWER_ON, &s->flags)) return 0; @@ -715,8 +713,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) u64tmp = -u64tmp; u32tmp = u64tmp & 0x3fffff; - dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n", - __func__, f_if, u32tmp); + dev_dbg(&s->udev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp); buf[0] = (u32tmp >> 16) & 0xff; buf[1] = (u32tmp >> 8) & 0xff; @@ -903,7 +900,7 @@ static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s) { int ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); /* PID filter */ ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1); @@ -964,8 +961,8 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) c->frequency = s->f_tuner; c->delivery_system = SYS_DVBT; - dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n", - __func__, c->frequency, c->bandwidth_hz); + dev_dbg(&s->udev->dev, "frequency=%u bandwidth=%d\n", + c->frequency, c->bandwidth_hz); if (!test_bit(POWER_ON, &s->flags)) return 0; @@ -980,7 +977,7 @@ static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s) { struct dvb_frontend *fe = s->fe; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); if (fe->ops.tuner_ops.init) fe->ops.tuner_ops.init(fe); @@ -992,7 +989,7 @@ static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s) { struct dvb_frontend *fe = s->fe; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); if (fe->ops.tuner_ops.sleep) fe->ops.tuner_ops.sleep(fe); @@ -1005,7 +1002,7 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); int ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); if (!s->udev) return -ENODEV; @@ -1054,7 +1051,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); mutex_lock(&s->v4l2_lock); @@ -1088,8 +1085,7 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n", - __func__, v->index, v->type); + dev_dbg(&s->udev->dev, "index=%d type=%d\n", v->index, v->type); if (v->index == 0) { strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name)); @@ -1115,7 +1111,7 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); if (v->index > 1) return -EINVAL; @@ -1127,8 +1123,8 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); + dev_dbg(&s->udev->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->tuner == 0) { if (band->index >= ARRAY_SIZE(bands_adc)) @@ -1153,8 +1149,8 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv, struct rtl2832_sdr_state *s = video_drvdata(file); int ret = 0; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); + dev_dbg(&s->udev->dev, "tuner=%d type=%d\n", + f->tuner, f->type); if (f->tuner == 0) { f->frequency = s->f_adc; @@ -1175,8 +1171,8 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, struct rtl2832_sdr_state *s = video_drvdata(file); int ret, band; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); + dev_dbg(&s->udev->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); /* ADC band midpoints */ #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) @@ -1194,15 +1190,13 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, bands_adc[band].rangelow, bands_adc[band].rangehigh); - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); + dev_dbg(&s->udev->dev, "ADC frequency=%u Hz\n", s->f_adc); ret = rtl2832_sdr_set_adc(s); } else if (f->tuner == 1) { s->f_tuner = clamp_t(unsigned int, f->frequency, bands_fm[0].rangelow, bands_fm[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", - __func__, f->frequency); + dev_dbg(&s->udev->dev, "RF frequency=%u Hz\n", f->frequency); ret = rtl2832_sdr_set_tuner_freq(s); } else { @@ -1217,7 +1211,7 @@ static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); if (f->index >= s->num_formats) return -EINVAL; @@ -1233,7 +1227,7 @@ static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(&s->udev->dev, "\n"); f->fmt.sdr.pixelformat = s->pixelformat; f->fmt.sdr.buffersize = s->buffersize; @@ -1250,7 +1244,7 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, struct vb2_queue *q = &s->vb_queue; int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n", (char *)&f->fmt.sdr.pixelformat); if (vb2_is_busy(q)) @@ -1280,7 +1274,7 @@ static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, struct rtl2832_sdr_state *s = video_drvdata(file); int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n", (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); @@ -1354,8 +1348,8 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) int ret; dev_dbg(&s->udev->dev, - "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - __func__, ctrl->id, ctrl->name, ctrl->val, + "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); switch (ctrl->id) { @@ -1432,7 +1426,7 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, s->pixelformat = formats[0].pixelformat; s->buffersize = formats[0].buffersize; s->num_formats = NUM_FORMATS; - if (rtl2832_sdr_emulated_fmt == false) + if (!rtl2832_sdr_emulated_fmt) s->num_formats -= 1; mutex_init(&s->v4l2_lock); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 3a2d6c5..98ddb49 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1,5 +1,5 @@ /* - Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org> @@ -44,9 +44,7 @@ struct si2165_state { struct si2165_config config; - /* chip revision */ - u8 revcode; - /* chip type */ + u8 chip_revcode; u8 chip_type; /* calculated by xtal and div settings */ @@ -312,7 +310,7 @@ static u32 si2165_get_fe_clk(struct si2165_state *state) return state->adc_clk; } -static bool si2165_wait_init_done(struct si2165_state *state) +static int si2165_wait_init_done(struct si2165_state *state) { int ret = -EINVAL; u8 val = 0; @@ -407,7 +405,7 @@ static int si2165_upload_firmware(struct si2165_state *state) int ret; const struct firmware *fw = NULL; - u8 *fw_file = SI2165_FIRMWARE; + u8 *fw_file; const u8 *data; u32 len; u32 offset; @@ -415,10 +413,20 @@ static int si2165_upload_firmware(struct si2165_state *state) u8 block_count; u16 crc_expected; + switch (state->chip_revcode) { + case 0x03: /* revision D */ + fw_file = SI2165_FIRMWARE_REV_D; + break; + default: + dev_info(&state->i2c->dev, "%s: no firmware file for revision=%d\n", + KBUILD_MODNAME, state->chip_revcode); + return 0; + } + /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); if (ret) { - dev_warn(&state->i2c->dev, "%s: firmare file '%s' not found\n", + dev_warn(&state->i2c->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); goto error; } @@ -908,7 +916,7 @@ static void si2165_release(struct dvb_frontend *fe) static struct dvb_frontend_ops si2165_ops = { .info = { - .name = "Silicon Labs Si2165", + .name = "Silicon Labs ", .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | @@ -948,6 +956,8 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, int n; int io_ret; u8 val; + char rev_char; + const char *chip_name; if (config == NULL || i2c == NULL) goto error; @@ -984,7 +994,7 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, if (val != state->config.chip_mode) goto error; - io_ret = si2165_readreg8(state, 0x0023 , &state->revcode); + io_ret = si2165_readreg8(state, 0x0023, &state->chip_revcode); if (io_ret < 0) goto error; @@ -997,22 +1007,35 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, if (io_ret < 0) goto error; - dev_info(&state->i2c->dev, "%s: hardware revision 0x%02x, chip type 0x%02x\n", - KBUILD_MODNAME, state->revcode, state->chip_type); + if (state->chip_revcode < 26) + rev_char = 'A' + state->chip_revcode; + else + rev_char = '?'; - /* It is a guess that register 0x0118 (chip type?) can be used to - * differ between si2161, si2163 and si2165 - * Only si2165 has been tested. - */ - if (state->revcode == 0x03 && state->chip_type == 0x07) { + switch (state->chip_type) { + case 0x06: + chip_name = "Si2161"; + state->has_dvbt = true; + break; + case 0x07: + chip_name = "Si2165"; state->has_dvbt = true; state->has_dvbc = true; - } else { - dev_err(&state->i2c->dev, "%s: Unsupported chip.\n", - KBUILD_MODNAME); + break; + default: + dev_err(&state->i2c->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n", + KBUILD_MODNAME, state->chip_type, state->chip_revcode); goto error; } + dev_info(&state->i2c->dev, + "%s: Detected Silicon Labs %s-%c (type %d, rev %d)\n", + KBUILD_MODNAME, chip_name, rev_char, state->chip_type, + state->chip_revcode); + + strlcat(state->frontend.ops.info.name, chip_name, + sizeof(state->frontend.ops.info.name)); + n = 0; if (state->has_dvbt) { state->frontend.ops.delsys[n++] = SYS_DVBT; @@ -1037,4 +1060,4 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver"); MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>"); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(SI2165_FIRMWARE); +MODULE_FIRMWARE(SI2165_FIRMWARE_REV_D); diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h index d4cc93f..2b70cf1 100644 --- a/drivers/media/dvb-frontends/si2165_priv.h +++ b/drivers/media/dvb-frontends/si2165_priv.h @@ -18,6 +18,6 @@ #ifndef _DVB_SI2165_PRIV #define _DVB_SI2165_PRIV -#define SI2165_FIRMWARE "dvb-demod-si2165.fw" +#define SI2165_FIRMWARE_REV_D "dvb-demod-si2165.fw" #endif /* _DVB_SI2165_PRIV */ diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 8f81d97..1cd93be 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -55,8 +55,7 @@ static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) break; } - dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", - __func__, + dev_dbg(&s->client->dev, "cmd execution took %d ms\n", jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - TIMEOUT)); @@ -75,7 +74,7 @@ err_mutex_unlock: return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -150,12 +149,12 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n", - __func__, *status, cmd.rlen, cmd.args); + dev_dbg(&s->client->dev, "status=%02x args=%*ph\n", + *status, cmd.rlen, cmd.args); return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -168,10 +167,10 @@ static int si2168_set_frontend(struct dvb_frontend *fe) u8 bandwidth, delivery_system; dev_dbg(&s->client->dev, - "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n", - __func__, c->delivery_system, c->modulation, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u, stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, c->bandwidth_hz, c->symbol_rate, - c->inversion); + c->inversion, c->stream_id); if (!s->active) { ret = -EAGAIN; @@ -235,6 +234,18 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + if (c->delivery_system == SYS_DVBT2) { + /* select PLP */ + cmd.args[0] = 0x52; + cmd.args[1] = c->stream_id & 0xff; + cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1; + cmd.wlen = 3; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + } + memcpy(cmd.args, "\x51\x03", 2); cmd.wlen = 2; cmd.rlen = 12; @@ -297,13 +308,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6); cmd.wlen = 6; cmd.rlen = 4; @@ -343,7 +347,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -357,8 +361,9 @@ static int si2168_init(struct dvb_frontend *fe) struct si2168_cmd cmd; unsigned int chip_id; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); + /* initialize */ memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); cmd.wlen = 13; cmd.rlen = 0; @@ -366,6 +371,26 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; + if (s->fw_loaded) { + /* resume */ + memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8); + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x85", 1); + cmd.wlen = 1; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + goto warm; + } + + /* power up */ memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); cmd.wlen = 8; cmd.rlen = 1; @@ -400,16 +425,16 @@ static int si2168_init(struct dvb_frontend *fe) break; default: dev_err(&s->client->dev, - "%s: unkown chip version Si21%d-%c%c%c\n", - KBUILD_MODNAME, cmd.args[2], cmd.args[1], + "unknown chip version Si21%d-%c%c%c\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); ret = -EINVAL; goto err; } /* cold state - try to download firmware */ - dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", - KBUILD_MODNAME, si2168_ops.info.name); + dev_info(&s->client->dev, "found a '%s' in cold state\n", + si2168_ops.info.name); /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, &s->client->dev); @@ -422,18 +447,18 @@ static int si2168_init(struct dvb_frontend *fe) if (ret == 0) { dev_notice(&s->client->dev, - "%s: please install firmware file '%s'\n", - KBUILD_MODNAME, SI2168_B40_FIRMWARE); + "please install firmware file '%s'\n", + SI2168_B40_FIRMWARE); } else { dev_err(&s->client->dev, - "%s: firmware file '%s' not found\n", - KBUILD_MODNAME, fw_file); + "firmware file '%s' not found\n", + fw_file); goto err; } } - dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", - KBUILD_MODNAME, fw_file); + dev_info(&s->client->dev, "downloading firmware from file '%s'\n", + fw_file); for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) { len = remaining; @@ -446,8 +471,8 @@ static int si2168_init(struct dvb_frontend *fe) ret = si2168_cmd_execute(s, &cmd); if (ret) { dev_err(&s->client->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); + "firmware download failed=%d\n", + ret); goto err; } } @@ -462,8 +487,20 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - dev_info(&s->client->dev, "%s: found a '%s' in warm state\n", - KBUILD_MODNAME, si2168_ops.info.name); + /* set ts mode */ + memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); + cmd.args[4] |= s->ts_mode; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + s->fw_loaded = true; + +warm: + dev_info(&s->client->dev, "found a '%s' in warm state\n", + si2168_ops.info.name); s->active = true; @@ -472,7 +509,7 @@ err: if (fw) release_firmware(fw); - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -482,7 +519,7 @@ static int si2168_sleep(struct dvb_frontend *fe) int ret; struct si2168_cmd cmd; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); s->active = false; @@ -495,7 +532,7 @@ static int si2168_sleep(struct dvb_frontend *fe) return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -528,8 +565,7 @@ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) /* open tuner I2C gate */ ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1); if (ret != 1) { - dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", - KBUILD_MODNAME, ret); + dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); if (ret >= 0) ret = -EREMOTEIO; } else { @@ -553,8 +589,7 @@ static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) /* close tuner I2C gate */ ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1); if (ret != 1) { - dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", - KBUILD_MODNAME, ret); + dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); if (ret >= 0) ret = -EREMOTEIO; } else { @@ -587,7 +622,8 @@ static const struct dvb_frontend_ops si2168_ops = { FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO | FE_CAN_MUTE_TS | - FE_CAN_2G_MODULATION + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM }, .get_tune_settings = si2168_get_tune_settings, @@ -607,12 +643,12 @@ static int si2168_probe(struct i2c_client *client, struct si2168 *s; int ret; - dev_dbg(&client->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); s = kzalloc(sizeof(struct si2168), GFP_KERNEL); if (!s) { ret = -ENOMEM; - dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + dev_err(&client->dev, "kzalloc() failed\n"); goto err; } @@ -633,16 +669,17 @@ static int si2168_probe(struct i2c_client *client, *config->i2c_adapter = s->adapter; *config->fe = &s->fe; + s->ts_mode = config->ts_mode; + s->fw_loaded = false; i2c_set_clientdata(client, s); dev_info(&s->client->dev, - "%s: Silicon Labs Si2168 successfully attached\n", - KBUILD_MODNAME); + "Silicon Labs Si2168 successfully attached\n"); return 0; err: kfree(s); - dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -650,7 +687,7 @@ static int si2168_remove(struct i2c_client *client) { struct si2168 *s = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); i2c_del_mux_adapter(s->adapter); diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h index 3c5b5ab..e086d67 100644 --- a/drivers/media/dvb-frontends/si2168.h +++ b/drivers/media/dvb-frontends/si2168.h @@ -34,6 +34,12 @@ struct si2168_config { * returned by driver */ struct i2c_adapter **i2c_adapter; + + /* TS mode */ + u8 ts_mode; }; +#define SI2168_TS_PARALLEL 0x06 +#define SI2168_TS_SERIAL 0x03 + #endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index ebbf502..e13983e 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -36,6 +36,8 @@ struct si2168 { fe_delivery_system_t delivery_system; fe_status_t fe_status; bool active; + bool fw_loaded; + u8 ts_mode; }; /* firmare command struct */ diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index 73b47cc..16850e2 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -236,6 +236,9 @@ static int si21_writeregs(struct si21xx_state *state, u8 reg1, .len = len + 1 }; + if (len > sizeof(buf) - 1) + return -EINVAL; + msg.buf[0] = reg1; memcpy(msg.buf + 1, data, len); diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c new file mode 100644 index 0000000..9b684d5 --- /dev/null +++ b/drivers/media/dvb-frontends/sp2.c @@ -0,0 +1,441 @@ +/* + * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + * + * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual + * DVB-S2 CI card (cimax2) with following copyrights: + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include "sp2_priv.h" + +static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) +{ + int ret; + struct i2c_client *client = s->client; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®, + .len = 1 + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len + } + }; + + ret = i2c_transfer(adap, msg, 2); + + if (ret != 2) { + dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n", + reg, ret); + if (ret < 0) + return ret; + else + return -EIO; + } + + dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n", + client->addr, reg, buf[0]); + + return 0; +} + +static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) +{ + int ret; + u8 buffer[35]; + struct i2c_client *client = s->client; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .buf = &buffer[0], + .len = len + 1 + }; + + if ((len + 1) > sizeof(buffer)) { + dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n", + reg, len); + return -EINVAL; + } + + buffer[0] = reg; + memcpy(&buffer[1], buf, len); + + ret = i2c_transfer(adap, &msg, 1); + + if (ret != 1) { + dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n", + reg, ret); + if (ret < 0) + return ret; + else + return -EIO; + } + + return 0; +} + +static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs, + u8 read, int addr, u8 data) +{ + struct sp2 *s = en50221->data; + u8 store; + int mem, ret; + int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control; + + dev_dbg(&s->client->dev, "slot=%d, acs=0x%02x, addr=0x%04x, data = 0x%02x", + slot, acs, addr, data); + + if (slot != 0) + return -EINVAL; + + /* + * change module access type between IO space and attribute memory + * when needed + */ + if (s->module_access_type != acs) { + ret = sp2_read_i2c(s, 0x00, &store, 1); + + if (ret) + return ret; + + store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0); + store |= acs; + + ret = sp2_write_i2c(s, 0x00, &store, 1); + if (ret) + return ret; + } + + s->module_access_type = acs; + + /* implementation of ci_op_cam is device specific */ + if (ci_op_cam) { + ret = ci_op_cam(s->priv, read, addr, data, &mem); + } else { + dev_err(&s->client->dev, "callback not defined"); + return -EINVAL; + } + + if (ret) + return ret; + + if (read) { + dev_dbg(&s->client->dev, "cam read, addr=0x%04x, data = 0x%04x", + addr, mem); + return mem; + } else { + return 0; + } +} + +int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, + SP2_CI_RD, addr, 0); +} + +int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, + SP2_CI_WR, addr, data); +} + +int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, + SP2_CI_RD, addr, 0); +} + +int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, + SP2_CI_WR, addr, data); +} + +int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + u8 buf; + int ret; + + dev_dbg(&s->client->dev, "slot: %d\n", slot); + + if (slot != 0) + return -EINVAL; + + /* RST on */ + buf = SP2_MOD_CTL_RST; + ret = sp2_write_i2c(s, 0x00, &buf, 1); + + if (ret) + return ret; + + usleep_range(500, 600); + + /* RST off */ + buf = 0x00; + ret = sp2_write_i2c(s, 0x00, &buf, 1); + + if (ret) + return ret; + + msleep(1000); + + return 0; +} + +int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + + dev_dbg(&s->client->dev, "slot:%d\n", slot); + + /* not implemented */ + return 0; +} + +int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + u8 buf; + + dev_dbg(&s->client->dev, "slot:%d\n", slot); + + if (slot != 0) + return -EINVAL; + + sp2_read_i2c(s, 0x00, &buf, 1); + + /* disable bypass and enable TS */ + buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN); + return sp2_write_i2c(s, 0, &buf, 1); +} + +int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open) +{ + struct sp2 *s = en50221->data; + u8 buf[2]; + int ret; + + dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open); + + /* + * CAM module INSERT/REMOVE processing. Slow operation because of i2c + * transfers. Throttle read to one per sec. + */ + if (time_after(jiffies, s->next_status_checked_time)) { + ret = sp2_read_i2c(s, 0x00, buf, 1); + s->next_status_checked_time = jiffies + msecs_to_jiffies(1000); + + if (ret) + return 0; + + if (buf[0] & SP2_MOD_CTL_DET) + s->status = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + else + s->status = 0; + } + + return s->status; +} + +int sp2_init(struct sp2 *s) +{ + int ret = 0; + u8 buf; + u8 cimax_init[34] = { + 0x00, /* module A control*/ + 0x00, /* auto select mask high A */ + 0x00, /* auto select mask low A */ + 0x00, /* auto select pattern high A */ + 0x00, /* auto select pattern low A */ + 0x44, /* memory access time A, 600 ns */ + 0x00, /* invert input A */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* module B control*/ + 0x00, /* auto select mask high B */ + 0x00, /* auto select mask low B */ + 0x00, /* auto select pattern high B */ + 0x00, /* auto select pattern low B */ + 0x44, /* memory access time B, 600 ns */ + 0x00, /* invert input B */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* auto select mask high Ext */ + 0x00, /* auto select mask low Ext */ + 0x00, /* auto select pattern high Ext */ + 0x00, /* auto select pattern low Ext */ + 0x00, /* RFU */ + 0x02, /* destination - module A */ + 0x01, /* power control reg, VCC power on */ + 0x00, /* RFU */ + 0x00, /* int status read only */ + 0x00, /* Interrupt Mask Register */ + 0x05, /* EXTINT=active-high, INT=push-pull */ + 0x00, /* USCG1 */ + 0x04, /* ack active low */ + 0x00, /* LOCK = 0 */ + 0x22, /* unknown */ + 0x00, /* synchronization? */ + }; + + dev_dbg(&s->client->dev, "\n"); + + s->ca.owner = THIS_MODULE; + s->ca.read_attribute_mem = sp2_ci_read_attribute_mem; + s->ca.write_attribute_mem = sp2_ci_write_attribute_mem; + s->ca.read_cam_control = sp2_ci_read_cam_control; + s->ca.write_cam_control = sp2_ci_write_cam_control; + s->ca.slot_reset = sp2_ci_slot_reset; + s->ca.slot_shutdown = sp2_ci_slot_shutdown; + s->ca.slot_ts_enable = sp2_ci_slot_ts_enable; + s->ca.poll_slot_status = sp2_ci_poll_slot_status; + s->ca.data = s; + s->module_access_type = 0; + + /* initialize all regs */ + ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34); + if (ret) + goto err; + + /* lock registers */ + buf = 1; + ret = sp2_write_i2c(s, 0x1f, &buf, 1); + if (ret) + goto err; + + /* power on slots */ + ret = sp2_write_i2c(s, 0x18, &buf, 1); + if (ret) + goto err; + + ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1); + if (ret) + goto err; + + return 0; + +err: + dev_dbg(&s->client->dev, "init failed=%d\n", ret); + return ret; +} + +int sp2_exit(struct i2c_client *client) +{ + struct sp2 *s; + + dev_dbg(&client->dev, "\n"); + + if (client == NULL) + return 0; + + s = i2c_get_clientdata(client); + if (s == NULL) + return 0; + + if (s->ca.data == NULL) + return 0; + + dvb_ca_en50221_release(&s->ca); + + return 0; +} + +static int sp2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sp2_config *cfg = client->dev.platform_data; + struct sp2 *s; + int ret; + + dev_dbg(&client->dev, "\n"); + + s = kzalloc(sizeof(struct sp2), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + dev_err(&client->dev, "kzalloc() failed\n"); + goto err; + } + + s->client = client; + s->dvb_adap = cfg->dvb_adap; + s->priv = cfg->priv; + s->ci_control = cfg->ci_control; + + i2c_set_clientdata(client, s); + + ret = sp2_init(s); + if (ret) + goto err; + + dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n"); + return 0; +err: + dev_dbg(&client->dev, "init failed=%d\n", ret); + kfree(s); + + return ret; +} + +static int sp2_remove(struct i2c_client *client) +{ + struct si2157 *s = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + sp2_exit(client); + if (s != NULL) + kfree(s); + + return 0; +} + +static const struct i2c_device_id sp2_id[] = { + {"sp2", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sp2_id); + +static struct i2c_driver sp2_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "sp2", + }, + .probe = sp2_probe, + .remove = sp2_remove, + .id_table = sp2_id, +}; + +module_i2c_driver(sp2_driver); + +MODULE_DESCRIPTION("CIMaX SP2/HF CI driver"); +MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/sp2.h b/drivers/media/dvb-frontends/sp2.h new file mode 100644 index 0000000..6cceea0 --- /dev/null +++ b/drivers/media/dvb-frontends/sp2.h @@ -0,0 +1,53 @@ +/* + * CIMaX SP2/HF CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + * + * 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. + */ + +#ifndef SP2_H +#define SP2_H + +#include <linux/kconfig.h> +#include "dvb_ca_en50221.h" + +/* + * I2C address + * 0x40 (port 0) + * 0x41 (port 1) + */ +struct sp2_config { + /* dvb_adapter to attach the ci to */ + struct dvb_adapter *dvb_adap; + + /* function ci_control handles the device specific ci ops */ + void *ci_control; + + /* priv is passed back to function ci_control */ + void *priv; +}; + +extern int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr); +extern int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data); +extern int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr); +extern int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data); +extern int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open); + +#endif diff --git a/drivers/media/dvb-frontends/sp2_priv.h b/drivers/media/dvb-frontends/sp2_priv.h new file mode 100644 index 0000000..37fef7b --- /dev/null +++ b/drivers/media/dvb-frontends/sp2_priv.h @@ -0,0 +1,50 @@ +/* + * CIMaX SP2/HF CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + * + * 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. + */ + +#ifndef SP2_PRIV_H +#define SP2_PRIV_H + +#include "sp2.h" +#include "dvb_frontend.h" + +/* state struct */ +struct sp2 { + int status; + struct i2c_client *client; + struct dvb_adapter *dvb_adap; + struct dvb_ca_en50221 ca; + int module_access_type; + unsigned long next_status_checked_time; + void *priv; + void *ci_control; +}; + +#define SP2_CI_ATTR_ACS 0x00 +#define SP2_CI_IO_ACS 0x04 +#define SP2_CI_WR 0 +#define SP2_CI_RD 1 + +/* Module control register (0x00 module A, 0x09 module B) bits */ +#define SP2_MOD_CTL_DET 0x01 +#define SP2_MOD_CTL_AUTO 0x02 +#define SP2_MOD_CTL_ACS0 0x04 +#define SP2_MOD_CTL_ACS1 0x08 +#define SP2_MOD_CTL_HAD 0x10 +#define SP2_MOD_CTL_TSIEN 0x20 +#define SP2_MOD_CTL_TSOEN 0x40 +#define SP2_MOD_CTL_RST 0x80 + +#endif diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c index 2aa8ef7..57dc2aba 100644 --- a/drivers/media/dvb-frontends/sp8870.c +++ b/drivers/media/dvb-frontends/sp8870.c @@ -394,8 +394,7 @@ static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) if (ret < 0) return -EIO; - tmp = ret << 6; - + tmp = ret << 6; if (tmp >= 0x3FFF0) tmp = ~0; diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 59b6e66..b31ff26 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -59,7 +59,6 @@ struct stv0367cab_state { int locked; /* channel found */ u32 freq_khz; /* found frequency (in kHz) */ u32 symbol_rate; /* found symbol rate (in Bds) */ - enum stv0367cab_mod modulation; /* modulation */ fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */ }; @@ -554,7 +553,7 @@ static struct st_register def0367ter[STV0367TER_NBREGS] = { #define RF_LOOKUP_TABLE_SIZE 31 #define RF_LOOKUP_TABLE2_SIZE 16 /* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/ -s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { +static const s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { {/*AGC1*/ 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, @@ -566,7 +565,7 @@ s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { } }; /* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/ -s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { +static const s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { {/*AGC2*/ 28, 29, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, @@ -1935,8 +1934,6 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct stv0367_state *state = fe->demodulator_priv; struct stv0367ter_state *ter_state = state->ter_state; - - int error = 0; enum stv0367_ter_mode mode; int constell = 0,/* snr = 0,*/ Data = 0; @@ -2020,7 +2017,7 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe) p->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD); - return error; + return 0; } static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) @@ -2999,7 +2996,6 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, if (QAMFEC_Lock) { signalType = FE_CAB_DATAOK; - cab_state->modulation = p->modulation; cab_state->spect_inv = stv0367_readbits(state, F367CAB_QUAD_INV); #if 0 @@ -3165,7 +3161,7 @@ static int stv0367cab_get_frontend(struct dvb_frontend *fe) case FE_CAB_MOD_QAM128: p->modulation = QAM_128; break; - case QAM_256: + case FE_CAB_MOD_QAM256: p->modulation = QAM_256; break; default: diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index e5a87b5..2c88abf 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1270,7 +1270,6 @@ enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, enum fe_stv0900_demod_mode LDPC_Mode, enum fe_stv0900_demod_num demod) { - enum fe_stv0900_error error = STV0900_NO_ERROR; s32 reg_ind; dprintk("%s\n", __func__); @@ -1337,7 +1336,7 @@ enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, break; } - return error; + return STV0900_NO_ERROR; } static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, @@ -1555,8 +1554,6 @@ static int stv0900_status(struct stv0900_internal *intp, 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) { @@ -1569,7 +1566,7 @@ static int stv0900_set_mis(struct stv0900_internal *intp, stv0900_write_reg(intp, ISIBITENA, 0xff); } - return error; + return STV0900_NO_ERROR; } diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c index 4ce1d26..a0a7b16 100644 --- a/drivers/media/dvb-frontends/stv0900_sw.c +++ b/drivers/media/dvb-frontends/stv0900_sw.c @@ -1733,9 +1733,10 @@ static void stv0900_set_search_standard(struct stv0900_internal *intp, break; case STV0900_SEARCH_DSS: dprintk("Search Standard = DSS\n"); - case STV0900_SEARCH_DVBS2: break; + case STV0900_SEARCH_DVBS2: dprintk("Search Standard = DVBS2\n"); + break; case STV0900_AUTO_SEARCH: default: dprintk("Search Standard = AUTO\n"); diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c new file mode 100644 index 0000000..d9905fb --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.c @@ -0,0 +1,840 @@ +/* + * Toshiba TC90522 Demodulator + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +/* + * NOTICE: + * This driver is incomplete and lacks init/config of the chips, + * as the necessary info is not disclosed. + * It assumes that users of this driver (such as a PCI bridge of + * DTV receiver cards) properly init and configure the chip + * via I2C *before* calling this driver's init() function. + * + * Currently, PT3 driver is the only one that uses this driver, + * and contains init/config code in its firmware. + * Thus some part of the code might be dependent on PT3 specific config. + */ + +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/dvb/frontend.h> +#include "dvb_math.h" +#include "tc90522.h" + +#define TC90522_I2C_THRU_REG 0xfe + +#define TC90522_MODULE_IDX(addr) (((u8)(addr) & 0x02U) >> 1) + +struct tc90522_state { + struct tc90522_config cfg; + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct i2c_adapter tuner_i2c; + + bool lna; +}; + +struct reg_val { + u8 reg; + u8 val; +}; + +static int +reg_write(struct tc90522_state *state, const struct reg_val *regs, int num) +{ + int i, ret; + struct i2c_msg msg; + + ret = 0; + msg.addr = state->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < num; i++) { + msg.buf = (u8 *)®s[i]; + ret = i2c_transfer(state->i2c_client->adapter, &msg, 1); + if (ret == 0) + ret = -EIO; + if (ret < 0) + return ret; + } + return 0; +} + +static int reg_read(struct tc90522_state *state, u8 reg, u8 *val, u8 len) +{ + struct i2c_msg msgs[2] = { + { + .addr = state->i2c_client->addr, + .flags = 0, + .buf = ®, + .len = 1, + }, + { + .addr = state->i2c_client->addr, + .flags = I2C_M_RD, + .buf = val, + .len = len, + }, + }; + int ret; + + ret = i2c_transfer(state->i2c_client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret == ARRAY_SIZE(msgs)) + ret = 0; + else if (ret >= 0) + ret = -EIO; + return ret; +} + +static struct tc90522_state *cfg_to_state(struct tc90522_config *c) +{ + return container_of(c, struct tc90522_state, cfg); +} + + +static int tc90522s_set_tsid(struct dvb_frontend *fe) +{ + struct reg_val set_tsid[] = { + { 0x8f, 00 }, + { 0x90, 00 } + }; + + set_tsid[0].val = (fe->dtv_property_cache.stream_id & 0xff00) >> 8; + set_tsid[1].val = fe->dtv_property_cache.stream_id & 0xff; + return reg_write(fe->demodulator_priv, set_tsid, ARRAY_SIZE(set_tsid)); +} + +static int tc90522t_set_layers(struct dvb_frontend *fe) +{ + struct reg_val rv; + u8 laysel; + + laysel = ~fe->dtv_property_cache.isdbt_layer_enabled & 0x07; + laysel = (laysel & 0x01) << 2 | (laysel & 0x02) | (laysel & 0x04) >> 2; + rv.reg = 0x71; + rv.val = laysel; + return reg_write(fe->demodulator_priv, &rv, 1); +} + +/* frontend ops */ + +static int tc90522s_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tc90522_state *state; + int ret; + u8 reg; + + state = fe->demodulator_priv; + ret = reg_read(state, 0xc3, ®, 1); + if (ret < 0) + return ret; + + *status = 0; + if (reg & 0x80) /* input level under min ? */ + return 0; + *status |= FE_HAS_SIGNAL; + + if (reg & 0x60) /* carrier? */ + return 0; + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC; + + if (reg & 0x10) + return 0; + if (reg_read(state, 0xc5, ®, 1) < 0 || !(reg & 0x03)) + return 0; + *status |= FE_HAS_LOCK; + return 0; +} + +static int tc90522t_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tc90522_state *state; + int ret; + u8 reg; + + state = fe->demodulator_priv; + ret = reg_read(state, 0x96, ®, 1); + if (ret < 0) + return ret; + + *status = 0; + if (reg & 0xe0) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } + + ret = reg_read(state, 0x80, ®, 1); + if (ret < 0) + return ret; + + if (reg & 0xf0) + return 0; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + if (reg & 0x0c) + return 0; + *status |= FE_HAS_SYNC | FE_HAS_VITERBI; + + if (reg & 0x02) + return 0; + *status |= FE_HAS_LOCK; + return 0; +} + +static const fe_code_rate_t fec_conv_sat[] = { + FEC_NONE, /* unused */ + FEC_1_2, /* for BPSK */ + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, /* for QPSK */ + FEC_2_3, /* for 8PSK. (trellis code) */ +}; + +static int tc90522s_get_frontend(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + struct dtv_frontend_properties *c; + struct dtv_fe_stats *stats; + int ret, i; + int layers; + u8 val[10]; + u32 cndat; + + state = fe->demodulator_priv; + c = &fe->dtv_property_cache; + c->delivery_system = SYS_ISDBS; + + layers = 0; + ret = reg_read(state, 0xe8, val, 3); + if (ret == 0) { + int slots; + u8 v; + + /* high/single layer */ + v = (val[0] & 0x70) >> 4; + c->modulation = (v == 7) ? PSK_8 : QPSK; + c->fec_inner = fec_conv_sat[v]; + c->layer[0].fec = c->fec_inner; + c->layer[0].modulation = c->modulation; + c->layer[0].segment_count = val[1] & 0x3f; /* slots */ + + /* low layer */ + v = (val[0] & 0x07); + c->layer[1].fec = fec_conv_sat[v]; + if (v == 0) /* no low layer */ + c->layer[1].segment_count = 0; + else + c->layer[1].segment_count = val[2] & 0x3f; /* slots */ + /* actually, BPSK if v==1, but not defined in fe_modulation_t */ + c->layer[1].modulation = QPSK; + layers = (v > 0) ? 2 : 1; + + slots = c->layer[0].segment_count + c->layer[1].segment_count; + c->symbol_rate = 28860000 * slots / 48; + } + + /* statistics */ + + stats = &c->strength; + stats->len = 0; + /* let the connected tuner set RSSI property cache */ + if (fe->ops.tuner_ops.get_rf_strength) { + u16 dummy; + + fe->ops.tuner_ops.get_rf_strength(fe, &dummy); + } + + stats = &c->cnr; + stats->len = 1; + stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE; + cndat = 0; + ret = reg_read(state, 0xbc, val, 2); + if (ret == 0) + cndat = val[0] << 8 | val[1]; + if (cndat >= 3000) { + u32 p, p4; + s64 cn; + + cndat -= 3000; /* cndat: 4.12 fixed point float */ + /* + * cnr[mdB] = -1634.6 * P^5 + 14341 * P^4 - 50259 * P^3 + * + 88977 * P^2 - 89565 * P + 58857 + * (P = sqrt(cndat) / 64) + */ + /* p := sqrt(cndat) << 8 = P << 14, 2.14 fixed point float */ + /* cn = cnr << 3 */ + p = int_sqrt(cndat << 16); + p4 = cndat * cndat; + cn = div64_s64(-16346LL * p4 * p, 10) >> 35; + cn += (14341LL * p4) >> 21; + cn -= (50259LL * cndat * p) >> 23; + cn += (88977LL * cndat) >> 9; + cn -= (89565LL * p) >> 11; + cn += 58857 << 3; + stats->stat[0].svalue = cn >> 3; + stats->stat[0].scale = FE_SCALE_DECIBEL; + } + + /* per-layer post viterbi BER (or PER? config dependent?) */ + stats = &c->post_bit_error; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + ret = reg_read(state, 0xeb, val, 10); + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = val[i * 5] << 16 + | val[i * 5 + 1] << 8 | val[i * 5 + 2]; + } + } + stats = &c->post_bit_count; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = + val[i * 5 + 3] << 8 | val[i * 5 + 4]; + stats->stat[i].uvalue *= 204 * 8; + } + } + + return 0; +} + + +static const fe_transmit_mode_t tm_conv[] = { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_4K, + TRANSMISSION_MODE_8K, + 0 +}; + +static const fe_code_rate_t fec_conv_ter[] = { + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, 0, 0, 0 +}; + +static const fe_modulation_t mod_conv[] = { + DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0 +}; + +static int tc90522t_get_frontend(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + struct dtv_frontend_properties *c; + struct dtv_fe_stats *stats; + int ret, i; + int layers; + u8 val[15], mode; + u32 cndat; + + state = fe->demodulator_priv; + c = &fe->dtv_property_cache; + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + mode = 1; + ret = reg_read(state, 0xb0, val, 1); + if (ret == 0) { + mode = (val[0] & 0xc0) >> 2; + c->transmission_mode = tm_conv[mode]; + c->guard_interval = (val[0] & 0x30) >> 4; + } + + ret = reg_read(state, 0xb2, val, 6); + layers = 0; + if (ret == 0) { + u8 v; + + c->isdbt_partial_reception = val[0] & 0x01; + c->isdbt_sb_mode = (val[0] & 0xc0) == 0x01; + + /* layer A */ + v = (val[2] & 0x78) >> 3; + if (v == 0x0f) + c->layer[0].segment_count = 0; + else { + layers++; + c->layer[0].segment_count = v; + c->layer[0].fec = fec_conv_ter[(val[1] & 0x1c) >> 2]; + c->layer[0].modulation = mod_conv[(val[1] & 0xe0) >> 5]; + v = (val[1] & 0x03) << 1 | (val[2] & 0x80) >> 7; + c->layer[0].interleaving = v; + } + + /* layer B */ + v = (val[3] & 0x03) << 1 | (val[4] & 0xc0) >> 6; + if (v == 0x0f) + c->layer[1].segment_count = 0; + else { + layers++; + c->layer[1].segment_count = v; + c->layer[1].fec = fec_conv_ter[(val[3] & 0xe0) >> 5]; + c->layer[1].modulation = mod_conv[(val[2] & 0x07)]; + c->layer[1].interleaving = (val[3] & 0x1c) >> 2; + } + + /* layer C */ + v = (val[5] & 0x1e) >> 1; + if (v == 0x0f) + c->layer[2].segment_count = 0; + else { + layers++; + c->layer[2].segment_count = v; + c->layer[2].fec = fec_conv_ter[(val[4] & 0x07)]; + c->layer[2].modulation = mod_conv[(val[4] & 0x38) >> 3]; + c->layer[2].interleaving = (val[5] & 0xe0) >> 5; + } + } + + /* statistics */ + + stats = &c->strength; + stats->len = 0; + /* let the connected tuner set RSSI property cache */ + if (fe->ops.tuner_ops.get_rf_strength) { + u16 dummy; + + fe->ops.tuner_ops.get_rf_strength(fe, &dummy); + } + + stats = &c->cnr; + stats->len = 1; + stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE; + cndat = 0; + ret = reg_read(state, 0x8b, val, 3); + if (ret == 0) + cndat = val[0] << 16 | val[1] << 8 | val[2]; + if (cndat != 0) { + u32 p, tmp; + s64 cn; + + /* + * cnr[mdB] = 0.024 P^4 - 1.6 P^3 + 39.8 P^2 + 549.1 P + 3096.5 + * (P = 10log10(5505024/cndat)) + */ + /* cn = cnr << 3 (61.3 fixed point float */ + /* p = 10log10(5505024/cndat) << 24 (8.24 fixed point float)*/ + p = intlog10(5505024) - intlog10(cndat); + p *= 10; + + cn = 24772; + cn += div64_s64(43827LL * p, 10) >> 24; + tmp = p >> 8; + cn += div64_s64(3184LL * tmp * tmp, 10) >> 32; + tmp = p >> 13; + cn -= div64_s64(128LL * tmp * tmp * tmp, 10) >> 33; + tmp = p >> 18; + cn += div64_s64(192LL * tmp * tmp * tmp * tmp, 1000) >> 24; + + stats->stat[0].svalue = cn >> 3; + stats->stat[0].scale = FE_SCALE_DECIBEL; + } + + /* per-layer post viterbi BER (or PER? config dependent?) */ + stats = &c->post_bit_error; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + ret = reg_read(state, 0x9d, val, 15); + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = val[i * 3] << 16 + | val[i * 3 + 1] << 8 | val[i * 3 + 2]; + } + } + stats = &c->post_bit_count; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = + val[9 + i * 2] << 8 | val[9 + i * 2 + 1]; + stats->stat[i].uvalue *= 204 * 8; + } + } + + return 0; +} + +static const struct reg_val reset_sat = { 0x03, 0x01 }; +static const struct reg_val reset_ter = { 0x01, 0x40 }; + +static int tc90522_set_frontend(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + state = fe->demodulator_priv; + + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + else + ret = -ENODEV; + if (ret < 0) + goto failed; + + if (fe->ops.delsys[0] == SYS_ISDBS) { + ret = tc90522s_set_tsid(fe); + if (ret < 0) + goto failed; + ret = reg_write(state, &reset_sat, 1); + } else { + ret = tc90522t_set_layers(fe); + if (ret < 0) + goto failed; + ret = reg_write(state, &reset_ter, 1); + } + if (ret < 0) + goto failed; + + return 0; + +failed: + dev_warn(&state->tuner_i2c.dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static int tc90522_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) +{ + if (fe->ops.delsys[0] == SYS_ISDBS) { + settings->min_delay_ms = 250; + settings->step_size = 1000; + settings->max_drift = settings->step_size * 2; + } else { + settings->min_delay_ms = 400; + settings->step_size = 142857; + settings->max_drift = settings->step_size; + } + return 0; +} + +static int tc90522_set_if_agc(struct dvb_frontend *fe, bool on) +{ + struct reg_val agc_sat[] = { + { 0x0a, 0x00 }, + { 0x10, 0x30 }, + { 0x11, 0x00 }, + { 0x03, 0x01 }, + }; + struct reg_val agc_ter[] = { + { 0x25, 0x00 }, + { 0x23, 0x4c }, + { 0x01, 0x40 }, + }; + struct tc90522_state *state; + struct reg_val *rv; + int num; + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) { + agc_sat[0].val = on ? 0xff : 0x00; + agc_sat[1].val |= 0x80; + agc_sat[1].val |= on ? 0x01 : 0x00; + agc_sat[2].val |= on ? 0x40 : 0x00; + rv = agc_sat; + num = ARRAY_SIZE(agc_sat); + } else { + agc_ter[0].val = on ? 0x40 : 0x00; + agc_ter[1].val |= on ? 0x00 : 0x01; + rv = agc_ter; + num = ARRAY_SIZE(agc_ter); + } + return reg_write(state, rv, num); +} + +static const struct reg_val sleep_sat = { 0x17, 0x01 }; +static const struct reg_val sleep_ter = { 0x03, 0x90 }; + +static int tc90522_sleep(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = reg_write(state, &sleep_sat, 1); + else { + ret = reg_write(state, &sleep_ter, 1); + if (ret == 0 && fe->ops.set_lna && + fe->dtv_property_cache.lna == LNA_AUTO) { + fe->dtv_property_cache.lna = 0; + ret = fe->ops.set_lna(fe); + fe->dtv_property_cache.lna = LNA_AUTO; + } + } + if (ret < 0) + dev_warn(&state->tuner_i2c.dev, + "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static const struct reg_val wakeup_sat = { 0x17, 0x00 }; +static const struct reg_val wakeup_ter = { 0x03, 0x80 }; + +static int tc90522_init(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + /* + * Because the init sequence is not public, + * the parent device/driver should have init'ed the device before. + * just wake up the device here. + */ + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = reg_write(state, &wakeup_sat, 1); + else { + ret = reg_write(state, &wakeup_ter, 1); + if (ret == 0 && fe->ops.set_lna && + fe->dtv_property_cache.lna == LNA_AUTO) { + fe->dtv_property_cache.lna = 1; + ret = fe->ops.set_lna(fe); + fe->dtv_property_cache.lna = LNA_AUTO; + } + } + if (ret < 0) { + dev_warn(&state->tuner_i2c.dev, + "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; + } + + /* prefer 'all-layers' to 'none' as a default */ + if (fe->dtv_property_cache.isdbt_layer_enabled == 0) + fe->dtv_property_cache.isdbt_layer_enabled = 7; + return tc90522_set_if_agc(fe, true); +} + + +/* + * tuner I2C adapter functions + */ + +static int +tc90522_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct tc90522_state *state; + struct i2c_msg *new_msgs; + int i, j; + int ret, rd_num; + u8 wbuf[256]; + u8 *p, *bufend; + + if (num <= 0) + return -EINVAL; + + rd_num = 0; + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + rd_num++; + new_msgs = kmalloc(sizeof(*new_msgs) * (num + rd_num), GFP_KERNEL); + if (!new_msgs) + return -ENOMEM; + + state = i2c_get_adapdata(adap); + p = wbuf; + bufend = wbuf + sizeof(wbuf); + for (i = 0, j = 0; i < num; i++, j++) { + new_msgs[j].addr = state->i2c_client->addr; + new_msgs[j].flags = msgs[i].flags; + + if (msgs[i].flags & I2C_M_RD) { + new_msgs[j].flags &= ~I2C_M_RD; + if (p + 2 > bufend) + break; + p[0] = TC90522_I2C_THRU_REG; + p[1] = msgs[i].addr << 1 | 0x01; + new_msgs[j].buf = p; + new_msgs[j].len = 2; + p += 2; + j++; + new_msgs[j].addr = state->i2c_client->addr; + new_msgs[j].flags = msgs[i].flags; + new_msgs[j].buf = msgs[i].buf; + new_msgs[j].len = msgs[i].len; + continue; + } + + if (p + msgs[i].len + 2 > bufend) + break; + p[0] = TC90522_I2C_THRU_REG; + p[1] = msgs[i].addr << 1; + memcpy(p + 2, msgs[i].buf, msgs[i].len); + new_msgs[j].buf = p; + new_msgs[j].len = msgs[i].len + 2; + p += new_msgs[j].len; + } + + if (i < num) + ret = -ENOMEM; + else + ret = i2c_transfer(state->i2c_client->adapter, new_msgs, j); + if (ret >= 0 && ret < j) + ret = -EIO; + kfree(new_msgs); + return (ret == j) ? num : ret; +} + +static u32 tc90522_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm tc90522_tuner_i2c_algo = { + .master_xfer = &tc90522_master_xfer, + .functionality = &tc90522_functionality, +}; + + +/* + * I2C driver functions + */ + +static const struct dvb_frontend_ops tc90522_ops_sat = { + .delsys = { SYS_ISDBS }, + .info = { + .name = "Toshiba TC90522 ISDB-S module", + .frequency_min = 950000, + .frequency_max = 2150000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .init = tc90522_init, + .sleep = tc90522_sleep, + .set_frontend = tc90522_set_frontend, + .get_tune_settings = tc90522_get_tune_settings, + + .get_frontend = tc90522s_get_frontend, + .read_status = tc90522s_read_status, +}; + +static const struct dvb_frontend_ops tc90522_ops_ter = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "Toshiba TC90522 ISDB-T module", + .frequency_min = 470000000, + .frequency_max = 770000000, + .frequency_stepsize = 142857, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .init = tc90522_init, + .sleep = tc90522_sleep, + .set_frontend = tc90522_set_frontend, + .get_tune_settings = tc90522_get_tune_settings, + + .get_frontend = tc90522t_get_frontend, + .read_status = tc90522t_read_status, +}; + + +static int tc90522_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tc90522_state *state; + struct tc90522_config *cfg; + const struct dvb_frontend_ops *ops; + struct i2c_adapter *adap; + int ret; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + state->i2c_client = client; + + cfg = client->dev.platform_data; + memcpy(&state->cfg, cfg, sizeof(state->cfg)); + cfg->fe = state->cfg.fe = &state->fe; + ops = id->driver_data == 0 ? &tc90522_ops_sat : &tc90522_ops_ter; + memcpy(&state->fe.ops, ops, sizeof(*ops)); + state->fe.demodulator_priv = state; + + adap = &state->tuner_i2c; + adap->owner = THIS_MODULE; + adap->algo = &tc90522_tuner_i2c_algo; + adap->dev.parent = &client->dev; + strlcpy(adap->name, "tc90522_sub", sizeof(adap->name)); + i2c_set_adapdata(adap, state); + ret = i2c_add_adapter(adap); + if (ret < 0) + goto err; + cfg->tuner_i2c = state->cfg.tuner_i2c = adap; + + i2c_set_clientdata(client, &state->cfg); + dev_info(&client->dev, "Toshiba TC90522 attached.\n"); + return 0; + +err: + kfree(state); + return ret; +} + +static int tc90522_remove(struct i2c_client *client) +{ + struct tc90522_state *state; + + state = cfg_to_state(i2c_get_clientdata(client)); + i2c_del_adapter(&state->tuner_i2c); + kfree(state); + return 0; +} + + +static const struct i2c_device_id tc90522_id[] = { + { TC90522_I2C_DEV_SAT, 0 }, + { TC90522_I2C_DEV_TER, 1 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tc90522_id); + +static struct i2c_driver tc90522_driver = { + .driver = { + .name = "tc90522", + }, + .probe = tc90522_probe, + .remove = tc90522_remove, + .id_table = tc90522_id, +}; + +module_i2c_driver(tc90522_driver); + +MODULE_DESCRIPTION("Toshiba TC90522 frontend"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h new file mode 100644 index 0000000..b1cbddf --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.h @@ -0,0 +1,42 @@ +/* + * Toshiba TC90522 Demodulator + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +/* + * The demod has 4 input (2xISDB-T and 2xISDB-S), + * and provides independent sub modules for each input. + * As the sub modules work in parallel and have the separate i2c addr's, + * this driver treats each sub module as one demod device. + */ + +#ifndef TC90522_H +#define TC90522_H + +#include <linux/i2c.h> +#include "dvb_frontend.h" + +/* I2C device types */ +#define TC90522_I2C_DEV_SAT "tc90522sat" +#define TC90522_I2C_DEV_TER "tc90522ter" + +struct tc90522_config { + /* [OUT] frontend returned by driver */ + struct dvb_frontend *fe; + + /* [OUT] tuner I2C adapter returned by driver */ + struct i2c_adapter *tuner_i2c; +}; + +#endif /* TC90522_H */ diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 9619be5..4a19b85 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1037,7 +1037,7 @@ static int tda10071_init(struct dvb_frontend *fe) ret = -EFAULT; goto error; } else { - priv->warm = 1; + priv->warm = true; } cmd.args[0] = CMD_GET_FW_VERSION; diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c index 91b6b2e..ee09ec2 100644 --- a/drivers/media/dvb-frontends/zl10039.c +++ b/drivers/media/dvb-frontends/zl10039.c @@ -111,7 +111,7 @@ static int zl10039_write(struct zl10039_state *state, if (1 + count > sizeof(buf)) { printk(KERN_WARNING - "%s: i2c wr reg=%04x: len=%zd is too big!\n", + "%s: i2c wr reg=%04x: len=%zu is too big!\n", KBUILD_MODNAME, reg, count); return -EINVAL; } diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c index d1a1a13..251a556 100644 --- a/drivers/media/firewire/firedtv-avc.c +++ b/drivers/media/firewire/firedtv-avc.c @@ -1157,6 +1157,10 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) if (pmt_cmd_id != 1 && pmt_cmd_id != 4) dev_err(fdtv->device, "invalid pmt_cmd_id %d\n", pmt_cmd_id); + if (program_info_length > sizeof(c->operand) - 4 - write_pos) { + ret = -EINVAL; + goto out; + } memcpy(&c->operand[write_pos], &msg[read_pos], program_info_length); @@ -1180,6 +1184,12 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) dev_err(fdtv->device, "invalid pmt_cmd_id %d " "at stream level\n", pmt_cmd_id); + if (es_info_length > sizeof(c->operand) - 4 - + write_pos) { + ret = -EINVAL; + goto out; + } + memcpy(&c->operand[write_pos], &msg[read_pos], es_info_length); read_pos += es_info_length; diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h index 4466067..2f04ce4 100644 --- a/drivers/media/i2c/adv7343_regs.h +++ b/drivers/media/i2c/adv7343_regs.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#ifndef ADV7343_REG_H +#ifndef ADV7343_REGS_H #define ADV7343_REGS_H struct adv7343_std_info { diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index de88b98..47795ff 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1593,7 +1593,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, bt->height += hdmi_read16(sd, 0x0b, 0xfff); bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2; bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2; - bt->vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2; + bt->il_vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2; } adv7604_fill_optional_dv_timings_fields(sd, timings); } else { diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 0d55491..48b628b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1435,6 +1435,8 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + /* SDP block */ if (state->mode == ADV7842_MODE_SDP) return -ENODATA; @@ -1483,7 +1485,7 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, hdmi_read(sd, 0x2d)) / 2; bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + hdmi_read(sd, 0x31)) / 2; - bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + + bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + hdmi_read(sd, 0x35)) / 2; } adv7842_fill_optional_dv_timings_fields(sd, timings); diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index e6588ee..4cf8f18 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -224,7 +224,7 @@ static inline unsigned int lpf_count_to_us(unsigned int count) } /* - * FIFO register pulse width count compuations + * FIFO register pulse width count computations */ static u32 clock_divider_to_resolution(u16 divider) { diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index c23de59..d9ece4b 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -100,14 +100,14 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash, int rval; if (led_no == LM3560_LED0) { - if (on == true) + if (on) rval = regmap_update_bits(flash->regmap, REG_ENABLE, 0x08, 0x08); else rval = regmap_update_bits(flash->regmap, REG_ENABLE, 0x08, 0x00); } else { - if (on == true) + if (on) rval = regmap_update_bits(flash->regmap, REG_ENABLE, 0x10, 0x10); else diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index cdd7c1b..dd3db24 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -19,6 +19,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-mediabus.h> +#include <media/v4l2-image-sizes.h> #include <media/ov7670.h> MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); @@ -30,19 +31,6 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* - * Basic window sizes. These probably belong somewhere more globally - * useful. - */ -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 - -/* * The 7670 sits on i2c with ID 0x42 */ #define OV7670_I2C_ADDR 0x42 diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 564f05f..0e461a6 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -816,7 +816,7 @@ static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) "error setting frame interval: %d\n", err); state->error = -EINVAL; } - }; + } v4l2_err(&state->sd, "cannot find correct frame interval\n"); state->error = -ERANGE; } diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 04e9e55..4024ea6 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -660,7 +660,7 @@ static const struct v4l2_subdev_ops saa6752hs_ops = { static int saa6752hs_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + struct saa6752hs_state *h; struct v4l2_subdev *sd; struct v4l2_ctrl_handler *hdl; u8 addr = 0x13; @@ -668,6 +668,8 @@ static int saa6752hs_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + + h = devm_kzalloc(&client->dev, sizeof(*h), GFP_KERNEL); if (h == NULL) return -ENOMEM; sd = &h->sd; @@ -752,7 +754,6 @@ static int saa6752hs_probe(struct i2c_client *client, int err = hdl->error; v4l2_ctrl_handler_free(hdl); - kfree(h); return err; } v4l2_ctrl_cluster(3, &h->video_bitrate_mode); @@ -767,7 +768,6 @@ static int saa6752hs_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&to_state(sd)->hdl); - kfree(to_state(sd)); return 0; } diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 62acb10..932ed9b 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -31,8 +31,9 @@ #include <linux/device.h> #include <linux/gpio.h> #include <linux/module.h> -#include <linux/slab.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/smiapp.h> #include <linux/v4l2-mediabus.h> #include <media/v4l2-device.h> @@ -297,8 +298,9 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor) if (rval < 0) return rval; - *sensor->pixel_rate_parray->p_cur.p_s64 = pll->vt_pix_clk_freq_hz; - *sensor->pixel_rate_csi->p_cur.p_s64 = pll->pixel_rate_csi; + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray, + pll->vt_pix_clk_freq_hz); + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi); return 0; } @@ -319,13 +321,7 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) + sensor->vblank->val - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; - ctrl->maximum = max; - if (ctrl->default_value > max) - ctrl->default_value = max; - if (ctrl->val > max) - ctrl->val = max; - if (ctrl->cur.val > max) - ctrl->cur.val = max; + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); } /* @@ -404,6 +400,14 @@ static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) pixel_order_str[pixel_order]); } +static const char * const smiapp_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", +}; + static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) { struct smiapp_sensor *sensor = @@ -477,6 +481,39 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) return smiapp_pll_update(sensor); + case V4L2_CID_TEST_PATTERN: { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) + v4l2_ctrl_activate( + sensor->test_data[i], + ctrl->val == + V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR); + + return smiapp_write( + sensor, SMIAPP_REG_U16_TEST_PATTERN_MODE, ctrl->val); + } + + case V4L2_CID_TEST_PATTERN_RED: + return smiapp_write( + sensor, SMIAPP_REG_U16_TEST_DATA_RED, ctrl->val); + + case V4L2_CID_TEST_PATTERN_GREENR: + return smiapp_write( + sensor, SMIAPP_REG_U16_TEST_DATA_GREENR, ctrl->val); + + case V4L2_CID_TEST_PATTERN_BLUE: + return smiapp_write( + sensor, SMIAPP_REG_U16_TEST_DATA_BLUE, ctrl->val); + + case V4L2_CID_TEST_PATTERN_GREENB: + return smiapp_write( + sensor, SMIAPP_REG_U16_TEST_DATA_GREENB, ctrl->val); + + case V4L2_CID_PIXEL_RATE: + /* For v4l2_ctrl_s_ctrl_int64() used internally. */ + return 0; + default: return -EINVAL; } @@ -489,10 +526,10 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { static int smiapp_init_controls(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int max; + unsigned int max, i; int rval; - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); if (rval) return rval; sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; @@ -535,6 +572,20 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); + v4l2_ctrl_new_std_menu_items(&sensor->pixel_array->ctrl_handler, + &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(smiapp_test_patterns) - 1, + 0, 0, smiapp_test_patterns); + + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { + int max_value = (1 << sensor->csi_format->width) - 1; + sensor->test_data[i] = + v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, + &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, + 0, max_value, 1, max_value); + } + if (sensor->pixel_array->ctrl_handler.error) { dev_err(&client->dev, "pixel array controls initialization failed (%d)\n", @@ -782,36 +833,25 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) { struct v4l2_ctrl *vblank = sensor->vblank; struct v4l2_ctrl *hblank = sensor->hblank; + int min, max; - vblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - vblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + min = max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + max = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; - vblank->val = clamp_t(int, vblank->val, - vblank->minimum, vblank->maximum); - vblank->default_value = vblank->minimum; - vblank->val = vblank->val; - vblank->cur.val = vblank->val; - - hblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); - hblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); + + min = max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + max = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; - hblank->val = clamp_t(int, hblank->val, - hblank->minimum, hblank->maximum); - hblank->default_value = hblank->minimum; - hblank->val = hblank->val; - hblank->cur.val = hblank->val; + __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); __smiapp_update_exposure_limits(sensor); } @@ -1272,7 +1312,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) clk_disable_unprepare(sensor->ext_clk); usleep_range(5000, 5000); regulator_disable(sensor->vana); - sensor->streaming = 0; + sensor->streaming = false; } static int smiapp_set_power(struct v4l2_subdev *subdev, int on) @@ -1462,13 +1502,13 @@ static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) return 0; if (enable) { - sensor->streaming = 1; + sensor->streaming = true; rval = smiapp_start_streaming(sensor); if (rval < 0) - sensor->streaming = 0; + sensor->streaming = false; } else { rval = smiapp_stop_streaming(sensor); - sensor->streaming = 0; + sensor->streaming = false; } return rval; @@ -1664,17 +1704,34 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->pad == ssd->source_pad) { u32 code = fmt->format.code; int rval = __smiapp_get_format(subdev, fh, fmt); + bool range_changed = false; + unsigned int i; if (!rval && subdev == &sensor->src->sd) { const struct smiapp_csi_data_format *csi_format = smiapp_validate_csi_data_format(sensor, code); - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (csi_format->width != + sensor->csi_format->width) + range_changed = true; + sensor->csi_format = csi_format; + } + fmt->format.code = csi_format->code; } mutex_unlock(&sensor->mutex); - return rval; + if (rval || !range_changed) + return rval; + + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) + v4l2_ctrl_modify_range( + sensor->test_data[i], + 0, (1 << sensor->csi_format->width) - 1, 1, 0); + + return 0; } /* Sink pad. Width and height are changeable here. */ diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 7cc5aae..874b49f 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -54,6 +54,8 @@ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + (clk) / 1000 - 1) / ((clk) / 1000)) +#define SMIAPP_COLOUR_COMPONENTS 4 + #include "smiapp-limits.h" struct smiapp_quirk; @@ -241,6 +243,8 @@ struct smiapp_sensor { /* src controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate_csi; + /* test pattern colour components */ + struct v4l2_ctrl *test_data[SMIAPP_COLOUR_COMPONENTS]; }; #define to_smiapp_subdev(_sd) \ diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 46f431a..996d7b4 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -29,6 +29,7 @@ #include <media/soc_camera.h> #include <media/v4l2-clk.h> #include <media/v4l2-common.h> +#include <media/v4l2-image-sizes.h> /* you can check PLL/clock info */ /* #define EXT_CLOCK 24000000 */ @@ -42,9 +43,6 @@ #define MAX_WIDTH 2048 #define MAX_HEIGHT 1536 -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 - /* * macro of read/write */ diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 7f2b3c8..970a04e 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -29,6 +29,7 @@ #include <media/v4l2-clk.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> +#include <media/v4l2-image-sizes.h> /* * register offset @@ -360,10 +361,6 @@ #define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ #define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 #define OV772X_MAX_WIDTH VGA_WIDTH #define OV772X_MAX_HEIGHT VGA_HEIGHT diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index ea76863..ee9eb63 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -564,13 +564,13 @@ static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) u32 y_start; u32 x_end; u32 y_end; - bool scaling = 0; + bool scaling = false; u32 scale_input_x; u32 scale_input_y; int ret; if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) - scaling = 1; + scaling = true; /* * Try to use as much of the sensor area as possible when supporting diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 72af644..cf93021 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -293,7 +293,7 @@ static int tda7432_s_ctrl(struct v4l2_ctrl *ctrl) if (t->mute->val) { lf |= TDA7432_MUTE; lr |= TDA7432_MUTE; - lf |= TDA7432_MUTE; + rf |= TDA7432_MUTE; rr |= TDA7432_MUTE; } /* Mute & update balance*/ diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 11f2387..51bac76 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -775,25 +775,20 @@ static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) { struct tvp7002 *device = to_tvp7002(sd); - int error = 0; + int error; if (device->streaming == enable) return 0; - if (enable) { - /* Set output state on (low impedance means stream on) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); - device->streaming = enable; - } else { - /* Set output state off (high impedance means stream off) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); - if (error) - v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); - - device->streaming = enable; + /* low impedance: on, high impedance: off */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, enable ? 0x00 : 0x03); + if (error) { + v4l2_dbg(1, debug, sd, "Fail to set streaming\n"); + return error; } - return error; + device->streaming = enable; + return 0; } /* diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 23f4f65..373f2df 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -30,22 +30,10 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> +#include <media/v4l2-image-sizes.h> #include "vs6624_regs.h" -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define QQVGA_WIDTH 160 -#define QQVGA_HEIGHT 120 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 -#define QQCIF_WIDTH 88 -#define QQCIF_HEIGHT 72 - #define MAX_FRAME_RATE 30 struct vs6624 { diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 73a4329..7b39440 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -103,10 +103,8 @@ static long media_device_enum_entities(struct media_device *mdev, return -EINVAL; u_ent.id = ent->id; - if (ent->name) { - strncpy(u_ent.name, ent->name, sizeof(u_ent.name)); - u_ent.name[sizeof(u_ent.name) - 1] = '\0'; - } + if (ent->name) + strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); u_ent.type = ent->type; u_ent.revision = ent->revision; u_ent.flags = ent->flags; diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 7acd19c..ebf9626 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -192,7 +192,6 @@ static int media_open(struct inode *inode, struct file *filp) static int media_release(struct inode *inode, struct file *filp) { struct media_devnode *mdev = media_devnode_data(filp); - int ret = 0; if (mdev->fops->release) mdev->fops->release(filp); @@ -201,7 +200,7 @@ static int media_release(struct inode *inode, struct file *filp) return value is ignored. */ put_device(&mdev->dev); filp->private_data = NULL; - return ret; + return 0; } static const struct file_operations media_devnode_fops = { diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c index 9bc105b..e6b4975 100644 --- a/drivers/media/parport/pms.c +++ b/drivers/media/parport/pms.c @@ -629,11 +629,15 @@ static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) { int y; int dw = 2 * dev->width; - char tmp[dw + 32]; /* using a temp buffer is faster than direct */ + char *tmp; /* using a temp buffer is faster than direct */ int cnt = 0; int len = 0; unsigned char r8 = 0x5; /* value for reg8 */ + tmp = kmalloc(dw + 32, GFP_KERNEL); + if (!tmp) + return 0; + if (rgb555) r8 |= 0x20; /* else use untranslated rgb = 565 */ mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ @@ -664,6 +668,7 @@ static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) len += dt; } } + kfree(tmp); return len; } diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 5c16c9c..f8cec8e 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -20,6 +20,7 @@ source "drivers/media/pci/ivtv/Kconfig" source "drivers/media/pci/zoran/Kconfig" source "drivers/media/pci/saa7146/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" +source "drivers/media/pci/tw68/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT @@ -41,6 +42,7 @@ source "drivers/media/pci/b2c2/Kconfig" source "drivers/media/pci/pluto2/Kconfig" source "drivers/media/pci/dm1105/Kconfig" source "drivers/media/pci/pt1/Kconfig" +source "drivers/media/pci/pt3/Kconfig" source "drivers/media/pci/mantis/Kconfig" source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index e5b53fb..a12926e 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -7,10 +7,10 @@ obj-y += ttpci/ \ pluto2/ \ dm1105/ \ pt1/ \ + pt3/ \ mantis/ \ ngene/ \ ddbridge/ \ - b2c2/ \ saa7146/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ @@ -22,6 +22,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ +obj-$(CONFIG_VIDEO_TW68) += tw68/ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 970e542..4a8176c 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1531,7 +1531,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, { struct bttv_buffer *old; unsigned long flags; - int retval = 0; dprintk("switch_overlay: enter [new=%p]\n", new); if (new) @@ -1551,7 +1550,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, if (NULL == new) free_btres_lock(btv,fh,RESOURCE_OVERLAY); dprintk("switch_overlay: done\n"); - return retval; + return 0; } /* ----------------------------------------------------------------------- */ @@ -3856,7 +3855,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT), BT848_INT_MASK); - }; + } bttv_print_irqbits(stat,astat); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index 0e788fc..c22c4ae 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -674,11 +674,9 @@ static int dst_ca_release(struct inode *inode, struct file *file) static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) { - ssize_t bytes_read = 0; - dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); - return bytes_read; + return 0; } static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c index 180077c..ffb6acd 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.c +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c @@ -80,7 +80,7 @@ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, int period_elapsed = 0; int length; - dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, + dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc, pcm_data, num_bytes); substream = cxsc->capture_pcm_substream; diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c index a1c1cec..c6c8344 100644 --- a/drivers/media/pci/cx18/cx18-firmware.c +++ b/drivers/media/pci/cx18/cx18-firmware.c @@ -130,7 +130,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) } } if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) - CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size); size = fw->size; release_firmware(fw); cx18_setup_page(cx, SCB_OFFSET); @@ -164,7 +164,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; while (offset + sizeof(seghdr) < fw->size) { - const u32 *shptr = src + offset / 4; + const __le32 *shptr = (__force __le32 *)src + offset / 4; seghdr.sync1 = le32_to_cpu(shptr[0]); seghdr.sync2 = le32_to_cpu(shptr[1]); @@ -202,7 +202,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, offset += seghdr.size; } if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) - CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", + CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n", fn, apu_version, fw->size); size = fw->size; release_firmware(fw); diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c index 8884537..2a247d2 100644 --- a/drivers/media/pci/cx18/cx18-queue.c +++ b/drivers/media/pci/cx18/cx18-queue.c @@ -364,7 +364,7 @@ int cx18_stream_alloc(struct cx18_stream *s) ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); - CX18_ERR("Max buffers = %zd\n", + CX18_ERR("Max buffers = %zu\n", bufsz / sizeof(struct cx18_mdl_ent)); return -ENOMEM; } diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index e12c006..f613314 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -3,12 +3,11 @@ config VIDEO_CX23885 depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND select SND_PCM select I2C_ALGOBIT - select VIDEO_BTCX select VIDEO_TUNER select VIDEO_TVEEPROM depends on RC_CORE - select VIDEOBUF_DVB - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DVB + select VIDEOBUF2_DMA_SG select VIDEO_CX25840 select VIDEO_CX2341X select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT @@ -32,12 +31,16 @@ config VIDEO_CX23885 select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88DS3103 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 select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index 2a2cafb..a2cbdcf 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ccflags-y += -Idrivers/media/i2c -ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c index 2926f7f..2bbbf54 100644 --- a/drivers/media/pci/cx23885/altera-ci.c +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -16,10 +16,6 @@ * 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. */ /* @@ -52,8 +48,8 @@ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| * +-------+-------+-------+-------+-------+-------+-------+-------+ */ -#include <media/videobuf-dma-sg.h> -#include <media/videobuf-dvb.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> #include "altera-ci.h" #include "dvb_ca_en50221.h" diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h index 4998c96..5028f0c 100644 --- a/drivers/media/pci/cx23885/altera-ci.h +++ b/drivers/media/pci/cx23885/altera-ci.h @@ -16,10 +16,6 @@ * 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 __ALTERA_CI_H #define __ALTERA_CI_H diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c index 16fa7ea..631e4f2 100644 --- a/drivers/media/pci/cx23885/cimax2.c +++ b/drivers/media/pci/cx23885/cimax2.c @@ -17,10 +17,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h index 518744a..565e958 100644 --- a/drivers/media/pci/cx23885/cimax2.h +++ b/drivers/media/pci/cx23885/cimax2.h @@ -17,10 +17,6 @@ * 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 CIMAX2_H diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index bf89fc8..3948db3 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -18,10 +18,6 @@ * 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 <linux/module.h> @@ -865,6 +861,11 @@ static int cx23885_api_cmd(struct cx23885_dev *dev, return err; } +static int cx23885_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + return cx23885_mbox_func(priv, cmd, in, out, data); +} + static int cx23885_find_mailbox(struct cx23885_dev *dev) { u32 signature[4] = { @@ -941,7 +942,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) if (firmware->size != CX23885_FIRM_IMAGE_SIZE) { printk(KERN_ERR "ERROR: Firmware size mismatch " - "(have %zd, expected %d)\n", + "(have %zu, expected %d)\n", firmware->size, CX23885_FIRM_IMAGE_SIZE); release_firmware(firmware); return -1; @@ -1033,12 +1034,12 @@ static void cx23885_codec_settings(struct cx23885_dev *dev) cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, dev->ts1.height, dev->ts1.width); - dev->mpeg_params.width = dev->ts1.width; - dev->mpeg_params.height = dev->ts1.height; - dev->mpeg_params.is_50hz = + dev->cxhdl.width = dev->ts1.width; + dev->cxhdl.height = dev->ts1.height; + dev->cxhdl.is_50hz = (dev->encodernorm.id & V4L2_STD_625_50) != 0; - cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params); + cx2341x_handler_setup(&dev->cxhdl); cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); @@ -1137,85 +1138,107 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder) /* ------------------------------------------------------------------ */ -static int bb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_fh *fh = q->priv_data; - - fh->dev->ts1.ts_packet_size = mpeglinesize; - fh->dev->ts1.ts_packet_count = mpeglines; - - *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - *count = mpegbufs; + struct cx23885_dev *dev = q->drv_priv; + dev->ts1.ts_packet_size = mpeglinesize; + dev->ts1.ts_packet_count = mpeglines; + *num_planes = 1; + sizes[0] = mpeglinesize * mpeglines; + *num_buffers = mpegbufs; return 0; } -static int bb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - return cx23885_buf_prepare(q, &fh->dev->ts1, - (struct cx23885_buffer *)vb, - field); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = + container_of(vb, struct cx23885_buffer, vb); + + return cx23885_buf_prepare(buf, &dev->ts1); } -static void bb_buf_queue(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(dev, buf); + + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void bb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_queue(struct vb2_buffer *vb) { - cx23885_free_buffer(q, (struct cx23885_buffer *)vb); -} + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); -static struct videobuf_queue_ops cx23885_qops = { - .buf_setup = bb_buf_setup, - .buf_prepare = bb_buf_prepare, - .buf_queue = bb_buf_queue, - .buf_release = bb_buf_release, -}; + cx23885_buf_queue(&dev->ts1, buf); +} -/* ------------------------------------------------------------------ */ +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->ts1.mpegq; + unsigned long flags; + int ret; + + ret = cx23885_initialize_codec(dev, 1); + if (ret == 0) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + cx23885_start_dma(&dev->ts1, dmaq, buf); + return 0; + } + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); -static const u32 *ctrl_classes[] = { - cx2341x_mpeg_ctrls, - NULL -}; + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&dev->slock, flags); + return ret; +} -static int cx23885_queryctrl(struct cx23885_dev *dev, - struct v4l2_queryctrl *qctrl) +static void cx23885_stop_streaming(struct vb2_queue *q) { - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; + struct cx23885_dev *dev = q->drv_priv; - /* MPEG V4L2 controls */ - if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + /* stop mpeg capture */ + cx23885_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); - return 0; + msleep(500); + cx23885_417_check_encoder(dev); + cx23885_cancel_buffers(&dev->ts1); } -static int cx23885_querymenu(struct cx23885_dev *dev, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; +static struct vb2_ops cx23885_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; - qctrl.id = qmenu->id; - cx23885_queryctrl(dev, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); -} +/* ------------------------------------------------------------------ */ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); *id = dev->tvnorm; return 0; @@ -1223,29 +1246,26 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) if (id & cx23885_tvnorms[i].id) break; if (i == ARRAY_SIZE(cx23885_tvnorms)) return -EINVAL; - dev->encodernorm = cx23885_tvnorms[i]; - /* Have the drier core notify the subdevices */ - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, id); - mutex_unlock(&dev->lock); - - return 0; + ret = cx23885_set_tvnorm(dev, id); + if (!ret) + dev->encodernorm = cx23885_tvnorms[i]; + return ret; } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); return cx23885_enum_input(dev, i); } @@ -1263,8 +1283,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1281,8 +1300,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1296,8 +1314,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1315,27 +1332,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, return cx23885_set_frequency(file, priv, f); } -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_get_control(dev, ctl); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_set_control(dev, ctl); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); struct cx23885_tsport *tsport = &dev->ts1; strlcpy(cap->driver, dev->name, sizeof(cap->driver)); @@ -1368,8 +1368,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; @@ -1378,285 +1377,63 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = 0; f->fmt.pix.width = dev->ts1.width; f->fmt.pix.height = dev->ts1.height; - f->fmt.pix.field = fh->mpegq.field; - dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; f->fmt.pix.colorspace = 0; - dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); return 0; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; f->fmt.pix.colorspace = 0; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_reqbufs(&fh->mpegq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_querybuf(&fh->mpegq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_qbuf(&fh->mpegq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx23885_fh *fh = priv; - - return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK); -} - - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_streamon(&fh->mpegq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_streamoff(&fh->mpegq); -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); -} - -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); - - if (err == 0) { - err = cx2341x_update(dev, cx23885_mbox_func, - &dev->mpeg_params, &p); - dev->mpeg_params = p; - } - return err; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); - return err; -} - static int vidioc_log_status(struct file *file, void *priv) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); char name[32 + 2]; snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO - "%s/2: ============ START LOG STATUS ============\n", - dev->name); call_all(dev, core, log_status); - cx2341x_log_status(&dev->mpeg_params, name); - printk(KERN_INFO - "%s/2: ============= END LOG STATUS =============\n", - dev->name); + v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name); return 0; } -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *a) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - return cx23885_querymenu(dev, a); -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - return cx23885_queryctrl(dev, c); -} - -static int mpeg_open(struct file *file) -{ - struct cx23885_dev *dev = video_drvdata(file); - struct cx23885_fh *fh; - - dprintk(2, "%s()\n", __func__); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - - file->private_data = fh; - fh->dev = dev; - - videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops, - &dev->pci->dev, &dev->ts1.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx23885_buffer), - fh, NULL); - return 0; -} - -static int mpeg_release(struct file *file) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - /* FIXME: Review this crap */ - /* Shut device down on last close */ - if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { - if (atomic_dec_return(&dev->v4l_reader_count) == 0) { - /* stop mpeg capture */ - cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX23885_END_NOW, CX23885_MPEG_CAPTURE, - CX23885_RAW_BITS_NONE); - - msleep(500); - cx23885_417_check_encoder(dev); - - cx23885_cancel_buffers(&fh->dev->ts1); - } - } - - if (fh->mpegq.streaming) - videobuf_streamoff(&fh->mpegq); - if (fh->mpegq.reading) - videobuf_read_stop(&fh->mpegq); - - videobuf_mmap_free(&fh->mpegq); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static ssize_t mpeg_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ - /* Start mpeg encoder on first read. */ - if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { - if (atomic_inc_return(&dev->v4l_reader_count) == 1) { - if (cx23885_initialize_codec(dev, 1) < 0) - return -EINVAL; - } - } - - return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int mpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s\n", __func__); - - return videobuf_poll_stream(file, &fh->mpegq, wait); -} - -static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - return videobuf_mmap_mapper(&fh->mpegq, vma); -} - static struct v4l2_file_operations mpeg_fops = { .owner = THIS_MODULE, - .open = mpeg_open, - .release = mpeg_release, - .read = mpeg_read, - .poll = mpeg_poll, - .mmap = mpeg_mmap, - .ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { @@ -1669,25 +1446,19 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_querymenu = vidioc_querymenu, - .vidioc_queryctrl = vidioc_queryctrl, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_chip_info = cx23885_g_chip_info, .vidioc_g_register = cx23885_g_register, @@ -1711,6 +1482,7 @@ void cx23885_417_unregister(struct cx23885_dev *dev) video_unregister_device(dev->v4l_device); else video_device_release(dev->v4l_device); + v4l2_ctrl_handler_free(&dev->cxhdl.hdl); dev->v4l_device = NULL; } } @@ -1742,6 +1514,7 @@ int cx23885_417_register(struct cx23885_dev *dev) /* FIXME: Port1 hardcoded here */ int err = -ENODEV; struct cx23885_tsport *tsport = &dev->ts1; + struct vb2_queue *q; dprintk(1, "%s()\n", __func__); @@ -1757,14 +1530,36 @@ int cx23885_417_register(struct cx23885_dev *dev) tsport->height = 576; tsport->width = 720; - cx2341x_fill_defaults(&dev->mpeg_params); - - dev->mpeg_params.port = CX2341X_PORT_SERIAL; + dev->cxhdl.port = CX2341X_PORT_SERIAL; + err = cx2341x_handler_init(&dev->cxhdl, 50); + if (err) + return err; + dev->cxhdl.priv = dev; + dev->cxhdl.func = cx23885_api_func; + cx2341x_handler_set_50hz(&dev->cxhdl, tsport->height == 576); + v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL); /* Allocate and initialize V4L video device */ dev->v4l_device = cx23885_video_dev_alloc(tsport, dev->pci, &cx23885_mpeg_template, "mpeg"); + q = &dev->vb2_mpegq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + return err; video_set_drvdata(dev->v4l_device, dev); + dev->v4l_device->lock = &dev->lock; + dev->v4l_device->queue = q; err = video_register_device(dev->v4l_device, VFL_TYPE_GRABBER, -1); if (err < 0) { diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index 554798d..ae7c2e8 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -15,10 +15,6 @@ * 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 <linux/module.h> @@ -84,6 +80,82 @@ MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); #define AUD_INT_MCHG_IRQ (1 << 21) #define GP_COUNT_CONTROL_RESET 0x3 +static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages) +{ + struct cx23885_audio_buffer *buf = chip->buf; + struct page *pg; + int i; + + buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == buf->vaddr) { + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + return -ENOMEM; + } + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)buf->vaddr, + nr_pages << PAGE_SHIFT); + + memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); + buf->nr_pages = nr_pages; + + buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist)); + if (NULL == buf->sglist) + goto vzalloc_err; + + sg_init_table(buf->sglist, buf->nr_pages); + for (i = 0; i < buf->nr_pages; i++) { + pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE); + if (NULL == pg) + goto vmalloc_to_page_err; + sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0); + } + return 0; + +vmalloc_to_page_err: + vfree(buf->sglist); + buf->sglist = NULL; +vzalloc_err: + vfree(buf->vaddr); + buf->vaddr = NULL; + return -ENOMEM; +} + +static int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev) +{ + struct cx23885_audio_buffer *buf = dev->buf; + + buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, + buf->nr_pages, PCI_DMA_FROMDEVICE); + + if (0 == buf->sglen) { + pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +static int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev) +{ + struct cx23885_audio_buffer *buf = dev->buf; + + if (!buf->sglen) + return 0; + + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + buf->sglen = 0; + return 0; +} + +static int cx23885_alsa_dma_free(struct cx23885_audio_buffer *buf) +{ + vfree(buf->sglist); + buf->sglist = NULL; + vfree(buf->vaddr); + buf->vaddr = NULL; + return 0; +} + /* * BOARD Specific: Sets audio DMA */ @@ -198,15 +270,18 @@ int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask) static int dsp_buffer_free(struct cx23885_audio_dev *chip) { + struct cx23885_riscmem *risc; + BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); - videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); - videobuf_dma_free(chip->dma_risc); - btcx_riscmem_free(chip->pci, &chip->buf->risc); + cx23885_alsa_dma_unmap(chip); + cx23885_alsa_dma_free(chip->buf); + risc = &chip->buf->risc; + pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); kfree(chip->buf); - chip->dma_risc = NULL; + chip->buf = NULL; chip->dma_size = 0; return 0; @@ -289,6 +364,7 @@ static int snd_cx23885_close(struct snd_pcm_substream *substream) return 0; } + /* * hw_params callback */ @@ -296,8 +372,6 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); - struct videobuf_dmabuf *dma; - struct cx23885_audio_buffer *buf; int ret; @@ -318,19 +392,18 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, return -ENOMEM; buf->bpl = chip->period_size; + chip->buf = buf; - dma = &buf->dma; - videobuf_dma_init(dma); - ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + ret = cx23885_alsa_dma_init(chip, (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_dma_map(&chip->pci->dev, dma); + ret = cx23885_alsa_dma_map(chip); if (ret < 0) goto error; - ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist, + ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); if (ret < 0) goto error; @@ -340,10 +413,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - chip->buf = buf; - chip->dma_risc = dma; - - substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_area = chip->buf->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; @@ -351,6 +421,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, error: kfree(buf); + chip->buf = NULL; return ret; } diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c index c443b7a..877dad8 100644 --- a/drivers/media/pci/cx23885/cx23885-av.c +++ b/drivers/media/pci/cx23885/cx23885-av.c @@ -14,11 +14,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/cx23885-av.h b/drivers/media/pci/cx23885/cx23885-av.h index d2915c3..97f232f 100644 --- a/drivers/media/pci/cx23885/cx23885-av.h +++ b/drivers/media/pci/cx23885/cx23885-av.h @@ -14,11 +14,6 @@ * 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. */ #ifndef _CX23885_AV_H_ diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index c2b6080..88c257d 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -13,10 +13,6 @@ * 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 <linux/init.h> @@ -679,6 +675,11 @@ struct cx23885_board cx23885_boards[] = { .amux = CX25840_AUDIO7, } }, }, + [CX23885_BOARD_DVBSKY_T9580] = { + .name = "DVBSky T9580", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -934,6 +935,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x18ac, .subdevice = 0xdb98, .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2, + }, { + .subvendor = 0x4254, + .subdevice = 0x9580, + .card = CX23885_BOARD_DVBSKY_T9580, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1528,6 +1533,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); mdelay(60); break; + case CX23885_BOARD_DVBSKY_T9580: + /* enable GPIO3-18 pins */ + cx_write(MC417_CTL, 0x00000037); + cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1); + cx23885_gpio_clear(dev, GPIO_2 | GPIO_11); + mdelay(100); + cx23885_gpio_set(dev, GPIO_2 | GPIO_11); + break; } } @@ -1851,6 +1864,14 @@ 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_DVBSKY_T9580: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0x8; /* Serial bus */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->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: @@ -1913,6 +1934,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_AVERMEDIA_HC81R: case CX23885_BOARD_TBS_6980: case CX23885_BOARD_TBS_6981: + case CX23885_BOARD_DVBSKY_T9580: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); @@ -1970,5 +1992,3 @@ void cx23885_card_setup(struct cx23885_dev *dev) } } } - -/* ------------------------------------------------------------------ */ diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index edcd79d..331edda 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -13,10 +13,6 @@ * 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 <linux/init.h> @@ -420,39 +416,23 @@ static int cx23885_risc_decode(u32 risc) return incr[risc >> 28] ? incr[risc >> 28] : 1; } -void cx23885_wakeup(struct cx23885_tsport *port, +static void cx23885_wakeup(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_dev *dev = port->dev; struct cx23885_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) - break; - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - - /* count comes from the hw and is is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - 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; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - if (bc != 1) - printk(KERN_WARNING "%s: %d buffers handled (should be 1)\n", - __func__, bc); + return; + buf = list_entry(q->active.next, + struct cx23885_buffer, queue); + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.sequence = q->count++; + dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, + count, q->count); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } int cx23885_sram_channel_setup(struct cx23885_dev *dev, @@ -482,8 +462,8 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, lines = 6; BUG_ON(lines < 2); - cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - cx_write(8 + 4, 8); + cx_write(8 + 0, RISC_JUMP | RISC_CNT_RESET); + cx_write(8 + 4, 12); cx_write(8 + 8, 0); /* write CDT */ @@ -590,7 +570,7 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev, } static void cx23885_risc_disasm(struct cx23885_tsport *port, - struct btcx_riscmem *risc) + struct cx23885_riscmem *risc) { struct cx23885_dev *dev = port->dev; unsigned int i, j, n; @@ -699,10 +679,6 @@ static int get_resources(struct cx23885_dev *dev) return -EBUSY; } -static void cx23885_timeout(unsigned long data); -int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value); - static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno) { @@ -719,11 +695,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, port->nr = portno; INIT_LIST_HEAD(&port->mpegq.active); - INIT_LIST_HEAD(&port->mpegq.queued); - port->mpegq.timeout.function = cx23885_timeout; - port->mpegq.timeout.data = (unsigned long)port; - init_timer(&port->mpegq.timeout); - mutex_init(&port->frontends.lock); INIT_LIST_HEAD(&port->frontends.felist); port->frontends.active_fe_id = 0; @@ -776,9 +747,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, BUG(); } - cx23885_risc_stopper(dev->pci, &port->mpegq.stopper, - port->reg_dma_ctl, port->dma_ctl_val, 0x00); - return 0; } @@ -1089,11 +1057,18 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, - unsigned int lines, unsigned int lpi) + unsigned int lines, unsigned int lpi, bool jump) { struct scatterlist *sg; unsigned int line, todo, sol; + + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(0); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + } + /* sync instruction */ if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); @@ -1146,14 +1121,13 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, return rp; } -int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions, fields; __le32 *rp; - int rc; fields = 0; if (UNSET != top_offset) @@ -1168,19 +1142,20 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + instructions += 5; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; if (UNSET != top_offset) rp = cx23885_risc_field(rp, sglist, top_offset, 0, - bpl, padding, lines, 0); + bpl, padding, lines, 0, true); if (UNSET != bottom_offset) rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, - bpl, padding, lines, 0); + bpl, padding, lines, 0, UNSET == top_offset); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1189,14 +1164,13 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, } int cx23885_risc_databuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi) { u32 instructions; __le32 *rp; - int rc; /* estimate risc mem: worst case is one write per page border + one write per scan line + syncs + jump (all 2 dwords). Here @@ -1204,16 +1178,17 @@ int cx23885_risc_databuffer(struct pci_dev *pci, than PAGE_SIZE */ /* Jump and write need an extra dword */ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; - instructions += 1; + instructions += 4; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, - bpl, 0, lines, lpi); + bpl, 0, lines, lpi, lpi == 0); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1221,14 +1196,13 @@ int cx23885_risc_databuffer(struct pci_dev *pci, return 0; } -int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx23885_risc_vbibuffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions, fields; __le32 *rp; - int rc; fields = 0; if (UNSET != top_offset) @@ -1243,22 +1217,23 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + instructions += 5; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; /* Sync to line 6, so US CC line 21 will appear in line '12' * in the userland vbi payload */ if (UNSET != top_offset) - rp = cx23885_risc_field(rp, sglist, top_offset, 6, - bpl, padding, lines, 0); + rp = cx23885_risc_field(rp, sglist, top_offset, 0, + bpl, padding, lines, 0, true); if (UNSET != bottom_offset) - rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207, - bpl, padding, lines, 0); + rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, + bpl, padding, lines, 0, UNSET == top_offset); @@ -1269,38 +1244,12 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, } -int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value) +void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf) { - __le32 *rp; - int rc; - - rc = btcx_riscmem_alloc(pci, risc, 4*16); - if (rc < 0) - return rc; - - /* write risc instructions */ - rp = risc->cpu; - *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2); - *(rp++) = cpu_to_le32(reg); - *(rp++) = cpu_to_le32(value); - *(rp++) = cpu_to_le32(mask); - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc->dma); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - return 0; -} - -void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + struct cx23885_riscmem *risc = &buf->risc; BUG_ON(in_interrupt()); - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma); } static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) @@ -1355,7 +1304,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk)); } -static int cx23885_start_dma(struct cx23885_tsport *port, +int cx23885_start_dma(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, struct cx23885_buffer *buf) { @@ -1363,7 +1312,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, u32 reg; dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, - buf->vb.width, buf->vb.height, buf->vb.field); + dev->width, dev->height, dev->field); /* Stop the fifo and risc engine for this port */ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); @@ -1379,7 +1328,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, } /* write TS length to chip */ - cx_write(port->reg_lngth, buf->vb.width); + cx_write(port->reg_lngth, port->ts_packet_size); if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) && (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) { @@ -1408,7 +1357,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, /* NOTE: this is 2 (reserved) for portb, does it matter? */ /* reset counter to zero */ cx_write(port->reg_gpcnt_ctl, 3); - q->count = 1; + q->count = 0; /* Set VIDB pins to input */ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { @@ -1497,134 +1446,83 @@ static int cx23885_stop_dma(struct cx23885_tsport *port) return 0; } -int cx23885_restart_queue(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q) -{ - struct cx23885_dev *dev = port->dev; - struct cx23885_buffer *buf; - - dprintk(5, "%s()\n", __func__); - if (list_empty(&q->active)) { - struct cx23885_buffer *prev; - prev = NULL; - - dprintk(5, "%s() queue is empty\n", __func__); - - for (;;) { - if (list_empty(&q->queued)) - return 0; - buf = list_entry(q->queued.next, struct cx23885_buffer, - vb.queue); - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx23885_start_dma(port, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(5, "[%p/%d] restart_queue - f/active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(5, "[%p/%d] restart_queue - m/active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } - return 0; - } - - buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_dma(port, q, buf); - list_for_each_entry(buf, &q->active, vb.queue) - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - return 0; -} - /* ------------------------------------------------------------------ */ -int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, - struct cx23885_buffer *buf, enum v4l2_field field) +int cx23885_buf_prepare(struct cx23885_buffer *buf, struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; int size = port->ts_packet_size * port->ts_packet_count; + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0); int rc; dprintk(1, "%s: %p\n", __func__, buf); - if (0 != buf->vb.baddr && buf->vb.bsize < size) + if (vb2_plane_size(&buf->vb, 0) < size) return -EINVAL; + vb2_set_plane_payload(&buf->vb, 0, size); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - buf->vb.width = port->ts_packet_size; - buf->vb.height = port->ts_packet_count; - buf->vb.size = size; - buf->vb.field = field /*V4L2_FIELD_TOP*/; - - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - cx23885_risc_databuffer(dev->pci, &buf->risc, - videobuf_to_dma(&buf->vb)->sglist, - buf->vb.width, buf->vb.height, 0); - } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!rc) + return -EIO; - fail: - cx23885_free_buffer(q, buf); - return rc; + cx23885_risc_databuffer(dev->pci, &buf->risc, + sgt->sgl, + port->ts_packet_size, port->ts_packet_count, 0); + return 0; } +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) { struct cx23885_buffer *prev; struct cx23885_dev *dev = port->dev; struct cx23885_dmaqueue *cx88q = &port->mpegq; + unsigned long flags; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + spin_lock_irqsave(&dev->slock, flags); if (list_empty(&cx88q->active)) { - dprintk(1, "queue is empty - first active\n"); - list_add_tail(&buf->vb.queue, &cx88q->active); - cx23885_start_dma(port, cx88q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = cx88q->count++; - mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT); + list_add_tail(&buf->queue, &cx88q->active); dprintk(1, "[%p/%d] %s - first active\n", - buf, buf->vb.i, __func__); + buf, buf->vb.v4l2_buf.index, __func__); } else { - dprintk(1, "queue is not empty - append to active\n"); + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(cx88q->active.prev, struct cx23885_buffer, - vb.queue); - list_add_tail(&buf->vb.queue, &cx88q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = cx88q->count++; + queue); + list_add_tail(&buf->queue, &cx88q->active); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */ dprintk(1, "[%p/%d] %s - append to active\n", - buf, buf->vb.i, __func__); + buf, buf->vb.v4l2_buf.index, __func__); } + spin_unlock_irqrestore(&dev->slock, flags); } /* ----------------------------------------------------------- */ -static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, - int restart) +static void do_cancel_buffers(struct cx23885_tsport *port, char *reason) { struct cx23885_dev *dev = port->dev; struct cx23885_dmaqueue *q = &port->mpegq; @@ -1634,16 +1532,11 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, spin_lock_irqsave(&port->slock, flags); while (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); + queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); dprintk(1, "[%p/%d] %s - dma=0x%08lx\n", - buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); - } - if (restart) { - dprintk(1, "restarting queue\n"); - cx23885_restart_queue(port, q); + buf, buf->vb.v4l2_buf.index, reason, (unsigned long)buf->risc.dma); } spin_unlock_irqrestore(&port->slock, flags); } @@ -1651,27 +1544,10 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, void cx23885_cancel_buffers(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; - struct cx23885_dmaqueue *q = &port->mpegq; - - dprintk(1, "%s()\n", __func__); - del_timer_sync(&q->timeout); - cx23885_stop_dma(port); - do_cancel_buffers(port, "cancel", 0); -} - -static void cx23885_timeout(unsigned long data) -{ - struct cx23885_tsport *port = (struct cx23885_tsport *)data; - struct cx23885_dev *dev = port->dev; dprintk(1, "%s()\n", __func__); - - if (debug > 5) - cx23885_sram_channel_dump(dev, - &dev->sram_channels[port->sram_chno]); - cx23885_stop_dma(port); - do_cancel_buffers(port, "timeout", 1); + do_cancel_buffers(port, "cancel"); } int cx23885_irq_417(struct cx23885_dev *dev, u32 status) @@ -1721,11 +1597,6 @@ int cx23885_irq_417(struct cx23885_dev *dev, u32 status) spin_lock(&port->slock); cx23885_wakeup(port, &port->mpegq, count); spin_unlock(&port->slock); - } else if (status & VID_B_MSK_RISCI2) { - dprintk(7, " VID_B_MSK_RISCI2\n"); - spin_lock(&port->slock); - cx23885_restart_queue(port, &port->mpegq); - spin_unlock(&port->slock); } if (status) { cx_write(port->reg_ts_int_stat, status); @@ -1777,14 +1648,6 @@ static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) cx23885_wakeup(port, &port->mpegq, count); spin_unlock(&port->slock); - } else if (status & VID_BC_MSK_RISCI2) { - - dprintk(7, " (RISCI2 0x%08x)\n", VID_BC_MSK_RISCI2); - - spin_lock(&port->slock); - cx23885_restart_queue(port, &port->mpegq); - spin_unlock(&port->slock); - } if (status) { cx_write(port->reg_ts_int_stat, status); @@ -2087,6 +1950,7 @@ static int cx23885_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { struct cx23885_dev *dev; + struct v4l2_ctrl_handler *hdl; int err; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -2097,6 +1961,14 @@ static int cx23885_initdev(struct pci_dev *pci_dev, if (err < 0) goto fail_free; + hdl = &dev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 6); + if (hdl->error) { + err = hdl->error; + goto fail_ctrl; + } + dev->v4l2_dev.ctrl_handler = hdl; + /* Prepare to handle notifications from subdevices */ cx23885_v4l2_dev_notify_init(dev); @@ -2104,12 +1976,12 @@ static int cx23885_initdev(struct pci_dev *pci_dev, dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; - goto fail_unreg; + goto fail_ctrl; } if (cx23885_dev_setup(dev) < 0) { err = -EINVAL; - goto fail_unreg; + goto fail_ctrl; } /* print pci info */ @@ -2157,7 +2029,8 @@ static int cx23885_initdev(struct pci_dev *pci_dev, fail_irq: cx23885_dev_unregister(dev); -fail_unreg: +fail_ctrl: + v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(&dev->v4l2_dev); fail_free: kfree(dev); @@ -2180,6 +2053,7 @@ static void cx23885_finidev(struct pci_dev *pci_dev) free_irq(pci_dev->irq, dev); cx23885_dev_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 968fecc..13734b8 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -13,10 +13,6 @@ * 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 <linux/module.h> @@ -73,6 +69,10 @@ #include "a8293.h" #include "mb86a20s.h" #include "si2165.h" +#include "si2168.h" +#include "si2157.h" +#include "m88ds3103.h" +#include "m88ts2022.h" static unsigned int debug; @@ -91,59 +91,95 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* ------------------------------------------------------------------ */ -static int dvb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_tsport *port = q->priv_data; + struct cx23885_tsport *port = q->drv_priv; port->ts_packet_size = 188 * 4; port->ts_packet_count = 32; - - *size = port->ts_packet_size * port->ts_packet_count; - *count = 32; + *num_planes = 1; + sizes[0] = port->ts_packet_size * port->ts_packet_count; + *num_buffers = 32; return 0; } -static int dvb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) + +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_tsport *port = q->priv_data; - return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = + container_of(vb, struct cx23885_buffer, vb); + + return cx23885_buf_prepare(buf, port); } -static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_tsport *port = q->priv_data; - cx23885_buf_queue(port, (struct cx23885_buffer *)vb); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_dev *dev = port->dev; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(dev, buf); + + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void dvb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_queue(struct vb2_buffer *vb) { - cx23885_free_buffer(q, (struct cx23885_buffer *)vb); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + + cx23885_buf_queue(port, buf); } static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) { - struct videobuf_dvb_frontends *f; - struct videobuf_dvb_frontend *fe; + struct vb2_dvb_frontends *f; + struct vb2_dvb_frontend *fe; f = &port->frontends; if (f->gate <= 1) /* undefined or fe0 */ - fe = videobuf_dvb_get_frontend(f, 1); + fe = vb2_dvb_get_frontend(f, 1); else - fe = videobuf_dvb_get_frontend(f, f->gate); + fe = vb2_dvb_get_frontend(f, f->gate); if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); } -static struct videobuf_queue_ops dvb_qops = { - .buf_setup = dvb_buf_setup, - .buf_prepare = dvb_buf_prepare, - .buf_queue = dvb_buf_queue, - .buf_release = dvb_buf_release, +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct cx23885_tsport *port = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &port->mpegq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + cx23885_start_dma(port, dmaq, buf); + return 0; +} + +static void cx23885_stop_streaming(struct vb2_queue *q) +{ + struct cx23885_tsport *port = q->drv_priv; + + cx23885_cancel_buffers(port); +} + +static struct vb2_ops dvb_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, }; static struct s5h1409_config hauppauge_generic_config = { @@ -551,6 +587,35 @@ static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static int dvbsky_t9580_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); + + switch (voltage) { + case SEC_VOLTAGE_13: + cx23885_gpio_set(dev, GPIO_1); + cx23885_gpio_clear(dev, GPIO_0); + break; + case SEC_VOLTAGE_18: + cx23885_gpio_set(dev, GPIO_1); + cx23885_gpio_set(dev, GPIO_0); + break; + case SEC_VOLTAGE_OFF: + cx23885_gpio_clear(dev, GPIO_1); + cx23885_gpio_clear(dev, GPIO_0); + break; + } + + /* call the frontend set_voltage function */ + port->fe_set_voltage(fe, voltage); + + return 0; +} + static int cx23885_dvb_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -715,6 +780,19 @@ static const struct si2165_config hauppauge_hvr4400_si2165_config = { .ref_freq_Hz = 16000000, }; +static const struct m88ds3103_config dvbsky_t9580_m88ds3103_config = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .lnb_en_pol = 1, + .lnb_hv_pol = 0, + .agc = 0x99, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -863,16 +941,23 @@ static int dvb_register(struct cx23885_tsport *port) struct dib7000p_ops dib7000p_ops; struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; - struct videobuf_dvb_frontend *fe0, *fe1 = NULL; + struct vb2_dvb_frontend *fe0, *fe1 = NULL; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + struct m88ts2022_config m88ts2022_config; + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client_demod; + struct i2c_client *client_tuner; int mfe_shared = 0; /* bus not shared by default */ int ret; /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); + fe0 = vb2_dvb_get_frontend(&port->frontends, 1); if (!fe0) return -EINVAL; - /* init struct videobuf_dvb */ + /* init struct vb2_dvb */ fe0->dvb.name = dev->name; /* multi-frontend gate control is undefined or defaults to fe0 */ @@ -1392,7 +1477,7 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend); } /* MFE frontend 2 */ - fe1 = videobuf_dvb_get_frontend(&port->frontends, 2); + fe1 = vb2_dvb_get_frontend(&port->frontends, 2); if (fe1 == NULL) goto frontend_detach; /* DVB-C init */ @@ -1491,7 +1576,7 @@ static int dvb_register(struct cx23885_tsport *port) &hauppauge_hvr4400_si2165_config, &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) { - fe0->dvb.frontend->ops.i2c_gate_ctrl = 0; + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; if (!dvb_attach(tda18271_attach, fe0->dvb.frontend, 0x60, &i2c_bus2->i2c_adap, @@ -1501,6 +1586,97 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_DVBSKY_T9580: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + switch (port->nr) { + /* port b - satellite */ + case 1: + /* attach frontend */ + fe0->dvb.frontend = dvb_attach(m88ds3103_attach, + &dvbsky_t9580_m88ds3103_config, + &i2c_bus2->i2c_adap, &adapter); + if (fe0->dvb.frontend == NULL) + break; + + /* attach tuner */ + m88ts2022_config.fe = fe0->dvb.frontend; + m88ts2022_config.clock = 27000000; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &m88ts2022_config; + request_module(info.type); + client_tuner = i2c_new_device(adapter, &info); + if (client_tuner == NULL || + client_tuner->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + goto frontend_detach; + } + + /* delegate signal strength measurement to tuner */ + fe0->dvb.frontend->ops.read_signal_strength = + fe0->dvb.frontend->ops.tuner_ops.get_rf_strength; + + /* + * for setting the voltage we need to set GPIOs on + * the card. + */ + port->fe_set_voltage = + fe0->dvb.frontend->ops.set_voltage; + fe0->dvb.frontend->ops.set_voltage = + dvbsky_t9580_set_voltage; + + port->i2c_client_tuner = client_tuner; + + break; + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (client_demod == NULL || + client_demod->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + si2157_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(adapter, &info); + if (client_tuner == NULL || + client_tuner->dev.driver == NULL) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -1532,7 +1708,7 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ - ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, + ret = vb2_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, mfe_shared); if (ret) goto frontend_detach; @@ -1575,20 +1751,36 @@ static int dvb_register(struct cx23885_tsport *port) memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); break; } + case CX23885_BOARD_DVBSKY_T9580: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr > 2) + break; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + printk(KERN_INFO "DVBSky T9580 port %d MAC address: %pM\n", + port->nr, eeprom + 0xc0 + (port->nr-1) * 8); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + + (port->nr-1) * 8, 6); + break; + } } return ret; frontend_detach: port->gate_ctrl = NULL; - videobuf_dvb_dealloc_frontends(&port->frontends); + vb2_dvb_dealloc_frontends(&port->frontends); return -EINVAL; } int cx23885_dvb_register(struct cx23885_tsport *port) { - struct videobuf_dvb_frontend *fe0; + struct vb2_dvb_frontend *fe0; struct cx23885_dev *dev = port->dev; int err, i; @@ -1605,13 +1797,15 @@ int cx23885_dvb_register(struct cx23885_tsport *port) port->num_frontends); for (i = 1; i <= port->num_frontends; i++) { - if (videobuf_dvb_alloc_frontend( + struct vb2_queue *q; + + if (vb2_dvb_alloc_frontend( &port->frontends, i) == NULL) { printk(KERN_ERR "%s() failed to alloc\n", __func__); return -ENOMEM; } - fe0 = videobuf_dvb_get_frontend(&port->frontends, i); + fe0 = vb2_dvb_get_frontend(&port->frontends, i); if (!fe0) err = -EINVAL; @@ -1627,10 +1821,21 @@ int cx23885_dvb_register(struct cx23885_tsport *port) /* dvb stuff */ /* We have to init the queue for each frontend on a port. */ printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name); - videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops, - &dev->pci->dev, &port->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, - sizeof(struct cx23885_buffer), port, NULL); + q = &fe0->dvb.dvbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = port; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &dvb_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + return err; } err = dvb_register(port); if (err != 0) @@ -1642,18 +1847,27 @@ int cx23885_dvb_register(struct cx23885_tsport *port) int cx23885_dvb_unregister(struct cx23885_tsport *port) { - struct videobuf_dvb_frontend *fe0; - - /* FIXME: in an error condition where the we have - * an expected number of frontends (attach problem) - * then this might not clean up correctly, if 1 - * is invalid. - * This comment only applies to future boards IF they - * implement MFE support. - */ - fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); + struct vb2_dvb_frontend *fe0; + struct i2c_client *client; + + /* remove I2C client for tuner */ + client = port->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C client for demodulator */ + client = port->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + fe0 = vb2_dvb_get_frontend(&port->frontends, 1); + if (fe0 && fe0->dvb.frontend) - videobuf_dvb_unregister_bus(&port->frontends); + vb2_dvb_unregister_bus(&port->frontends); switch (port->dev->board) { case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: @@ -1668,4 +1882,3 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) return 0; } - diff --git a/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c index 5444cc5..6f817d8 100644 --- a/drivers/media/pci/cx23885/cx23885-f300.c +++ b/drivers/media/pci/cx23885/cx23885-f300.c @@ -22,10 +22,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c index 4887314..fd71306 100644 --- a/drivers/media/pci/cx23885/cx23885-i2c.c +++ b/drivers/media/pci/cx23885/cx23885-i2c.c @@ -13,10 +13,6 @@ * 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 <linux/module.h> @@ -386,11 +382,3 @@ void cx23885_av_clk(struct cx23885_dev *dev, int enable) i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); } - -/* ----------------------------------------------------------------------- */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 1940c18..9d37fe6 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -28,11 +28,6 @@ * 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 <linux/slab.h> diff --git a/drivers/media/pci/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h index 87dc44e..6199c7e 100644 --- a/drivers/media/pci/cx23885/cx23885-input.h +++ b/drivers/media/pci/cx23885/cx23885-input.h @@ -14,11 +14,6 @@ * 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. */ #ifndef _CX23885_INPUT_H_ diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c index 271d69d..d2cdd40 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.c +++ b/drivers/media/pci/cx23885/cx23885-ioctl.c @@ -15,10 +15,6 @@ * 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 "cx23885.h" @@ -28,7 +24,7 @@ int cx23885_g_chip_info(struct file *file, void *fh, struct v4l2_dbg_chip_info *chip) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (chip->match.addr > 1) return -EINVAL; @@ -64,7 +60,7 @@ static int cx23417_g_register(struct cx23885_dev *dev, int cx23885_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (reg->match.addr > 1) return -EINVAL; @@ -96,7 +92,7 @@ static int cx23417_s_register(struct cx23885_dev *dev, int cx23885_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (reg->match.addr > 1) return -EINVAL; diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h index 92d9f07..cc5dbb6 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.h +++ b/drivers/media/pci/cx23885/cx23885-ioctl.h @@ -15,10 +15,6 @@ * 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 _CX23885_IOCTL_H_ diff --git a/drivers/media/pci/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c index bfef193..89dc4cc 100644 --- a/drivers/media/pci/cx23885/cx23885-ir.c +++ b/drivers/media/pci/cx23885/cx23885-ir.c @@ -14,11 +14,6 @@ * 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/v4l2-device.h> diff --git a/drivers/media/pci/cx23885/cx23885-ir.h b/drivers/media/pci/cx23885/cx23885-ir.h index 0c9d8bd..8e93d1f 100644 --- a/drivers/media/pci/cx23885/cx23885-ir.h +++ b/drivers/media/pci/cx23885/cx23885-ir.h @@ -14,11 +14,6 @@ * 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. */ #ifndef _CX23885_IR_H_ diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h index a99936e..2d3cbaf 100644 --- a/drivers/media/pci/cx23885/cx23885-reg.h +++ b/drivers/media/pci/cx23885/cx23885-reg.h @@ -13,10 +13,6 @@ * 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 _CX23885_REG_H_ diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c index a1154f0..a7c6ef8 100644 --- a/drivers/media/pci/cx23885/cx23885-vbi.c +++ b/drivers/media/pci/cx23885/cx23885-vbi.c @@ -13,10 +13,6 @@ * 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 <linux/kernel.h> @@ -42,33 +38,32 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ #define VBI_LINE_LENGTH 1440 -#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ -#define NTSC_VBI_END_LINE 21 -#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1) +#define VBI_NTSC_LINE_COUNT 12 +#define VBI_PAL_LINE_COUNT 18 int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); + f->fmt.vbi.sampling_rate = 27000000; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 0; + f->fmt.vbi.flags = 0; if (dev->tvnorm & V4L2_STD_525_60) { /* ntsc */ - f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; - f->fmt.vbi.sampling_rate = 27000000; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 0; - f->fmt.vbi.flags = 0; - f->fmt.vbi.start[0] = 10; - f->fmt.vbi.count[0] = 17; - f->fmt.vbi.start[1] = 263 + 10 + 1; - f->fmt.vbi.count[1] = 17; + f->fmt.vbi.start[0] = V4L2_VBI_ITU_525_F1_START + 9; + f->fmt.vbi.start[1] = V4L2_VBI_ITU_525_F2_START + 9; + f->fmt.vbi.count[0] = VBI_NTSC_LINE_COUNT; + f->fmt.vbi.count[1] = VBI_NTSC_LINE_COUNT; } else if (dev->tvnorm & V4L2_STD_625_50) { /* pal */ - f->fmt.vbi.sampling_rate = 35468950; - f->fmt.vbi.start[0] = 7 - 1; - f->fmt.vbi.start[1] = 319 - 1; + f->fmt.vbi.start[0] = V4L2_VBI_ITU_625_F1_START + 5; + f->fmt.vbi.start[1] = V4L2_VBI_ITU_625_F2_START + 5; + f->fmt.vbi.count[0] = VBI_PAL_LINE_COUNT; + f->fmt.vbi.count[1] = VBI_PAL_LINE_COUNT; } return 0; @@ -94,15 +89,6 @@ int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status) handled++; } - if (status & VID_BC_MSK_VBI_RISCI2) { - dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__); - dprintk(2, "stopper vbi\n"); - spin_lock(&dev->slock); - cx23885_restart_vbi_queue(dev, &dev->vbiq); - spin_unlock(&dev->slock); - handled++; - } - return handled; } @@ -114,13 +100,13 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], - buf->vb.width, buf->risc.dma); + VBI_LINE_LENGTH, buf->risc.dma); /* reset counter */ cx_write(VID_A_GPCNT_CTL, 3); cx_write(VID_A_VBI_CTRL, 3); cx_write(VBI_A_GPCNT_CTL, 3); - q->count = 1; + q->count = 0; /* enable irq */ cx23885_irq_add_enable(dev, 0x01); @@ -133,163 +119,153 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, return 0; } +/* ------------------------------------------------------------------ */ -int cx23885_restart_vbi_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_buffer *buf; - struct list_head *item; - - if (list_empty(&q->active)) - return 0; - - buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_vbi_dma(dev, q, buf); - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx23885_buffer, vb.queue); - buf->count = q->count++; - } - mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); + struct cx23885_dev *dev = q->drv_priv; + unsigned lines = VBI_PAL_LINE_COUNT; + + if (dev->tvnorm & V4L2_STD_525_60) + lines = VBI_NTSC_LINE_COUNT; + *num_planes = 1; + sizes[0] = lines * VBI_LINE_LENGTH * 2; return 0; } -void cx23885_vbi_timeout(unsigned long data) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_dev *dev = (struct cx23885_dev *)data; - struct cx23885_dmaqueue *q = &dev->vbiq; - struct cx23885_buffer *buf; - unsigned long flags; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + unsigned lines = VBI_PAL_LINE_COUNT; + int ret; - /* Stop the VBI engine */ - cx_clear(VID_A_DMA_CTL, 0x22); + if (dev->tvnorm & V4L2_STD_525_60) + lines = VBI_NTSC_LINE_COUNT; - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name, - buf, buf->vb.i, (unsigned long)buf->risc.dma); - } - cx23885_restart_vbi_queue(dev, q); - spin_unlock_irqrestore(&dev->slock, flags); -} + if (vb2_plane_size(vb, 0) < lines * VBI_LINE_LENGTH * 2) + return -EINVAL; + vb2_set_plane_payload(vb, 0, lines * VBI_LINE_LENGTH * 2); -/* ------------------------------------------------------------------ */ -#define VBI_LINE_LENGTH 1440 -#define VBI_LINE_COUNT 17 + ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; + cx23885_risc_vbibuffer(dev->pci, &buf->risc, + sgt->sgl, + 0, VBI_LINE_LENGTH * lines, + VBI_LINE_LENGTH, 0, + lines); return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - unsigned int size; - int rc; - - size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < size) - return -EINVAL; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - buf->vb.width = VBI_LINE_LENGTH; - buf->vb.height = VBI_LINE_COUNT; - buf->vb.size = size; - buf->vb.field = V4L2_FIELD_SEQ_TB; - - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - cx23885_risc_vbibuffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->vb.width * buf->vb.height, - buf->vb.width, 0, - buf->vb.height); - } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); - fail: - cx23885_free_buffer(q, buf); - return rc; + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void buffer_queue(struct vb2_buffer *vb) { - struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); - struct cx23885_buffer *prev; - struct cx23885_fh *fh = vq->priv_data; - struct cx23885_dev *dev = fh->dev; - struct cx23885_dmaqueue *q = &dev->vbiq; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); + struct cx23885_buffer *prev; + struct cx23885_dmaqueue *q = &dev->vbiq; + unsigned long flags; + + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx23885_start_vbi_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->queue, &q->active); + spin_unlock_irqrestore(&dev->slock, flags); dprintk(2, "[%p/%d] vbi_queue - first active\n", - buf, buf->vb.i); + buf, buf->vb.v4l2_buf.index); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx23885_buffer, - vb.queue); - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; + queue); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->queue, &q->active); + spin_unlock_irqrestore(&dev->slock, flags); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */ dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); + buf, buf->vb.v4l2_buf.index); } } -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) { - struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vbiq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); - cx23885_free_buffer(q, buf); + cx23885_start_vbi_dma(dev, dmaq, buf); + return 0; } -struct videobuf_queue_ops cx23885_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, -}; +static void cx23885_stop_streaming(struct vb2_queue *q) +{ + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vbiq; + unsigned long flags; -/* ------------------------------------------------------------------ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ + cx_clear(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */ + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dev->slock, flags); +} + + +struct vb2_ops cx23885_vbi_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 91e4cb4..682a4f9 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -13,10 +13,6 @@ * 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 <linux/init.h> @@ -35,6 +31,7 @@ #include "cx23885-video.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "cx23885-ioctl.h" #include "tuner-xc2028.h" @@ -48,15 +45,12 @@ MODULE_LICENSE("GPL"); static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); -module_param_array(radio_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); -MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug; module_param(video_debug, int, 0644); @@ -79,77 +73,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* static data */ #define FORMAT_FLAGS_PACKED 0x01 -#if 0 -static struct cx23885_fmt formats[] = { - { - .name = "8 bpp, gray", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "15 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_RGB555, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "15 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB555X, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "16 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "16 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB565X, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "24 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_BGR24, - .depth = 24, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "32 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_BGR32, - .depth = 32, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "32 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB32, - .depth = 32, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, -}; -#else static struct cx23885_fmt formats[] = { { -#if 0 - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { -#endif .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .flags = FORMAT_FLAGS_PACKED, } }; -#endif static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) { @@ -158,163 +89,27 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) for (i = 0; i < ARRAY_SIZE(formats); i++) if (formats[i].fourcc == fourcc) return formats+i; - - printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__, - (fourcc & 0xff), - ((fourcc >> 8) & 0xff), - ((fourcc >> 16) & 0xff), - ((fourcc >> 24) & 0xff) - ); return NULL; } /* ------------------------------------------------------------------- */ -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static struct cx23885_ctrl cx23885_ctls[] = { - /* --- video --- */ - { - .v = { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = LUMA_CTRL, - .mask = 0x00ff, - .shift = 0, - }, { - .v = { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = LUMA_CTRL, - .mask = 0xff00, - .shift = 8, - }, { - .v = { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = -127, - .maximum = 128, - .step = 1, - .default_value = 0x0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = CHROMA_CTRL, - .mask = 0xff0000, - .shift = 16, - }, { - /* strictly, this only describes only U saturation. - * V saturation is handled specially through code. - */ - .v = { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = CHROMA_CTRL, - .mask = 0x00ff, - .shift = 0, - }, { - /* --- audio --- */ - .v = { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - .reg = PATH1_CTL1, - .mask = (0x1f << 24), - .shift = 24, - }, { - .v = { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535 / 100, - .default_value = 65535, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = PATH1_VOL_CTL, - .mask = 0xff, - .shift = 0, - } -}; -static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); - -/* Must be sorted from low to high control ID! */ -static const u32 cx23885_user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_MUTE, - 0 -}; - -static const u32 *ctrl_classes[] = { - cx23885_user_ctrls, - NULL -}; - void cx23885_video_wakeup(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) - break; - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - - /* count comes from the hw and is is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - - 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; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } + if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - if (bc != 1) - printk(KERN_ERR "%s: %d buffers handled (should be 1)\n", - __func__, bc); + return; + buf = list_entry(q->active.next, + struct cx23885_buffer, queue); + + buf->vb.v4l2_buf.sequence = q->count++; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, + count, q->count); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) @@ -324,6 +119,12 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) (unsigned int)norm, v4l2_norm_to_name(norm)); + if (dev->tvnorm != norm) { + if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) || + vb2_is_busy(&dev->vb2_mpegq)) + return -EBUSY; + } + dev->tvnorm = norm; call_all(dev, video, s_std, norm); @@ -345,79 +146,13 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; + vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s (%s)", cx23885_boards[dev->board].name, type); video_set_drvdata(vfd, dev); return vfd; } -static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) -{ - int i; - - if (qctrl->id < V4L2_CID_BASE || - qctrl->id >= V4L2_CID_LASTP1) - return -EINVAL; - for (i = 0; i < CX23885_CTLS; i++) - if (cx23885_ctls[i].v.id == qctrl->id) - break; - if (i == CX23885_CTLS) { - *qctrl = no_ctl; - return 0; - } - *qctrl = cx23885_ctls[i].v; - return 0; -} - -/* ------------------------------------------------------------------- */ -/* resource management */ - -static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh, - unsigned int bit) -{ - dprintk(1, "%s()\n", __func__); - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - mutex_lock(&dev->lock); - if (dev->resources & bit) { - /* no, someone else uses it */ - mutex_unlock(&dev->lock); - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - dprintk(1, "res: get %d\n", bit); - mutex_unlock(&dev->lock); - return 1; -} - -static int res_check(struct cx23885_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct cx23885_dev *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, - unsigned int bits) -{ - BUG_ON((fh->resources & bits) != bits); - dprintk(1, "%s()\n", __func__); - - mutex_lock(&dev->lock); - fh->resources &= ~bits; - dev->resources &= ~bits; - dprintk(1, "res: put %d\n", bits); - mutex_unlock(&dev->lock); -} - int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) { /* 8 bit registers, 8 bit values */ @@ -567,7 +302,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, /* reset counter */ cx_write(VID_A_GPCNT_CTL, 3); - q->count = 1; + q->count = 0; /* enable irq */ cx23885_irq_add_enable(dev, 0x01); @@ -580,479 +315,206 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, return 0; } - -static int cx23885_restart_video_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q) -{ - struct cx23885_buffer *buf, *prev; - struct list_head *item; - dprintk(1, "%s()\n", __func__); - - if (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_video_dma(dev, q, buf); - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx23885_buffer, - vb.queue); - buf->count = q->count++; - } - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&q->queued)) - return 0; - buf = list_entry(q->queued.next, struct cx23885_buffer, - vb.queue); - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx23885_start_video_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] restart_queue - first active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ - dprintk(2, "[%p/%d] restart_queue - move to active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } -} - -static int buffer_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_fh *fh = q->priv_data; + struct cx23885_dev *dev = q->drv_priv; - *size = fh->fmt->depth*fh->width*fh->height >> 3; - if (0 == *count) - *count = 32; - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; + *num_planes = 1; + sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3; return 0; } -static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); - int rc, init_buffer = 0; u32 line0_offset, line1_offset; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); int field_tff; + int ret; - BUG_ON(NULL == fh->fmt); - if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) || - fh->height < 32 || fh->height > norm_maxh(dev->tvnorm)) - return -EINVAL; - buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + buf->bpl = (dev->width * dev->fmt->depth) >> 3; + + if (vb2_plane_size(vb, 0) < dev->height * buf->bpl) return -EINVAL; + vb2_set_plane_payload(vb, 0, dev->height * buf->bpl); - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - init_buffer = 1; - } + ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - init_buffer = 1; - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - } + switch (dev->field) { + case V4L2_FIELD_TOP: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, UNSET, + buf->bpl, 0, dev->height); + break; + case V4L2_FIELD_BOTTOM: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, UNSET, 0, + buf->bpl, 0, dev->height); + break; + case V4L2_FIELD_INTERLACED: + if (dev->tvnorm & V4L2_STD_525_60) + /* NTSC or */ + field_tff = 1; + else + field_tff = 0; + + if (cx23885_boards[dev->board].force_bff) + /* PAL / SECAM OR 888 in NTSC MODE */ + field_tff = 0; - if (init_buffer) { - buf->bpl = buf->vb.width * buf->fmt->depth >> 3; - switch (buf->vb.field) { - case V4L2_FIELD_TOP: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, 0, UNSET, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_BOTTOM: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, UNSET, 0, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_INTERLACED: - if (dev->tvnorm & V4L2_STD_NTSC) - /* NTSC or */ - field_tff = 1; - else - field_tff = 0; - - if (cx23885_boards[dev->board].force_bff) - /* PAL / SECAM OR 888 in NTSC MODE */ - field_tff = 0; - - if (field_tff) { - /* cx25840 transmits NTSC bottom field first */ - dprintk(1, "%s() Creating TFF/NTSC risc\n", + if (field_tff) { + /* cx25840 transmits NTSC bottom field first */ + dprintk(1, "%s() Creating TFF/NTSC risc\n", __func__); - line0_offset = buf->bpl; - line1_offset = 0; - } else { - /* All other formats are top field first */ - dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n", + line0_offset = buf->bpl; + line1_offset = 0; + } else { + /* All other formats are top field first */ + dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n", __func__); - line0_offset = 0; - line1_offset = buf->bpl; - } - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, line0_offset, - line1_offset, - buf->bpl, buf->bpl, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_TB: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->bpl * (buf->vb.height >> 1), - buf->bpl, 0, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_BT: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - buf->bpl * (buf->vb.height >> 1), 0, - buf->bpl, 0, - buf->vb.height >> 1); - break; - default: - BUG(); + line0_offset = 0; + line1_offset = buf->bpl; } + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, line0_offset, + line1_offset, + buf->bpl, buf->bpl, + dev->height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + 0, buf->bpl * (dev->height >> 1), + buf->bpl, 0, + dev->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + buf->bpl * (dev->height >> 1), 0, + buf->bpl, 0, + dev->height >> 1); + break; + default: + BUG(); } - dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.i, - fh->width, fh->height, fh->fmt->depth, fh->fmt->name, + dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", + buf, buf->vb.v4l2_buf.index, + dev->width, dev->height, dev->fmt->depth, dev->fmt->name, (unsigned long)buf->risc.dma); - - buf->vb.state = VIDEOBUF_PREPARED; return 0; +} + +static void buffer_finish(struct vb2_buffer *vb) +{ + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); - fail: - cx23885_free_buffer(q, buf); - return rc; + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void buffer_queue(struct vb2_buffer *vb) { + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); struct cx23885_buffer *prev; - struct cx23885_fh *fh = vq->priv_data; - struct cx23885_dev *dev = fh->dev; struct cx23885_dmaqueue *q = &dev->vidq; + unsigned long flags; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + /* add jump to start */ + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", - buf, buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx23885_start_video_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&q->active)) { + list_add_tail(&buf->queue, &q->active); dprintk(2, "[%p/%d] buffer_queue - first active\n", - buf, buf->vb.i); - + buf, buf->vb.v4l2_buf.index); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx23885_buffer, - vb.queue); - if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", - buf, buf->vb.i); - } - } -} - -static void buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) -{ - struct cx23885_buffer *buf = container_of(vb, - struct cx23885_buffer, vb); - - cx23885_free_buffer(q, buf); -} - -static struct videobuf_queue_ops cx23885_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -static struct videobuf_queue *get_queue(struct cx23885_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &fh->vidq; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return &fh->vbiq; - default: - BUG(); - return NULL; - } -} - -static int get_resource(struct cx23885_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return RESOURCE_VBI; - default: - BUG(); - return 0; + queue); + list_add_tail(&buf->queue, &q->active); + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.v4l2_buf.index); } + spin_unlock_irqrestore(&dev->slock, flags); } -static int video_open(struct file *file) +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) { - struct video_device *vdev = video_devdata(file); - struct cx23885_dev *dev = video_drvdata(file); - struct cx23885_fh *fh; - enum v4l2_buf_type type = 0; - int radio = 0; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - } - - dprintk(1, "open dev=%s radio=%d type=%s\n", - video_device_node_name(vdev), radio, v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - file->private_data = fh; - fh->dev = dev; - fh->radio = radio; - fh->type = type; - fh->width = 320; - fh->height = 240; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); - - videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx23885_buffer), - fh, NULL); - - videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct cx23885_buffer), - fh, NULL); - - - dprintk(1, "post videobuf_queue_init()\n"); + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vidq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + cx23885_start_video_dma(dev, dmaq, buf); return 0; } -static ssize_t video_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +static void cx23885_stop_streaming(struct vb2_queue *q) { - struct cx23885_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO)) - return -EBUSY; - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (!res_get(fh->dev, fh, RESOURCE_VBI)) - return -EBUSY; - return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, - file->f_flags & O_NONBLOCK); - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_buffer *buf; - unsigned int rc = POLLERR; - - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { - if (!res_get(fh->dev, fh, RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(file, &fh->vbiq, wait); - } - - mutex_lock(&fh->vidq.vb_lock); - if (res_check(fh, RESOURCE_VIDEO)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - goto done; - buf = list_entry(fh->vidq.stream.next, - struct cx23885_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx23885_buffer *)fh->vidq.read_buf; - if (NULL == buf) - goto done; - } - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; - else - rc = 0; -done: - mutex_unlock(&fh->vidq.vb_lock); - return rc; -} - -static int video_release(struct file *file) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - /* turn off overlay */ - if (res_check(fh, RESOURCE_OVERLAY)) { - /* FIXME */ - res_free(dev, fh, RESOURCE_OVERLAY); - } + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vidq; + unsigned long flags; - /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO)) { - videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO); - } - if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } + cx_clear(VID_A_DMA_CTL, 0x11); + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); - /* stop vbi capture */ - if (res_check(fh, RESOURCE_VBI)) { - if (fh->vbiq.streaming) - videobuf_streamoff(&fh->vbiq); - if (fh->vbiq.reading) - videobuf_read_stop(&fh->vbiq); - res_free(dev, fh, RESOURCE_VBI); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - - videobuf_mmap_free(&fh->vidq); - videobuf_mmap_free(&fh->vbiq); - - file->private_data = NULL; - kfree(fh); - - /* We are not putting the tuner to sleep here on exit, because - * we want to use the mpeg encoder in another session to capture - * tuner video. Closing this will result in no video to the encoder. - */ - - return 0; -} - -static int video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_mmap_mapper(get_queue(fh), vma); -} - -/* ------------------------------------------------------------------ */ -/* VIDEO CTRL IOCTLS */ - -int cx23885_get_control(struct cx23885_dev *dev, - struct v4l2_control *ctl) -{ - dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); - call_all(dev, core, g_ctrl, ctl); - return 0; -} - -int cx23885_set_control(struct cx23885_dev *dev, - struct v4l2_control *ctl) -{ - dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__); - call_all(dev, core, s_ctrl, ctl); - - return 0; + spin_unlock_irqrestore(&dev->slock, flags); } -static void init_controls(struct cx23885_dev *dev) -{ - struct v4l2_control ctrl; - int i; - - for (i = 0; i < CX23885_CTLS; i++) { - ctrl.id = cx23885_ctls[i].v.id; - ctrl.value = cx23885_ctls[i].v.default_value; - - cx23885_set_control(dev, &ctrl); - } -} +static struct vb2_ops cx23885_video_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; /* ------------------------------------------------------------------ */ /* VIDEO IOCTLS */ @@ -1060,16 +522,17 @@ static void init_controls(struct cx23885_dev *dev) static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = video_drvdata(file); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; + (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1077,7 +540,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); struct cx23885_fmt *fmt; enum v4l2_field field; unsigned int maxw, maxh; @@ -1102,9 +565,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: break; default: - return -EINVAL; + field = V4L2_FIELD_INTERLACED; + break; } f->fmt.pix.field = field; @@ -1114,6 +580,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1121,8 +588,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; int err; @@ -1131,34 +597,44 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (0 != err) return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; + + if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) || + vb2_is_busy(&dev->vb2_mpegq)) + return -EBUSY; + + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, - fh->width, fh->height, fh->vidq.field); + dev->width, dev->height, dev->field); v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); call_all(dev, video, s_mbus_fmt, &mbus_fmt); v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + /* s_mbus_fmt overwrites f->fmt.pix.field, restore it */ + f->fmt.pix.field = dev->field; return 0; } static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); strcpy(cap->driver, "cx23885"); strlcpy(cap->card, cx23885_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_VBI_CAPTURE; + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_VBI) + cap->device_caps |= V4L2_CAP_VBI_CAPTURE; + else + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = cap->device_caps | V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1175,85 +651,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_reqbufs(get_queue(fh), p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_querybuf(get_queue(fh), p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_qbuf(get_queue(fh), p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, - file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - dprintk(1, "%s()\n", __func__); - - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; - if (unlikely(i != fh->type)) - return -EINVAL; - - if (unlikely(!res_get(dev, fh, get_resource(fh)))) - return -EBUSY; - - /* Don't start VBI streaming unless vida streaming - * has already started. - */ - if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) && - ((cx_read(VID_A_DMA_CTL) & 0x11) == 0)) - return -EINVAL; - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - int err, res; - dprintk(1, "%s()\n", __func__); - - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = get_resource(fh); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - res_free(dev, fh, res); - return 0; -} - static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); *id = dev->tvnorm; @@ -1262,14 +662,10 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, tvnorms); - mutex_unlock(&dev->lock); - - return 0; + return cx23885_set_tvnorm(dev, tvnorms); } int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) @@ -1299,16 +695,16 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name, iname[INPUT(n)->type]); + i->std = CX23885_NORMS; if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) || (CX23885_VMUX_CABLE == INPUT(n)->type)) { i->type = V4L2_INPUT_TYPE_TUNER; - i->std = CX23885_NORMS; + i->audioset = 4; + } else { + /* Two selectable audio inputs for non-tv inputs */ + i->audioset = 3; } - /* Two selectable audio inputs for non-tv inputs */ - if (INPUT(n)->type != CX23885_VMUX_TELEVISION) - i->audioset = 0x3; - if (dev->input == n) { /* enum'd input matches our configured input. * Ask the video decoder to process the call @@ -1324,14 +720,14 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); return cx23885_enum_input(dev, i); } int cx23885_get_input(struct file *file, void *priv, unsigned int *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); *i = dev->input; dprintk(1, "%s() returns %d\n", __func__, *i); @@ -1345,7 +741,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) int cx23885_set_input(struct file *file, void *priv, unsigned int i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s(%d)\n", __func__, i); @@ -1357,13 +753,11 @@ int cx23885_set_input(struct file *file, void *priv, unsigned int i) if (INPUT(i)->type == 0) return -EINVAL; - mutex_lock(&dev->lock); cx23885_video_mux(dev, i); /* By default establish the default audio input for the card also */ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */ cx23885_audio_mux(dev, i); - mutex_unlock(&dev->lock); return 0; } @@ -1374,39 +768,32 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_log_status(struct file *file, void *priv) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); - printk(KERN_INFO - "%s/0: ============ START LOG STATUS ============\n", - dev->name); call_all(dev, core, log_status); - printk(KERN_INFO - "%s/0: ============= END LOG STATUS =============\n", - dev->name); return 0; } static int cx23885_query_audinput(struct file *file, void *priv, struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); static const char *iname[] = { [0] = "Baseband L/R 1", [1] = "Baseband L/R 2", + [2] = "TV", }; unsigned int n; dprintk(1, "%s()\n", __func__); n = i->index; - if (n >= 2) + if (n >= 3) return -EINVAL; memset(i, 0, sizeof(*i)); i->index = n; strcpy(i->name, iname[n]); - i->capability = V4L2_AUDCAP_STEREO; - i->mode = V4L2_AUDMODE_AVL; + i->capability = V4L2_AUDCAP_STEREO; return 0; } @@ -1420,9 +807,13 @@ static int vidioc_enum_audinput(struct file *file, void *priv, static int vidioc_g_audinput(struct file *file, void *priv, struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); - i->index = dev->audinput; + if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) || + (CX23885_VMUX_CABLE == INPUT(dev->input)->type)) + i->index = 2; + else + i->index = dev->audinput; dprintk(1, "%s(input=%d)\n", __func__, i->index); return cx23885_query_audinput(file, priv, i); @@ -1431,8 +822,13 @@ static int vidioc_g_audinput(struct file *file, void *priv, static int vidioc_s_audinput(struct file *file, void *priv, const struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - if (i->index >= 2) + struct cx23885_dev *dev = video_drvdata(file); + + if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) || + (CX23885_VMUX_CABLE == INPUT(dev->input)->type)) { + return i->index != 2 ? -EINVAL : 0; + } + if (i->index > 1) return -EINVAL; dprintk(1, "%s(%d)\n", __func__, i->index); @@ -1445,35 +841,10 @@ static int vidioc_s_audinput(struct file *file, void *priv, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (unlikely(qctrl->id == 0)) - return -EINVAL; - return cx23885_ctrl_query(qctrl); -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_get_control(dev, ctl); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_set_control(dev, ctl); -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1489,7 +860,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1504,14 +875,12 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; - /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; call_all(dev, tuner, g_frequency, f); @@ -1521,20 +890,23 @@ static int vidioc_g_frequency(struct file *file, void *priv, static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency *f) { - struct v4l2_control ctrl; + struct v4l2_ctrl *mute; + int old_mute_val = 1; if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL; - mutex_lock(&dev->lock); dev->freq = f->frequency; /* I need to mute audio here */ - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = 1; - cx23885_set_control(dev, &ctrl); + mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (mute) { + old_mute_val = v4l2_ctrl_g_ctrl(mute); + if (!old_mute_val) + v4l2_ctrl_s_ctrl(mute, 1); + } call_all(dev, tuner, s_frequency, f); @@ -1542,10 +914,8 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency msleep(100); /* I need to unmute audio here */ - ctrl.value = 0; - cx23885_set_control(dev, &ctrl); - - mutex_unlock(&dev->lock); + if (old_mute_val == 0) + v4l2_ctrl_s_ctrl(mute, old_mute_val); return 0; } @@ -1553,8 +923,9 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, const struct v4l2_frequency *f) { - struct v4l2_control ctrl; - struct videobuf_dvb_frontend *vfe; + struct v4l2_ctrl *mute; + int old_mute_val = 1; + struct vb2_dvb_frontend *vfe; struct dvb_frontend *fe; struct analog_parameters params = { @@ -1564,21 +935,22 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, .frequency = f->frequency }; - mutex_lock(&dev->lock); dev->freq = f->frequency; /* I need to mute audio here */ - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = 1; - cx23885_set_control(dev, &ctrl); + mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (mute) { + old_mute_val = v4l2_ctrl_g_ctrl(mute); + if (!old_mute_val) + v4l2_ctrl_s_ctrl(mute, 1); + } /* If HVR1850 */ dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__, params.frequency, f->tuner, params.std); - vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1); + vfe = vb2_dvb_get_frontend(&dev->ts2.frontends, 1); if (!vfe) { - mutex_unlock(&dev->lock); return -EINVAL; } @@ -1600,10 +972,8 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, msleep(100); /* I need to unmute audio here */ - ctrl.value = 0; - cx23885_set_control(dev, &ctrl); - - mutex_unlock(&dev->lock); + if (old_mute_val == 0) + v4l2_ctrl_s_ctrl(mute, old_mute_val); return 0; } @@ -1611,8 +981,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, int cx23885_set_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); int ret; switch (dev->board) { @@ -1636,28 +1005,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, /* ----------------------------------------------------------- */ -static void cx23885_vid_timeout(unsigned long data) -{ - struct cx23885_dev *dev = (struct cx23885_dev *)data; - struct cx23885_dmaqueue *q = &dev->vidq; - struct cx23885_buffer *buf; - unsigned long flags; - - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n", - dev->name, buf, buf->vb.i, - (unsigned long)buf->risc.dma); - } - cx23885_restart_video_queue(dev, q); - spin_unlock_irqrestore(&dev->slock, flags); -} - int cx23885_video_irq(struct cx23885_dev *dev, u32 status) { u32 mask, count; @@ -1702,13 +1049,6 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) spin_unlock(&dev->slock); handled++; } - if (status & VID_BC_MSK_RISCI2) { - dprintk(2, "stopper video\n"); - spin_lock(&dev->slock); - cx23885_restart_video_queue(dev, &dev->vidq); - spin_unlock(&dev->slock); - handled++; - } /* Allow the VBI framework to process it's payload */ handled += cx23885_vbi_irq(dev, status); @@ -1721,12 +1061,12 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = video_mmap, - .ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -1738,21 +1078,19 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt, .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt, .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_log_status = vidioc_log_status, - .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_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -1765,6 +1103,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enumaudio = vidioc_enum_audinput, .vidioc_g_audio = vidioc_g_audinput, .vidioc_s_audio = vidioc_s_audinput, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx23885_vbi_template; @@ -1775,14 +1115,6 @@ static struct video_device cx23885_video_template = { .tvnorms = CX23885_NORMS, }; -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .ioctl = video_ioctl2, -}; - - void cx23885_video_unregister(struct cx23885_dev *dev) { dprintk(1, "%s()\n", __func__); @@ -1794,7 +1126,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev) else video_device_release(dev->vbi_dev); dev->vbi_dev = NULL; - btcx_riscmem_free(dev->pci, &dev->vbiq.stopper); } if (dev->video_dev) { if (video_is_registered(dev->video_dev)) @@ -1802,8 +1133,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev) else video_device_release(dev->video_dev); dev->video_dev = NULL; - - btcx_riscmem_free(dev->pci, &dev->vidq.stopper); } if (dev->audio_dev) @@ -1812,6 +1141,7 @@ void cx23885_video_unregister(struct cx23885_dev *dev) int cx23885_video_register(struct cx23885_dev *dev) { + struct vb2_queue *q; int err; dprintk(1, "%s()\n", __func__); @@ -1822,24 +1152,16 @@ int cx23885_video_register(struct cx23885_dev *dev) strcpy(cx23885_vbi_template.name, "cx23885-vbi"); dev->tvnorm = V4L2_STD_NTSC_M; + dev->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); + dev->field = V4L2_FIELD_INTERLACED; + dev->width = 720; + dev->height = norm_maxh(dev->tvnorm); /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - dev->vidq.timeout.function = cx23885_vid_timeout; - dev->vidq.timeout.data = (unsigned long)dev; - init_timer(&dev->vidq.timeout); - cx23885_risc_stopper(dev->pci, &dev->vidq.stopper, - VID_A_DMA_CTL, 0x11, 0x00); /* init vbi dma queues */ INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); - dev->vbiq.timeout.function = cx23885_vbi_timeout; - dev->vbiq.timeout.data = (unsigned long)dev; - init_timer(&dev->vbiq.timeout); - cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper, - VID_A_DMA_CTL, 0x22, 0x00); cx23885_irq_add_enable(dev, 0x01); @@ -1893,9 +1215,49 @@ int cx23885_video_register(struct cx23885_dev *dev) } } + /* initial device configuration */ + mutex_lock(&dev->lock); + cx23885_set_tvnorm(dev, dev->tvnorm); + cx23885_video_mux(dev, 0); + cx23885_audio_mux(dev, 0); + mutex_unlock(&dev->lock); + + q = &dev->vb2_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_video_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + + q = &dev->vb2_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_vbi_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + /* register Video device */ dev->video_dev = cx23885_vdev_init(dev, dev->pci, &cx23885_video_template, "video"); + dev->video_dev->queue = &dev->vb2_vidq; err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, video_nr[dev->nr]); if (err < 0) { @@ -1909,6 +1271,7 @@ int cx23885_video_register(struct cx23885_dev *dev) /* register VBI device */ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci, &cx23885_vbi_template, "vbi"); + dev->vbi_dev->queue = &dev->vb2_vbiq; err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->nr]); if (err < 0) { @@ -1922,18 +1285,9 @@ int cx23885_video_register(struct cx23885_dev *dev) /* Register ALSA audio device */ dev->audio_dev = cx23885_audio_register(dev); - /* initial device configuration */ - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, dev->tvnorm); - init_controls(dev); - cx23885_video_mux(dev, 0); - cx23885_audio_mux(dev, 0); - mutex_unlock(&dev->lock); - return 0; fail_unreg: cx23885_video_unregister(dev); return err; } - diff --git a/drivers/media/pci/cx23885/cx23885-video.h b/drivers/media/pci/cx23885/cx23885-video.h index c961a2b..291e8f3 100644 --- a/drivers/media/pci/cx23885/cx23885-video.h +++ b/drivers/media/pci/cx23885/cx23885-video.h @@ -12,11 +12,6 @@ * 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. */ #ifndef _CX23885_VIDEO_H_ diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 0e086c0..6c35e61 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -13,10 +13,6 @@ * 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 <linux/pci.h> @@ -25,19 +21,20 @@ #include <linux/slab.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> #include <media/tuner.h> #include <media/tveeprom.h> -#include <media/videobuf-dma-sg.h> -#include <media/videobuf-dvb.h> +#include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dvb.h> #include <media/rc-core.h> -#include "btcx-risc.h" #include "cx23885-reg.h" #include "media/cx2341x.h" #include <linux/mutex.h> -#define CX23885_VERSION "0.0.3" +#define CX23885_VERSION "0.0.4" #define UNSET (-1U) @@ -46,9 +43,6 @@ /* Max number of inputs by card */ #define MAX_CX23885_INPUT 8 #define INPUT(nr) (&cx23885_boards[dev->board].input[nr]) -#define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 -#define RESOURCE_VBI 4 #define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ @@ -98,6 +92,7 @@ #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42 #define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE 43 #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2 44 +#define CX23885_BOARD_DVBSKY_T9580 45 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -131,14 +126,6 @@ struct cx23885_fmt { u32 cxformat; }; -struct cx23885_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 mask; - u32 shift; -}; - struct cx23885_tvnorm { char *name; v4l2_std_id id; @@ -146,30 +133,6 @@ struct cx23885_tvnorm { u32 cxoformat; }; -struct cx23885_fh { - struct cx23885_dev *dev; - enum v4l2_buf_type type; - int radio; - u32 resources; - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - - /* video capture */ - struct cx23885_fmt *fmt; - unsigned int width, height; - - /* vbi capture */ - struct videobuf_queue vidq; - struct videobuf_queue vbiq; - - /* MPEG Encoder specifics ONLY */ - struct videobuf_queue mpegq; - atomic_t v4l_reading; -}; - enum cx23885_itype { CX23885_VMUX_COMPOSITE1 = 1, CX23885_VMUX_COMPOSITE2, @@ -189,14 +152,22 @@ enum cx23885_src_sel_type { CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO }; +struct cx23885_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + /* buffer for one video frame */ struct cx23885_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head queue; /* cx23885 specific */ unsigned int bpl; - struct btcx_riscmem risc; + struct cx23885_riscmem risc; struct cx23885_fmt *fmt; u32 count; }; @@ -268,9 +239,6 @@ struct cx23885_i2c { struct cx23885_dmaqueue { struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; u32 count; }; @@ -280,7 +248,7 @@ struct cx23885_tsport { int nr; int sram_chno; - struct videobuf_dvb_frontends frontends; + struct vb2_dvb_frontends frontends; /* dma queues */ struct cx23885_dmaqueue mpegq; @@ -326,7 +294,12 @@ struct cx23885_tsport { /* Workaround for a temp dvb_frontend that the tuner can attached to */ struct dvb_frontend analog_fe; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + int (*set_frontend)(struct dvb_frontend *fe); + int (*fe_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); }; struct cx23885_kernel_ir { @@ -339,8 +312,11 @@ struct cx23885_kernel_ir { struct cx23885_audio_buffer { unsigned int bpl; - struct btcx_riscmem risc; - struct videobuf_dmabuf dma; + struct cx23885_riscmem risc; + void *vaddr; + struct scatterlist *sglist; + int sglen; + int nr_pages; }; struct cx23885_audio_dev { @@ -358,8 +334,6 @@ struct cx23885_audio_dev { unsigned int period_size; unsigned int num_periods; - struct videobuf_dmabuf *dma_risc; - struct cx23885_audio_buffer *buf; struct snd_pcm_substream *substream; @@ -368,6 +342,7 @@ struct cx23885_audio_dev { struct cx23885_dev { atomic_t refcount; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; /* pci stuff */ struct pci_dev *pci; @@ -407,7 +382,6 @@ struct cx23885_dev { } bridge; /* Analog video */ - u32 resources; unsigned int input; unsigned int audinput; /* Selectable audio input */ u32 tvaudio; @@ -417,7 +391,6 @@ struct cx23885_dev { unsigned int tuner_bus; unsigned int radio_type; unsigned char radio_addr; - unsigned int has_radio; struct v4l2_subdev *sd_cx25840; struct work_struct cx25840_work; @@ -435,17 +408,24 @@ struct cx23885_dev { u32 freq; struct video_device *video_dev; struct video_device *vbi_dev; - struct video_device *radio_dev; + + /* video capture */ + struct cx23885_fmt *fmt; + unsigned int width, height; + unsigned field; struct cx23885_dmaqueue vidq; + struct vb2_queue vb2_vidq; struct cx23885_dmaqueue vbiq; + struct vb2_queue vb2_vbiq; + spinlock_t slock; /* MPEG Encoder ONLY settings */ u32 cx23417_mailbox; - struct cx2341x_mpeg_params mpeg_params; + struct cx2341x_handler cxhdl; struct video_device *v4l_device; - atomic_t v4l_reader_count; + struct vb2_queue vb2_mpegq; struct cx23885_tvnorm encodernorm; /* Analog raw audio */ @@ -521,26 +501,21 @@ extern int cx23885_sram_channel_setup(struct cx23885_dev *dev, extern void cx23885_sram_channel_dump(struct cx23885_dev *dev, struct sram_channel *ch); -extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value); - -extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +extern int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); extern int cx23885_risc_vbibuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, struct scatterlist *sglist, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); +int cx23885_start_dma(struct cx23885_tsport *port, + struct cx23885_dmaqueue *q, + struct cx23885_buffer *buf); void cx23885_cancel_buffers(struct cx23885_tsport *port); -extern int cx23885_restart_queue(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q); - -extern void cx23885_wakeup(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q, u32 count); extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); @@ -574,13 +549,11 @@ extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); extern int cx23885_dvb_register(struct cx23885_tsport *port); extern int cx23885_dvb_unregister(struct cx23885_tsport *port); -extern int cx23885_buf_prepare(struct videobuf_queue *q, - struct cx23885_tsport *port, - struct cx23885_buffer *buf, - enum v4l2_field field); +extern int cx23885_buf_prepare(struct cx23885_buffer *buf, + struct cx23885_tsport *port); extern void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf); -extern void cx23885_free_buffer(struct videobuf_queue *q, +extern void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf); /* ----------------------------------------------------------- */ @@ -595,8 +568,6 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i); int cx23885_set_input(struct file *file, void *priv, unsigned int i); int cx23885_get_input(struct file *file, void *priv, unsigned int *i); int cx23885_set_frequency(struct file *file, void *priv, const struct v4l2_frequency *f); -int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl); -int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl); int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm); /* ----------------------------------------------------------- */ @@ -604,9 +575,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm); extern int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f); extern void cx23885_vbi_timeout(unsigned long data); -extern struct videobuf_queue_ops cx23885_vbi_qops; -extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q); +extern struct vb2_ops cx23885_vbi_qops; extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status); /* cx23885-i2c.c */ @@ -638,7 +607,7 @@ extern struct cx23885_audio_dev *cx23885_audio_register( extern void cx23885_audio_unregister(struct cx23885_dev *dev); extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask); extern int cx23885_risc_databuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, @@ -649,15 +618,10 @@ extern int cx23885_risc_databuffer(struct pci_dev *pci, static inline unsigned int norm_maxw(v4l2_std_id norm) { - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; + return (norm & V4L2_STD_525_60) ? 720 : 768; } static inline unsigned int norm_maxh(v4l2_std_id norm) { - return (norm & V4L2_STD_625_50) ? 576 : 480; -} - -static inline unsigned int norm_swidth(v4l2_std_id norm) -{ - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; + return (norm & V4L2_STD_525_60) ? 480 : 576; } diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index 2c951de..c1aa888 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -14,11 +14,6 @@ * 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 <linux/kfifo.h> @@ -263,7 +258,7 @@ static inline unsigned int lpf_count_to_us(unsigned int count) } /* - * FIFO register pulse width count compuations + * FIFO register pulse width count computations */ static u32 clock_divider_to_resolution(u16 divider) { diff --git a/drivers/media/pci/cx23885/cx23888-ir.h b/drivers/media/pci/cx23885/cx23888-ir.h index d2de41c..ff74a93 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.h +++ b/drivers/media/pci/cx23885/cx23888-ir.h @@ -14,11 +14,6 @@ * 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. */ #ifndef _CX23888_IR_H_ diff --git a/drivers/media/pci/cx23885/netup-eeprom.c b/drivers/media/pci/cx23885/netup-eeprom.c index 98a48f5..b6542ee 100644 --- a/drivers/media/pci/cx23885/netup-eeprom.c +++ b/drivers/media/pci/cx23885/netup-eeprom.c @@ -17,10 +17,6 @@ * 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. */ # diff --git a/drivers/media/pci/cx23885/netup-eeprom.h b/drivers/media/pci/cx23885/netup-eeprom.h index 13926e1..90cac5b 100644 --- a/drivers/media/pci/cx23885/netup-eeprom.h +++ b/drivers/media/pci/cx23885/netup-eeprom.h @@ -16,10 +16,6 @@ * 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 NETUP_EEPROM_H diff --git a/drivers/media/pci/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c index 0044fef..76d9487 100644 --- a/drivers/media/pci/cx23885/netup-init.c +++ b/drivers/media/pci/cx23885/netup-init.c @@ -17,10 +17,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/netup-init.h b/drivers/media/pci/cx23885/netup-init.h index d26ae4b..daaa212 100644 --- a/drivers/media/pci/cx23885/netup-init.h +++ b/drivers/media/pci/cx23885/netup-init.h @@ -17,9 +17,5 @@ * 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. */ extern void netup_initialize(struct cx23885_dev *dev); diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c index 1f43be0..a664997 100644 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.c +++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c @@ -330,8 +330,9 @@ int cx25821_write_frame(struct cx25821_channel *chan, if (frame_size - curpos < count) count = frame_size - curpos; - memcpy((char *)out->_data_buf_virt_addr + frame_offset + curpos, - data, count); + if (copy_from_user((__force char *)out->_data_buf_virt_addr + frame_offset + curpos, + data, count)) + return -EFAULT; curpos += count; if (curpos == frame_size) { out->_frame_count++; diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index e18a7ac..851754b 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -78,19 +78,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE2, .vmux = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE3, .vmux = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE4, .vmux = 3, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE] = { .name = "Hauppauge WinTV 34xxx models", @@ -99,23 +99,23 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xff00, // internal decoder - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0xff01, // mono from tuner chip - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xff02, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xff02, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xff01, @@ -127,13 +127,13 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, }, [CX88_BOARD_PIXELVIEW] = { .name = "PixelView", @@ -141,17 +141,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xff00, // internal decoder - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xff10, @@ -164,19 +164,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x03ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x03fe, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x03fe, - }}, + } }, }, [CX88_BOARD_WINFAST2000XP_EXPERT] = { .name = "Leadtek Winfast 2000XP Expert", @@ -185,28 +185,28 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00F5e700, .gpio1 = 0x00003004, .gpio2 = 0x00F5e700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00F5c700, .gpio1 = 0x00003004, .gpio2 = 0x00F5c700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00F5c700, .gpio1 = 0x00003004, .gpio2 = 0x00F5c700, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00F5d700, @@ -222,19 +222,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio1 = 0xe09f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio1 = 0xe05f, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio1 = 0xe05f, - }}, + } }, .radio = { .gpio1 = 0xe0df, .type = CX88_RADIO, @@ -249,25 +249,25 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - }}, + } }, .radio = { .type = CX88_RADIO, .vmux = 3, @@ -283,14 +283,14 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0035e700, .gpio1 = 0x00003004, .gpio2 = 0x0035e700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -298,14 +298,14 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x00003004, .gpio2 = 0x0035c700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0035c700, .gpio1 = 0x0035c700, .gpio2 = 0x02000000, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0035d700, @@ -322,22 +322,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000bde2, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000bde6, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000bde6, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000bd62, @@ -351,16 +351,16 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE2, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, }, [CX88_BOARD_PROLINK_PLAYTVPVR] = { .name = "Prolink PlayTV PVR", @@ -369,19 +369,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xbff0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xbff3, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xbff3, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xbff0, @@ -394,16 +394,16 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000fde6, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000fde2, @@ -417,22 +417,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc08, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc68, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc68, - }}, + } }, }, [CX88_BOARD_KWORLD_DVB_T] = { .name = "KWorld/VStream XPert DVB-T", @@ -440,17 +440,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { @@ -459,15 +459,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000027df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000027df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_LTV883] = { @@ -476,23 +476,23 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x07f8, - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0x07f9, // mono from tuner chip - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000007fa, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000007fa, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x000007f8, @@ -521,23 +521,23 @@ static const struct cx88_board cx88_boards[] = { 0 - normal RF 1 - high RF */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0f0d, - },{ + }, { .type = CX88_VMUX_CABLE, .vmux = 0, .gpio0 = 0x0f05, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0f00, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0f00, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_DVB_T1] = { @@ -546,10 +546,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_CONEXANT_DVB_T1] = { @@ -558,10 +558,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PROVIDEO_PV259] = { @@ -570,11 +570,11 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .audioroute = 1, - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { @@ -583,15 +583,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000027df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000027df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DNTV_LIVE_DVB_T] = { @@ -600,17 +600,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000700, .gpio2 = 0x00000101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000700, .gpio2 = 0x00000101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PCHDTV_HD3000] = { @@ -632,19 +632,19 @@ static const struct cx88_board cx88_boards[] = { * * GPIO[16] = Remote control input */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00008484, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00008400, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00008400, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00008404, @@ -659,25 +659,25 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xed1a, .gpio2 = 0x00ff, - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0xff01, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xff02, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xed92, .gpio2 = 0x00ff, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xed96, @@ -692,22 +692,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00009d80, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00009d76, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00009d76, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00009d00, @@ -722,19 +722,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 1, .gpio1 = 0x0000e03f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 2, .gpio1 = 0x0000e07f, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 3, .gpio1 = 0x0000e07f, - }} + } } }, [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = { .name = "PixelView PlayTV Ultra Pro (Stereo)", @@ -745,19 +745,19 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, /* Some variants use a tda9874 and so need the tvaudio module. */ .audio_chip = CX88_AUDIO_TVAUDIO, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xbf61, /* internal decoder */ - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xbf63, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xbf63, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xbf60, @@ -770,19 +770,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x97ed, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x97e9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x97e9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_ADSTECH_DVB_T_PCI] = { @@ -791,32 +791,32 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = { .name = "TerraTec Cinergy 1400 DVB-T", .tuner_type = TUNER_ABSENT, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = { @@ -826,19 +826,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x87fd, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x87f9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x87f9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = { @@ -848,22 +848,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, .gpio0 = 0x0000cd73, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 1, .gpio0 = 0x0000cd73, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_TELEVISION, .vmux = 3, .gpio0 = 0x0000cdb3, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .vmux = 2, @@ -876,21 +876,21 @@ static const struct cx88_board cx88_boards[] = { /* Alexander Wold <awold@bigfoot.com> */ .name = "Kworld V-Stream Xpert DVD", .tuner_type = UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x03000000, .gpio1 = 0x01000000, .gpio2 = 0x02000000, .gpio3 = 0x00100000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x03000000, .gpio1 = 0x01000000, .gpio2 = 0x02000000, .gpio3 = 0x00100000, - }}, + } }, }, [CX88_BOARD_ATI_HDTVWONDER] = { .name = "ATI HDTV Wonder", @@ -898,28 +898,28 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00000ff7, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000ffe, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000ffe, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_WINFAST_DTV1000] = { @@ -928,16 +928,16 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_AVERTV_303] = { @@ -947,28 +947,28 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00ff, .gpio1 = 0xe09f, .gpio2 = 0x0010, .gpio3 = 0x0000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00ff, .gpio1 = 0xe05f, .gpio2 = 0x0010, .gpio3 = 0x0000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00ff, .gpio1 = 0xe05f, .gpio2 = 0x0010, .gpio3 = 0x0000, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = { .name = "Hauppauge Nova-S-Plus DVB-S", @@ -978,22 +978,22 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .audio_chip = CX88_AUDIO_WM8775, .i2sinputcntl = 2, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = { @@ -1002,10 +1002,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_DVBS_100] = { @@ -1015,22 +1015,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_chip = CX88_AUDIO_WM8775, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_HVR1100] = { @@ -1040,16 +1040,16 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB, }, @@ -1060,13 +1060,13 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - }}, + } }, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB, }, @@ -1078,19 +1078,19 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xf80808, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xf80808, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xf80808, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xf80808, @@ -1106,17 +1106,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = { @@ -1125,15 +1125,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000067df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000067df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { @@ -1142,22 +1142,22 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x3de2, .gpio2 = 0x00ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x3de6, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x3de6, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x3de6, @@ -1171,19 +1171,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000a75f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000a75b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000a75b, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PCHDTV_HD5500] = { @@ -1193,19 +1193,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x87fd, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x87f9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x87f9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_MCE200_DELUXE] = { @@ -1217,11 +1217,11 @@ static const struct cx88_board cx88_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000BDE6 - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = { @@ -1233,11 +1233,11 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x5da6, - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = { @@ -1246,19 +1246,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0788, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x078b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x078b, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x074a, @@ -1271,7 +1271,7 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00017304, @@ -1299,7 +1299,7 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x0000b207, .gpio2 = 0x0001d701, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00015702, @@ -1316,35 +1316,35 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00017300, .gpio1 = 0x00008207, .gpio2 = 0x00000000, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00018300, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00018301, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00018301, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00015702, @@ -1360,13 +1360,13 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_HVR3000] = { @@ -1377,25 +1377,25 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .audio_chip = CX88_AUDIO_WM8775, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x84bf, /* 1: TV Audio / FM Mono */ .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x84bf, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x84bf, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x84bf, @@ -1411,19 +1411,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0709, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x070b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x070b, - }}, + } }, }, [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", @@ -1431,28 +1431,28 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE_HVR1300] = { .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", @@ -1465,25 +1465,25 @@ static const struct cx88_board cx88_boards[] = { /* * gpio0 as reported by Mike Crash <mike AT mikecrash.com> */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xef88, /* 1: TV Audio / FM Mono */ .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xef88, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xef88, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, .radio = { .type = CX88_RADIO, @@ -1510,19 +1510,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DEBUG, .vmux = 3, .gpio0 = 0x04ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x07fa, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x07fa, - }}, + } }, }, [CX88_BOARD_PINNACLE_PCTV_HD_800i] = { .name = "Pinnacle PCTV HD 800i", @@ -1530,24 +1530,24 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x04fb, .gpio1 = 0x10ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x04fb, .gpio1 = 0x10ef, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x04fb, .gpio1 = 0x10ef, .audioroute = 1, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = { @@ -1557,7 +1557,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x000027df, /* Unconfirmed */ @@ -1815,19 +1815,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x10df, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x16d9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x16d9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PROLINK_PV_8000GT] = { @@ -1967,7 +1967,7 @@ static const struct cx88_board cx88_boards[] = { * 3: Line-In Expansion * 4: FM Stereo */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xc4bf, @@ -2001,7 +2001,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2013,7 +2013,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2025,7 +2025,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2037,7 +2037,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2049,7 +2049,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2061,7 +2061,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2073,7 +2073,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, .gpio0 = 0x8080, @@ -2086,7 +2086,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2098,7 +2098,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2110,7 +2110,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2170,7 +2170,7 @@ static const struct cx88_board cx88_boards[] = { * 13: audio source (0=tuner audio,1=line in) * 14: FM (0=on,1=off ???) */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2211,7 +2211,7 @@ static const struct cx88_board cx88_boards[] = { * 13: audio source (0=tuner audio,1=line in) * 14: FM (0=on,1=off ???) */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2229,7 +2229,7 @@ static const struct cx88_board cx88_boards[] = { .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ .gpio2 = 0x0000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2252,7 +2252,7 @@ static const struct cx88_board cx88_boards[] = { * 14: 0: FM radio * 16: 0: RF input is cable */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0403, @@ -2280,7 +2280,7 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0xF0F7, .gpio2 = 0x0101, .gpio3 = 0x0000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0403, @@ -2308,7 +2308,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2324,19 +2324,19 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x3400, .card = CX88_BOARD_HAUPPAUGE, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x3401, .card = CX88_BOARD_HAUPPAUGE, - },{ + }, { .subvendor = 0x14c7, .subdevice = 0x0106, .card = CX88_BOARD_GDI, - },{ + }, { .subvendor = 0x14c7, .subdevice = 0x0107, /* with mpeg encoder */ .card = CX88_BOARD_GDI, - },{ + }, { .subvendor = PCI_VENDOR_ID_ATI, .subdevice = 0x00f8, .card = CX88_BOARD_ATI_WONDER_PRO, @@ -2348,176 +2348,176 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6611, .card = CX88_BOARD_WINFAST2000XP_EXPERT, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6613, /* NTSC */ .card = CX88_BOARD_WINFAST2000XP_EXPERT, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6620, .card = CX88_BOARD_WINFAST_DV2000, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x663b, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x663c, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x000b, .card = CX88_BOARD_AVERTV_STUDIO_303, - },{ + }, { .subvendor = 0x1462, .subdevice = 0x8606, .card = CX88_BOARD_MSI_TVANYWHERE_MASTER, - },{ + }, { .subvendor = 0x10fc, .subdevice = 0xd003, .card = CX88_BOARD_IODATA_GVVCP3PCI, - },{ + }, { .subvendor = 0x1043, .subdevice = 0x4823, /* with mpeg encoder */ .card = CX88_BOARD_ASUS_PVR_416, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08a6, .card = CX88_BOARD_KWORLD_DVB_T, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd810, .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd820, .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb00, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9002, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0187, .card = CX88_BOARD_CONEXANT_DVB_T1, - },{ + }, { .subvendor = 0x1540, .subdevice = 0x2580, .card = CX88_BOARD_PROVIDEO_PV259, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb10, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, - },{ + }, { .subvendor = 0x1554, .subdevice = 0x4811, .card = CX88_BOARD_PIXELVIEW, - },{ + }, { .subvendor = 0x7063, .subdevice = 0x3000, /* HD-3000 card */ .card = CX88_BOARD_PCHDTV_HD3000, - },{ + }, { .subvendor = 0x17de, .subdevice = 0xa8a6, .card = CX88_BOARD_DNTV_LIVE_DVB_T, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x2801, .card = CX88_BOARD_HAUPPAUGE_ROSLYN, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0342, .card = CX88_BOARD_DIGITALLOGIC_MEC, - },{ + }, { .subvendor = 0x10fc, .subdevice = 0xd035, .card = CX88_BOARD_IODATA_GVBCTV7E, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0334, .card = CX88_BOARD_ADSTECH_DVB_T_PCI, - },{ + }, { .subvendor = 0x153b, .subdevice = 0x1166, .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd500, .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x8011, .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550, - },{ + }, { .subvendor = PCI_VENDOR_ID_ATI, .subdevice = 0xa101, .card = CX88_BOARD_ATI_HDTVWONDER, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x665f, .card = CX88_BOARD_WINFAST_DTV1000, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x000a, .card = CX88_BOARD_AVERTV_303, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9200, .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9201, .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9202, .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08b2, .card = CX88_BOARD_KWORLD_DVBS_100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9400, .card = CX88_BOARD_HAUPPAUGE_HVR1100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9402, .card = CX88_BOARD_HAUPPAUGE_HVR1100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9800, .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9802, .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9001, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x1822, .subdevice = 0x0025, .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08a1, .card = CX88_BOARD_KWORLD_DVB_T_CX22702, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb50, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb54, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, /* Re-branded DViCO: DigitalNow DVB-T Dual */ - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb11, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, @@ -2530,55 +2530,55 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x17de, .subdevice = 0x0840, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb40, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb44, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, - },{ + }, { .subvendor = 0x7063, .subdevice = 0x5500, .card = CX88_BOARD_PCHDTV_HD5500, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x0841, .card = CX88_BOARD_KWORLD_MCE200_DELUXE, - },{ + }, { .subvendor = 0x1822, .subdevice = 0x0019, .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, - },{ + }, { .subvendor = 0x1554, .subdevice = 0x4813, .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0842, .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x665e, .card = CX88_BOARD_WINFAST_DTV2000H, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6f2b, .card = CX88_BOARD_WINFAST_DTV2000H_J, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0084, .card = CX88_BOARD_GENIATECH_DVBS, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1404, .card = CX88_BOARD_HAUPPAUGE_HVR3000, @@ -2590,60 +2590,60 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x18ac, .subdevice = 0xdccd, .card = CX88_BOARD_SAMSUNG_SMT_7020, - },{ + }, { .subvendor = 0x1461, .subdevice = 0xc111, /* AverMedia M150-D */ /* This board is known to work with the ASUS PVR416 config */ .card = CX88_BOARD_ASUS_PVR_416, - },{ + }, { .subvendor = 0xc180, .subdevice = 0xc980, .card = CX88_BOARD_TE_DTV_250_OEM_SWANN, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9600, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9601, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9602, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6632, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x12ab, .subdevice = 0x2300, /* Club3D Zap TV2100 */ .card = CX88_BOARD_KWORLD_DVB_T_CX22702, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9000, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1400, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1401, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1402, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ .card = CX88_BOARD_KWORLD_DVBS_100, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0390, .card = CX88_BOARD_ADSTECH_PTV_390, - },{ + }, { .subvendor = 0x11bd, .subdevice = 0x0051, .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index ed8cb90..ce27e6d 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -696,7 +696,6 @@ static struct videobuf_queue *get_queue(struct file *file) return &fh->vbiq; default: BUG(); - return NULL; } } @@ -711,7 +710,6 @@ static int get_resource(struct file *file) return RESOURCE_VBI; default: BUG(); - return 0; } } @@ -812,7 +810,6 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) file->f_flags & O_NONBLOCK); default: BUG(); - return 0; } } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index da8f848..c82e855 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -149,7 +149,7 @@ static u32 ddb_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -struct i2c_algorithm ddb_i2c_algo = { +static struct i2c_algorithm ddb_i2c_algo = { .master_xfer = ddb_i2c_master_xfer, .functionality = ddb_i2c_functionality, }; @@ -266,7 +266,7 @@ static void io_free(struct pci_dev *pdev, u8 **vbuf, for (i = 0; i < num; i++) { if (vbuf[i]) { pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); - vbuf[i] = 0; + vbuf[i] = NULL; } } } @@ -440,7 +440,7 @@ static u32 ddb_output_free(struct ddb_output *output) } static ssize_t ddb_output_write(struct ddb_output *output, - const u8 *buf, size_t count) + const __user u8 *buf, size_t count) { struct ddb *dev = output->port->dev; u32 idx, off, stat = output->stat; @@ -506,7 +506,7 @@ static u32 ddb_input_avail(struct ddb_input *input) return 0; } -static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) +static ssize_t ddb_input_read(struct ddb_input *input, __user u8 *buf, size_t count) { struct ddb *dev = input->port->dev; u32 left = count; @@ -849,7 +849,7 @@ static int dvb_input_attach(struct ddb_input *input) return ret; input->attached = 4; - input->fe = 0; + input->fe = NULL; switch (port->type) { case DDB_TUNER_DVBS_ST: if (demod_attach_stv0900(input, 0) < 0) @@ -895,7 +895,7 @@ static int dvb_input_attach(struct ddb_input *input) /****************************************************************************/ /****************************************************************************/ -static ssize_t ts_write(struct file *file, const char *buf, +static ssize_t ts_write(struct file *file, const __user char *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -920,7 +920,7 @@ static ssize_t ts_write(struct file *file, const char *buf, return (left == count) ? -EAGAIN : (count - left); } -static ssize_t ts_read(struct file *file, char *buf, +static ssize_t ts_read(struct file *file, __user char *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -975,11 +975,9 @@ static const struct file_operations ci_fops = { .open = dvb_generic_open, .release = dvb_generic_release, .poll = ts_poll, - .mmap = 0, }; static struct dvb_device dvbdev_ci = { - .priv = 0, .readers = -1, .writers = -1, .users = -1, @@ -1038,7 +1036,7 @@ static void output_tasklet(unsigned long data) } -struct cxd2099_cfg cxd_cfg = { +static struct cxd2099_cfg cxd_cfg = { .bitrate = 62000, .adr = 0x40, .polarity = 1, @@ -1127,7 +1125,7 @@ static void ddb_ports_detach(struct ddb *dev) ddb_output_stop(port->output); dvb_ca_en50221_release(port->en); kfree(port->en); - port->en = 0; + port->en = NULL; dvb_unregister_adapter(&port->output->adap); } break; @@ -1413,9 +1411,9 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) #define DDB_MAGIC 'd' struct ddb_flashio { - __u8 *write_buf; + __user __u8 *write_buf; __u32 write_len; - __u8 *read_buf; + __user __u8 *read_buf; __u32 read_len; }; @@ -1439,7 +1437,7 @@ static int ddb_open(struct inode *inode, struct file *file) static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ddb *dev = file->private_data; - void *parg = (void *)arg; + __user void *parg = (__user void *)arg; int res; switch (cmd) { @@ -1558,7 +1556,7 @@ static void ddb_remove(struct pci_dev *pdev) ddb_device_destroy(dev); ddb_unmap(dev); - pci_set_drvdata(pdev, 0); + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } @@ -1637,7 +1635,7 @@ fail1: fail: printk(KERN_ERR "fail\n"); ddb_unmap(dev); - pci_set_drvdata(pdev, 0); + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return -1; } diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 8b1b41d..be87fbd 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -156,7 +156,7 @@ struct ddb_port { struct ddb { struct pci_dev *pdev; - unsigned char *regs; + unsigned char __iomem *regs; struct ddb_port port[DDB_MAX_PORT]; struct ddb_i2c i2c[DDB_MAX_I2C]; struct ddb_input input[DDB_MAX_INPUT]; @@ -173,12 +173,10 @@ struct ddb { /****************************************************************************/ #define ddbwritel(_val, _adr) writel((_val), \ - (char *) (dev->regs+(_adr))) -#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) -#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ - (dev->regs+(_adr)), (_src), (_count)) -#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ - (dev->regs+(_adr)), (_count)) + dev->regs+(_adr)) +#define ddbreadl(_adr) readl(dev->regs+(_adr)) +#define ddbcpyto(_adr, _src, _count) memcpy_toio(dev->regs+(_adr), (_src), (_count)) +#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), dev->regs+(_adr), (_count)) /****************************************************************************/ diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index e8826c5..ed11716 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -614,7 +614,7 @@ static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) static void dm1105_set_dma_addr(struct dm1105_dev *dev) { - dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); + dm_writel(DM1105_STADR, (__force u32)cpu_to_le32(dev->dma_addr)); } static int dm1105_dma_map(struct dm1105_dev *dev) diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c index 7a9b98b..7bf9cbc 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c @@ -81,7 +81,7 @@ static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, int period_elapsed = 0; int length; - dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc, + dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc, pcm_data, num_bytes); substream = itvsc->capture_pcm_substream; diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c index ed73edd..4b0e758 100644 --- a/drivers/media/pci/ivtv/ivtv-firmware.c +++ b/drivers/media/pci/ivtv/ivtv-firmware.c @@ -65,7 +65,7 @@ retry: the wrong file was sometimes loaded. So we check filesizes to see if at least the right-sized file was loaded. If not, then we retry. */ - IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); + IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zu)\n", fn, size, fw->size); release_firmware(fw); retries--; goto retry; @@ -76,7 +76,7 @@ retry: dst++; src++; } - IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size); + IVTV_INFO("Loaded %s firmware (%zu bytes)\n", fn, fw->size); release_firmware(fw); return size; } diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c index 19a7c9b..ab6d5d2 100644 --- a/drivers/media/pci/ivtv/ivtv-irq.c +++ b/drivers/media/pci/ivtv/ivtv-irq.c @@ -192,11 +192,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || s->type == IVTV_DEC_STREAM_TYPE_VBI)) { s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET); - write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); + write_dec_sync(DMA_MAGIC_COOKIE, offset - IVTV_DECODER_OFFSET); } else { s->pending_backup = read_enc(offset); - write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); + write_enc_sync(DMA_MAGIC_COOKIE, offset); } s->pending_offset = offset; } @@ -275,13 +275,11 @@ static void dma_post(struct ivtv_stream *s) if (x == 0 && ivtv_use_dma(s)) { offset = s->dma_last_offset; - if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) + if (le32_to_cpu(u32buf[offset / 4]) != DMA_MAGIC_COOKIE) { - for (offset = 0; offset < 64; offset++) { - if (u32buf[offset] == DMA_MAGIC_COOKIE) { + for (offset = 0; offset < 64; offset++) + if (le32_to_cpu(u32buf[offset]) == DMA_MAGIC_COOKIE) break; - } - } offset *= 4; if (offset == 256) { IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c index 68a29f8..1032db6 100644 --- a/drivers/media/pci/mantis/hopper_vp3028.c +++ b/drivers/media/pci/mantis/hopper_vp3028.c @@ -34,7 +34,7 @@ #include "mantis_dvb.h" #include "hopper_vp3028.h" -struct zl10353_config hopper_vp3028_config = { +static struct zl10353_config hopper_vp3028_config = { .demod_address = 0x0f, }; diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h index f2410cf..8ff448b 100644 --- a/drivers/media/pci/mantis/mantis_common.h +++ b/drivers/media/pci/mantis/mantis_common.h @@ -127,7 +127,7 @@ struct mantis_pci { u32 last_block; u8 *buf_cpu; dma_addr_t buf_dma; - u32 *risc_cpu; + __le32 *risc_cpu; dma_addr_t risc_dma; struct tasklet_struct tasklet; diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c index 115003e8..12a6adb 100644 --- a/drivers/media/pci/mantis/mantis_vp1033.c +++ b/drivers/media/pci/mantis/mantis_vp1033.c @@ -35,7 +35,7 @@ #include "mantis_vp1033.h" #include "mantis_reg.h" -u8 lgtdqcs001f_inittab[] = { +static u8 lgtdqcs001f_inittab[] = { 0x01, 0x15, 0x02, 0x30, 0x03, 0x00, @@ -150,7 +150,7 @@ static int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe, return 0; } -struct stv0299_config lgtdqcs001f_config = { +static struct stv0299_config lgtdqcs001f_config = { .demod_address = 0x68, .inittab = lgtdqcs001f_inittab, .mclk = 88000000UL, diff --git a/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c index 430ae84..7c1bd16 100644 --- a/drivers/media/pci/mantis/mantis_vp1034.c +++ b/drivers/media/pci/mantis/mantis_vp1034.c @@ -36,7 +36,7 @@ #include "mantis_vp1034.h" #include "mantis_reg.h" -struct mb86a16_config vp1034_mb86a16_config = { +static struct mb86a16_config vp1034_mb86a16_config = { .demod_address = 0x08, .set_voltage = vp1034_set_voltage, }; diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c index 07a2074..7082fcb 100644 --- a/drivers/media/pci/mantis/mantis_vp1041.c +++ b/drivers/media/pci/mantis/mantis_vp1041.c @@ -263,7 +263,7 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { { 0xffff , 0xff }, }; -struct stb0899_config vp1041_stb0899_config = { +static struct stb0899_config vp1041_stb0899_config = { .init_dev = vp1041_stb0899_s1_init_1, .init_s2_demod = stb0899_s2_init_2, .init_s1_demod = vp1041_stb0899_s1_init_3, @@ -300,7 +300,7 @@ struct stb0899_config vp1041_stb0899_config = { .tuner_set_rfsiggain = NULL, }; -struct stb6100_config vp1041_stb6100_config = { +static struct stb6100_config vp1041_stb6100_config = { .tuner_address = 0x60, .refclock = 27000000, }; diff --git a/drivers/media/pci/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c index 1ca6837..8d48b5a 100644 --- a/drivers/media/pci/mantis/mantis_vp2033.c +++ b/drivers/media/pci/mantis/mantis_vp2033.c @@ -37,12 +37,12 @@ #define MANTIS_MODEL_NAME "VP-2033" #define MANTIS_DEV_TYPE "DVB-C" -struct tda1002x_config vp2033_tda1002x_cu1216_config = { +static struct tda1002x_config vp2033_tda1002x_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; -struct tda10023_config vp2033_tda10023_cu1216_config = { +static struct tda10023_config vp2033_tda10023_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; diff --git a/drivers/media/pci/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c index d480741..8dd17d7 100644 --- a/drivers/media/pci/mantis/mantis_vp2040.c +++ b/drivers/media/pci/mantis/mantis_vp2040.c @@ -37,12 +37,12 @@ #define MANTIS_MODEL_NAME "VP-2040" #define MANTIS_DEV_TYPE "DVB-C" -struct tda1002x_config vp2040_tda1002x_cu1216_config = { +static struct tda1002x_config vp2040_tda1002x_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; -struct tda10023_config vp2040_tda10023_cu1216_config = { +static struct tda10023_config vp2040_tda10023_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c index c09308cd..5c1dd92 100644 --- a/drivers/media/pci/mantis/mantis_vp3030.c +++ b/drivers/media/pci/mantis/mantis_vp3030.c @@ -35,11 +35,11 @@ #include "mantis_dvb.h" #include "mantis_vp3030.h" -struct zl10353_config mantis_vp3030_config = { +static struct zl10353_config mantis_vp3030_config = { .demod_address = 0x0f, }; -struct tda665x_config env57h12d5_config = { +static struct tda665x_config env57h12d5_config = { .name = "ENV57H12D5 (ET-50DT)", .addr = 0x60, .frequency_min = 47000000, diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 9e82d21..039bed3 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -696,7 +696,7 @@ static struct ngene_info ngene_info_m780 = { .demod_attach = { NULL, demod_attach_lg330x }, /* Ensure these are NULL else the frame will call them (as funcs) */ - .tuner_attach = { 0, 0, 0, 0 }, + .tuner_attach = { NULL, NULL, NULL, NULL }, .fe_config = { NULL, &aver_m780 }, .avf = { 0 }, diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 4930b55..e29bc3af4 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -57,15 +57,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define dprintk if (debug) printk -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) -#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) +#define ngwritel(dat, adr) writel((dat), dev->iomem + (adr)) +#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) #define ngreadl(adr) readl(dev->iomem + (adr)) #define ngreadb(adr) readb(dev->iomem + (adr)) -#define ngcpyto(adr, src, count) memcpy_toio((char *) \ - (dev->iomem + (adr)), (src), (count)) -#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ - (dev->iomem + (adr)), (count)) +#define ngcpyto(adr, src, count) memcpy_toio(dev->iomem + (adr), (src), (count)) +#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), dev->iomem + (adr), (count)) /****************************************************************************/ /* nGene interrupt handler **************************************************/ @@ -1592,7 +1590,7 @@ static void cxd_detach(struct ngene *dev) dvb_ca_en50221_release(ci->en); kfree(ci->en); - ci->en = 0; + ci->en = NULL; } /***********************************/ diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c index fcb16a6..59bb285 100644 --- a/drivers/media/pci/ngene/ngene-dvb.c +++ b/drivers/media/pci/ngene/ngene-dvb.c @@ -47,7 +47,7 @@ /* COMMAND API interface ****************************************************/ /****************************************************************************/ -static ssize_t ts_write(struct file *file, const char *buf, +static ssize_t ts_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -59,12 +59,12 @@ static ssize_t ts_write(struct file *file, const char *buf, (&dev->tsout_rbuf) >= count) < 0) return 0; - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); + dvb_ringbuffer_write_user(&dev->tsout_rbuf, buf, count); return count; } -static ssize_t ts_read(struct file *file, char *buf, +static ssize_t ts_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -97,7 +97,6 @@ static const struct file_operations ci_fops = { }; struct dvb_device ngene_dvbdev_ci = { - .priv = 0, .readers = -1, .writers = -1, .users = -1, diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index 22c39ff..51e2fbd 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -737,7 +737,7 @@ typedef void (tx_cb_t)(struct ngene *, u32); struct ngene { int nr; struct pci_dev *pci_dev; - unsigned char *iomem; + unsigned char __iomem *iomem; /*struct i2c_adapter i2c_adapter;*/ diff --git a/drivers/media/pci/pt3/Kconfig b/drivers/media/pci/pt3/Kconfig new file mode 100644 index 0000000..16c208a --- /dev/null +++ b/drivers/media/pci/pt3/Kconfig @@ -0,0 +1,10 @@ +config DVB_PT3 + tristate "Earthsoft PT3 cards" + depends on DVB_CORE && PCI && I2C + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT + help + Support for Earthsoft PT3 PCIe cards. + + Say Y or M if you own such a device and want to use it. diff --git a/drivers/media/pci/pt3/Makefile b/drivers/media/pci/pt3/Makefile new file mode 100644 index 0000000..396f146 --- /dev/null +++ b/drivers/media/pci/pt3/Makefile @@ -0,0 +1,8 @@ + +earth-pt3-objs += pt3.o pt3_i2c.o pt3_dma.o + +obj-$(CONFIG_DVB_PT3) += earth-pt3.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c new file mode 100644 index 0000000..1fdeac1 --- /dev/null +++ b/drivers/media/pci/pt3/pt3.c @@ -0,0 +1,876 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +#include <linux/freezer.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/string.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "pt3.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static bool one_adapter; +module_param(one_adapter, bool, 0444); +MODULE_PARM_DESC(one_adapter, "Place FE's together under one adapter."); + +static int num_bufs = 4; +module_param(num_bufs, int, 0444); +MODULE_PARM_DESC(num_bufs, "Number of DMA buffer (188KiB) per FE."); + + +static const struct i2c_algorithm pt3_i2c_algo = { + .master_xfer = &pt3_i2c_master_xfer, + .functionality = &pt3_i2c_functionality, +}; + +static const struct pt3_adap_config adap_conf[PT3_NUM_FE] = { + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x11), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1c0042", 0x63), + }, + .tuner_cfg.qm1d1c0042 = { + .lpf = 1, + }, + .init_freq = 1049480 - 300, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x10), + }, + .tuner_info = { + I2C_BOARD_INFO("mxl301rf", 0x62), + }, + .init_freq = 515142857, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x13), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1c0042", 0x60), + }, + .tuner_cfg.qm1d1c0042 = { + .lpf = 1, + }, + .init_freq = 1049480 + 300, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x12), + }, + .tuner_info = { + I2C_BOARD_INFO("mxl301rf", 0x61), + }, + .init_freq = 521142857, + }, +}; + + +struct reg_val { + u8 reg; + u8 val; +}; + +static int +pt3_demod_write(struct pt3_adapter *adap, const struct reg_val *data, int num) +{ + struct i2c_msg msg; + int i, ret; + + ret = 0; + msg.addr = adap->i2c_demod->addr; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < num; i++) { + msg.buf = (u8 *)&data[i]; + ret = i2c_transfer(adap->i2c_demod->adapter, &msg, 1); + if (ret == 0) + ret = -EREMOTE; + if (ret < 0) + return ret; + } + return 0; +} + +static inline void pt3_lnb_ctrl(struct pt3_board *pt3, bool on) +{ + iowrite32((on ? 0x0f : 0x0c), pt3->regs[0] + REG_SYSTEM_W); +} + +static inline struct pt3_adapter *pt3_find_adapter(struct dvb_frontend *fe) +{ + struct pt3_board *pt3; + int i; + + if (one_adapter) { + pt3 = fe->dvb->priv; + for (i = 0; i < PT3_NUM_FE; i++) + if (pt3->adaps[i]->fe == fe) + return pt3->adaps[i]; + } + return container_of(fe->dvb, struct pt3_adapter, dvb_adap); +} + +/* + * all 4 tuners in PT3 are packaged in a can module (Sharp VA4M6JC2103). + * it seems that they share the power lines and Amp power line and + * adaps[3] controls those powers. + */ +static int +pt3_set_tuner_power(struct pt3_board *pt3, bool tuner_on, bool amp_on) +{ + struct reg_val rv = { 0x1e, 0x99 }; + + if (tuner_on) + rv.val |= 0x40; + if (amp_on) + rv.val |= 0x04; + return pt3_demod_write(pt3->adaps[PT3_NUM_FE - 1], &rv, 1); +} + +static int pt3_set_lna(struct dvb_frontend *fe) +{ + struct pt3_adapter *adap; + struct pt3_board *pt3; + u32 val; + int ret; + + /* LNA is shared btw. 2 TERR-tuners */ + + adap = pt3_find_adapter(fe); + val = fe->dtv_property_cache.lna; + if (val == LNA_AUTO || val == adap->cur_lna) + return 0; + + pt3 = adap->dvb_adap.priv; + if (mutex_lock_interruptible(&pt3->lock)) + return -ERESTARTSYS; + if (val) + pt3->lna_on_cnt++; + else + pt3->lna_on_cnt--; + + if (val && pt3->lna_on_cnt <= 1) { + pt3->lna_on_cnt = 1; + ret = pt3_set_tuner_power(pt3, true, true); + } else if (!val && pt3->lna_on_cnt <= 0) { + pt3->lna_on_cnt = 0; + ret = pt3_set_tuner_power(pt3, true, false); + } else + ret = 0; + mutex_unlock(&pt3->lock); + adap->cur_lna = (val != 0); + return ret; +} + +static int pt3_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +{ + struct pt3_adapter *adap; + struct pt3_board *pt3; + bool on; + + /* LNB power is shared btw. 2 SAT-tuners */ + + adap = pt3_find_adapter(fe); + on = (volt != SEC_VOLTAGE_OFF); + if (on == adap->cur_lnb) + return 0; + adap->cur_lnb = on; + pt3 = adap->dvb_adap.priv; + if (mutex_lock_interruptible(&pt3->lock)) + return -ERESTARTSYS; + if (on) + pt3->lnb_on_cnt++; + else + pt3->lnb_on_cnt--; + + if (on && pt3->lnb_on_cnt <= 1) { + pt3->lnb_on_cnt = 1; + pt3_lnb_ctrl(pt3, true); + } else if (!on && pt3->lnb_on_cnt <= 0) { + pt3->lnb_on_cnt = 0; + pt3_lnb_ctrl(pt3, false); + } + mutex_unlock(&pt3->lock); + return 0; +} + +/* register values used in pt3_fe_init() */ + +static const struct reg_val init0_sat[] = { + { 0x03, 0x01 }, + { 0x1e, 0x10 }, +}; +static const struct reg_val init0_ter[] = { + { 0x01, 0x40 }, + { 0x1c, 0x10 }, +}; +static const struct reg_val cfg_sat[] = { + { 0x1c, 0x15 }, + { 0x1f, 0x04 }, +}; +static const struct reg_val cfg_ter[] = { + { 0x1d, 0x01 }, +}; + +/* + * pt3_fe_init: initialize demod sub modules and ISDB-T tuners all at once. + * + * As for demod IC (TC90522) and ISDB-T tuners (MxL301RF), + * the i2c sequences for init'ing them are not public and hidden in a ROM, + * and include the board specific configurations as well. + * They are stored in a lump and cannot be taken out / accessed separately, + * thus cannot be moved to the FE/tuner driver. + */ +static int pt3_fe_init(struct pt3_board *pt3) +{ + int i, ret; + struct dvb_frontend *fe; + + pt3_i2c_reset(pt3); + ret = pt3_init_all_demods(pt3); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to init demod chips."); + return ret; + } + + /* additional config? */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = pt3_demod_write(pt3->adaps[i], + init0_sat, ARRAY_SIZE(init0_sat)); + else + ret = pt3_demod_write(pt3->adaps[i], + init0_ter, ARRAY_SIZE(init0_ter)); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "demod[%d] faild in init sequence0.", i); + return ret; + } + ret = fe->ops.init(fe); + if (ret < 0) + return ret; + } + + usleep_range(2000, 4000); + ret = pt3_set_tuner_power(pt3, true, false); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to control tuner module."); + return ret; + } + + /* output pin configuration */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = pt3_demod_write(pt3->adaps[i], + cfg_sat, ARRAY_SIZE(cfg_sat)); + else + ret = pt3_demod_write(pt3->adaps[i], + cfg_ter, ARRAY_SIZE(cfg_ter)); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "demod[%d] faild in init sequence1.", i); + return ret; + } + } + usleep_range(4000, 6000); + + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.delsys[0] != SYS_ISDBS) + continue; + /* init and wake-up ISDB-S tuners */ + ret = fe->ops.tuner_ops.init(fe); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "Failed to init SAT-tuner[%d].", i); + return ret; + } + } + ret = pt3_init_all_mxl301rf(pt3); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners."); + return ret; + } + + ret = pt3_set_tuner_power(pt3, true, true); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to control tuner module."); + return ret; + } + + /* Wake up all tuners and make an initial tuning, + * in order to avoid interference among the tuners in the module, + * according to the doc from the manufacturer. + */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + ret = 0; + if (fe->ops.delsys[0] == SYS_ISDBT) + ret = fe->ops.tuner_ops.init(fe); + /* set only when called from pt3_probe(), not resume() */ + if (ret == 0 && fe->dtv_property_cache.frequency == 0) { + fe->dtv_property_cache.frequency = + adap_conf[i].init_freq; + ret = fe->ops.tuner_ops.set_params(fe); + } + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "Failed in initial tuning of tuner[%d].", i); + return ret; + } + } + + /* and sleep again, waiting to be opened by users. */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.tuner_ops.sleep) + ret = fe->ops.tuner_ops.sleep(fe); + if (ret < 0) + break; + if (fe->ops.sleep) + ret = fe->ops.sleep(fe); + if (ret < 0) + break; + if (fe->ops.delsys[0] == SYS_ISDBS) + fe->ops.set_voltage = &pt3_set_voltage; + else + fe->ops.set_lna = &pt3_set_lna; + } + if (i < PT3_NUM_FE) { + dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby.", i); + return ret; + } + return 0; +} + + +static int pt3_attach_fe(struct pt3_board *pt3, int i) +{ + struct i2c_board_info info; + struct tc90522_config cfg; + struct i2c_client *cl; + struct dvb_adapter *dvb_adap; + int ret; + + info = adap_conf[i].demod_info; + cfg = adap_conf[i].demod_cfg; + cfg.tuner_i2c = NULL; + info.platform_data = &cfg; + + ret = -ENODEV; + request_module("tc90522"); + cl = i2c_new_device(&pt3->i2c_adap, &info); + if (!cl || !cl->dev.driver) + return -ENODEV; + pt3->adaps[i]->i2c_demod = cl; + if (!try_module_get(cl->dev.driver->owner)) + goto err_demod_i2c_unregister_device; + + if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) { + struct qm1d1c0042_config tcfg; + + tcfg = adap_conf[i].tuner_cfg.qm1d1c0042; + tcfg.fe = cfg.fe; + info = adap_conf[i].tuner_info; + info.platform_data = &tcfg; + request_module("qm1d1c0042"); + cl = i2c_new_device(cfg.tuner_i2c, &info); + } else { + struct mxl301rf_config tcfg; + + tcfg = adap_conf[i].tuner_cfg.mxl301rf; + tcfg.fe = cfg.fe; + info = adap_conf[i].tuner_info; + info.platform_data = &tcfg; + request_module("mxl301rf"); + cl = i2c_new_device(cfg.tuner_i2c, &info); + } + if (!cl || !cl->dev.driver) + goto err_demod_module_put; + pt3->adaps[i]->i2c_tuner = cl; + if (!try_module_get(cl->dev.driver->owner)) + goto err_tuner_i2c_unregister_device; + + dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap; + ret = dvb_register_frontend(dvb_adap, cfg.fe); + if (ret < 0) + goto err_tuner_module_put; + pt3->adaps[i]->fe = cfg.fe; + return 0; + +err_tuner_module_put: + module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner); +err_tuner_i2c_unregister_device: + i2c_unregister_device(pt3->adaps[i]->i2c_tuner); +err_demod_module_put: + module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner); +err_demod_i2c_unregister_device: + i2c_unregister_device(pt3->adaps[i]->i2c_demod); + + return ret; +} + + +static int pt3_fetch_thread(void *data) +{ + struct pt3_adapter *adap = data; + ktime_t delay; + bool was_frozen; + +#define PT3_INITIAL_BUF_DROPS 4 +#define PT3_FETCH_DELAY 10 +#define PT3_FETCH_DELAY_DELTA 2 + + pt3_init_dmabuf(adap); + adap->num_discard = PT3_INITIAL_BUF_DROPS; + + dev_dbg(adap->dvb_adap.device, + "PT3: [%s] started.\n", adap->thread->comm); + set_freezable(); + while (!kthread_freezable_should_stop(&was_frozen)) { + if (was_frozen) + adap->num_discard = PT3_INITIAL_BUF_DROPS; + + pt3_proc_dma(adap); + + delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC); + set_current_state(TASK_UNINTERRUPTIBLE); + freezable_schedule_hrtimeout_range(&delay, + PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC, + HRTIMER_MODE_REL); + } + dev_dbg(adap->dvb_adap.device, + "PT3: [%s] exited.\n", adap->thread->comm); + adap->thread = NULL; + return 0; +} + +static int pt3_start_streaming(struct pt3_adapter *adap) +{ + struct task_struct *thread; + + /* start fetching thread */ + thread = kthread_run(pt3_fetch_thread, adap, "pt3-ad%i-dmx%i", + adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + if (IS_ERR(thread)) { + int ret = PTR_ERR(thread); + + dev_warn(adap->dvb_adap.device, + "PT3 (adap:%d, dmx:%d): failed to start kthread.\n", + adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + return ret; + } + adap->thread = thread; + + return pt3_start_dma(adap); +} + +static int pt3_stop_streaming(struct pt3_adapter *adap) +{ + int ret; + + ret = pt3_stop_dma(adap); + if (ret) + dev_warn(adap->dvb_adap.device, + "PT3: failed to stop streaming of adap:%d/FE:%d\n", + adap->dvb_adap.num, adap->fe->id); + + /* kill the fetching thread */ + ret = kthread_stop(adap->thread); + return ret; +} + +static int pt3_start_feed(struct dvb_demux_feed *feed) +{ + struct pt3_adapter *adap; + + if (signal_pending(current)) + return -EINTR; + + adap = container_of(feed->demux, struct pt3_adapter, demux); + adap->num_feeds++; + if (adap->thread) + return 0; + if (adap->num_feeds != 1) { + dev_warn(adap->dvb_adap.device, + "%s: unmatched start/stop_feed in adap:%i/dmx:%i.\n", + __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + adap->num_feeds = 1; + } + + return pt3_start_streaming(adap); + +} + +static int pt3_stop_feed(struct dvb_demux_feed *feed) +{ + struct pt3_adapter *adap; + + adap = container_of(feed->demux, struct pt3_adapter, demux); + + adap->num_feeds--; + if (adap->num_feeds > 0 || !adap->thread) + return 0; + adap->num_feeds = 0; + + return pt3_stop_streaming(adap); +} + + +static int pt3_alloc_adapter(struct pt3_board *pt3, int index) +{ + int ret; + struct pt3_adapter *adap; + struct dvb_adapter *da; + + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) { + dev_err(&pt3->pdev->dev, "failed to alloc mem for adapter.\n"); + return -ENOMEM; + } + pt3->adaps[index] = adap; + adap->adap_idx = index; + + if (index == 0 || !one_adapter) { + ret = dvb_register_adapter(&adap->dvb_adap, "PT3 DVB", + THIS_MODULE, &pt3->pdev->dev, adapter_nr); + if (ret < 0) { + dev_err(&pt3->pdev->dev, + "failed to register adapter dev.\n"); + goto err_mem; + } + da = &adap->dvb_adap; + } else + da = &pt3->adaps[0]->dvb_adap; + + adap->dvb_adap.priv = pt3; + adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + adap->demux.priv = adap; + adap->demux.feednum = 256; + adap->demux.filternum = 256; + adap->demux.start_feed = pt3_start_feed; + adap->demux.stop_feed = pt3_stop_feed; + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + dev_err(&pt3->pdev->dev, "failed to init dmx dev.\n"); + goto err_adap; + } + + adap->dmxdev.filternum = 256; + adap->dmxdev.demux = &adap->demux.dmx; + ret = dvb_dmxdev_init(&adap->dmxdev, da); + if (ret < 0) { + dev_err(&pt3->pdev->dev, "failed to init dmxdev.\n"); + goto err_demux; + } + + ret = pt3_alloc_dmabuf(adap); + if (ret) { + dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers.\n"); + goto err_dmabuf; + } + + return 0; + +err_dmabuf: + pt3_free_dmabuf(adap); + dvb_dmxdev_release(&adap->dmxdev); +err_demux: + dvb_dmx_release(&adap->demux); +err_adap: + if (index == 0 || !one_adapter) + dvb_unregister_adapter(da); +err_mem: + kfree(adap); + pt3->adaps[index] = NULL; + return ret; +} + +static void pt3_cleanup_adapter(struct pt3_board *pt3, int index) +{ + struct pt3_adapter *adap; + struct dmx_demux *dmx; + + adap = pt3->adaps[index]; + if (adap == NULL) + return; + + /* stop demux kthread */ + if (adap->thread) + pt3_stop_streaming(adap); + + dmx = &adap->demux.dmx; + dmx->close(dmx); + if (adap->fe) { + adap->fe->callback = NULL; + if (adap->fe->frontend_priv) + dvb_unregister_frontend(adap->fe); + if (adap->i2c_tuner) { + module_put(adap->i2c_tuner->dev.driver->owner); + i2c_unregister_device(adap->i2c_tuner); + } + if (adap->i2c_demod) { + module_put(adap->i2c_demod->dev.driver->owner); + i2c_unregister_device(adap->i2c_demod); + } + } + pt3_free_dmabuf(adap); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + if (index == 0 || !one_adapter) + dvb_unregister_adapter(&adap->dvb_adap); + kfree(adap); + pt3->adaps[index] = NULL; +} + +#ifdef CONFIG_PM_SLEEP + +static int pt3_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt3_board *pt3 = pci_get_drvdata(pdev); + int i; + struct pt3_adapter *adap; + + for (i = 0; i < PT3_NUM_FE; i++) { + adap = pt3->adaps[i]; + if (adap->num_feeds > 0) + pt3_stop_dma(adap); + dvb_frontend_suspend(adap->fe); + pt3_free_dmabuf(adap); + } + + pt3_lnb_ctrl(pt3, false); + pt3_set_tuner_power(pt3, false, false); + return 0; +} + +static int pt3_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt3_board *pt3 = pci_get_drvdata(pdev); + int i, ret; + struct pt3_adapter *adap; + + ret = pt3_fe_init(pt3); + if (ret) + return ret; + + if (pt3->lna_on_cnt > 0) + pt3_set_tuner_power(pt3, true, true); + if (pt3->lnb_on_cnt > 0) + pt3_lnb_ctrl(pt3, true); + + for (i = 0; i < PT3_NUM_FE; i++) { + adap = pt3->adaps[i]; + dvb_frontend_resume(adap->fe); + ret = pt3_alloc_dmabuf(adap); + if (ret) { + dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs.\n"); + continue; + } + if (adap->num_feeds > 0) + pt3_start_dma(adap); + } + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + + +static void pt3_remove(struct pci_dev *pdev) +{ + struct pt3_board *pt3; + int i; + + pt3 = pci_get_drvdata(pdev); + for (i = PT3_NUM_FE - 1; i >= 0; i--) + pt3_cleanup_adapter(pt3, i); + i2c_del_adapter(&pt3->i2c_adap); + kfree(pt3->i2c_buf); + pci_iounmap(pt3->pdev, pt3->regs[0]); + pci_iounmap(pt3->pdev, pt3->regs[1]); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(pt3); +} + +static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + u8 rev; + u32 ver; + int i, ret; + struct pt3_board *pt3; + struct i2c_adapter *i2c; + + if (pci_read_config_byte(pdev, PCI_REVISION_ID, &rev) || rev != 1) + return -ENODEV; + + ret = pci_enable_device(pdev); + if (ret < 0) + return -ENODEV; + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret < 0) + goto err_disable_device; + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret == 0) + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + else { + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret == 0) + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + else { + dev_err(&pdev->dev, "Failed to set DMA mask.\n"); + goto err_release_regions; + } + dev_info(&pdev->dev, "Use 32bit DMA.\n"); + } + + pt3 = kzalloc(sizeof(*pt3), GFP_KERNEL); + if (!pt3) { + dev_err(&pdev->dev, "Failed to alloc mem for this dev.\n"); + ret = -ENOMEM; + goto err_release_regions; + } + pci_set_drvdata(pdev, pt3); + pt3->pdev = pdev; + mutex_init(&pt3->lock); + pt3->regs[0] = pci_ioremap_bar(pdev, 0); + pt3->regs[1] = pci_ioremap_bar(pdev, 2); + if (pt3->regs[0] == NULL || pt3->regs[1] == NULL) { + dev_err(&pdev->dev, "Failed to ioremap.\n"); + ret = -ENOMEM; + goto err_kfree; + } + + ver = ioread32(pt3->regs[0] + REG_VERSION); + if ((ver >> 16) != 0x0301) { + dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported", + ver >> 24, (ver & 0x00ff0000) >> 16); + ret = -ENODEV; + goto err_iounmap; + } + + pt3->num_bufs = clamp_val(num_bufs, MIN_DATA_BUFS, MAX_DATA_BUFS); + + pt3->i2c_buf = kmalloc(sizeof(*pt3->i2c_buf), GFP_KERNEL); + if (pt3->i2c_buf == NULL) { + dev_err(&pdev->dev, "Failed to alloc mem for i2c.\n"); + ret = -ENOMEM; + goto err_iounmap; + } + i2c = &pt3->i2c_adap; + i2c->owner = THIS_MODULE; + i2c->algo = &pt3_i2c_algo; + i2c->algo_data = NULL; + i2c->dev.parent = &pdev->dev; + strlcpy(i2c->name, DRV_NAME, sizeof(i2c->name)); + i2c_set_adapdata(i2c, pt3); + ret = i2c_add_adapter(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add i2c adapter.\n"); + goto err_i2cbuf; + } + + for (i = 0; i < PT3_NUM_FE; i++) { + ret = pt3_alloc_adapter(pt3, i); + if (ret < 0) + break; + + ret = pt3_attach_fe(pt3, i); + if (ret < 0) + break; + } + if (i < PT3_NUM_FE) { + dev_err(&pdev->dev, "Failed to create FE%d.\n", i); + goto err_cleanup_adapters; + } + + ret = pt3_fe_init(pt3); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init frontends.\n"); + i = PT3_NUM_FE - 1; + goto err_cleanup_adapters; + } + + dev_info(&pdev->dev, + "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x).\n", + ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff); + return 0; + +err_cleanup_adapters: + while (i >= 0) + pt3_cleanup_adapter(pt3, i--); + i2c_del_adapter(i2c); +err_i2cbuf: + kfree(pt3->i2c_buf); +err_iounmap: + if (pt3->regs[0]) + pci_iounmap(pdev, pt3->regs[0]); + if (pt3->regs[1]) + pci_iounmap(pdev, pt3->regs[1]); +err_kfree: + kfree(pt3); +err_release_regions: + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); + return ret; + +} + +static const struct pci_device_id pt3_id_table[] = { + { PCI_DEVICE_SUB(0x1172, 0x4c15, 0xee8d, 0x0368) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pt3_id_table); + +static SIMPLE_DEV_PM_OPS(pt3_pm_ops, pt3_suspend, pt3_resume); + +static struct pci_driver pt3_driver = { + .name = DRV_NAME, + .probe = pt3_probe, + .remove = pt3_remove, + .id_table = pt3_id_table, + + .driver.pm = &pt3_pm_ops, +}; + +module_pci_driver(pt3_driver); + +MODULE_DESCRIPTION("Earthsoft PT3 Driver"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h new file mode 100644 index 0000000..1b3f2ad --- /dev/null +++ b/drivers/media/pci/pt3/pt3.h @@ -0,0 +1,186 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +#ifndef PT3_H +#define PT3_H + +#include <linux/atomic.h> +#include <linux/types.h> + +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +#include "tc90522.h" +#include "mxl301rf.h" +#include "qm1d1c0042.h" + +#define DRV_NAME KBUILD_MODNAME + +#define PT3_NUM_FE 4 + +/* + * register index of the FPGA chip + */ +#define REG_VERSION 0x00 +#define REG_BUS 0x04 +#define REG_SYSTEM_W 0x08 +#define REG_SYSTEM_R 0x0c +#define REG_I2C_W 0x10 +#define REG_I2C_R 0x14 +#define REG_RAM_W 0x18 +#define REG_RAM_R 0x1c +#define REG_DMA_BASE 0x40 /* regs for FE[i] = REG_DMA_BASE + 0x18 * i */ +#define OFST_DMA_DESC_L 0x00 +#define OFST_DMA_DESC_H 0x04 +#define OFST_DMA_CTL 0x08 +#define OFST_TS_CTL 0x0c +#define OFST_STATUS 0x10 +#define OFST_TS_ERR 0x14 + +/* + * internal buffer for I2C + */ +#define PT3_I2C_MAX 4091 +struct pt3_i2cbuf { + u8 data[PT3_I2C_MAX]; + u8 tmp; + u32 num_cmds; +}; + +/* + * DMA things + */ +#define TS_PACKET_SZ 188 +/* DMA transfers must not cross 4GiB, so use one page / transfer */ +#define DATA_XFER_SZ 4096 +#define DATA_BUF_XFERS 47 +/* (num_bufs * DATA_BUF_SZ) % TS_PACKET_SZ must be 0 */ +#define DATA_BUF_SZ (DATA_BUF_XFERS * DATA_XFER_SZ) +#define MAX_DATA_BUFS 16 +#define MIN_DATA_BUFS 2 + +#define DESCS_IN_PAGE (PAGE_SIZE / sizeof(struct xfer_desc)) +#define MAX_NUM_XFERS (MAX_DATA_BUFS * DATA_BUF_XFERS) +#define MAX_DESC_BUFS DIV_ROUND_UP(MAX_NUM_XFERS, DESCS_IN_PAGE) + +/* DMA transfer description. + * device is passed a pointer to this struct, dma-reads it, + * and gets the DMA buffer ring for storing TS data. + */ +struct xfer_desc { + u32 addr_l; /* bus address of target data buffer */ + u32 addr_h; + u32 size; + u32 next_l; /* bus adddress of the next xfer_desc */ + u32 next_h; +}; + +/* A DMA mapping of a page containing xfer_desc's */ +struct xfer_desc_buffer { + dma_addr_t b_addr; + struct xfer_desc *descs; /* PAGE_SIZE (xfer_desc[DESCS_IN_PAGE]) */ +}; + +/* A DMA mapping of a data buffer */ +struct dma_data_buffer { + dma_addr_t b_addr; + u8 *data; /* size: u8[PAGE_SIZE] */ +}; + +/* + * device things + */ +struct pt3_adap_config { + struct i2c_board_info demod_info; + struct tc90522_config demod_cfg; + + struct i2c_board_info tuner_info; + union tuner_config { + struct qm1d1c0042_config qm1d1c0042; + struct mxl301rf_config mxl301rf; + } tuner_cfg; + u32 init_freq; +}; + +struct pt3_adapter { + struct dvb_adapter dvb_adap; /* dvb_adap.priv => struct pt3_board */ + int adap_idx; + + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dvb_frontend *fe; + struct i2c_client *i2c_demod; + struct i2c_client *i2c_tuner; + + /* data fetch thread */ + struct task_struct *thread; + int num_feeds; + + bool cur_lna; + bool cur_lnb; /* current LNB power status (on/off) */ + + /* items below are for DMA */ + struct dma_data_buffer buffer[MAX_DATA_BUFS]; + int buf_idx; + int buf_ofs; + int num_bufs; /* == pt3_board->num_bufs */ + int num_discard; /* how many access units to discard initially */ + + struct xfer_desc_buffer desc_buf[MAX_DESC_BUFS]; + int num_desc_bufs; /* == num_bufs * DATA_BUF_XFERS / DESCS_IN_PAGE */ +}; + + +struct pt3_board { + struct pci_dev *pdev; + void __iomem *regs[2]; + /* regs[0]: registers, regs[1]: internal memory, used for I2C */ + + struct mutex lock; + + /* LNB power shared among sat-FEs */ + int lnb_on_cnt; /* LNB power on count */ + + /* LNA shared among terr-FEs */ + int lna_on_cnt; /* booster enabled count */ + + int num_bufs; /* number of DMA buffers allocated/mapped per FE */ + + struct i2c_adapter i2c_adap; + struct pt3_i2cbuf *i2c_buf; + + struct pt3_adapter *adaps[PT3_NUM_FE]; +}; + + +/* + * prototypes + */ +extern int pt3_alloc_dmabuf(struct pt3_adapter *adap); +extern void pt3_init_dmabuf(struct pt3_adapter *adap); +extern void pt3_free_dmabuf(struct pt3_adapter *adap); +extern int pt3_start_dma(struct pt3_adapter *adap); +extern int pt3_stop_dma(struct pt3_adapter *adap); +extern int pt3_proc_dma(struct pt3_adapter *adap); + +extern int pt3_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num); +extern u32 pt3_i2c_functionality(struct i2c_adapter *adap); +extern void pt3_i2c_reset(struct pt3_board *pt3); +extern int pt3_init_all_demods(struct pt3_board *pt3); +extern int pt3_init_all_mxl301rf(struct pt3_board *pt3); +#endif /* PT3_H */ diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c new file mode 100644 index 0000000..f0ce904 --- /dev/null +++ b/drivers/media/pci/pt3/pt3_dma.c @@ -0,0 +1,225 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/pci.h> + +#include "pt3.h" + +#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128) +#define PT3_BUF_CANARY (0x74) + +static u32 get_dma_base(int idx) +{ + int i; + + i = (idx == 1 || idx == 2) ? 3 - idx : idx; + return REG_DMA_BASE + 0x18 * i; +} + +int pt3_stop_dma(struct pt3_adapter *adap) +{ + struct pt3_board *pt3 = adap->dvb_adap.priv; + u32 base; + u32 stat; + int retry; + + base = get_dma_base(adap->adap_idx); + stat = ioread32(pt3->regs[0] + base + OFST_STATUS); + if (!(stat & 0x01)) + return 0; + + iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); + for (retry = 0; retry < 5; retry++) { + stat = ioread32(pt3->regs[0] + base + OFST_STATUS); + if (!(stat & 0x01)) + return 0; + msleep(50); + } + return -EIO; +} + +int pt3_start_dma(struct pt3_adapter *adap) +{ + struct pt3_board *pt3 = adap->dvb_adap.priv; + u32 base = get_dma_base(adap->adap_idx); + + iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); + iowrite32(lower_32_bits(adap->desc_buf[0].b_addr), + pt3->regs[0] + base + OFST_DMA_DESC_L); + iowrite32(upper_32_bits(adap->desc_buf[0].b_addr), + pt3->regs[0] + base + OFST_DMA_DESC_H); + iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL); + return 0; +} + + +static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs) +{ + *ofs += PT3_ACCESS_UNIT; + if (*ofs >= DATA_BUF_SZ) { + *ofs -= DATA_BUF_SZ; + (*idx)++; + if (*idx == adap->num_bufs) + *idx = 0; + } + return &adap->buffer[*idx].data[*ofs]; +} + +int pt3_proc_dma(struct pt3_adapter *adap) +{ + int idx, ofs; + + idx = adap->buf_idx; + ofs = adap->buf_ofs; + + if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY) + return 0; + + while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) { + u8 *p; + + p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs]; + if (adap->num_discard > 0) + adap->num_discard--; + else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) { + dvb_dmx_swfilter_packets(&adap->demux, p, + (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ); + dvb_dmx_swfilter_packets(&adap->demux, + adap->buffer[idx].data, ofs / TS_PACKET_SZ); + } else + dvb_dmx_swfilter_packets(&adap->demux, p, + PT3_ACCESS_UNIT / TS_PACKET_SZ); + + *p = PT3_BUF_CANARY; + adap->buf_idx = idx; + adap->buf_ofs = ofs; + } + return 0; +} + +void pt3_init_dmabuf(struct pt3_adapter *adap) +{ + int idx, ofs; + u8 *p; + + idx = 0; + ofs = 0; + p = adap->buffer[0].data; + /* mark the whole buffers as "not written yet" */ + while (idx < adap->num_bufs) { + p[ofs] = PT3_BUF_CANARY; + ofs += PT3_ACCESS_UNIT; + if (ofs >= DATA_BUF_SZ) { + ofs -= DATA_BUF_SZ; + idx++; + p = adap->buffer[idx].data; + } + } + adap->buf_idx = 0; + adap->buf_ofs = 0; +} + +void pt3_free_dmabuf(struct pt3_adapter *adap) +{ + struct pt3_board *pt3; + int i; + + pt3 = adap->dvb_adap.priv; + for (i = 0; i < adap->num_bufs; i++) + dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ, + adap->buffer[i].data, adap->buffer[i].b_addr); + adap->num_bufs = 0; + + for (i = 0; i < adap->num_desc_bufs; i++) + dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE, + adap->desc_buf[i].descs, adap->desc_buf[i].b_addr); + adap->num_desc_bufs = 0; +} + + +int pt3_alloc_dmabuf(struct pt3_adapter *adap) +{ + struct pt3_board *pt3; + void *p; + int i, j; + int idx, ofs; + int num_desc_bufs; + dma_addr_t data_addr, desc_addr; + struct xfer_desc *d; + + pt3 = adap->dvb_adap.priv; + adap->num_bufs = 0; + adap->num_desc_bufs = 0; + for (i = 0; i < pt3->num_bufs; i++) { + p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ, + &adap->buffer[i].b_addr, GFP_KERNEL); + if (p == NULL) + goto failed; + adap->buffer[i].data = p; + adap->num_bufs++; + } + pt3_init_dmabuf(adap); + + /* build circular-linked pointers (xfer_desc) to the data buffers*/ + idx = 0; + ofs = 0; + num_desc_bufs = + DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE); + for (i = 0; i < num_desc_bufs; i++) { + p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE, + &desc_addr, GFP_KERNEL); + if (p == NULL) + goto failed; + adap->num_desc_bufs++; + adap->desc_buf[i].descs = p; + adap->desc_buf[i].b_addr = desc_addr; + + if (i > 0) { + d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1]; + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + } + for (j = 0; j < DESCS_IN_PAGE; j++) { + data_addr = adap->buffer[idx].b_addr + ofs; + d = &adap->desc_buf[i].descs[j]; + d->addr_l = lower_32_bits(data_addr); + d->addr_h = upper_32_bits(data_addr); + d->size = DATA_XFER_SZ; + + desc_addr += sizeof(struct xfer_desc); + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + + ofs += DATA_XFER_SZ; + if (ofs >= DATA_BUF_SZ) { + ofs -= DATA_BUF_SZ; + idx++; + if (idx >= adap->num_bufs) { + desc_addr = adap->desc_buf[0].b_addr; + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + return 0; + } + } + } + } + return 0; + +failed: + pt3_free_dmabuf(adap); + return -ENOMEM; +} diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c new file mode 100644 index 0000000..ec6a8a2 --- /dev/null +++ b/drivers/media/pci/pt3/pt3_i2c.c @@ -0,0 +1,240 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/pci.h> + +#include "pt3.h" + +#define PT3_I2C_BASE 2048 +#define PT3_CMD_ADDR_NORMAL 0 +#define PT3_CMD_ADDR_INIT_DEMOD 4096 +#define PT3_CMD_ADDR_INIT_TUNER (4096 + 2042) + +/* masks for I2C status register */ +#define STAT_SEQ_RUNNING 0x1 +#define STAT_SEQ_ERROR 0x6 +#define STAT_NO_SEQ 0x8 + +#define PT3_I2C_RUN (1 << 16) +#define PT3_I2C_RESET (1 << 17) + +enum ctl_cmd { + I_END, + I_ADDRESS, + I_CLOCK_L, + I_CLOCK_H, + I_DATA_L, + I_DATA_H, + I_RESET, + I_SLEEP, + I_DATA_L_NOP = 0x08, + I_DATA_H_NOP = 0x0c, + I_DATA_H_READ = 0x0d, + I_DATA_H_ACK0 = 0x0e, + I_DATA_H_ACK1 = 0x0f, +}; + + +static void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd) +{ + int buf_idx; + + if ((cbuf->num_cmds % 2) == 0) + cbuf->tmp = cmd; + else { + cbuf->tmp |= cmd << 4; + buf_idx = cbuf->num_cmds / 2; + if (buf_idx < ARRAY_SIZE(cbuf->data)) + cbuf->data[buf_idx] = cbuf->tmp; + } + cbuf->num_cmds++; +} + +static void put_end(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_END); + if (cbuf->num_cmds % 2) + cmdbuf_add(cbuf, I_END); +} + +static void put_start(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_DATA_H); + cmdbuf_add(cbuf, I_CLOCK_H); + cmdbuf_add(cbuf, I_DATA_L); + cmdbuf_add(cbuf, I_CLOCK_L); +} + +static void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val) +{ + u8 mask; + + mask = 0x80; + for (mask = 0x80; mask > 0; mask >>= 1) + cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP); + cmdbuf_add(cbuf, I_DATA_H_ACK0); +} + +static void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size) +{ + int i, j; + + for (i = 0; i < size; i++) { + for (j = 0; j < 8; j++) + cmdbuf_add(cbuf, I_DATA_H_READ); + cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP); + } +} + +static void put_stop(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_DATA_L); + cmdbuf_add(cbuf, I_CLOCK_H); + cmdbuf_add(cbuf, I_DATA_H); +} + + +/* translates msgs to internal commands for bit-banging */ +static void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num) +{ + int i, j; + bool rd; + + cbuf->num_cmds = 0; + for (i = 0; i < num; i++) { + rd = !!(msgs[i].flags & I2C_M_RD); + put_start(cbuf); + put_byte_write(cbuf, msgs[i].addr << 1 | rd); + if (rd) + put_byte_read(cbuf, msgs[i].len); + else + for (j = 0; j < msgs[i].len; j++) + put_byte_write(cbuf, msgs[i].buf[j]); + } + if (num > 0) { + put_stop(cbuf); + put_end(cbuf); + } +} + +static int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait) +{ + int i; + u32 v; + + for (i = 0; i < max_wait; i++) { + v = ioread32(pt3->regs[0] + REG_I2C_R); + if (!(v & STAT_SEQ_RUNNING)) + break; + usleep_range(500, 750); + } + if (i >= max_wait) + return -EIO; + if (result) + *result = v; + return 0; +} + +/* send [pre-]translated i2c msgs stored at addr */ +static int send_i2c_cmd(struct pt3_board *pt3, u32 addr) +{ + u32 ret; + + /* make sure that previous transactions had finished */ + if (wait_i2c_result(pt3, NULL, 50)) { + dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n", + __func__); + return -EIO; + } + + iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W); + usleep_range(200, 300); + /* wait for the current transaction to finish */ + if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) { + dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__); + return -EIO; + } + return 0; +} + + +/* init commands for each demod are combined into one transaction + * and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD. + */ +int pt3_init_all_demods(struct pt3_board *pt3) +{ + ioread32(pt3->regs[0] + REG_I2C_R); + return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD); +} + +/* init commands for two ISDB-T tuners are hidden in ROM. */ +int pt3_init_all_mxl301rf(struct pt3_board *pt3) +{ + usleep_range(1000, 2000); + return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER); +} + +void pt3_i2c_reset(struct pt3_board *pt3) +{ + iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W); +} + +/* + * I2C algorithm + */ +int +pt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct pt3_board *pt3; + struct pt3_i2cbuf *cbuf; + int i; + void __iomem *p; + + pt3 = i2c_get_adapdata(adap); + cbuf = pt3->i2c_buf; + + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RECV_LEN) { + dev_warn(&pt3->pdev->dev, + "(%s) I2C_M_RECV_LEN not supported.\n", + __func__); + return -EINVAL; + } + + translate(cbuf, msgs, num); + memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2, + cbuf->data, cbuf->num_cmds); + + if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0) + return -EIO; + + p = pt3->regs[1] + PT3_I2C_BASE; + for (i = 0; i < num; i++) + if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) { + memcpy_fromio(msgs[i].buf, p, msgs[i].len); + p += msgs[i].len; + } + + return num; +} + +u32 pt3_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 18ae755..b44e0d7 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -63,3 +63,11 @@ config VIDEO_SAA7134_DVB To compile this driver as a module, choose M here: the module will be called saa7134-dvb. + +config VIDEO_SAA7134_GO7007 + tristate "go7007 support for saa7134 based TV cards" + depends on VIDEO_SAA7134 + depends on VIDEO_GO7007 + ---help--- + Enables saa7134 driver support for boards with go7007 + MPEG encoder (WIS Voyager or compatible). diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index 58de9b0..09c43da 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -5,6 +5,7 @@ saa7134-y += saa7134-video.o saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o +obj-$(CONFIG_VIDEO_SAA7134_GO7007) += saa7134-go7007.o obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o @@ -14,3 +15,4 @@ ccflags-y += -I$(srctree)/drivers/media/i2c ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +ccflags-y += -I$(srctree)/drivers/media/usb/go7007 diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index 6e4bdb9..3ca0780 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5827,6 +5827,29 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0000800, }, }, + [SAA7134_BOARD_WIS_VOYAGER] = { + .name = "WIS Voyager or compatible", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_GO7007, + .inputs = { { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + }, }; @@ -7080,6 +7103,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2055, /* AverTV Satellite Hybrid+FM A706 */ .driver_data = SAA7134_BOARD_AVERMEDIA_A706, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1905, /* WIS */ + .subdevice = 0x7007, + .driver_data = SAA7134_BOARD_WIS_VOYAGER, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 9ff03a6..236ed72 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -160,6 +160,8 @@ static void request_module_async(struct work_struct *work){ request_module("saa7134-empress"); if (card_is_dvb(dev)) request_module("saa7134-dvb"); + if (card_is_go7007(dev)) + request_module("saa7134-go7007"); if (alsa) { if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) request_module("saa7134-alsa"); @@ -563,8 +565,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) saa7134_irq_vbi_done(dev,status); if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && - card_has_mpeg(dev)) - saa7134_irq_ts_done(dev,status); + card_has_mpeg(dev)) { + if (dev->mops->irq_ts_done != NULL) + dev->mops->irq_ts_done(dev, status); + else + saa7134_irq_ts_done(dev, status); + } if (report & SAA7134_IRQ_REPORT_GPIO16) { switch (dev->has_remote) { diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c new file mode 100644 index 0000000..54e650b --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-go7007.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <asm/byteorder.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "saa7134.h" +#include "saa7134-reg.h" +#include "go7007-priv.h" + +/*#define GO7007_HPI_DEBUG*/ + +enum hpi_address { + HPI_ADDR_VIDEO_BUFFER = 0xe4, + HPI_ADDR_INIT_BUFFER = 0xea, + HPI_ADDR_INTR_RET_VALUE = 0xee, + HPI_ADDR_INTR_RET_DATA = 0xec, + HPI_ADDR_INTR_STATUS = 0xf4, + HPI_ADDR_INTR_WR_PARAM = 0xf6, + HPI_ADDR_INTR_WR_INDEX = 0xf8, +}; + +enum gpio_command { + GPIO_COMMAND_RESET = 0x00, /* 000b */ + GPIO_COMMAND_REQ1 = 0x04, /* 001b */ + GPIO_COMMAND_WRITE = 0x20, /* 010b */ + GPIO_COMMAND_REQ2 = 0x24, /* 011b */ + GPIO_COMMAND_READ = 0x80, /* 100b */ + GPIO_COMMAND_VIDEO = 0x84, /* 101b */ + GPIO_COMMAND_IDLE = 0xA0, /* 110b */ + GPIO_COMMAND_ADDR = 0xA4, /* 111b */ +}; + +struct saa7134_go7007 { + struct v4l2_subdev sd; + struct saa7134_dev *dev; + u8 *top; + u8 *bottom; + dma_addr_t top_dma; + dma_addr_t bottom_dma; +}; + +static inline struct saa7134_go7007 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7134_go7007, sd); +} + +static const struct go7007_board_info board_voyager = { + .flags = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_inputs = 1, + .inputs = { + { + .name = "SAA7134", + }, + }, +}; + +/********************* Driver for GPIO HPI interface *********************/ + +static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Read low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data = saa_readb(SAA7134_GPIO_GPSTATUS0); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Read high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8; + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int saa7134_go7007_interface_reset(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u16 intr_val, intr_data; + int count = 20; + + saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */ + saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4); + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET); + msleep(1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + msleep(10); + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + saa_readb(SAA7134_GPIO_GPSTATUS2); + /*pr_debug("status is %s\n", saa_readb(SAA7134_GPIO_GPSTATUS2) & 0x40 ? "OK" : "not OK"); */ + + /* enter command mode...(?) */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + + do { + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_readb(SAA7134_GPIO_GPSTATUS2); + /*pr_info("gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ + } while (--count > 0); + + /* Wait for an interrupt to indicate successful hardware reset */ + if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x55aa) { + pr_err("saa7134-go7007: unable to reset the GO7007\n"); + return -1; + } + return 0; +} + +static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + int i; + u16 status_reg; + +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); +#endif + + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0010)) + break; + msleep(10); + } + if (i == 100) { + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data); + gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr); + + return 0; +} + +static int saa7134_go7007_read_interrupt(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + /* XXX we need to wait if there is no interrupt available */ + go->interrupt_available = 1; + gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); + gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: ReadInterrupt: %04x %04x\n", + go->interrupt_value, go->interrupt_data); +#endif + return 0; +} + +static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, + unsigned long status) +{ + struct go7007 *go = video_get_drvdata(dev->empress_dev); + struct saa7134_go7007 *saa = go->hpi_context; + + if (!vb2_is_streaming(&go->vidq)) + return; + if (0 != (status & 0x000f0000)) + pr_debug("saa7134-go7007: irq: lost %ld\n", + (status >> 16) & 0x0f); + if (status & 0x100000) { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE); + saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma); + } else { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->top, PAGE_SIZE); + saa_writel(SAA7134_RS_BA1(5), saa->top_dma); + } +} + +static int saa7134_go7007_stream_start(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&dev->pci->dev, saa->top_dma)) + return -ENOMEM; + saa->bottom_dma = dma_map_page(&dev->pci->dev, + virt_to_page(saa->bottom), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&dev->pci->dev, saa->bottom_dma)) { + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + return -ENOMEM; + } + + saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000); + saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200); + + /* Set HPI interface for video */ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Enable TS interface */ + saa_writeb(SAA7134_TS_PARALLEL, 0xe6); + + /* Reset TS interface */ + saa_setb(SAA7134_TS_SERIAL1, 0x01); + saa_clearb(SAA7134_TS_SERIAL1, 0x01); + + /* Set up transfer block size */ + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); + saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); + saa_writeb(SAA7134_TS_DMA1, 0); + saa_writeb(SAA7134_TS_DMA2, 0); + + /* Enable video streaming mode */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); + + saa_writel(SAA7134_RS_BA1(5), saa->top_dma); + saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma); + saa_writel(SAA7134_RS_PITCH(5), 128); + saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX); + + /* Enable TS FIFO */ + saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Enable DMA IRQ */ + saa_setl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + return 0; +} + +static int saa7134_go7007_stream_stop(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev; + + if (!saa) + return -EINVAL; + dev = saa->dev; + if (!dev) + return -EINVAL; + + /* Shut down TS FIFO */ + saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Disable DMA IRQ */ + saa_clearl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + /* Disable TS interface */ + saa_clearb(SAA7134_TS_PARALLEL, 0x80); + + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + + return 0; +} + +static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u16 status_reg; + int i; + +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: DownloadBuffer sending %d bytes\n", len); +#endif + + while (len > 0) { + i = len > 64 ? 64 : len; + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + while (i-- > 0) { + saa_writeb(SAA7134_GPIO_GPSTATUS0, *data); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + ++data; + --len; + } + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0002)) + break; + } + if (i == 100) { + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + } + return 0; +} + +static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { + .interface_reset = saa7134_go7007_interface_reset, + .write_interrupt = saa7134_go7007_write_interrupt, + .read_interrupt = saa7134_go7007_read_interrupt, + .stream_start = saa7134_go7007_stream_start, + .stream_stop = saa7134_go7007_stream_stop, + .send_firmware = saa7134_go7007_send_firmware, +}; +MODULE_FIRMWARE("go7007/go7007tv.bin"); + +/* --------------------------------------------------------------------------*/ + +static int saa7134_go7007_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ +#if 0 + struct saa7134_go7007 *saa = to_state(sd); + struct saa7134_dev *dev = saa->dev; + + return saa7134_s_std_internal(dev, NULL, norm); +#else + return 0; +#endif +} + +static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = { + .s_std = saa7134_go7007_s_std, +}; + +static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = { + .video = &saa7134_go7007_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + + +/********************* Add/remove functions *********************/ + +static int saa7134_go7007_init(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + struct v4l2_subdev *sd; + + pr_debug("saa7134-go7007: probing new SAA713X board\n"); + + go = go7007_alloc(&board_voyager, &dev->pci->dev); + if (go == NULL) + return -ENOMEM; + + saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); + if (saa == NULL) { + kfree(go); + return -ENOMEM; + } + + go->board_id = GO7007_BOARDID_PCI_VOYAGER; + snprintf(go->bus_info, sizeof(go->bus_info), "PCI:%s", pci_name(dev->pci)); + strlcpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); + go->hpi_ops = &saa7134_go7007_hpi_ops; + go->hpi_context = saa; + saa->dev = dev; + + /* Init the subdevice interface */ + sd = &saa->sd; + v4l2_subdev_init(sd, &saa7134_go7007_sd_ops); + v4l2_set_subdevdata(sd, saa); + strncpy(sd->name, "saa7134-go7007", sizeof(sd->name)); + + /* Allocate a couple pages for receiving the compressed stream */ + saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->top) + goto allocfail; + saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->bottom) + goto allocfail; + + /* Boot the GO7007 */ + if (go7007_boot_encoder(go, go->board_info->flags & + GO7007_BOARD_USE_ONBOARD_I2C) < 0) + goto allocfail; + + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go, go->board_info->num_i2c_devs) < 0) + goto allocfail; + + /* Register the subdevice interface with the go7007 device */ + if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0) + pr_info("saa7134-go7007: register subdev failed\n"); + + dev->empress_dev = &go->vdev; + + go->status = STATUS_ONLINE; + return 0; + +allocfail: + if (saa->top) + free_page((unsigned long)saa->top); + if (saa->bottom) + free_page((unsigned long)saa->bottom); + kfree(saa); + kfree(go); + return -ENOMEM; +} + +static int saa7134_go7007_fini(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + + if (NULL == dev->empress_dev) + return 0; + + go = video_get_drvdata(dev->empress_dev); + if (go->audio_enabled) + go7007_snd_remove(go); + + saa = go->hpi_context; + go->status = STATUS_SHUTDOWN; + free_page((unsigned long)saa->top); + free_page((unsigned long)saa->bottom); + v4l2_device_unregister_subdev(&saa->sd); + kfree(saa); + video_unregister_device(&go->vdev); + + v4l2_device_put(&go->v4l2_dev); + dev->empress_dev = NULL; + + return 0; +} + +static struct saa7134_mpeg_ops saa7134_go7007_ops = { + .type = SAA7134_MPEG_GO7007, + .init = saa7134_go7007_init, + .fini = saa7134_go7007_fini, + .irq_ts_done = saa7134_go7007_irq_ts_done, +}; + +static int __init saa7134_go7007_mod_init(void) +{ + return saa7134_ts_register(&saa7134_go7007_ops); +} + +static void __exit saa7134_go7007_mod_cleanup(void) +{ + saa7134_ts_unregister(&saa7134_go7007_ops); +} + +module_init(saa7134_go7007_mod_init); +module_exit(saa7134_go7007_mod_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index c06dbe1..4f0b101 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); /* ------------------------------------------------------------------ */ -#define VBI_LINE_COUNT 16 +#define VBI_LINE_COUNT 17 #define VBI_LINE_LENGTH 2048 #define VBI_SCALE 0x200 diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 0cfa2ca..fc4a427 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -201,7 +201,7 @@ static struct saa7134_format formats[] = { .video_v_start = 24, \ .video_v_stop = 311, \ .vbi_v_start_0 = 7, \ - .vbi_v_stop_0 = 22, \ + .vbi_v_stop_0 = 23, \ .vbi_v_start_1 = 319, \ .src_timing = 4 diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index e47edd4..1a82dd0 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -338,6 +338,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_ASUSTeK_PS3_100 190 #define SAA7134_BOARD_HAWELL_HW_9004V1 191 #define SAA7134_BOARD_AVERMEDIA_A706 192 +#define SAA7134_BOARD_WIS_VOYAGER 193 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -368,6 +369,7 @@ enum saa7134_mpeg_type { SAA7134_MPEG_UNUSED, SAA7134_MPEG_EMPRESS, SAA7134_MPEG_DVB, + SAA7134_MPEG_GO7007, }; enum saa7134_mpeg_ts_type { @@ -407,6 +409,7 @@ struct saa7134_board { #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) #define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) #define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) +#define card_is_go7007(dev) (SAA7134_MPEG_GO7007 == saa7134_boards[dev->board].mpeg) #define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) #define card(dev) (saa7134_boards[dev->board]) #define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) @@ -522,6 +525,8 @@ struct saa7134_mpeg_ops { int (*init)(struct saa7134_dev *dev); int (*fini)(struct saa7134_dev *dev); void (*signal_change)(struct saa7134_dev *dev); + void (*irq_ts_done)(struct saa7134_dev *dev, + unsigned long status); }; /* global device status */ diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c index e042963..4f3b1dd 100644 --- a/drivers/media/pci/saa7164/saa7164-api.c +++ b/drivers/media/pci/saa7164/saa7164-api.c @@ -680,7 +680,6 @@ static int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val) int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) { struct saa7164_dev *dev = port->dev; - int ret = 0; u8 agc_disable; dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std); @@ -733,7 +732,7 @@ int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */ msleep(100); - return ret; + return 0; } /* Ensure the dif is in the correct state for the operating mode diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 1bf0697..cc1be8a 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -52,7 +52,7 @@ unsigned int saa_debug; module_param_named(debug, saa_debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); -unsigned int fw_debug; +static unsigned int fw_debug; module_param(fw_debug, int, 0644); MODULE_PARM_DESC(fw_debug, "Firmware debug level def:2"); @@ -72,7 +72,7 @@ static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -unsigned int print_histogram = 64; +static unsigned int print_histogram = 64; module_param(print_histogram, int, 0644); MODULE_PARM_DESC(print_histogram, "print histogram values once"); @@ -80,7 +80,7 @@ unsigned int crc_checking = 1; module_param(crc_checking, int, 0644); MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); -unsigned int guard_checking = 1; +static unsigned int guard_checking = 1; module_param(guard_checking, int, 0644); MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns"); diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index d9e06a6..0fb91dc 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -1,6 +1,7 @@ config VIDEO_SOLO6X10 tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" depends on PCI && VIDEO_DEV && SND && I2C + depends on HAS_DMA select BITREVERSE select FONT_SUPPORT select FONT_8x16 diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index 5ea9cac..11c98f0 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -172,7 +172,7 @@ static void solo_vout_config(struct solo_dev *solo_dev) static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, u16 val, int reg_size) { - u16 *buf; + __le16 *buf; const int n = 64, size = n * sizeof(*buf); int i, ret = 0; @@ -211,7 +211,7 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, { const unsigned size = sizeof(u16) * 64; u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; - u16 *buf; + __le16 *buf; int x, y; int ret = 0; diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index af40b3a..da25ce4 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -100,7 +100,7 @@ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) return retval; } -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) +__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc) { int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); unsigned short retval = 0; @@ -117,11 +117,11 @@ unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) solo_eeprom_reg_write(solo_dev, ~EE_CS); - return retval; + return (__force __be16)retval; } int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data) + __be16 data) { int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); unsigned int retval; @@ -130,7 +130,7 @@ int solo_eeprom_write(struct solo_dev *solo_dev, int loc, solo_eeprom_cmd(solo_dev, write_cmd); for (i = 15; i >= 0; i--) { - unsigned int dataval = (data >> i) & 1; + unsigned int dataval = ((__force unsigned)data >> i) & 1; solo_eeprom_reg_write(solo_dev, EE_ENB); solo_eeprom_reg_write(solo_dev, diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index c6154b0..72017b7 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -394,9 +394,9 @@ int solo_osd_print(struct solo_enc_dev *solo_enc); /* EEPROM commands */ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en); -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc); +__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc); int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data); + __be16 data); /* JPEG Qp functions */ void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 0313015..f6f30ab 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,6 +1,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 + depends on HAS_DMA select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 365bd21..22450f5 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -152,7 +152,7 @@ struct sta2x11_vip { int tcount, bcount; int overflow; - void *iomem; /* I/O Memory */ + void __iomem *iomem; /* I/O Memory */ struct vip_config *config; }; diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 0dcb8cd..7b83151 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -1,8 +1,12 @@ +config DVB_AV7110_IR + bool + config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C select TTPCI_EEPROM select VIDEO_SAA7146_VV + select DVB_AV7110_IR if INPUT_EVDEV=y || INPUT_EVDEV=DVB_AV7110 depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile index 9890596..49f71b1 100644 --- a/drivers/media/pci/ttpci/Makefile +++ b/drivers/media/pci/ttpci/Makefile @@ -5,7 +5,7 @@ dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o -ifdef CONFIG_INPUT_EVDEV +ifdef CONFIG_DVB_AV7110_IR dvb-ttpci-objs += av7110_ir.o endif diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index f38329d..c1f0617 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -235,7 +235,7 @@ static void recover_arm(struct av7110 *av7110) restart_feeds(av7110); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_check_ir_config(av7110, true); #endif } @@ -268,7 +268,7 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_check_ir_config(av7110, false); #endif @@ -2725,7 +2725,7 @@ static int av7110_attach(struct saa7146_dev* dev, mutex_init(&av7110->ioctl_mutex); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_ir_init(av7110); #endif printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); @@ -2768,7 +2768,7 @@ static int av7110_detach(struct saa7146_dev* saa) struct av7110 *av7110 = saa->ext_priv; dprintk(4, "%p\n", av7110); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_ir_exit(av7110); #endif if (budgetpatch || av7110->full_ts) { diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig new file mode 100644 index 0000000..5425ba1 --- /dev/null +++ b/drivers/media/pci/tw68/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_TW68 + tristate "Techwell tw68x Video For Linux" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + select I2C_ALGOBIT + select VIDEOBUF2_DMA_SG + ---help--- + Support for Techwell tw68xx based frame grabber boards. + + To compile this driver as a module, choose M here: the + module will be called tw68. diff --git a/drivers/media/pci/tw68/Makefile b/drivers/media/pci/tw68/Makefile new file mode 100644 index 0000000..3d02f28 --- /dev/null +++ b/drivers/media/pci/tw68/Makefile @@ -0,0 +1,3 @@ +tw68-objs := tw68-core.o tw68-video.o tw68-risc.o + +obj-$(CONFIG_VIDEO_TW68) += tw68.o diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c new file mode 100644 index 0000000..a6fb48c --- /dev/null +++ b/drivers/media/pci/tw68/tw68-core.c @@ -0,0 +1,434 @@ +/* + * tw68-core.c + * Core functions for the Techwell 68xx driver + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/sound.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/pm.h> + +#include <media/v4l2-dev.h> +#include "tw68.h" +#include "tw68-reg.h" + +MODULE_DESCRIPTION("v4l2 driver module for tw6800 based video capture cards"); +MODULE_AUTHOR("William M. Brack"); +MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); +MODULE_LICENSE("GPL"); + +static unsigned int latency = UNSET; +module_param(latency, int, 0444); +MODULE_PARM_DESC(latency, "pci latency timer"); + +static unsigned int video_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET }; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video device number"); + +static unsigned int card[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static atomic_t tw68_instance = ATOMIC_INIT(0); + +/* ------------------------------------------------------------------ */ + +/* + * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * the PCI ID database up to date. Note that the entries must be + * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. + */ +static const struct pci_device_id tw68_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)}, + {0,} +}; + +/* ------------------------------------------------------------------ */ + + +/* + * The device is given a "soft reset". According to the specifications, + * after this "all register content remain unchanged", so we also write + * to all specified registers manually as well (mostly to manufacturer's + * specified reset values) + */ +static int tw68_hw_init1(struct tw68_dev *dev) +{ + /* Assure all interrupts are disabled */ + tw_writel(TW68_INTMASK, 0); /* 020 */ + /* Clear any pending interrupts */ + tw_writel(TW68_INTSTAT, 0xffffffff); /* 01C */ + /* Stop risc processor, set default buffer level */ + tw_writel(TW68_DMAC, 0x1600); + + tw_writeb(TW68_ACNTL, 0x80); /* 218 soft reset */ + msleep(100); + + tw_writeb(TW68_INFORM, 0x40); /* 208 mux0, 27mhz xtal */ + tw_writeb(TW68_OPFORM, 0x04); /* 20C analog line-lock */ + tw_writeb(TW68_HSYNC, 0); /* 210 color-killer high sens */ + tw_writeb(TW68_ACNTL, 0x42); /* 218 int vref #2, chroma adc off */ + + tw_writeb(TW68_CROP_HI, 0x02); /* 21C Hactive m.s. bits */ + tw_writeb(TW68_VDELAY_LO, 0x12);/* 220 Mfg specified reset value */ + tw_writeb(TW68_VACTIVE_LO, 0xf0); + tw_writeb(TW68_HDELAY_LO, 0x0f); + tw_writeb(TW68_HACTIVE_LO, 0xd0); + + tw_writeb(TW68_CNTRL1, 0xcd); /* 230 Wide Chroma BPF B/W + * Secam reduction, Adap comb for + * NTSC, Op Mode 1 */ + + tw_writeb(TW68_VSCALE_LO, 0); /* 234 */ + tw_writeb(TW68_SCALE_HI, 0x11); /* 238 */ + tw_writeb(TW68_HSCALE_LO, 0); /* 23c */ + tw_writeb(TW68_BRIGHT, 0); /* 240 */ + tw_writeb(TW68_CONTRAST, 0x5c); /* 244 */ + tw_writeb(TW68_SHARPNESS, 0x51);/* 248 */ + tw_writeb(TW68_SAT_U, 0x80); /* 24C */ + tw_writeb(TW68_SAT_V, 0x80); /* 250 */ + tw_writeb(TW68_HUE, 0x00); /* 254 */ + + /* TODO - Check that none of these are set by control defaults */ + tw_writeb(TW68_SHARP2, 0x53); /* 258 Mfg specified reset val */ + tw_writeb(TW68_VSHARP, 0x80); /* 25C Sharpness Coring val 8 */ + tw_writeb(TW68_CORING, 0x44); /* 260 CTI and Vert Peak coring */ + tw_writeb(TW68_CNTRL2, 0x00); /* 268 No power saving enabled */ + tw_writeb(TW68_SDT, 0x07); /* 270 Enable shadow reg, auto-det */ + tw_writeb(TW68_SDTR, 0x7f); /* 274 All stds recog, don't start */ + tw_writeb(TW68_CLMPG, 0x50); /* 280 Clamp end at 40 sys clocks */ + tw_writeb(TW68_IAGC, 0x22); /* 284 Mfg specified reset val */ + tw_writeb(TW68_AGCGAIN, 0xf0); /* 288 AGC gain when loop disabled */ + tw_writeb(TW68_PEAKWT, 0xd8); /* 28C White peak threshold */ + tw_writeb(TW68_CLMPL, 0x3c); /* 290 Y channel clamp level */ +/* tw_writeb(TW68_SYNCT, 0x38);*/ /* 294 Sync amplitude */ + tw_writeb(TW68_SYNCT, 0x30); /* 294 Sync amplitude */ + tw_writeb(TW68_MISSCNT, 0x44); /* 298 Horiz sync, VCR detect sens */ + tw_writeb(TW68_PCLAMP, 0x28); /* 29C Clamp pos from PLL sync */ + /* Bit DETV of VCNTL1 helps sync multi cams/chip board */ + tw_writeb(TW68_VCNTL1, 0x04); /* 2A0 */ + tw_writeb(TW68_VCNTL2, 0); /* 2A4 */ + tw_writeb(TW68_CKILL, 0x68); /* 2A8 Mfg specified reset val */ + tw_writeb(TW68_COMB, 0x44); /* 2AC Mfg specified reset val */ + tw_writeb(TW68_LDLY, 0x30); /* 2B0 Max positive luma delay */ + tw_writeb(TW68_MISC1, 0x14); /* 2B4 Mfg specified reset val */ + tw_writeb(TW68_LOOP, 0xa5); /* 2B8 Mfg specified reset val */ + tw_writeb(TW68_MISC2, 0xe0); /* 2BC Enable colour killer */ + tw_writeb(TW68_MVSN, 0); /* 2C0 */ + tw_writeb(TW68_CLMD, 0x05); /* 2CC slice level auto, clamp med. */ + tw_writeb(TW68_IDCNTL, 0); /* 2D0 Writing zero to this register + * selects NTSC ID detection, + * but doesn't change the + * sensitivity (which has a reset + * value of 1E). Since we are + * not doing auto-detection, it + * has no real effect */ + tw_writeb(TW68_CLCNTL1, 0); /* 2D4 */ + tw_writel(TW68_VBIC, 0x03); /* 010 */ + tw_writel(TW68_CAP_CTL, 0x03); /* 040 Enable both even & odd flds */ + tw_writel(TW68_DMAC, 0x2000); /* patch set had 0x2080 */ + tw_writel(TW68_TESTREG, 0); /* 02C */ + + /* + * Some common boards, especially inexpensive single-chip models, + * use the GPIO bits 0-3 to control an on-board video-output mux. + * For these boards, we need to set up the GPIO register into + * "normal" mode, set bits 0-3 as output, and then set those bits + * zero. + * + * Eventually, it would be nice if we could identify these boards + * uniquely, and only do this initialisation if the board has been + * identify. For the moment, however, it shouldn't hurt anything + * to do these steps. + */ + tw_writel(TW68_GPIOC, 0); /* Set the GPIO to "normal", no ints */ + tw_writel(TW68_GPOE, 0x0f); /* Set bits 0-3 to "output" */ + tw_writel(TW68_GPDATA, 0); /* Set all bits to low state */ + + /* Initialize the device control structures */ + mutex_init(&dev->lock); + spin_lock_init(&dev->slock); + + /* Initialize any subsystems */ + tw68_video_init1(dev); + return 0; +} + +static irqreturn_t tw68_irq(int irq, void *dev_id) +{ + struct tw68_dev *dev = dev_id; + u32 status, orig; + int loop; + + status = orig = tw_readl(TW68_INTSTAT) & dev->pci_irqmask; + /* Check if anything to do */ + if (0 == status) + return IRQ_NONE; /* Nope - return */ + for (loop = 0; loop < 10; loop++) { + if (status & dev->board_virqmask) /* video interrupt */ + tw68_irq_video_done(dev, status); + status = tw_readl(TW68_INTSTAT) & dev->pci_irqmask; + if (0 == status) + return IRQ_HANDLED; + } + dev_dbg(&dev->pci->dev, "%s: **** INTERRUPT NOT HANDLED - clearing mask (orig 0x%08x, cur 0x%08x)", + dev->name, orig, tw_readl(TW68_INTSTAT)); + dev_dbg(&dev->pci->dev, "%s: pci_irqmask 0x%08x; board_virqmask 0x%08x ****\n", + dev->name, dev->pci_irqmask, dev->board_virqmask); + tw_clearl(TW68_INTMASK, dev->pci_irqmask); + return IRQ_HANDLED; +} + +static int tw68_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw68_dev *dev; + int vidnr = -1; + int err; + + dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + dev->instance = v4l2_device_set_name(&dev->v4l2_dev, "tw68", + &tw68_instance); + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + return err; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail1; + } + + dev->name = dev->v4l2_dev.name; + + if (UNSET != latency) { + pr_info("%s: setting pci latency timer to %d\n", + dev->name, latency); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + } + + /* print pci info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + pr_info("%s: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n", + dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat, (u64)pci_resource_start(pci_dev, 0)); + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { + pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name); + err = -EIO; + goto fail1; + } + + switch (pci_id->device) { + case PCI_DEVICE_ID_6800: /* TW6800 */ + dev->vdecoder = TW6800; + dev->board_virqmask = TW68_VID_INTS; + break; + case PCI_DEVICE_ID_6801: /* Video decoder for TW6802 */ + dev->vdecoder = TW6801; + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + case PCI_DEVICE_ID_6804: /* Video decoder for TW6804 */ + dev->vdecoder = TW6804; + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + default: + dev->vdecoder = TWXXXX; /* To be announced */ + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + } + + /* get mmio */ + if (!request_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0), + dev->name)) { + err = -EBUSY; + pr_err("%s: can't get MMIO memory @ 0x%llx\n", + dev->name, + (unsigned long long)pci_resource_start(pci_dev, 0)); + goto fail1; + } + dev->lmmio = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + dev->bmmio = (__u8 __iomem *)dev->lmmio; + if (NULL == dev->lmmio) { + err = -EIO; + pr_err("%s: can't ioremap() MMIO memory\n", + dev->name); + goto fail2; + } + /* initialize hardware #1 */ + /* Then do any initialisation wanted before interrupts are on */ + tw68_hw_init1(dev); + + /* get irq */ + err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + pr_err("%s: can't get IRQ %d\n", + dev->name, pci_dev->irq); + goto fail3; + } + + /* + * Now do remainder of initialisation, first for + * things unique for this card, then for general board + */ + if (dev->instance < TW68_MAXBOARDS) + vidnr = video_nr[dev->instance]; + /* initialise video function first */ + err = tw68_video_init2(dev, vidnr); + if (err < 0) { + pr_err("%s: can't register video device\n", + dev->name); + goto fail4; + } + tw_setl(TW68_INTMASK, dev->pci_irqmask); + + pr_info("%s: registered device %s\n", + dev->name, video_device_node_name(&dev->vdev)); + + return 0; + +fail4: + video_unregister_device(&dev->vdev); +fail3: + iounmap(dev->lmmio); +fail2: + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); +fail1: + v4l2_device_unregister(&dev->v4l2_dev); + return err; +} + +static void tw68_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = + container_of(v4l2_dev, struct tw68_dev, v4l2_dev); + + /* shutdown subsystems */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + tw_writel(TW68_INTMASK, 0); + + /* unregister */ + video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + + /* release resources */ + iounmap(dev->lmmio); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + + v4l2_device_unregister(&dev->v4l2_dev); +} + +#ifdef CONFIG_PM + +static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = container_of(v4l2_dev, + struct tw68_dev, v4l2_dev); + + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + dev->pci_irqmask &= ~TW68_VID_INTS; + tw_writel(TW68_INTMASK, 0); + + synchronize_irq(pci_dev->irq); + + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); + vb2_discard_done(&dev->vidq); + + return 0; +} + +static int tw68_resume(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = container_of(v4l2_dev, + struct tw68_dev, v4l2_dev); + struct tw68_buf *buf; + unsigned long flags; + + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + /* Do things that are done in tw68_initdev , + except of initializing memory structures.*/ + + msleep(100); + + tw68_set_tvnorm_hw(dev); + + /*resume unfinished buffer(s)*/ + spin_lock_irqsave(&dev->slock, flags); + buf = container_of(dev->active.next, struct tw68_buf, list); + + tw68_video_start_dma(dev, buf); + + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} +#endif + +/* ----------------------------------------------------------- */ + +static struct pci_driver tw68_pci_driver = { + .name = "tw68", + .id_table = tw68_pci_tbl, + .probe = tw68_initdev, + .remove = tw68_finidev, +#ifdef CONFIG_PM + .suspend = tw68_suspend, + .resume = tw68_resume +#endif +}; + +module_pci_driver(tw68_pci_driver); diff --git a/drivers/media/pci/tw68/tw68-reg.h b/drivers/media/pci/tw68/tw68-reg.h new file mode 100644 index 0000000..f60b3a8 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-reg.h @@ -0,0 +1,195 @@ +/* + * tw68-reg.h - TW68xx register offsets + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. +*/ + +#ifndef _TW68_REG_H_ +#define _TW68_REG_H_ + +/* ---------------------------------------------------------------------- */ +#define TW68_DMAC 0x000 +#define TW68_DMAP_SA 0x004 +#define TW68_DMAP_EXE 0x008 +#define TW68_DMAP_PP 0x00c +#define TW68_VBIC 0x010 +#define TW68_SBUSC 0x014 +#define TW68_SBUSSD 0x018 +#define TW68_INTSTAT 0x01C +#define TW68_INTMASK 0x020 +#define TW68_GPIOC 0x024 +#define TW68_GPOE 0x028 +#define TW68_TESTREG 0x02C +#define TW68_SBUSRD 0x030 +#define TW68_SBUS_TRIG 0x034 +#define TW68_CAP_CTL 0x040 +#define TW68_SUBSYS 0x054 +#define TW68_I2C_RST 0x064 +#define TW68_VBIINST 0x06C +/* define bits in FIFO and DMAP Control reg */ +#define TW68_DMAP_EN (1 << 0) +#define TW68_FIFO_EN (1 << 1) +/* define the Interrupt Status Register bits */ +#define TW68_SBDONE (1 << 0) +#define TW68_DMAPI (1 << 1) +#define TW68_GPINT (1 << 2) +#define TW68_FFOF (1 << 3) +#define TW68_FDMIS (1 << 4) +#define TW68_DMAPERR (1 << 5) +#define TW68_PABORT (1 << 6) +#define TW68_SBDONE2 (1 << 12) +#define TW68_SBERR2 (1 << 13) +#define TW68_PPERR (1 << 14) +#define TW68_FFERR (1 << 15) +#define TW68_DET50 (1 << 16) +#define TW68_FLOCK (1 << 17) +#define TW68_CCVALID (1 << 18) +#define TW68_VLOCK (1 << 19) +#define TW68_FIELD (1 << 20) +#define TW68_SLOCK (1 << 21) +#define TW68_HLOCK (1 << 22) +#define TW68_VDLOSS (1 << 23) +#define TW68_SBERR (1 << 24) +/* define the i2c control register bits */ +#define TW68_SBMODE (0) +#define TW68_WREN (1) +#define TW68_SSCLK (6) +#define TW68_SSDAT (7) +#define TW68_SBCLK (8) +#define TW68_WDLEN (16) +#define TW68_RDLEN (20) +#define TW68_SBRW (24) +#define TW68_SBDEV (25) + +#define TW68_SBMODE_B (1 << TW68_SBMODE) +#define TW68_WREN_B (1 << TW68_WREN) +#define TW68_SSCLK_B (1 << TW68_SSCLK) +#define TW68_SSDAT_B (1 << TW68_SSDAT) +#define TW68_SBRW_B (1 << TW68_SBRW) + +#define TW68_GPDATA 0x100 +#define TW68_STATUS1 0x204 +#define TW68_INFORM 0x208 +#define TW68_OPFORM 0x20C +#define TW68_HSYNC 0x210 +#define TW68_ACNTL 0x218 +#define TW68_CROP_HI 0x21C +#define TW68_VDELAY_LO 0x220 +#define TW68_VACTIVE_LO 0x224 +#define TW68_HDELAY_LO 0x228 +#define TW68_HACTIVE_LO 0x22C +#define TW68_CNTRL1 0x230 +#define TW68_VSCALE_LO 0x234 +#define TW68_SCALE_HI 0x238 +#define TW68_HSCALE_LO 0x23C +#define TW68_BRIGHT 0x240 +#define TW68_CONTRAST 0x244 +#define TW68_SHARPNESS 0x248 +#define TW68_SAT_U 0x24C +#define TW68_SAT_V 0x250 +#define TW68_HUE 0x254 +#define TW68_SHARP2 0x258 +#define TW68_VSHARP 0x25C +#define TW68_CORING 0x260 +#define TW68_VBICNTL 0x264 +#define TW68_CNTRL2 0x268 +#define TW68_CC_DATA 0x26C +#define TW68_SDT 0x270 +#define TW68_SDTR 0x274 +#define TW68_RESERV2 0x278 +#define TW68_RESERV3 0x27C +#define TW68_CLMPG 0x280 +#define TW68_IAGC 0x284 +#define TW68_AGCGAIN 0x288 +#define TW68_PEAKWT 0x28C +#define TW68_CLMPL 0x290 +#define TW68_SYNCT 0x294 +#define TW68_MISSCNT 0x298 +#define TW68_PCLAMP 0x29C +#define TW68_VCNTL1 0x2A0 +#define TW68_VCNTL2 0x2A4 +#define TW68_CKILL 0x2A8 +#define TW68_COMB 0x2AC +#define TW68_LDLY 0x2B0 +#define TW68_MISC1 0x2B4 +#define TW68_LOOP 0x2B8 +#define TW68_MISC2 0x2BC +#define TW68_MVSN 0x2C0 +#define TW68_STATUS2 0x2C4 +#define TW68_HFREF 0x2C8 +#define TW68_CLMD 0x2CC +#define TW68_IDCNTL 0x2D0 +#define TW68_CLCNTL1 0x2D4 + +/* Audio */ +#define TW68_ACKI1 0x300 +#define TW68_ACKI2 0x304 +#define TW68_ACKI3 0x308 +#define TW68_ACKN1 0x30C +#define TW68_ACKN2 0x310 +#define TW68_ACKN3 0x314 +#define TW68_SDIV 0x318 +#define TW68_LRDIV 0x31C +#define TW68_ACCNTL 0x320 + +#define TW68_VSCTL 0x3B8 +#define TW68_CHROMAGVAL 0x3BC + +#define TW68_F2CROP_HI 0x3DC +#define TW68_F2VDELAY_LO 0x3E0 +#define TW68_F2VACTIVE_LO 0x3E4 +#define TW68_F2HDELAY_LO 0x3E8 +#define TW68_F2HACTIVE_LO 0x3EC +#define TW68_F2CNT 0x3F0 +#define TW68_F2VSCALE_LO 0x3F4 +#define TW68_F2SCALE_HI 0x3F8 +#define TW68_F2HSCALE_LO 0x3FC + +#define RISC_INT_BIT 0x08000000 +#define RISC_SYNCO 0xC0000000 +#define RISC_SYNCE 0xD0000000 +#define RISC_JUMP 0xB0000000 +#define RISC_LINESTART 0x90000000 +#define RISC_INLINE 0xA0000000 + +#define VideoFormatNTSC 0 +#define VideoFormatNTSCJapan 0 +#define VideoFormatPALBDGHI 1 +#define VideoFormatSECAM 2 +#define VideoFormatNTSC443 3 +#define VideoFormatPALM 4 +#define VideoFormatPALN 5 +#define VideoFormatPALNC 5 +#define VideoFormatPAL60 6 +#define VideoFormatAuto 7 + +#define ColorFormatRGB32 0x00 +#define ColorFormatRGB24 0x10 +#define ColorFormatRGB16 0x20 +#define ColorFormatRGB15 0x30 +#define ColorFormatYUY2 0x40 +#define ColorFormatBSWAP 0x04 +#define ColorFormatWSWAP 0x08 +#define ColorFormatGamma 0x80 +#endif diff --git a/drivers/media/pci/tw68/tw68-risc.c b/drivers/media/pci/tw68/tw68-risc.c new file mode 100644 index 0000000..7439db2 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-risc.c @@ -0,0 +1,230 @@ +/* + * tw68_risc.c + * Part of the device driver for Techwell 68xx based cards + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include "tw68.h" + +/** + * @rp pointer to current risc program position + * @sglist pointer to "scatter-gather list" of buffer pointers + * @offset offset to target memory buffer + * @sync_line 0 -> no sync, 1 -> odd sync, 2 -> even sync + * @bpl number of bytes per scan line + * @padding number of bytes of padding to add + * @lines number of lines in field + * @jump insert a jump at the start + */ +static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines, bool jump) +{ + struct scatterlist *sg; + unsigned int line, todo, done; + + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = 0; + } + + /* sync instruction */ + if (sync_line == 1) + *(rp++) = cpu_to_le32(RISC_SYNCO); + else + *(rp++) = cpu_to_le32(RISC_SYNCE); + *(rp++) = 0; + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + /* calculate next starting position */ + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg = sg_next(sg); + } + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = cpu_to_le32(RISC_LINESTART | + /* (offset<<12) |*/ bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + offset += bpl; + } else { + /* + * scanline needs to be split. Put the start in + * whatever memory remains using RISC_LINESTART, + * then the remainder into following addresses + * given by the scatter-gather list. + */ + todo = bpl; /* one full line to be done */ + /* first fragment */ + done = (sg_dma_len(sg) - offset); + *(rp++) = cpu_to_le32(RISC_LINESTART | + (7 << 24) | + done); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + todo -= done; + sg = sg_next(sg); + /* succeeding fragments have no offset */ + while (todo > sg_dma_len(sg)) { + *(rp++) = cpu_to_le32(RISC_INLINE | + (done << 12) | + sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + todo -= sg_dma_len(sg); + sg = sg_next(sg); + done += sg_dma_len(sg); + } + if (todo) { + /* final chunk - offset 0, count 'todo' */ + *(rp++) = cpu_to_le32(RISC_INLINE | + (done << 12) | + todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + } + offset = todo; + } + offset += padding; + } + + return rp; +} + +/** + * tw68_risc_buffer + * + * This routine is called by tw68-video. It allocates + * memory for the dma controller "program" and then fills in that + * memory with the appropriate "instructions". + * + * @pci_dev structure with info about the pci + * slot which our device is in. + * @risc structure with info about the memory + * used for our controller program. + * @sglist scatter-gather list entry + * @top_offset offset within the risc program area for the + * first odd frame line + * @bottom_offset offset within the risc program area for the + * first even frame line + * @bpl number of data bytes per scan line + * @padding number of extra bytes to add at end of line + * @lines number of scan lines + */ +int tw68_risc_buffer(struct pci_dev *pci, + struct tw68_buf *buf, + struct scatterlist *sglist, + unsigned int top_offset, + unsigned int bottom_offset, + unsigned int bpl, + unsigned int padding, + unsigned int lines) +{ + u32 instructions, fields; + __le32 *rp; + + fields = 0; + if (UNSET != top_offset) + fields++; + if (UNSET != bottom_offset) + fields++; + /* + * estimate risc mem: worst case is one write per page border + + * one write per scan line + syncs + 2 jumps (all 2 dwords). + * Padding can cause next bpl to start close to a page border. + * First DMA region may be smaller than PAGE_SIZE + */ + instructions = fields * (1 + (((bpl + padding) * lines) / + PAGE_SIZE) + lines) + 4; + buf->size = instructions * 8; + buf->cpu = pci_alloc_consistent(pci, buf->size, &buf->dma); + if (buf->cpu == NULL) + return -ENOMEM; + + /* write risc instructions */ + rp = buf->cpu; + if (UNSET != top_offset) /* generates SYNCO */ + rp = tw68_risc_field(rp, sglist, top_offset, 1, + bpl, padding, lines, true); + if (UNSET != bottom_offset) /* generates SYNCE */ + rp = tw68_risc_field(rp, sglist, bottom_offset, 2, + bpl, padding, lines, top_offset == UNSET); + + /* save pointer to jmp instruction address */ + buf->jmp = rp; + buf->cpu[1] = cpu_to_le32(buf->dma + 8); + /* assure risc buffer hasn't overflowed */ + BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size); + return 0; +} + +#if 0 +/* ------------------------------------------------------------------ */ +/* debug helper code */ + +static void tw68_risc_decode(u32 risc, u32 addr) +{ +#define RISC_OP(reg) (((reg) >> 28) & 7) + static struct instr_details { + char *name; + u8 has_data_type; + u8 has_byte_info; + u8 has_addr; + } instr[8] = { + [RISC_OP(RISC_SYNCO)] = {"syncOdd", 0, 0, 0}, + [RISC_OP(RISC_SYNCE)] = {"syncEven", 0, 0, 0}, + [RISC_OP(RISC_JUMP)] = {"jump", 0, 0, 1}, + [RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1}, + [RISC_OP(RISC_INLINE)] = {"inline", 1, 1, 1}, + }; + u32 p; + + p = RISC_OP(risc); + if (!(risc & 0x80000000) || !instr[p].name) { + pr_debug("0x%08x [ INVALID ]\n", risc); + return; + } + pr_debug("0x%08x %-9s IRQ=%d", + risc, instr[p].name, (risc >> 27) & 1); + if (instr[p].has_data_type) + pr_debug(" Type=%d", (risc >> 24) & 7); + if (instr[p].has_byte_info) + pr_debug(" Start=0x%03x Count=%03u", + (risc >> 12) & 0xfff, risc & 0xfff); + if (instr[p].has_addr) + pr_debug(" StartAddr=0x%08x", addr); + pr_debug("\n"); +} + +void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf) +{ + const __le32 *addr; + + pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n", + core->name, buf, buf->cpu, buf->jmp); + for (addr = buf->cpu; addr <= buf->jmp; addr += 2) + tw68_risc_decode(*addr, *(addr+1)); +} +#endif diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c new file mode 100644 index 0000000..5c94ac7 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-video.c @@ -0,0 +1,1051 @@ +/* + * tw68 functions to handle video data + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/module.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-sg.h> + +#include "tw68.h" +#include "tw68-reg.h" + +/* ------------------------------------------------------------------ */ +/* data structs for video */ +/* + * FIXME - + * Note that the saa7134 has formats, e.g. YUV420, which are classified + * as "planar". These affect overlay mode, and are flagged with a field + * ".planar" in the format. Do we need to implement this in this driver? + */ +static const struct tw68_format formats[] = { + { + .name = "15 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB555, + .depth = 16, + .twformat = ColorFormatRGB15, + }, { + .name = "15 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB555X, + .depth = 16, + .twformat = ColorFormatRGB15 | ColorFormatBSWAP, + }, { + .name = "16 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .twformat = ColorFormatRGB16, + }, { + .name = "16 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .twformat = ColorFormatRGB16 | ColorFormatBSWAP, + }, { + .name = "24 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + .twformat = ColorFormatRGB24, + }, { + .name = "24 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + .twformat = ColorFormatRGB24 | ColorFormatBSWAP, + }, { + .name = "32 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR32, + .depth = 32, + .twformat = ColorFormatRGB32, + }, { + .name = "32 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .twformat = ColorFormatRGB32 | ColorFormatBSWAP | + ColorFormatWSWAP, + }, { + .name = "4:2:2 packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .twformat = ColorFormatYUY2, + }, { + .name = "4:2:2 packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .twformat = ColorFormatYUY2 | ColorFormatBSWAP, + } +}; +#define FORMATS ARRAY_SIZE(formats) + +#define NORM_625_50 \ + .h_delay = 3, \ + .h_delay0 = 133, \ + .h_start = 0, \ + .h_stop = 719, \ + .v_delay = 24, \ + .vbi_v_start_0 = 7, \ + .vbi_v_stop_0 = 22, \ + .video_v_start = 24, \ + .video_v_stop = 311, \ + .vbi_v_start_1 = 319 + +#define NORM_525_60 \ + .h_delay = 8, \ + .h_delay0 = 138, \ + .h_start = 0, \ + .h_stop = 719, \ + .v_delay = 22, \ + .vbi_v_start_0 = 10, \ + .vbi_v_stop_0 = 21, \ + .video_v_start = 22, \ + .video_v_stop = 262, \ + .vbi_v_start_1 = 273 + +/* + * The following table is searched by tw68_s_std, first for a specific + * match, then for an entry which contains the desired id. The table + * entries should therefore be ordered in ascending order of specificity. + */ +static const struct tw68_tvnorm tvnorms[] = { + { + .name = "PAL", /* autodetect */ + .id = V4L2_STD_PAL, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPALBDGHI, + }, { + .name = "NTSC", + .id = V4L2_STD_NTSC, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0x89, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + .format = VideoFormatNTSC, + }, { + .name = "SECAM", + .id = V4L2_STD_SECAM, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + .format = VideoFormatSECAM, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0xb9, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + .format = VideoFormatPALM, + }, { + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0xa1, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPALNC, + }, { + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + .h_delay = 186, + .h_start = 0, + .h_stop = 719, + .v_delay = 26, + .video_v_start = 23, + .video_v_stop = 262, + .vbi_v_start_0 = 10, + .vbi_v_stop_0 = 21, + .vbi_v_start_1 = 273, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPAL60, + } +}; +#define TVNORMS ARRAY_SIZE(tvnorms) + +static const struct tw68_format *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < FORMATS; i++) + if (formats[i].fourcc == fourcc) + return formats+i; + return NULL; +} + + +/* ------------------------------------------------------------------ */ +/* + * Note that the cropping rectangles are described in terms of a single + * frame, i.e. line positions are only 1/2 the interlaced equivalent + */ +static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm) +{ + if (norm != dev->tvnorm) { + dev->width = 720; + dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576; + dev->tvnorm = norm; + tw68_set_tvnorm_hw(dev); + } +} + +/* + * tw68_set_scale + * + * Scaling and Cropping for video decoding + * + * We are working with 3 values for horizontal and vertical - scale, + * delay and active. + * + * HACTIVE represent the actual number of pixels in the "usable" image, + * before scaling. HDELAY represents the number of pixels skipped + * between the start of the horizontal sync and the start of the image. + * HSCALE is calculated using the formula + * HSCALE = (HACTIVE / (#pixels desired)) * 256 + * + * The vertical registers are similar, except based upon the total number + * of lines in the image, and the first line of the image (i.e. ignoring + * vertical sync and VBI). + * + * Note that the number of bytes reaching the FIFO (and hence needing + * to be processed by the DMAP program) is completely dependent upon + * these values, especially HSCALE. + * + * Parameters: + * @dev pointer to the device structure, needed for + * getting current norm (as well as debug print) + * @width actual image width (from user buffer) + * @height actual image height + * @field indicates Top, Bottom or Interlaced + */ +static int tw68_set_scale(struct tw68_dev *dev, unsigned int width, + unsigned int height, enum v4l2_field field) +{ + const struct tw68_tvnorm *norm = dev->tvnorm; + /* set individually for debugging clarity */ + int hactive, hdelay, hscale; + int vactive, vdelay, vscale; + int comb; + + if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */ + height /= 2; /* we must set for 1-frame */ + + pr_debug("%s: width=%d, height=%d, both=%d\n" + " tvnorm h_delay=%d, h_start=%d, h_stop=%d, " + "v_delay=%d, v_start=%d, v_stop=%d\n" , __func__, + width, height, V4L2_FIELD_HAS_BOTH(field), + norm->h_delay, norm->h_start, norm->h_stop, + norm->v_delay, norm->video_v_start, + norm->video_v_stop); + + switch (dev->vdecoder) { + case TW6800: + hdelay = norm->h_delay0; + break; + default: + hdelay = norm->h_delay; + break; + } + + hdelay += norm->h_start; + hactive = norm->h_stop - norm->h_start + 1; + + hscale = (hactive * 256) / (width); + + vdelay = norm->v_delay; + vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start; + vscale = (vactive * 256) / height; + + pr_debug("%s: %dx%d [%s%s,%s]\n", __func__, + width, height, + V4L2_FIELD_HAS_TOP(field) ? "T" : "", + V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", + v4l2_norm_to_name(dev->tvnorm->id)); + pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; " + "vactive=%d, vdelay=%d, vscale=%d\n", __func__, + hactive, hdelay, hscale, vactive, vdelay, vscale); + + comb = ((vdelay & 0x300) >> 2) | + ((vactive & 0x300) >> 4) | + ((hdelay & 0x300) >> 6) | + ((hactive & 0x300) >> 8); + pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, " + "VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n", + __func__, comb, vdelay, vactive, hdelay, hactive); + tw_writeb(TW68_CROP_HI, comb); + tw_writeb(TW68_VDELAY_LO, vdelay & 0xff); + tw_writeb(TW68_VACTIVE_LO, vactive & 0xff); + tw_writeb(TW68_HDELAY_LO, hdelay & 0xff); + tw_writeb(TW68_HACTIVE_LO, hactive & 0xff); + + comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8); + pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, " + "HSCALE_LO=%02x\n", __func__, comb, vscale, hscale); + tw_writeb(TW68_SCALE_HI, comb); + tw_writeb(TW68_VSCALE_LO, vscale); + tw_writeb(TW68_HSCALE_LO, hscale); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf) +{ + /* Set cropping and scaling */ + tw68_set_scale(dev, dev->width, dev->height, dev->field); + /* + * Set start address for RISC program. Note that if the DMAP + * processor is currently running, it must be stopped before + * a new address can be set. + */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN); + tw_writel(TW68_DMAP_SA, buf->dma); + /* Clear any pending interrupts */ + tw_writel(TW68_INTSTAT, dev->board_virqmask); + /* Enable the risc engine and the fifo */ + tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat | + ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN); + dev->pci_irqmask |= dev->board_virqmask; + tw_setl(TW68_INTMASK, dev->pci_irqmask); + return 0; +} + +/* ------------------------------------------------------------------ */ + +/* calc max # of buffers from size (must not exceed the 4MB virtual + * address space per DMA channel) */ +static int tw68_buffer_count(unsigned int size, unsigned int count) +{ + unsigned int maxcount; + + maxcount = (4 * 1024 * 1024) / roundup(size, PAGE_SIZE); + if (count > maxcount) + count = maxcount; + return count; +} + +/* ------------------------------------------------------------- */ +/* vb2 queue operations */ + +static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + unsigned tot_bufs = q->num_buffers + *num_buffers; + + sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3; + /* + * We allow create_bufs, but only if the sizeimage is the same as the + * current sizeimage. The tw68_buffer_count calculation becomes quite + * difficult otherwise. + */ + if (fmt && fmt->fmt.pix.sizeimage < sizes[0]) + return -EINVAL; + *num_planes = 1; + if (tot_bufs < 2) + tot_bufs = 2; + tot_bufs = tw68_buffer_count(sizes[0], tot_bufs); + *num_buffers = tot_bufs - q->num_buffers; + + return 0; +} + +/* + * The risc program for each buffers works as follows: it starts with a simple + * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the + * buffer follows and at the end we have a JUMP back to the start + 8 (skipping + * the initial JUMP). + * + * This is the program of the first buffer to be queued if the active list is + * empty and it just keeps DMAing this buffer without generating any interrupts. + * + * If a new buffer is added then the initial JUMP in the program generates an + * interrupt as well which signals that the previous buffer has been DMAed + * successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void tw68_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct tw68_buf *prev; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + /* append a 'JUMP to start of buffer' to the buffer risc program */ + buf->jmp[0] = cpu_to_le32(RISC_JUMP); + buf->jmp[1] = cpu_to_le32(buf->dma + 8); + + if (!list_empty(&dev->active)) { + prev = list_entry(dev->active.prev, struct tw68_buf, list); + buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT); + prev->jmp[1] = cpu_to_le32(buf->dma); + } + list_add_tail(&buf->list, &dev->active); + spin_unlock_irqrestore(&dev->slock, flags); +} + +/* + * buffer_prepare + * + * Set the ancilliary information into the buffer structure. This + * includes generating the necessary risc program if it hasn't already + * been done for the current buffer format. + * The structure fh contains the details of the format requested by the + * user - type, width, height and #fields. This is compared with the + * last format set for the current buffer. If they differ, the risc + * code (which controls the filling of the buffer) is (re-)generated. + */ +static int tw68_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0); + unsigned size, bpl; + int rc; + + size = (dev->width * dev->height * dev->fmt->depth) >> 3; + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + vb2_set_plane_payload(vb, 0, size); + + rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + if (!rc) + return -EIO; + + bpl = (dev->width * dev->fmt->depth) >> 3; + switch (dev->field) { + case V4L2_FIELD_TOP: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, UNSET, bpl, 0, dev->height); + break; + case V4L2_FIELD_BOTTOM: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + UNSET, 0, bpl, 0, dev->height); + break; + case V4L2_FIELD_SEQ_TB: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, bpl * (dev->height >> 1), + bpl, 0, dev->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + bpl * (dev->height >> 1), 0, + bpl, 0, dev->height >> 1); + break; + case V4L2_FIELD_INTERLACED: + default: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, bpl, bpl, bpl, dev->height >> 1); + break; + } + return 0; +} + +static void tw68_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + + dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + + pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma); +} + +static int tw68_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + struct tw68_buf *buf = + container_of(dev->active.next, struct tw68_buf, list); + + dev->seqnr = 0; + tw68_video_start_dma(dev, buf); + return 0; +} + +static void tw68_stop_streaming(struct vb2_queue *q) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + + /* Stop risc & fifo */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + while (!list_empty(&dev->active)) { + struct tw68_buf *buf = + container_of(dev->active.next, struct tw68_buf, list); + + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } +} + +static struct vb2_ops tw68_video_qops = { + .queue_setup = tw68_queue_setup, + .buf_queue = tw68_buf_queue, + .buf_prepare = tw68_buf_prepare, + .buf_finish = tw68_buf_finish, + .start_streaming = tw68_start_streaming, + .stop_streaming = tw68_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ */ + +static int tw68_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw68_dev *dev = + container_of(ctrl->handler, struct tw68_dev, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tw_writeb(TW68_BRIGHT, ctrl->val); + break; + case V4L2_CID_HUE: + tw_writeb(TW68_HUE, ctrl->val); + break; + case V4L2_CID_CONTRAST: + tw_writeb(TW68_CONTRAST, ctrl->val); + break; + case V4L2_CID_SATURATION: + tw_writeb(TW68_SAT_U, ctrl->val); + tw_writeb(TW68_SAT_V, ctrl->val); + break; + case V4L2_CID_COLOR_KILLER: + if (ctrl->val) + tw_andorb(TW68_MISC2, 0xe0, 0xe0); + else + tw_andorb(TW68_MISC2, 0xe0, 0x00); + break; + case V4L2_CID_CHROMA_AGC: + if (ctrl->val) + tw_andorb(TW68_LOOP, 0x30, 0x20); + else + tw_andorb(TW68_LOOP, 0x30, 0x00); + break; + } + return 0; +} + +/* ------------------------------------------------------------------ */ + +/* + * Note that this routine returns what is stored in the fh structure, and + * does not interrogate any of the device registers. + */ +static int tw68_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * (dev->fmt->depth)) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + return 0; +} + +static int tw68_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + const struct tw68_format *fmt; + enum v4l2_field field; + unsigned int maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxh = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_SEQ_TB: + maxh = maxh * 2; + break; + default: + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + break; + } + + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > 720) + f->fmt.pix.width = 720; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * (fmt->depth)) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +/* + * Note that tw68_s_fmt_vid_cap sets the information into the fh structure, + * and it will be used for all future new buffers. However, there could be + * some number of buffers on the "active" chain which will be filled before + * the change takes place. + */ +static int tw68_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + int err; + + err = tw68_try_fmt_vid_cap(file, priv, f); + if (0 != err) + return err; + + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; + return 0; +} + +static int tw68_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tw68_dev *dev = video_drvdata(file); + unsigned int n; + + n = i->index; + if (n >= TW68_INPUT_MAX) + return -EINVAL; + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + snprintf(i->name, sizeof(i->name), "Composite %d", n); + + /* If the query is for the current input, get live data */ + if (n == dev->input) { + int v1 = tw_readb(TW68_STATUS1); + int v2 = tw_readb(TW68_MVSN); + + if (0 != (v1 & (1 << 7))) + i->status |= V4L2_IN_ST_NO_SYNC; + if (0 != (v1 & (1 << 6))) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (0 != (v1 & (1 << 2))) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (0 != (v1 & 1 << 1)) + i->status |= V4L2_IN_ST_NO_COLOR; + if (0 != (v2 & (1 << 2))) + i->status |= V4L2_IN_ST_MACROVISION; + } + i->std = video_devdata(file)->tvnorms; + return 0; +} + +static int tw68_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tw68_dev *dev = video_drvdata(file); + + *i = dev->input; + return 0; +} + +static int tw68_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (i >= TW68_INPUT_MAX) + return -EINVAL; + dev->input = i; + tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2); + return 0; +} + +static int tw68_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw68_dev *dev = video_drvdata(file); + + strcpy(cap->driver, "tw68"); + strlcpy(cap->card, "Techwell Capture Card", + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->device_caps = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw68_dev *dev = video_drvdata(file); + unsigned int i; + + if (vb2_is_busy(&dev->vidq)) + return -EBUSY; + + /* Look for match on complete norm id (may have mult bits) */ + for (i = 0; i < TVNORMS; i++) { + if (id == tvnorms[i].id) + break; + } + + /* If no exact match, look for norm which contains this one */ + if (i == TVNORMS) { + for (i = 0; i < TVNORMS; i++) + if (id & tvnorms[i].id) + break; + } + /* If still not matched, give up */ + if (i == TVNORMS) + return -EINVAL; + + set_tvnorm(dev, &tvnorms[i]); /* do the actual setting */ + return 0; +} + +static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct tw68_dev *dev = video_drvdata(file); + + *id = dev->tvnorm->id; + return 0; +} + +static int tw68_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +/* + * Used strictly for internal development and debugging, this routine + * prints out the current register contents for the tw68xx device. + */ +static void tw68_dump_regs(struct tw68_dev *dev) +{ + unsigned char line[80]; + int i, j, k; + unsigned char *cptr; + + pr_info("Full dump of TW68 registers:\n"); + /* First we do the PCI regs, 8 4-byte regs per line */ + for (i = 0; i < 0x100; i += 32) { + cptr = line; + cptr += sprintf(cptr, "%03x ", i); + /* j steps through the next 4 words */ + for (j = i; j < i + 16; j += 4) + cptr += sprintf(cptr, "%08x ", tw_readl(j)); + *cptr++ = ' '; + for (; j < i + 32; j += 4) + cptr += sprintf(cptr, "%08x ", tw_readl(j)); + *cptr++ = '\n'; + *cptr = 0; + pr_info("%s", line); + } + /* Next the control regs, which are single-byte, address mod 4 */ + while (i < 0x400) { + cptr = line; + cptr += sprintf(cptr, "%03x ", i); + /* Print out 4 groups of 4 bytes */ + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + cptr += sprintf(cptr, "%02x ", + tw_readb(i)); + i += 4; + } + *cptr++ = ' '; + } + *cptr++ = '\n'; + *cptr = 0; + pr_info("%s", line); + } +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct tw68_dev *dev = video_drvdata(file); + + tw68_dump_regs(dev); + return v4l2_ctrl_log_status(file, priv); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (reg->size == 1) + reg->val = tw_readb(reg->reg); + else + reg->val = tw_readl(reg->reg); + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (reg->size == 1) + tw_writeb(reg->reg, reg->val); + else + tw_writel(reg->reg & 0xffff, reg->val); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops tw68_ctrl_ops = { + .s_ctrl = tw68_s_ctrl, +}; + +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = tw68_querycap, + .vidioc_enum_fmt_vid_cap = tw68_enum_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_s_std = tw68_s_std, + .vidioc_g_std = tw68_g_std, + .vidioc_enum_input = tw68_enum_input, + .vidioc_g_input = tw68_g_input, + .vidioc_s_input = tw68_s_input, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_fmt_vid_cap = tw68_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = tw68_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw68_s_fmt_vid_cap, + .vidioc_log_status = vidioc_log_status, + .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, +#endif +}; + +static struct video_device tw68_video_template = { + .name = "tw68_video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TW68_NORMS, +}; + +/* ------------------------------------------------------------------ */ +/* exported stuff */ +void tw68_set_tvnorm_hw(struct tw68_dev *dev) +{ + tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format); +} + +int tw68_video_init1(struct tw68_dev *dev) +{ + struct v4l2_ctrl_handler *hdl = &dev->hdl; + + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 20); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 100); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + /* NTSC only */ + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; + } + dev->v4l2_dev.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + return 0; +} + +int tw68_video_init2(struct tw68_dev *dev, int video_nr) +{ + int ret; + + set_tvnorm(dev, &tvnorms[0]); + + dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + dev->width = 720; + dev->height = 576; + dev->field = V4L2_FIELD_INTERLACED; + + INIT_LIST_HEAD(&dev->active); + dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF; + dev->vidq.ops = &tw68_video_qops; + dev->vidq.mem_ops = &vb2_dma_sg_memops; + dev->vidq.drv_priv = dev; + dev->vidq.gfp_flags = __GFP_DMA32; + dev->vidq.buf_struct_size = sizeof(struct tw68_buf); + dev->vidq.lock = &dev->lock; + dev->vidq.min_buffers_needed = 2; + ret = vb2_queue_init(&dev->vidq); + if (ret) + return ret; + dev->vdev = tw68_video_template; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->lock; + dev->vdev.queue = &dev->vidq; + video_set_drvdata(&dev->vdev, dev); + return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); +} + +/* + * tw68_irq_video_done + */ +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status) +{ + __u32 reg; + + /* reset interrupts handled by this routine */ + tw_writel(TW68_INTSTAT, status); + /* + * Check most likely first + * + * DMAPI shows we have reached the end of the risc code + * for the current buffer. + */ + if (status & TW68_DMAPI) { + struct tw68_buf *buf; + + spin_lock(&dev->slock); + buf = list_entry(dev->active.next, struct tw68_buf, list); + list_del(&buf->list); + spin_unlock(&dev->slock); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.field = dev->field; + buf->vb.v4l2_buf.sequence = dev->seqnr++; + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + status &= ~(TW68_DMAPI); + if (0 == status) + return; + } + if (status & (TW68_VLOCK | TW68_HLOCK)) + dev_dbg(&dev->pci->dev, "Lost sync\n"); + if (status & TW68_PABORT) + dev_err(&dev->pci->dev, "PABORT interrupt\n"); + if (status & TW68_DMAPERR) + dev_err(&dev->pci->dev, "DMAPERR interrupt\n"); + /* + * On TW6800, FDMIS is apparently generated if video input is switched + * during operation. Therefore, it is not enabled for that chip. + */ + if (status & TW68_FDMIS) + dev_dbg(&dev->pci->dev, "FDMIS interrupt\n"); + if (status & TW68_FFOF) { + /* probably a logic error */ + reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN; + tw_clearl(TW68_DMAC, TW68_FIFO_EN); + dev_dbg(&dev->pci->dev, "FFOF interrupt\n"); + tw_setl(TW68_DMAC, reg); + } + if (status & TW68_FFERR) + dev_dbg(&dev->pci->dev, "FFERR interrupt\n"); +} diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h new file mode 100644 index 0000000..2c8abe2 --- /dev/null +++ b/drivers/media/pci/tw68/tw68.h @@ -0,0 +1,231 @@ +/* + * tw68 driver common header file + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/videodev2.h> +#include <linux/notifier.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-sg.h> + +#include "tw68-reg.h" + +#define UNSET (-1U) + +/* system vendor and device ID's */ +#define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_DEVICE_ID_6800 0x6800 +#define PCI_DEVICE_ID_6801 0x6801 +#define PCI_DEVICE_ID_AUDIO2 0x6802 +#define PCI_DEVICE_ID_TS3 0x6803 +#define PCI_DEVICE_ID_6804 0x6804 +#define PCI_DEVICE_ID_AUDIO5 0x6805 +#define PCI_DEVICE_ID_TS6 0x6806 + +/* tw6816 based cards */ +#define PCI_DEVICE_ID_6816_1 0x6810 +#define PCI_DEVICE_ID_6816_2 0x6811 +#define PCI_DEVICE_ID_6816_3 0x6812 +#define PCI_DEVICE_ID_6816_4 0x6813 + +#define TW68_NORMS ( \ + V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_Nc | V4L2_STD_PAL_60) + +#define TW68_VID_INTS (TW68_FFERR | TW68_PABORT | TW68_DMAPERR | \ + TW68_FFOF | TW68_DMAPI) +/* TW6800 chips have trouble with these, so we don't set them for that chip */ +#define TW68_VID_INTSX (TW68_FDMIS | TW68_HLOCK | TW68_VLOCK) + +#define TW68_I2C_INTS (TW68_SBERR | TW68_SBDONE | TW68_SBERR2 | \ + TW68_SBDONE2) + +enum tw68_decoder_type { + TW6800, + TW6801, + TW6804, + TWXXXX, +}; + +/* ----------------------------------------------------------- */ +/* static data */ + +struct tw68_tvnorm { + char *name; + v4l2_std_id id; + + /* video decoder */ + u32 sync_control; + u32 luma_control; + u32 chroma_ctrl1; + u32 chroma_gain; + u32 chroma_ctrl2; + u32 vgate_misc; + + /* video scaler */ + u32 h_delay; + u32 h_delay0; /* for TW6800 */ + u32 h_start; + u32 h_stop; + u32 v_delay; + u32 video_v_start; + u32 video_v_stop; + u32 vbi_v_start_0; + u32 vbi_v_stop_0; + u32 vbi_v_start_1; + + /* Techwell specific */ + u32 format; +}; + +struct tw68_format { + char *name; + u32 fourcc; + u32 depth; + u32 twformat; +}; + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define TW68_BOARD_NOAUTO UNSET +#define TW68_BOARD_UNKNOWN 0 +#define TW68_BOARD_GENERIC_6802 1 + +#define TW68_MAXBOARDS 16 +#define TW68_INPUT_MAX 4 + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +struct tw68_dev; /* forward delclaration */ + +/* buffer for one video/vbi/ts frame */ +struct tw68_buf { + struct vb2_buffer vb; + struct list_head list; + + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct tw68_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 twformat; +}; + +/* global device status */ +struct tw68_dev { + struct mutex lock; + spinlock_t slock; + u16 instance; + struct v4l2_device v4l2_dev; + + /* various device info */ + enum tw68_decoder_type vdecoder; + struct video_device vdev; + struct v4l2_ctrl_handler hdl; + + /* pci i/o */ + char *name; + struct pci_dev *pci; + unsigned char pci_rev, pci_lat; + u32 __iomem *lmmio; + u8 __iomem *bmmio; + u32 pci_irqmask; + /* The irq mask to be used will depend upon the chip type */ + u32 board_virqmask; + + /* video capture */ + const struct tw68_format *fmt; + unsigned width, height; + unsigned seqnr; + unsigned field; + struct vb2_queue vidq; + struct list_head active; + + /* various v4l controls */ + const struct tw68_tvnorm *tvnorm; /* video */ + + int input; +}; + +/* ----------------------------------------------------------- */ + +#define tw_readl(reg) readl(dev->lmmio + ((reg) >> 2)) +#define tw_readb(reg) readb(dev->bmmio + (reg)) +#define tw_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) +#define tw_writeb(reg, value) writeb((value), dev->bmmio + (reg)) + +#define tw_andorl(reg, mask, value) \ + writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+((reg)>>2)) +#define tw_andorb(reg, mask, value) \ + writeb((readb(dev->bmmio + (reg)) & ~(mask)) |\ + ((value) & (mask)), dev->bmmio+(reg)) +#define tw_setl(reg, bit) tw_andorl((reg), (bit), (bit)) +#define tw_setb(reg, bit) tw_andorb((reg), (bit), (bit)) +#define tw_clearl(reg, bit) \ + writel((readl(dev->lmmio + ((reg) >> 2)) & ~(bit)), \ + dev->lmmio + ((reg) >> 2)) +#define tw_clearb(reg, bit) \ + writeb((readb(dev->bmmio+(reg)) & ~(bit)), \ + dev->bmmio + (reg)) + +#define tw_wait(us) { udelay(us); } + +/* ----------------------------------------------------------- */ +/* tw68-video.c */ + +void tw68_set_tvnorm_hw(struct tw68_dev *dev); + +int tw68_video_init1(struct tw68_dev *dev); +int tw68_video_init2(struct tw68_dev *dev, int video_nr); +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status); +int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf); + +/* ----------------------------------------------------------- */ +/* tw68-risc.c */ + +int tw68_risc_buffer(struct pci_dev *pci, struct tw68_buf *buf, + struct scatterlist *sglist, unsigned int top_offset, + unsigned int bottom_offset, unsigned int bpl, + unsigned int padding, unsigned int lines); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index bf34b93..b6801e0 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -682,7 +682,7 @@ set_videobus_dir (struct zoran *zr, switch (zr->card.type) { case LML33: case LML33R10: - if (lml33dpath == 0) + if (!lml33dpath) GPIO(zr, 5, val); else GPIO(zr, 5, 1); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6d86646..bee9074 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -56,7 +56,8 @@ config VIDEO_VIU config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" - depends on MFD_TIMBERDALE && VIDEO_V4L2 && I2C && DMADEVICES + depends on VIDEO_V4L2 && I2C && DMADEVICES + depends on MFD_TIMBERDALE || COMPILE_TEST select DMA_ENGINE select TIMB_DMA select VIDEO_ADV7180 @@ -74,7 +75,8 @@ config VIDEO_VINO config VIDEO_M32R_AR tristate "AR devices" - depends on M32R && VIDEO_V4L2 + depends on VIDEO_V4L2 + depends on M32R || COMPILE_TEST ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -94,6 +96,7 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP3 tristate "OMAP 3 Camera support" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 + depends on HAS_DMA select ARM_DMA_USE_IOMMU select OMAP_IOMMU select VIDEOBUF2_DMA_CONTIG @@ -109,7 +112,9 @@ config VIDEO_OMAP3_DEBUG config VIDEO_S3C_CAMIF tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API - depends on (ARCH_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME + depends on PM_RUNTIME + depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera @@ -140,6 +145,7 @@ if V4L_MEM2MEM_DRIVERS config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC + depends on HAS_DMA select SRAM select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -151,6 +157,7 @@ config VIDEO_CODA config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -158,7 +165,9 @@ config VIDEO_MEM2MEM_DEINTERLACE config VIDEO_SAMSUNG_S5P_G2D tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" - depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS) + depends on VIDEO_DEV && VIDEO_V4L2 + depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV default n @@ -168,7 +177,9 @@ config VIDEO_SAMSUNG_S5P_G2D config VIDEO_SAMSUNG_S5P_JPEG tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" - depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS) + depends on VIDEO_DEV && VIDEO_V4L2 + depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -177,7 +188,9 @@ config VIDEO_SAMSUNG_S5P_JPEG config VIDEO_SAMSUNG_S5P_MFC tristate "Samsung S5P MFC Video Codec" - depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS) + depends on VIDEO_DEV && VIDEO_V4L2 + depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG default n help @@ -185,7 +198,9 @@ config VIDEO_SAMSUNG_S5P_MFC config VIDEO_MX2_EMMAPRP tristate "MX2 eMMa-PrP support" - depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27 + depends on VIDEO_DEV && VIDEO_V4L2 + depends on SOC_IMX27 || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -195,7 +210,9 @@ config VIDEO_MX2_EMMAPRP config VIDEO_SAMSUNG_EXYNOS_GSC tristate "Samsung Exynos G-Scaler driver" - depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5 + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_EXYNOS5 || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -204,6 +221,7 @@ config VIDEO_SAMSUNG_EXYNOS_GSC config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -213,6 +231,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on ARCH_SHMOBILE || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. @@ -222,7 +241,9 @@ config VIDEO_RENESAS_VSP1 config VIDEO_TI_VPE tristate "TI VPE (Video Processing Engine) driver" - depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX + depends on VIDEO_DEV && VIDEO_V4L2 + depends on SOC_DRA7XX || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV default n @@ -243,19 +264,8 @@ menuconfig V4L_TEST_DRIVERS depends on MEDIA_CAMERA_SUPPORT if V4L_TEST_DRIVERS -config VIDEO_VIVI - tristate "Virtual Video Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 - select FONT_SUPPORT - select FONT_8x16 - select VIDEOBUF2_VMALLOC - default n - ---help--- - Enables a virtual video driver. This device shows a color bar - and a timestamp, as a real device would generate by using V4L2 - api. - Say Y here if you want to test video apps or debug V4L devices. - In doubt, say N. + +source "drivers/media/platform/vivid/Kconfig" config VIDEO_MEM2MEM_TESTDEV tristate "Virtual test device for mem2mem framework" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index e5269da..579046b 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -15,14 +15,14 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o -obj-$(CONFIG_VIDEO_VIVI) += vivi.o +obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o -obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_CODA) += coda/ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o @@ -47,8 +47,6 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ -obj-y += davinci/ - -obj-$(CONFIG_ARCH_OMAP) += omap/ +obj-y += omap/ ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index cc23997..68fa901 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -1,6 +1,7 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG help V4L2 bridge driver for Blackfin video capture device. diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c deleted file mode 100644 index 3a6d1d2..0000000 --- a/drivers/media/platform/coda.c +++ /dev/null @@ -1,3933 +0,0 @@ -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, <javier.martin@vista-silicon.com> - * Xavier Duret - * - * 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. - */ - -#include <linux/clk.h> -#include <linux/debugfs.h> -#include <linux/delay.h> -#include <linux/firmware.h> -#include <linux/genalloc.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/kfifo.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/of.h> -#include <linux/platform_data/coda.h> -#include <linux/reset.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mem2mem.h> -#include <media/videobuf2-core.h> -#include <media/videobuf2-dma-contig.h> - -#include "coda.h" - -#define CODA_NAME "coda" - -#define CODADX6_MAX_INSTANCES 4 - -#define CODA_PARA_BUF_SIZE (10 * 1024) -#define CODA_ISRAM_SIZE (2048 * 2) - -#define CODA7_PS_BUF_SIZE 0x28000 -#define CODA9_PS_SAVE_SIZE (512 * 1024) - -#define CODA_MAX_FRAMEBUFFERS 8 - -#define CODA_MAX_FRAME_SIZE 0x100000 -#define FMO_SLICE_SAVE_BUF_SIZE (32) -#define CODA_DEFAULT_GAMMA 4096 -#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ - -#define MIN_W 176 -#define MIN_H 144 - -#define S_ALIGN 1 /* multiple of 2 */ -#define W_ALIGN 1 /* multiple of 2 */ -#define H_ALIGN 1 /* multiple of 2 */ - -#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) - -static int coda_debug; -module_param(coda_debug, int, 0644); -MODULE_PARM_DESC(coda_debug, "Debug level (0-1)"); - -enum { - V4L2_M2M_SRC = 0, - V4L2_M2M_DST = 1, -}; - -enum coda_inst_type { - CODA_INST_ENCODER, - CODA_INST_DECODER, -}; - -enum coda_product { - CODA_DX6 = 0xf001, - CODA_7541 = 0xf012, - CODA_960 = 0xf020, -}; - -struct coda_fmt { - char *name; - u32 fourcc; -}; - -struct coda_codec { - u32 mode; - u32 src_fourcc; - u32 dst_fourcc; - u32 max_w; - u32 max_h; -}; - -struct coda_devtype { - char *firmware; - enum coda_product product; - struct coda_codec *codecs; - unsigned int num_codecs; - size_t workbuf_size; - size_t tempbuf_size; - size_t iram_size; -}; - -/* Per-queue, driver-specific private data */ -struct coda_q_data { - unsigned int width; - unsigned int height; - unsigned int bytesperline; - unsigned int sizeimage; - unsigned int fourcc; - struct v4l2_rect rect; -}; - -struct coda_aux_buf { - void *vaddr; - dma_addr_t paddr; - u32 size; - struct debugfs_blob_wrapper blob; - struct dentry *dentry; -}; - -struct coda_dev { - struct v4l2_device v4l2_dev; - struct video_device vfd; - struct platform_device *plat_dev; - const struct coda_devtype *devtype; - - void __iomem *regs_base; - struct clk *clk_per; - struct clk *clk_ahb; - struct reset_control *rstc; - - struct coda_aux_buf codebuf; - struct coda_aux_buf tempbuf; - struct coda_aux_buf workbuf; - struct gen_pool *iram_pool; - struct coda_aux_buf iram; - - spinlock_t irqlock; - struct mutex dev_mutex; - struct mutex coda_mutex; - struct workqueue_struct *workqueue; - struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; - struct list_head instances; - unsigned long instance_mask; - struct dentry *debugfs_root; -}; - -struct coda_params { - u8 rot_mode; - u8 h264_intra_qp; - u8 h264_inter_qp; - u8 h264_min_qp; - u8 h264_max_qp; - u8 h264_deblk_enabled; - u8 h264_deblk_alpha; - u8 h264_deblk_beta; - u8 mpeg4_intra_qp; - u8 mpeg4_inter_qp; - u8 gop_size; - int intra_refresh; - int codec_mode; - int codec_mode_aux; - enum v4l2_mpeg_video_multi_slice_mode slice_mode; - u32 framerate; - u16 bitrate; - u32 slice_max_bits; - u32 slice_max_mb; -}; - -struct coda_iram_info { - u32 axi_sram_use; - phys_addr_t buf_bit_use; - phys_addr_t buf_ip_ac_dc_use; - phys_addr_t buf_dbk_y_use; - phys_addr_t buf_dbk_c_use; - phys_addr_t buf_ovl_use; - phys_addr_t buf_btp_use; - phys_addr_t search_ram_paddr; - int search_ram_size; - int remaining; - phys_addr_t next_paddr; -}; - -struct gdi_tiled_map { - int xy2ca_map[16]; - int xy2ba_map[16]; - int xy2ra_map[16]; - int rbc2axi_map[32]; - int xy2rbc_config; - int map_type; -#define GDI_LINEAR_FRAME_MAP 0 -}; - -struct coda_timestamp { - struct list_head list; - u32 sequence; - struct v4l2_timecode timecode; - struct timeval timestamp; -}; - -struct coda_ctx { - struct coda_dev *dev; - struct mutex buffer_mutex; - struct list_head list; - struct work_struct pic_run_work; - struct work_struct seq_end_work; - struct completion completion; - int aborting; - int initialized; - int streamon_out; - int streamon_cap; - u32 isequence; - u32 qsequence; - u32 osequence; - u32 sequence_offset; - struct coda_q_data q_data[2]; - enum coda_inst_type inst_type; - struct coda_codec *codec; - enum v4l2_colorspace colorspace; - struct coda_params params; - struct v4l2_ctrl_handler ctrls; - struct v4l2_fh fh; - int gopcounter; - int runcounter; - char vpu_header[3][64]; - int vpu_header_size[3]; - struct kfifo bitstream_fifo; - struct mutex bitstream_mutex; - struct coda_aux_buf bitstream; - bool hold; - struct coda_aux_buf parabuf; - struct coda_aux_buf psbuf; - struct coda_aux_buf slicebuf; - struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; - u32 frame_types[CODA_MAX_FRAMEBUFFERS]; - struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS]; - u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; - struct list_head timestamp_list; - struct coda_aux_buf workbuf; - int num_internal_frames; - int idx; - int reg_idx; - struct coda_iram_info iram_info; - struct gdi_tiled_map tiled_map; - u32 bit_stream_param; - u32 frm_dis_flg; - u32 frame_mem_ctrl; - int display_idx; - struct dentry *debugfs_entry; -}; - -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, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - writel(data, dev->regs_base + reg); -} - -static inline unsigned int coda_read(struct coda_dev *dev, u32 reg) -{ - u32 data; - data = readl(dev->regs_base + reg); - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - return data; -} - -static inline unsigned long coda_isbusy(struct coda_dev *dev) -{ - return coda_read(dev, CODA_REG_BIT_BUSY); -} - -static inline int coda_is_initialized(struct coda_dev *dev) -{ - return (coda_read(dev, CODA_REG_BIT_CUR_PC) != 0); -} - -static int coda_wait_timeout(struct coda_dev *dev) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while (coda_isbusy(dev)) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - } - return 0; -} - -static void coda_command_async(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - - if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541) { - /* Restore context related registers to CODA */ - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - coda_write(dev, ctx->frame_mem_ctrl, - CODA_REG_BIT_FRAME_MEM_CTRL); - coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); - } - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - } - - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - - coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); - - coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); -} - -static int coda_command_sync(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - - coda_command_async(ctx, cmd); - return coda_wait_timeout(dev); -} - -static int coda_hw_reset(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - unsigned long timeout; - unsigned int idx; - int ret; - - if (!dev->rstc) - return -ENOENT; - - idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); - - timeout = jiffies + msecs_to_jiffies(100); - coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); - while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - ret = reset_control_reset(dev->rstc); - if (ret < 0) - return ret; - - coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - ret = coda_wait_timeout(dev); - coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); - - return ret; -} - -static struct coda_q_data *get_q_data(struct coda_ctx *ctx, - enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return &(ctx->q_data[V4L2_M2M_SRC]); - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &(ctx->q_data[V4L2_M2M_DST]); - default: - return NULL; - } -} - -/* - * Array of all formats supported by any version of Coda: - */ -static struct coda_fmt coda_formats[] = { - { - .name = "YUV 4:2:0 Planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - }, - { - .name = "YUV 4:2:0 Planar, YCrCb", - .fourcc = V4L2_PIX_FMT_YVU420, - }, - { - .name = "H264 Encoded Stream", - .fourcc = V4L2_PIX_FMT_H264, - }, - { - .name = "MPEG4 Encoded Stream", - .fourcc = V4L2_PIX_FMT_MPEG4, - }, -}; - -#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ - { mode, src_fourcc, dst_fourcc, max_w, max_h } - -/* - * Arrays of codecs supported by each given version of Coda: - * i.MX27 -> codadx6 - * i.MX5x -> coda7 - * i.MX6 -> coda960 - * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants - */ -static struct coda_codec codadx6_codecs[] = { - CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), - CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), -}; - -static struct coda_codec coda7_codecs[] = { - CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), - CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), - CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080), - CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080), -}; - -static struct coda_codec coda9_codecs[] = { - CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1080), - CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1080), - CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080), - CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080), -}; - -static bool coda_format_is_yuv(u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - return true; - default: - return false; - } -} - -/* - * Normalize all supported YUV 4:2:0 formats to the value used in the codec - * tables. - */ -static u32 coda_format_normalize_yuv(u32 fourcc) -{ - return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc; -} - -static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc, - int dst_fourcc) -{ - struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - int k; - - src_fourcc = coda_format_normalize_yuv(src_fourcc); - dst_fourcc = coda_format_normalize_yuv(dst_fourcc); - if (src_fourcc == dst_fourcc) - return NULL; - - for (k = 0; k < num_codecs; k++) { - if (codecs[k].src_fourcc == src_fourcc && - codecs[k].dst_fourcc == dst_fourcc) - break; - } - - if (k == num_codecs) - return NULL; - - return &codecs[k]; -} - -static void coda_get_max_dimensions(struct coda_dev *dev, - struct coda_codec *codec, - int *max_w, int *max_h) -{ - struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - unsigned int w, h; - int k; - - if (codec) { - w = codec->max_w; - h = codec->max_h; - } else { - for (k = 0, w = 0, h = 0; k < num_codecs; k++) { - w = max(w, codecs[k].max_w); - h = max(h, codecs[k].max_h); - } - } - - if (max_w) - *max_w = w; - if (max_h) - *max_h = h; -} - -static char *coda_product_name(int product) -{ - static char buf[9]; - - switch (product) { - case CODA_DX6: - return "CodaDx6"; - case CODA_7541: - return "CODA7541"; - case CODA_960: - return "CODA960"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", product); - return buf; - } -} - -/* - * V4L2 ioctl() operations. - */ -static int coda_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver)); - strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product), - sizeof(cap->card)); - strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | - V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, - enum v4l2_buf_type type, int src_fourcc) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_codec *codecs = ctx->dev->devtype->codecs; - struct coda_fmt *formats = coda_formats; - struct coda_fmt *fmt; - int num_codecs = ctx->dev->devtype->num_codecs; - int num_formats = ARRAY_SIZE(coda_formats); - int i, k, num = 0; - - for (i = 0; i < num_formats; i++) { - /* Both uncompressed formats are always supported */ - if (coda_format_is_yuv(formats[i].fourcc) && - !coda_format_is_yuv(src_fourcc)) { - if (num == f->index) - break; - ++num; - continue; - } - /* Compressed formats may be supported, check the codec list */ - for (k = 0; k < num_codecs; k++) { - /* if src_fourcc is set, only consider matching codecs */ - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[i].fourcc == codecs[k].dst_fourcc && - (!src_fourcc || src_fourcc == codecs[k].src_fourcc)) - break; - if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - formats[i].fourcc == codecs[k].src_fourcc) - break; - } - if (k < num_codecs) { - if (num == f->index) - break; - ++num; - } - } - - if (i < num_formats) { - fmt = &formats[i]; - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; - if (!coda_format_is_yuv(fmt->fourcc)) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; - return 0; - } - - /* Format not found */ - return -EINVAL; -} - -static int coda_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct vb2_queue *src_vq; - struct coda_q_data *q_data_src; - - /* If the source format is already fixed, only list matching formats */ - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (vb2_is_streaming(src_vq)) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, - q_data_src->fourcc); - } - - return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0); -} - -static int coda_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0); -} - -static int coda_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_q_data *q_data; - struct coda_ctx *ctx = fh_to_ctx(priv); - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fourcc; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; - f->fmt.pix.bytesperline = q_data->bytesperline; - - f->fmt.pix.sizeimage = q_data->sizeimage; - f->fmt.pix.colorspace = ctx->colorspace; - - return 0; -} - -static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, - struct v4l2_format *f) -{ - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data; - unsigned int max_w, max_h; - enum v4l2_field field; - - field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_NONE; - else if (V4L2_FIELD_NONE != field) - return -EINVAL; - - /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported */ - f->fmt.pix.field = field; - - coda_get_max_dimensions(dev, codec, &max_w, &max_h); - v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, - &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, - S_ALIGN); - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_JPEG: - break; - default: - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - f->fmt.pix.pixelformat = q_data->fourcc; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - /* Frame stride must be multiple of 8, but 16 for h.264 */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - break; - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_JPEG: - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE; - break; - default: - BUG(); - } - - return 0; -} - -static int coda_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_codec *codec; - struct vb2_queue *src_vq; - int ret; - - /* - * If the source format is already fixed, try to find a codec that - * converts to the given destination format - */ - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (vb2_is_streaming(src_vq)) { - struct coda_q_data *q_data_src; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - f->fmt.pix.pixelformat); - if (!codec) - return -EINVAL; - } else { - /* Otherwise determine codec by encoded format, if possible */ - codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, - f->fmt.pix.pixelformat); - } - - f->fmt.pix.colorspace = ctx->colorspace; - - ret = coda_try_fmt(ctx, codec, f); - if (ret < 0) - return ret; - - /* The h.264 decoder only returns complete 16x16 macroblocks */ - if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) { - f->fmt.pix.width = f->fmt.pix.width; - f->fmt.pix.height = round_up(f->fmt.pix.height, 16); - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - } - - return 0; -} - -static int coda_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_codec *codec; - - /* Determine codec by encoded format, returns NULL if raw or invalid */ - codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, - V4L2_PIX_FMT_YUV420); - - if (!f->fmt.pix.colorspace) - f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - - return coda_try_fmt(ctx, codec, f); -} - -static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) -{ - struct coda_q_data *q_data; - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - if (vb2_is_busy(vq)) { - v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - q_data->fourcc = f->fmt.pix.pixelformat; - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->bytesperline = f->fmt.pix.bytesperline; - q_data->sizeimage = f->fmt.pix.sizeimage; - q_data->rect.left = 0; - q_data->rect.top = 0; - q_data->rect.width = f->fmt.pix.width; - q_data->rect.height = f->fmt.pix.height; - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "Setting format for type %d, wxh: %dx%d, fmt: %d\n", - f->type, q_data->width, q_data->height, q_data->fourcc); - - return 0; -} - -static int coda_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = coda_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - return coda_s_fmt(ctx, f); -} - -static int coda_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = coda_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - ret = coda_s_fmt(ctx, f); - if (ret) - ctx->colorspace = f->fmt.pix.colorspace; - - return ret; -} - -static int coda_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); -} - -static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, - struct v4l2_buffer *buf) -{ - struct vb2_queue *src_vq; - - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) && - (buf->sequence == (ctx->qsequence - 1))); -} - -static int coda_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); - - /* If this is the last capture buffer, emit an end-of-stream event */ - if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - coda_buf_is_end_of_stream(ctx, buf)) { - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - v4l2_event_queue_fh(&ctx->fh, &eos_event); - } - - return ret; -} - -static int coda_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - struct v4l2_rect r, *rsel; - - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - r.left = 0; - r.top = 0; - r.width = q_data->width; - r.height = q_data->height; - rsel = &q_data->rect; - - switch (s->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - rsel = &r; - /* fallthrough */ - case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - break; - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: - rsel = &r; - /* fallthrough */ - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - break; - default: - return -EINVAL; - } - - s->r = *rsel; - - return 0; -} - -static int coda_try_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - if (dc->cmd != V4L2_DEC_CMD_STOP) - return -EINVAL; - - if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - - if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) - return -EINVAL; - - return 0; -} - -static int coda_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_dev *dev = ctx->dev; - int ret; - - ret = coda_try_decoder_cmd(file, fh, dc); - if (ret < 0) - return ret; - - /* Ignore decoder stop command silently in encoder context */ - if (ctx->inst_type != CODA_INST_DECODER) - return 0; - - /* Set the strem-end flag on this context */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - if ((dev->devtype->product == CODA_960) && - coda_isbusy(dev) && - (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { - /* If this context is currently running, update the hardware flag */ - coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); - } - ctx->hold = false; - v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); - - return 0; -} - -static int coda_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 0, NULL); - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static const struct v4l2_ioctl_ops coda_ioctl_ops = { - .vidioc_querycap = coda_querycap, - - .vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = coda_g_fmt, - .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, - - .vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out = coda_g_fmt, - .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - - .vidioc_qbuf = coda_qbuf, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_dqbuf = coda_dqbuf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_g_selection = coda_g_selection, - - .vidioc_try_decoder_cmd = coda_try_decoder_cmd, - .vidioc_decoder_cmd = coda_decoder_cmd, - - .vidioc_subscribe_event = coda_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int coda_start_decoding(struct coda_ctx *ctx); - -static inline int coda_get_bitstream_payload(struct coda_ctx *ctx) -{ - return kfifo_len(&ctx->bitstream_fifo); -} - -static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr; - - rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - kfifo->out = (kfifo->in & ~kfifo->mask) | - (rd_ptr - ctx->bitstream.paddr); - if (kfifo->out > kfifo->in) - kfifo->out -= kfifo->mask + 1; -} - -static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr, wr_ptr; - - rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); - coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr; - - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf) -{ - u32 src_size = vb2_get_plane_payload(src_buf, 0); - u32 n; - - n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size); - if (n < src_size) - return -ENOSPC; - - dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr, - ctx->bitstream.size, DMA_TO_DEVICE); - - src_buf->v4l2_buf.sequence = ctx->qsequence++; - - return 0; -} - -static bool coda_bitstream_try_queue(struct coda_ctx *ctx, - struct vb2_buffer *src_buf) -{ - int ret; - - if (coda_get_bitstream_payload(ctx) + - vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size) - return false; - - if (vb2_plane_vaddr(src_buf, 0) == NULL) { - v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); - return true; - } - - ret = coda_bitstream_queue(ctx, src_buf); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); - return false; - } - /* Sync read pointer to device */ - if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) - coda_kfifo_sync_to_device_write(ctx); - - ctx->hold = false; - - return true; -} - -static void coda_fill_bitstream(struct coda_ctx *ctx) -{ - struct vb2_buffer *src_buf; - struct coda_timestamp *ts; - - while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - - if (coda_bitstream_try_queue(ctx, src_buf)) { - /* - * Source buffer is queued in the bitstream ringbuffer; - * queue the timestamp and mark source buffer as done - */ - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - - ts = kmalloc(sizeof(*ts), GFP_KERNEL); - if (ts) { - ts->sequence = src_buf->v4l2_buf.sequence; - ts->timecode = src_buf->v4l2_buf.timecode; - ts->timestamp = src_buf->v4l2_buf.timestamp; - list_add_tail(&ts->list, &ctx->timestamp_list); - } - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } else { - break; - } - } -} - -static void coda_set_gdi_regs(struct coda_ctx *ctx) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - struct coda_dev *dev = ctx->dev; - int i; - - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ca_map[i], - CODA9_GDI_XY2_CAS_0 + 4 * i); - for (i = 0; i < 4; i++) - coda_write(dev, tiled_map->xy2ba_map[i], - CODA9_GDI_XY2_BA_0 + 4 * i); - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ra_map[i], - CODA9_GDI_XY2_RAS_0 + 4 * i); - coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); - for (i = 0; i < 32; i++) - coda_write(dev, tiled_map->rbc2axi_map[i], - CODA9_GDI_RBC2_AXI_0 + 4 * i); -} - -/* - * Mem-to-mem operations. - */ -static int coda_prepare_decode(struct coda_ctx *ctx) -{ - struct vb2_buffer *dst_buf; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_dst; - u32 stridey, height; - u32 picture_y, picture_cb, picture_cr; - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - if (ctx->params.rot_mode & CODA_ROT_90) { - stridey = q_data_dst->height; - height = q_data_dst->width; - } else { - stridey = q_data_dst->width; - height = q_data_dst->height; - } - - /* Try to copy source buffer contents into the bitstream ringbuffer */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx); - mutex_unlock(&ctx->bitstream_mutex); - - if (coda_get_bitstream_payload(ctx) < 512 && - (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "bitstream payload: %d, skipping\n", - coda_get_bitstream_payload(ctx)); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return -EAGAIN; - } - - /* Run coda_start_decoding (again) if not yet initialized */ - if (!ctx->initialized) { - int ret = coda_start_decoding(ctx); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); - return -EAGAIN; - } else { - ctx->initialized = 1; - } - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - /* Set rotator output */ - picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) { - /* Switch Cr and Cb for YVU420 format */ - picture_cr = picture_y + stridey * height; - picture_cb = picture_cr + stridey / 2 * height / 2; - } else { - picture_cb = picture_y + stridey * height; - picture_cr = picture_cb + stridey / 2 * height / 2; - } - - if (dev->devtype->product == CODA_960) { - /* - * The CODA960 seems to have an internal list of buffers with - * 64 entries that includes the registered frame buffers as - * well as the rotator buffer output. - * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames. - */ - coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index, - CODA9_CMD_DEC_PIC_ROT_INDEX); - coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y); - coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB); - coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR); - coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE); - } else { - coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y); - coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB); - coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR); - coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE); - } - coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, - CODA_CMD_DEC_PIC_ROT_MODE); - - switch (dev->devtype->product) { - case CODA_DX6: - /* TBD */ - case CODA_7541: - coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); - break; - case CODA_960: - coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); /* 'hardcode to use interrupt disable mode'? */ - break; - } - - coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); - - coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); - coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); - - return 0; -} - -static void coda_prepare_encode(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - int force_ipicture; - int quant_param = 0; - u32 picture_y, picture_cb, picture_cr; - u32 pic_stream_buffer_addr, pic_stream_buffer_size; - u32 dst_fourcc; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - src_buf->v4l2_buf.sequence = ctx->osequence; - dst_buf->v4l2_buf.sequence = ctx->osequence; - ctx->osequence++; - - /* - * Workaround coda firmware BUG that only marks the first - * frame as IDR. This is a problem for some decoders that can't - * recover when a frame is lost. - */ - if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) { - src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } else { - src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - /* - * Copy headers at the beginning of the first frame for H.264 only. - * In MPEG4 they are already copied by the coda. - */ - if (src_buf->v4l2_buf.sequence == 0) { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(dst_buf, 0) + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]; - pic_stream_buffer_size = CODA_MAX_FRAME_SIZE - - ctx->vpu_header_size[0] - - ctx->vpu_header_size[1] - - ctx->vpu_header_size[2]; - memcpy(vb2_plane_vaddr(dst_buf, 0), - &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); - memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0], - &ctx->vpu_header[1][0], ctx->vpu_header_size[1]); - memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] + - ctx->vpu_header_size[1], &ctx->vpu_header[2][0], - ctx->vpu_header_size[2]); - } else { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(dst_buf, 0); - pic_stream_buffer_size = CODA_MAX_FRAME_SIZE; - } - - if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { - force_ipicture = 1; - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_intra_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_intra_qp; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set intra qp, fmt not supported\n"); - break; - } - } else { - force_ipicture = 0; - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_inter_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_inter_qp; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set inter qp, fmt not supported\n"); - break; - } - } - - /* submit */ - coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); - coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); - - - picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0); - switch (q_data_src->fourcc) { - case V4L2_PIX_FMT_YVU420: - /* Switch Cb and Cr for YVU420 format */ - picture_cr = picture_y + q_data_src->bytesperline * - q_data_src->height; - picture_cb = picture_cr + q_data_src->bytesperline / 2 * - q_data_src->height / 2; - break; - case V4L2_PIX_FMT_YUV420: - default: - picture_cb = picture_y + q_data_src->bytesperline * - q_data_src->height; - picture_cr = picture_cb + q_data_src->bytesperline / 2 * - q_data_src->height / 2; - break; - } - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); - coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE); - coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); - - coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y); - coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB); - coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR); - } else { - coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); - coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); - coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR); - } - coda_write(dev, force_ipicture << 1 & 0x2, - CODA_CMD_ENC_PIC_OPTION); - - coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); - coda_write(dev, pic_stream_buffer_size / 1024, - CODA_CMD_ENC_PIC_BB_SIZE); - - if (!ctx->streamon_out) { - /* After streamoff on the output side, set the stream end flag */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); - } -} - -static void coda_device_run(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *dev = ctx->dev; - - queue_work(dev->workqueue, &ctx->pic_run_work); -} - -static void coda_free_framebuffers(struct coda_ctx *ctx); -static void coda_free_context_buffers(struct coda_ctx *ctx); - -static void coda_seq_end_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); - struct coda_dev *dev = ctx->dev; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__); - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_END failed\n"); - } - - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - coda_free_framebuffers(ctx); - coda_free_context_buffers(ctx); - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); -} - -static void coda_finish_decode(struct coda_ctx *ctx); -static void coda_finish_encode(struct coda_ctx *ctx); - -static void coda_pic_run_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); - struct coda_dev *dev = ctx->dev; - int ret; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - if (ctx->inst_type == CODA_INST_DECODER) { - ret = coda_prepare_decode(ctx); - if (ret < 0) { - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - /* job_finish scheduled by prepare_decode */ - return; - } - } else { - coda_prepare_encode(ctx); - } - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, ctx->iram_info.axi_sram_use, - CODA7_REG_BIT_AXI_SRAM_USE); - - if (ctx->inst_type == CODA_INST_DECODER) - coda_kfifo_sync_to_device_full(ctx); - coda_command_async(ctx, CODA_COMMAND_PIC_RUN); - - if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) { - dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); - - ctx->hold = true; - - coda_hw_reset(ctx); - } else if (!ctx->aborting) { - if (ctx->inst_type == CODA_INST_DECODER) - coda_finish_decode(ctx); - else - coda_finish_encode(ctx); - } - - if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) - queue_work(dev->workqueue, &ctx->seq_end_work); - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); -} - -static int coda_job_ready(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - - /* - * For both 'P' and 'key' frame cases 1 picture - * and 1 frame are needed. In the decoder case, - * the compressed frame can be in the bitstream. - */ - if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - ctx->inst_type != CODA_INST_DECODER) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "not ready: not enough video buffers.\n"); - return 0; - } - - if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "not ready: not enough video capture buffers.\n"); - return 0; - } - - if (ctx->hold || - ((ctx->inst_type == CODA_INST_DECODER) && - (coda_get_bitstream_payload(ctx) < 512) && - !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "%d: not ready: not enough bitstream data.\n", - ctx->idx); - return 0; - } - - if (ctx->aborting) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "not ready: aborting\n"); - return 0; - } - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "job ready\n"); - return 1; -} - -static void coda_job_abort(void *priv) -{ - struct coda_ctx *ctx = priv; - - ctx->aborting = 1; - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "Aborting task\n"); -} - -static void coda_lock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - mutex_lock(&pcdev->dev_mutex); -} - -static void coda_unlock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - mutex_unlock(&pcdev->dev_mutex); -} - -static struct v4l2_m2m_ops coda_m2m_ops = { - .device_run = coda_device_run, - .job_ready = coda_job_ready, - .job_abort = coda_job_abort, - .lock = coda_lock, - .unlock = coda_unlock, -}; - -static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - int luma_map, chro_map, i; - - memset(tiled_map, 0, sizeof(*tiled_map)); - - luma_map = 64; - chro_map = 64; - tiled_map->map_type = tiled_map_type; - for (i = 0; i < 16; i++) - tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 4; i++) - tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 16; i++) - tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map; - - if (tiled_map_type == GDI_LINEAR_FRAME_MAP) { - tiled_map->xy2rbc_config = 0; - } else { - dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n", - tiled_map_type); - return; - } -} - -static void set_default_params(struct coda_ctx *ctx) -{ - int max_w; - int max_h; - - ctx->codec = &ctx->dev->devtype->codecs[0]; - max_w = ctx->codec->max_w; - max_h = ctx->codec->max_h; - - ctx->params.codec_mode = CODA_MODE_INVALID; - ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->params.framerate = 30; - ctx->aborting = 0; - - /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc; - ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; - ctx->q_data[V4L2_M2M_SRC].width = max_w; - ctx->q_data[V4L2_M2M_SRC].height = max_h; - ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; - ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2; - ctx->q_data[V4L2_M2M_DST].width = max_w; - ctx->q_data[V4L2_M2M_DST].height = max_h; - ctx->q_data[V4L2_M2M_DST].bytesperline = 0; - ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; - ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; - ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; - ctx->q_data[V4L2_M2M_DST].rect.width = max_w; - ctx->q_data[V4L2_M2M_DST].rect.height = max_h; - - if (ctx->dev->devtype->product == CODA_960) - coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); -} - -/* - * Queue operations - */ -static int coda_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(vq); - struct coda_q_data *q_data; - unsigned int size; - - q_data = get_q_data(ctx, vq->type); - size = q_data->sizeimage; - - *nplanes = 1; - sizes[0] = size; - - alloc_ctxs[0] = ctx->dev->alloc_ctx; - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "get %d buffer(s) of size %d each.\n", *nbuffers, size); - - return 0; -} - -static int coda_buf_prepare(struct vb2_buffer *vb) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - v4l2_warn(&ctx->dev->v4l2_dev, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - - return 0; -} - -static void coda_buf_queue(struct vb2_buffer *vb) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - - /* - * In the decoder case, immediately try to copy the buffer into the - * bitstream ringbuffer and mark it as ready to be dequeued. - */ - if (q_data->fourcc == V4L2_PIX_FMT_H264 && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* - * For backwards compatibility, queuing an empty buffer marks - * the stream end - */ - if (vb2_get_plane_payload(vb, 0) == 0) { - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - if ((dev->devtype->product == CODA_960) && - coda_isbusy(dev) && - (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { - /* if this decoder instance is running, set the stream end flag */ - coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); - } - } - mutex_lock(&ctx->bitstream_mutex); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); - coda_fill_bitstream(ctx); - mutex_unlock(&ctx->bitstream_mutex); - } else { - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); - } -} - -static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) -{ - struct coda_dev *dev = ctx->dev; - u32 *p = ctx->parabuf.vaddr; - - if (dev->devtype->product == CODA_DX6) - p[index] = value; - else - p[index ^ 1] = value; -} - -static int coda_alloc_aux_buf(struct coda_dev *dev, - struct coda_aux_buf *buf, size_t size, - const char *name, struct dentry *parent) -{ - buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr, - GFP_KERNEL); - if (!buf->vaddr) - return -ENOMEM; - - buf->size = size; - - if (name && parent) { - buf->blob.data = buf->vaddr; - buf->blob.size = size; - buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob); - if (!buf->dentry) - dev_warn(&dev->plat_dev->dev, - "failed to create debugfs entry %s\n", name); - } - - return 0; -} - -static inline int coda_alloc_context_buf(struct coda_ctx *ctx, - struct coda_aux_buf *buf, size_t size, - const char *name) -{ - return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); -} - -static void coda_free_aux_buf(struct coda_dev *dev, - struct coda_aux_buf *buf) -{ - if (buf->vaddr) { - dma_free_coherent(&dev->plat_dev->dev, buf->size, - buf->vaddr, buf->paddr); - buf->vaddr = NULL; - buf->size = 0; - } - debugfs_remove(buf->dentry); -} - -static void coda_free_framebuffers(struct coda_ctx *ctx) -{ - int i; - - for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) - coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]); -} - -static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) -{ - struct coda_dev *dev = ctx->dev; - int width, height; - dma_addr_t paddr; - int ysize; - int ret; - int i; - - if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) { - width = round_up(q_data->width, 16); - height = round_up(q_data->height, 16); - } else { - width = round_up(q_data->width, 8); - height = q_data->height; - } - ysize = width * height; - - /* Allocate frame buffers */ - for (i = 0; i < ctx->num_internal_frames; i++) { - size_t size; - char *name; - - size = ysize + ysize / 2; - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && - dev->devtype->product != CODA_DX6) - size += ysize / 4; - name = kasprintf(GFP_KERNEL, "fb%d", i); - ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], - size, name); - kfree(name); - if (ret < 0) { - coda_free_framebuffers(ctx); - return ret; - } - } - - /* Register frame buffers in the parameter buffer */ - for (i = 0; i < ctx->num_internal_frames; i++) { - paddr = ctx->internal_frames[i].paddr; - coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */ - coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */ - coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */ - - /* mvcol buffer for h.264 */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && - dev->devtype->product != CODA_DX6) - coda_parabuf_write(ctx, 96 + i, - ctx->internal_frames[i].paddr + - ysize + ysize/4 + ysize/4); - } - - /* mvcol buffer for mpeg4 */ - if ((dev->devtype->product != CODA_DX6) && - (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4)) - coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr + - ysize + ysize/4 + ysize/4); - - 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 phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) -{ - phys_addr_t ret; - - size = round_up(size, 1024); - if (size > iram->remaining) - return 0; - iram->remaining -= size; - - ret = iram->next_paddr; - iram->next_paddr += size; - - return ret; -} - -static void coda_setup_iram(struct coda_ctx *ctx) -{ - struct coda_iram_info *iram_info = &ctx->iram_info; - struct coda_dev *dev = ctx->dev; - int mb_width; - int dbk_bits; - int bit_bits; - int ip_bits; - - memset(iram_info, 0, sizeof(*iram_info)); - iram_info->next_paddr = dev->iram.paddr; - iram_info->remaining = dev->iram.size; - - switch (dev->devtype->product) { - case CODA_7541: - dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; - bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - break; - case CODA_960: - dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; - bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - break; - default: /* CODA_DX6 */ - return; - } - - if (ctx->inst_type == CODA_INST_ENCODER) { - struct coda_q_data *q_data_src; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - mb_width = DIV_ROUND_UP(q_data_src->width, 16); - - /* Prioritize in case IRAM is too small for everything */ - if (dev->devtype->product == CODA_7541) { - iram_info->search_ram_size = round_up(mb_width * 16 * - 36 + 2048, 1024); - iram_info->search_ram_paddr = coda_iram_alloc(iram_info, - iram_info->search_ram_size); - if (!iram_info->search_ram_paddr) { - pr_err("IRAM is smaller than the search ram size\n"); - goto out; - } - iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE | - CODA7_USE_ME_ENABLE; - } - - /* Only H.264BP and H.263P3 are considered */ - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 64 * mb_width); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 64 * mb_width); - if (!iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP disabled for encoder */ - } else if (ctx->inst_type == CODA_INST_DECODER) { - struct coda_q_data *q_data_dst; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - mb_width = DIV_ROUND_UP(q_data_dst->width, 16); - - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 128 * mb_width); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 128 * mb_width); - if (!iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP unused as there is no VC1 support yet */ - } - -out: - if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "IRAM smaller than needed\n"); - - if (dev->devtype->product == CODA_7541) { - /* TODO - Enabling these causes picture errors on CODA7541 */ - if (ctx->inst_type == CODA_INST_DECODER) { - /* fw 1.4.50 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_IP_ENABLE); - } else { - /* fw 13.4.29 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_HOST_DBK_ENABLE | - CODA7_USE_IP_ENABLE | - CODA7_USE_DBK_ENABLE); - } - } -} - -static void coda_free_context_buffers(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - - coda_free_aux_buf(dev, &ctx->slicebuf); - coda_free_aux_buf(dev, &ctx->psbuf); - if (dev->devtype->product != CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); -} - -static int coda_alloc_context_buffers(struct coda_ctx *ctx, - struct coda_q_data *q_data) -{ - struct coda_dev *dev = ctx->dev; - size_t size; - int ret; - - if (dev->devtype->product == CODA_DX6) - return 0; - - if (ctx->psbuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n"); - return -EBUSY; - } - if (ctx->slicebuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n"); - return -EBUSY; - } - if (ctx->workbuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n"); - ret = -EBUSY; - return -ENOMEM; - } - - if (q_data->fourcc == V4L2_PIX_FMT_H264) { - /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->width, 16) * - DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; - ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer", - ctx->slicebuf.size); - return ret; - } - } - - if (dev->devtype->product == CODA_7541) { - ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer"); - goto err; - } - } - - size = dev->devtype->workbuf_size; - if (dev->devtype->product == CODA_960 && - q_data->fourcc == V4L2_PIX_FMT_H264) - size += CODA9_PS_SAVE_SIZE; - ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer", - ctx->workbuf.size); - goto err; - } - - return 0; - -err: - coda_free_context_buffers(ctx); - return ret; -} - -static int coda_start_decoding(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct coda_dev *dev = ctx->dev; - int width, height; - u32 src_fourcc; - u32 val; - int ret; - - /* Start decoding */ - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - bitstream_buf = ctx->bitstream.paddr; - bitstream_size = ctx->bitstream.size; - src_fourcc = q_data_src->fourcc; - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - - /* Update coda bitstream read and write pointers from kfifo */ - coda_kfifo_sync_to_device_full(ctx); - - ctx->display_idx = -1; - ctx->frm_dis_flg = 0; - coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE, - CODA_REG_BIT_BIT_STREAM_PARAM); - - coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); - val = 0; - if ((dev->devtype->product == CODA_7541) || - (dev->devtype->product == CODA_960)) - val |= CODA_REORDER_ENABLE; - coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); - - ctx->params.codec_mode = ctx->codec->mode; - if (dev->devtype->product == CODA_960 && - src_fourcc == V4L2_PIX_FMT_MPEG4) - ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; - else - ctx->params.codec_mode_aux = 0; - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (dev->devtype->product == CODA_7541) { - coda_write(dev, ctx->psbuf.paddr, - CODA_CMD_DEC_SEQ_PS_BB_START); - coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), - CODA_CMD_DEC_SEQ_PS_BB_SIZE); - } - if (dev->devtype->product == CODA_960) { - coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); - coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); - } - } - if (dev->devtype->product != CODA_960) { - coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); - } - - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); - return -ETIMEDOUT; - } - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); - - if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_INIT failed, error code = %d\n", - coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); - return -EAGAIN; - } - - val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); - if (dev->devtype->product == CODA_DX6) { - width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; - height = val & CODADX6_PICHEIGHT_MASK; - } else { - width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; - height = val & CODA7_PICHEIGHT_MASK; - } - - if (width > q_data_dst->width || height > q_data_dst->height) { - v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", - width, height, q_data_dst->width, q_data_dst->height); - return -EINVAL; - } - - width = round_up(width, 16); - height = round_up(height, 16); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n", - __func__, ctx->idx, width, height); - - ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); - if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { - v4l2_err(&dev->v4l2_dev, - "not enough framebuffers to decode (%d < %d)\n", - CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); - return -EINVAL; - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); - - q_data_dst->rect.left = (left_right >> 10) & 0x3ff; - q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; - q_data_dst->rect.width = width - q_data_dst->rect.left - - (left_right & 0x3ff); - q_data_dst->rect.height = height - q_data_dst->rect.top - - (top_bottom & 0x3ff); - } - - ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); - if (ret < 0) - return ret; - - /* Tell the decoder how many frame buffers we allocated. */ - coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE); - - if (dev->devtype->product != CODA_DX6) { - /* Set secondary AXI IRAM */ - coda_setup_iram(ctx); - - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - } - - if (dev->devtype->product == CODA_960) { - coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); - - coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE); - coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET | - 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET, - CODA9_CMD_SET_FRAME_CACHE_CONFIG); - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - coda_write(dev, ctx->slicebuf.paddr, - CODA_CMD_SET_FRAME_SLICE_BB_START); - coda_write(dev, ctx->slicebuf.size / 1024, - CODA_CMD_SET_FRAME_SLICE_BB_SIZE); - } - - if (dev->devtype->product == CODA_7541) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); - } else if (dev->devtype->product == CODA_960) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); - } - - if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { - v4l2_err(&ctx->dev->v4l2_dev, - "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, - int header_code, u8 *header, int *size) -{ - struct coda_dev *dev = ctx->dev; - size_t bufsize; - int ret; - int i; - - if (dev->devtype->product == CODA_960) - memset(vb2_plane_vaddr(buf, 0), 0, 64); - - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), - CODA_CMD_ENC_HEADER_BB_START); - bufsize = vb2_plane_size(buf, 0); - if (dev->devtype->product == CODA_960) - bufsize /= 1024; - coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); - ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return ret; - } - - if (dev->devtype->product == CODA_960) { - for (i = 63; i > 0; i--) - if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0) - break; - *size = i + 1; - } else { - *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - } - memcpy(header, vb2_plane_vaddr(buf, 0), *size); - - return 0; -} - -static int coda_start_encoding(struct coda_ctx *ctx); - -static int coda_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src, *q_data_dst; - u32 dst_fourcc; - int ret = 0; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (q_data_src->fourcc == V4L2_PIX_FMT_H264) { - if (coda_get_bitstream_payload(ctx) < 512) - return -EINVAL; - } else { - if (count < 1) - return -EINVAL; - } - - ctx->streamon_out = 1; - - if (coda_format_is_yuv(q_data_src->fourcc)) - ctx->inst_type = CODA_INST_ENCODER; - else - ctx->inst_type = CODA_INST_DECODER; - } else { - if (count < 1) - return -EINVAL; - - ctx->streamon_cap = 1; - } - - /* Don't start the coda unless both queues are on */ - if (!(ctx->streamon_out & ctx->streamon_cap)) - return 0; - - /* Allow decoder device_run with no new buffers queued */ - if (ctx->inst_type == CODA_INST_DECODER) - v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); - - ctx->gopcounter = ctx->params.gop_size - 1; - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - q_data_dst->fourcc); - if (!ctx->codec) { - v4l2_err(v4l2_dev, "couldn't tell instance type.\n"); - return -EINVAL; - } - - /* Allocate per-instance buffers */ - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - - if (ctx->inst_type == CODA_INST_DECODER) { - mutex_lock(&dev->coda_mutex); - ret = coda_start_decoding(ctx); - mutex_unlock(&dev->coda_mutex); - if (ret == -EAGAIN) - return 0; - else if (ret < 0) - return ret; - } else { - ret = coda_start_encoding(ctx); - } - - ctx->initialized = 1; - return ret; -} - -static int coda_start_encoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct vb2_buffer *buf; - int gamma, ret, value; - u32 dst_fourcc; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); - bitstream_size = q_data_dst->sizeimage; - - if (!coda_is_initialized(dev)) { - v4l2_err(v4l2_dev, "coda is not initialized.\n"); - return -EFAULT; - } - - mutex_lock(&dev->coda_mutex); - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | - CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - case CODA_960: - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - /* fallthrough */ - case CODA_7541: - coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | - CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - } - - value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL); - value &= ~(1 << 2 | 0x7 << 9); - ctx->frame_mem_ctrl = value; - coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product == CODA_DX6) { - /* Configure the coda */ - coda_write(dev, dev->iram.paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); - } - - /* Could set rotation here if needed */ - switch (dev->devtype->product) { - case CODA_DX6: - value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; - break; - case CODA_7541: - if (dst_fourcc == V4L2_PIX_FMT_H264) { - value = (round_up(q_data_src->width, 16) & - CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (round_up(q_data_src->height, 16) & - CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; - break; - } - /* fallthrough */ - case CODA_960: - value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); - coda_write(dev, ctx->params.framerate, - CODA_CMD_ENC_SEQ_SRC_F_RATE); - - ctx->params.codec_mode = ctx->codec->mode; - switch (dst_fourcc) { - case V4L2_PIX_FMT_MPEG4: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); - break; - case V4L2_PIX_FMT_H264: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); - if (ctx->params.h264_deblk_enabled) { - value = ((ctx->params.h264_deblk_alpha & - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | - ((ctx->params.h264_deblk_beta & - CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET); - } else { - value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); - break; - default: - v4l2_err(v4l2_dev, - "dst format (0x%08x) invalid.\n", dst_fourcc); - ret = -EINVAL; - goto out; - } - - switch (ctx->params.slice_mode) { - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: - value = 0; - break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: - value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; - value |= (1 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET; - value |= 1 & CODA_SLICING_MODE_MASK; - break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: - value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; - value |= (0 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET; - value |= 1 & CODA_SLICING_MODE_MASK; - break; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); - value = ctx->params.gop_size & CODA_GOP_SIZE_MASK; - coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); - - if (ctx->params.bitrate) { - /* Rate control enabled */ - value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; - value |= 1 & CODA_RATECONTROL_ENABLE_MASK; - if (dev->devtype->product == CODA_960) - value |= BIT(31); /* disable autoskip */ - } else { - value = 0; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); - - coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); - coda_write(dev, ctx->params.intra_refresh, - CODA_CMD_ENC_SEQ_INTRA_REFRESH); - - coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); - - - value = 0; - if (dev->devtype->product == CODA_960) - gamma = CODA9_DEFAULT_GAMMA; - else - gamma = CODA_DEFAULT_GAMMA; - if (gamma > 0) { - coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, - CODA_CMD_ENC_SEQ_RC_GAMMA); - } - - if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { - coda_write(dev, - ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | - ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, - CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); - } - if (dev->devtype->product == CODA_960) { - if (ctx->params.h264_max_qp) - value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; - if (CODA_DEFAULT_GAMMA > 0) - value |= 1 << CODA9_OPTION_GAMMA_OFFSET; - } else { - if (CODA_DEFAULT_GAMMA > 0) { - if (dev->devtype->product == CODA_DX6) - value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; - else - value |= 1 << CODA7_OPTION_GAMMA_OFFSET; - } - if (ctx->params.h264_min_qp) - value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; - if (ctx->params.h264_max_qp) - value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); - - coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); - - coda_setup_iram(ctx); - - if (dst_fourcc == V4L2_PIX_FMT_H264) { - switch (dev->devtype->product) { - case CODA_DX6: - value = FMO_SLICE_SAVE_BUF_SIZE << 7; - coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); - break; - case CODA_7541: - coda_write(dev, ctx->iram_info.search_ram_paddr, - CODA7_CMD_ENC_SEQ_SEARCH_BASE); - coda_write(dev, ctx->iram_info.search_ram_size, - CODA7_CMD_ENC_SEQ_SEARCH_SIZE); - break; - case CODA_960: - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - goto out; - } - - if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); - ret = -EFAULT; - goto out; - } - - if (dev->devtype->product == CODA_960) - ctx->num_internal_frames = 4; - else - ctx->num_internal_frames = 2; - ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); - if (ret < 0) { - v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); - goto out; - } - - coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, q_data_src->bytesperline, - CODA_CMD_SET_FRAME_BUF_STRIDE); - if (dev->devtype->product == CODA_7541) { - coda_write(dev, q_data_src->bytesperline, - CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); - } - if (dev->devtype->product != CODA_DX6) { - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) { - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - - /* FIXME */ - coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A); - coda_write(dev, ctx->internal_frames[3].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_B); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - goto out; - } - - /* Save stream headers */ - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - /* - * Get SPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - /* - * Get PPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - /* - * 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: - /* - * Get VOS in the first frame and copy it to an - * intermediate buffer - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, - &ctx->vpu_header[2][0], - &ctx->vpu_header_size[2]); - if (ret < 0) - goto out; - break; - default: - /* No more formats need to save headers at the moment */ - break; - } - -out: - mutex_unlock(&dev->coda_mutex); - return ret; -} - -static void coda_stop_streaming(struct vb2_queue *q) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct coda_dev *dev = ctx->dev; - - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%s: output\n", __func__); - ctx->streamon_out = 0; - - if (ctx->inst_type == CODA_INST_DECODER && - coda_isbusy(dev) && ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX)) { - /* if this decoder instance is running, set the stream end flag */ - if (dev->devtype->product == CODA_960) { - u32 val = coda_read(dev, CODA_REG_BIT_BIT_STREAM_PARAM); - - val |= CODA_BIT_STREAM_END_FLAG; - coda_write(dev, val, CODA_REG_BIT_BIT_STREAM_PARAM); - ctx->bit_stream_param = val; - } - } - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - ctx->isequence = 0; - } else { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%s: capture\n", __func__); - ctx->streamon_cap = 0; - - ctx->osequence = 0; - ctx->sequence_offset = 0; - } - - if (!ctx->streamon_out && !ctx->streamon_cap) { - struct coda_timestamp *ts; - - while (!list_empty(&ctx->timestamp_list)) { - ts = list_first_entry(&ctx->timestamp_list, - struct coda_timestamp, list); - list_del(&ts->list); - kfree(ts); - } - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - ctx->runcounter = 0; - } -} - -static struct vb2_ops coda_qops = { - .queue_setup = coda_queue_setup, - .buf_prepare = coda_buf_prepare, - .buf_queue = coda_buf_queue, - .start_streaming = coda_start_streaming, - .stop_streaming = coda_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int coda_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct coda_ctx *ctx = - container_of(ctrl->handler, struct coda_ctx, ctrls); - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_HOR; - else - ctx->params.rot_mode &= ~CODA_MIR_HOR; - break; - case V4L2_CID_VFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_VER; - else - ctx->params.rot_mode &= ~CODA_MIR_VER; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctx->params.bitrate = ctrl->val / 1000; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctx->params.gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - ctx->params.h264_intra_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - ctx->params.h264_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: - ctx->params.h264_min_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - ctx->params.h264_max_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: - ctx->params.h264_deblk_alpha = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: - ctx->params.h264_deblk_beta = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: - ctx->params.h264_deblk_enabled = (ctrl->val == - V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: - ctx->params.mpeg4_intra_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: - ctx->params.mpeg4_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: - ctx->params.slice_mode = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: - ctx->params.slice_max_mb = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: - ctx->params.slice_max_bits = ctrl->val * 8; - break; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - break; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: - ctx->params.intra_refresh = ctrl->val; - break; - default: - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - return -EINVAL; - } - - return 0; -} - -static struct v4l2_ctrl_ops coda_ctrl_ops = { - .s_ctrl = coda_s_ctrl, -}; - -static int coda_ctrls_setup(struct coda_ctx *ctx) -{ - v4l2_ctrl_handler_init(&ctx->ctrls, 9); - - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); - if (ctx->dev->devtype->product != CODA_960) { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); - } - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, - V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0, - V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0, - V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 500); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_HEADER_MODE, - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0); - - if (ctx->ctrls.error) { - v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)", - ctx->ctrls.error); - return -EINVAL; - } - - return v4l2_ctrl_handler_setup(&ctx->ctrls); -} - -static int coda_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct coda_ctx *ctx = priv; - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = ctx; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->ops = &coda_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = ctx; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &coda_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; - - return vb2_queue_init(dst_vq); -} - -static int coda_next_free_instance(struct coda_dev *dev) -{ - int idx = ffz(dev->instance_mask); - - if ((idx < 0) || - (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES)) - return -EBUSY; - - return idx; -} - -static int coda_open(struct file *file) -{ - struct coda_dev *dev = video_drvdata(file); - struct coda_ctx *ctx = NULL; - char *name; - int ret; - int idx; - - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - idx = coda_next_free_instance(dev); - if (idx < 0) { - ret = idx; - goto err_coda_max; - } - set_bit(idx, &dev->instance_mask); - - name = kasprintf(GFP_KERNEL, "context%d", idx); - ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); - kfree(name); - - init_completion(&ctx->completion); - INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); - INIT_WORK(&ctx->seq_end_work, coda_seq_end_work); - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - ctx->dev = dev; - ctx->idx = idx; - switch (dev->devtype->product) { - case CODA_7541: - case CODA_960: - ctx->reg_idx = 0; - break; - default: - ctx->reg_idx = idx; - } - - /* Power up and upload firmware if necessary */ - ret = pm_runtime_get_sync(&dev->plat_dev->dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); - goto err_pm_get; - } - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - set_default_params(ctx); - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, - &coda_queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - - v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", - __func__, ret); - goto err_ctx_init; - } - - ret = coda_ctrls_setup(ctx); - if (ret) { - v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); - goto err_ctrls_setup; - } - - ctx->fh.ctrl_handler = &ctx->ctrls; - - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE, - "parabuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); - goto err_dma_alloc; - } - - ctx->bitstream.size = CODA_MAX_FRAME_SIZE; - ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev, - ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL); - if (!ctx->bitstream.vaddr) { - v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer"); - ret = -ENOMEM; - goto err_dma_writecombine; - } - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - mutex_init(&ctx->bitstream_mutex); - mutex_init(&ctx->buffer_mutex); - INIT_LIST_HEAD(&ctx->timestamp_list); - - coda_lock(ctx); - list_add(&ctx->list, &dev->instances); - coda_unlock(ctx); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", - ctx->idx, ctx); - - return 0; - -err_dma_writecombine: - coda_free_context_buffers(ctx); - if (ctx->dev->devtype->product == CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - coda_free_aux_buf(dev, &ctx->parabuf); -err_dma_alloc: - v4l2_ctrl_handler_free(&ctx->ctrls); -err_ctrls_setup: - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); -err_ctx_init: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - pm_runtime_put_sync(&dev->plat_dev->dev); -err_pm_get: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - clear_bit(ctx->idx, &dev->instance_mask); -err_coda_max: - kfree(ctx); - return ret; -} - -static int coda_release(struct file *file) -{ - struct coda_dev *dev = video_drvdata(file); - struct coda_ctx *ctx = fh_to_ctx(file->private_data); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", - ctx); - - debugfs_remove_recursive(ctx->debugfs_entry); - - /* If this instance is running, call .job_abort and wait for it to end */ - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - - /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->initialized) { - queue_work(dev->workqueue, &ctx->seq_end_work); - flush_work(&ctx->seq_end_work); - } - - coda_free_framebuffers(ctx); - - coda_lock(ctx); - list_del(&ctx->list); - coda_unlock(ctx); - - dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size, - ctx->bitstream.vaddr, ctx->bitstream.paddr); - coda_free_context_buffers(ctx); - if (ctx->dev->devtype->product == CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - - coda_free_aux_buf(dev, &ctx->parabuf); - v4l2_ctrl_handler_free(&ctx->ctrls); - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - pm_runtime_put_sync(&dev->plat_dev->dev); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - clear_bit(ctx->idx, &dev->instance_mask); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations coda_fops = { - .owner = THIS_MODULE, - .open = coda_open, - .release = coda_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static void coda_finish_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src; - struct coda_q_data *q_data_dst; - struct vb2_buffer *dst_buf; - struct coda_timestamp *ts; - int width, height; - int decoded_idx; - int display_idx; - u32 src_fourcc; - int success; - u32 err_mb; - u32 val; - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - /* - * in stream-end mode, the read pointer can overshoot the write pointer - * by up to 512 bytes - */ - if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { - if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512) - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - } - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - src_fourcc = q_data_src->fourcc; - - val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); - if (val != 1) - pr_err("DEC_PIC_SUCCESS = %d\n", val); - - success = val & 0x1; - if (!success) - v4l2_err(&dev->v4l2_dev, "decode failed\n"); - - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (val & (1 << 3)) - v4l2_err(&dev->v4l2_dev, - "insufficient PS buffer space (%d bytes)\n", - ctx->psbuf.size); - if (val & (1 << 2)) - v4l2_err(&dev->v4l2_dev, - "insufficient slice buffer space (%d bytes)\n", - ctx->slicebuf.size); - } - - val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); - width = (val >> 16) & 0xffff; - height = val & 0xffff; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - /* frame crop information */ - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); - - if (left_right == 0xffffffff && top_bottom == 0xffffffff) { - /* Keep current crop information */ - } else { - struct v4l2_rect *rect = &q_data_dst->rect; - - rect->left = left_right >> 16 & 0xffff; - rect->top = top_bottom >> 16 & 0xffff; - rect->width = width - rect->left - - (left_right & 0xffff); - rect->height = height - rect->top - - (top_bottom & 0xffff); - } - } else { - /* no cropping */ - } - - err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); - if (err_mb > 0) - v4l2_err(&dev->v4l2_dev, - "errors in %d macroblocks\n", err_mb); - - if (dev->devtype->product == CODA_7541) { - val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); - if (val == 0) { - /* not enough bitstream data */ - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "prescan failed: %d\n", val); - ctx->hold = true; - return; - } - } - - ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - /* - * The previous display frame was copied out by the rotator, - * now it can be overwritten again - */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - ctx->frm_dis_flg &= ~(1 << ctx->display_idx); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - } - - /* - * The index of the last decoded frame, not necessarily in - * display order, and the index of the next display frame. - * The latter could have been decoded in a previous run. - */ - decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); - display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); - - if (decoded_idx == -1) { - /* no frame was decoded, but we might have a display frame */ - if (display_idx >= 0 && display_idx < ctx->num_internal_frames) - ctx->sequence_offset++; - else if (ctx->display_idx < 0) - ctx->hold = true; - } else if (decoded_idx == -2) { - /* no frame was decoded, we still return the remaining buffers */ - } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "decoded frame index out of range: %d\n", decoded_idx); - } else { - ts = list_first_entry(&ctx->timestamp_list, - struct coda_timestamp, list); - list_del(&ts->list); - val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; - val -= ctx->sequence_offset; - if (val != (ts->sequence & 0xffff)) { - v4l2_err(&dev->v4l2_dev, - "sequence number mismatch (%d(%d) != %d)\n", - val, ctx->sequence_offset, ts->sequence); - } - ctx->frame_timestamps[decoded_idx] = *ts; - kfree(ts); - - val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; - if (val == 0) - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; - else if (val == 1) - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME; - else - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME; - - ctx->frame_errors[decoded_idx] = err_mb; - } - - if (display_idx == -1) { - /* - * no more frames to be decoded, but there could still - * be rotator output to dequeue - */ - ctx->hold = true; - } else if (display_idx == -3) { - /* possibly prescan failure */ - } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "presentation frame index out of range: %d\n", - display_idx); - } - - /* If a frame was copied out, return it */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->v4l2_buf.sequence = ctx->osequence++; - - dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME); - dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx]; - ts = &ctx->frame_timestamps[ctx->display_idx]; - dst_buf->v4l2_buf.timecode = ts->timecode; - dst_buf->v4l2_buf.timestamp = ts->timestamp; - - vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2); - - v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ? - VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "job finished: decoding frame (%d) (%s)\n", - dst_buf->v4l2_buf.sequence, - (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? - "KEYFRAME" : "PFRAME"); - } else { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "job finished: no frame decoded\n"); - } - - /* The rotator will copy the current display frame next time */ - ctx->display_idx = display_idx; -} - -static void coda_finish_encode(struct coda_ctx *ctx) -{ - struct vb2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr, start_ptr; - - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - /* Get results from the coda */ - start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); - wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - - /* Calculate bytesused field */ - if (dst_buf->v4l2_buf.sequence == 0) { - vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]); - } else { - vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr); - } - - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n", - wr_ptr - start_ptr); - - coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); - coda_read(dev, CODA_RET_ENC_PIC_FLAG); - - if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; - } else { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } - - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->v4l2_buf.flags |= - src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - - ctx->gopcounter--; - if (ctx->gopcounter < 0) - ctx->gopcounter = ctx->params.gop_size - 1; - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "job finished: encoding frame (%d) (%s)\n", - dst_buf->v4l2_buf.sequence, - (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? - "KEYFRAME" : "PFRAME"); -} - -static irqreturn_t coda_irq_handler(int irq, void *data) -{ - struct coda_dev *dev = data; - struct coda_ctx *ctx; - - /* read status register to attend the IRQ */ - coda_read(dev, CODA_REG_BIT_INT_STATUS); - coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, - CODA_REG_BIT_INT_CLEAR); - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (ctx == NULL) { - v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; - } - - if (ctx->aborting) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "task has been aborted\n"); - } - - if (coda_isbusy(ctx->dev)) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "coda is still busy!!!!\n"); - return IRQ_NONE; - } - - complete(&ctx->completion); - - return IRQ_HANDLED; -} - -static u32 coda_supported_firmwares[] = { - CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), - CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), -}; - -static bool coda_firmware_supported(u32 vernum) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) - if (vernum == coda_supported_firmwares[i]) - return true; - return false; -} - -static int coda_hw_init(struct coda_dev *dev) -{ - u32 data; - u16 *p; - int i, ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - if (dev->rstc) - reset_control_reset(dev->rstc); - - /* - * Copy the first CODA_ISRAM_SIZE in the internal SRAM. - * The 16-bit chars in the code buffer are in memory access - * order, re-sort them to CODA order for register download. - * Data in this SRAM survives a reboot. - */ - p = (u16 *)dev->codebuf.vaddr; - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[i ^ 1]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } else { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[round_down(i, 4) + - 3 - (i % 4)]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } - - /* Clear registers */ - for (i = 0; i < 64; i++) - coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); - - /* Tell the BIT where to find everything it needs */ - if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541) { - coda_write(dev, dev->tempbuf.paddr, - CODA_REG_BIT_TEMP_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); - } else { - coda_write(dev, dev->workbuf.paddr, - CODA_REG_BIT_WORK_BUF_ADDR); - } - coda_write(dev, dev->codebuf.paddr, - CODA_REG_BIT_CODE_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); - - /* Set default values */ - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL); - break; - default: - coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL); - } - if (dev->devtype->product == CODA_960) - coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL); - else - coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); - - coda_write(dev, CODA_INT_INTERRUPT_ENABLE, - CODA_REG_BIT_INT_ENABLE); - - /* Reset VPU and start processor */ - data = coda_read(dev, CODA_REG_BIT_CODE_RESET); - data |= CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - udelay(10); - data &= ~CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - - return 0; - -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static int coda_check_firmware(struct coda_dev *dev) -{ - u16 product, major, minor, release; - u32 data; - int ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); - if (coda_wait_timeout(dev)) { - v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); - ret = -EIO; - goto err_run_cmd; - } - - if (dev->devtype->product == CODA_960) { - data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); - v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", - data); - } - - /* Check we are compatible with the loaded firmware */ - data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); - product = CODA_FIRMWARE_PRODUCT(data); - major = CODA_FIRMWARE_MAJOR(data); - minor = CODA_FIRMWARE_MINOR(data); - release = CODA_FIRMWARE_RELEASE(data); - - clk_disable_unprepare(dev->clk_per); - clk_disable_unprepare(dev->clk_ahb); - - if (product != dev->devtype->product) { - v4l2_err(&dev->v4l2_dev, "Wrong firmware. Hw: %s, Fw: %s," - " Version: %u.%u.%u\n", - coda_product_name(dev->devtype->product), - coda_product_name(product), major, minor, release); - return -EINVAL; - } - - v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", - coda_product_name(product)); - - if (coda_firmware_supported(data)) { - v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", - major, minor, release); - } else { - v4l2_warn(&dev->v4l2_dev, "Unsupported firmware version: " - "%u.%u.%u\n", major, minor, release); - } - - return 0; - -err_run_cmd: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static void coda_fw_callback(const struct firmware *fw, void *context) -{ - struct coda_dev *dev = context; - struct platform_device *pdev = dev->plat_dev; - int ret; - - if (!fw) { - v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); - return; - } - - /* allocate auxiliary per-device code buffer for the BIT processor */ - ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", - dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate code buffer\n"); - return; - } - - /* Copy the whole firmware image to the code buffer */ - memcpy(dev->codebuf.vaddr, fw->data, fw->size); - release_firmware(fw); - - if (pm_runtime_enabled(&pdev->dev) && pdev->dev.pm_domain) { - /* - * Enabling power temporarily will cause coda_hw_init to be - * called via coda_runtime_resume by the pm domain. - */ - ret = pm_runtime_get_sync(&dev->plat_dev->dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to power on: %d\n", - ret); - return; - } - - ret = coda_check_firmware(dev); - if (ret < 0) - return; - - pm_runtime_put_sync(&dev->plat_dev->dev); - } else { - /* - * If runtime pm is disabled or pm_domain is not set, - * initialize once manually. - */ - ret = coda_hw_init(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); - return; - } - - ret = coda_check_firmware(dev); - if (ret < 0) - return; - } - - dev->vfd.fops = &coda_fops, - dev->vfd.ioctl_ops = &coda_ioctl_ops; - dev->vfd.release = video_device_release_empty, - dev->vfd.lock = &dev->dev_mutex; - dev->vfd.v4l2_dev = &dev->v4l2_dev; - dev->vfd.vfl_dir = VFL_DIR_M2M; - snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME); - video_set_drvdata(&dev->vfd, dev); - - dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(dev->alloc_ctx)) { - v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n"); - return; - } - - dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); - goto rel_ctx; - } - - ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto rel_m2m; - } - v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video%d\n", - dev->vfd.num); - - return; - -rel_m2m: - v4l2_m2m_release(dev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); -} - -static int coda_firmware_request(struct coda_dev *dev) -{ - char *fw = dev->devtype->firmware; - - dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, - coda_product_name(dev->devtype->product)); - - return request_firmware_nowait(THIS_MODULE, true, - fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback); -} - -enum coda_platform { - CODA_IMX27, - CODA_IMX53, - CODA_IMX6Q, - CODA_IMX6DL, -}; - -static const struct coda_devtype coda_devdata[] = { - [CODA_IMX27] = { - .firmware = "v4l-codadx6-imx27.bin", - .product = CODA_DX6, - .codecs = codadx6_codecs, - .num_codecs = ARRAY_SIZE(codadx6_codecs), - .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, - .iram_size = 0xb000, - }, - [CODA_IMX53] = { - .firmware = "v4l-coda7541-imx53.bin", - .product = CODA_7541, - .codecs = coda7_codecs, - .num_codecs = ARRAY_SIZE(coda7_codecs), - .workbuf_size = 128 * 1024, - .tempbuf_size = 304 * 1024, - .iram_size = 0x14000, - }, - [CODA_IMX6Q] = { - .firmware = "v4l-coda960-imx6q.bin", - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x21000, - }, - [CODA_IMX6DL] = { - .firmware = "v4l-coda960-imx6dl.bin", - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x20000, - }, -}; - -static struct platform_device_id coda_platform_ids[] = { - { .name = "coda-imx27", .driver_data = CODA_IMX27 }, - { .name = "coda-imx53", .driver_data = CODA_IMX53 }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, coda_platform_ids); - -#ifdef CONFIG_OF -static const struct of_device_id coda_dt_ids[] = { - { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, - { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, - { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, - { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, coda_dt_ids); -#endif - -static int coda_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev); - const struct platform_device_id *pdev_id; - struct coda_platform_data *pdata = pdev->dev.platform_data; - struct device_node *np = pdev->dev.of_node; - struct gen_pool *pool; - struct coda_dev *dev; - struct resource *res; - int ret, irq; - - dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL); - if (!dev) { - dev_err(&pdev->dev, "Not enough memory for %s\n", - CODA_NAME); - return -ENOMEM; - } - - spin_lock_init(&dev->irqlock); - INIT_LIST_HEAD(&dev->instances); - - dev->plat_dev = pdev; - dev->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(dev->clk_per)) { - dev_err(&pdev->dev, "Could not get per clock\n"); - return PTR_ERR(dev->clk_per); - } - - dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(dev->clk_ahb)) { - dev_err(&pdev->dev, "Could not get ahb clock\n"); - return PTR_ERR(dev->clk_ahb); - } - - /* Get memory for physical registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->regs_base)) - return PTR_ERR(dev->regs_base); - - /* IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq resource\n"); - return irq; - } - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, - IRQF_ONESHOT, dev_name(&pdev->dev), dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); - if (IS_ERR(dev->rstc)) { - ret = PTR_ERR(dev->rstc); - if (ret == -ENOENT || ret == -ENOSYS) { - dev->rstc = NULL; - } else { - dev_err(&pdev->dev, "failed get reset control: %d\n", ret); - return ret; - } - } - - /* Get IRAM pool from device tree or platform data */ - pool = of_get_named_gen_pool(np, "iram", 0); - if (!pool && pdata) - pool = dev_get_gen_pool(pdata->iram_dev); - if (!pool) { - dev_err(&pdev->dev, "iram pool not available\n"); - return -ENOMEM; - } - dev->iram_pool = pool; - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - mutex_init(&dev->dev_mutex); - mutex_init(&dev->coda_mutex); - - pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); - - if (of_id) { - dev->devtype = of_id->data; - } else if (pdev_id) { - dev->devtype = &coda_devdata[pdev_id->driver_data]; - } else { - v4l2_device_unregister(&dev->v4l2_dev); - return -EINVAL; - } - - dev->debugfs_root = debugfs_create_dir("coda", NULL); - if (!dev->debugfs_root) - dev_warn(&pdev->dev, "failed to create debugfs root\n"); - - /* allocate auxiliary per-device buffers for the BIT processor */ - if (dev->devtype->product == CODA_DX6) { - ret = coda_alloc_aux_buf(dev, &dev->workbuf, - dev->devtype->workbuf_size, "workbuf", - dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate work buffer\n"); - v4l2_device_unregister(&dev->v4l2_dev); - return ret; - } - } - - if (dev->devtype->tempbuf_size) { - ret = coda_alloc_aux_buf(dev, &dev->tempbuf, - dev->devtype->tempbuf_size, "tempbuf", - dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate temp buffer\n"); - v4l2_device_unregister(&dev->v4l2_dev); - return ret; - } - } - - dev->iram.size = dev->devtype->iram_size; - dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, - &dev->iram.paddr); - if (!dev->iram.vaddr) { - dev_err(&pdev->dev, "unable to alloc iram\n"); - return -ENOMEM; - } - - dev->iram.blob.data = dev->iram.vaddr; - dev->iram.blob.size = dev->iram.size; - dev->iram.dentry = debugfs_create_blob("iram", 0644, dev->debugfs_root, - &dev->iram.blob); - - dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); - if (!dev->workqueue) { - dev_err(&pdev->dev, "unable to alloc workqueue\n"); - return -ENOMEM; - } - - platform_set_drvdata(pdev, dev); - - pm_runtime_enable(&pdev->dev); - - return coda_firmware_request(dev); -} - -static int coda_remove(struct platform_device *pdev) -{ - struct coda_dev *dev = platform_get_drvdata(pdev); - - video_unregister_device(&dev->vfd); - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - pm_runtime_disable(&pdev->dev); - if (dev->alloc_ctx) - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); - v4l2_device_unregister(&dev->v4l2_dev); - destroy_workqueue(dev->workqueue); - if (dev->iram.vaddr) - gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, - dev->iram.size); - coda_free_aux_buf(dev, &dev->codebuf); - coda_free_aux_buf(dev, &dev->tempbuf); - coda_free_aux_buf(dev, &dev->workbuf); - debugfs_remove_recursive(dev->debugfs_root); - return 0; -} - -#ifdef CONFIG_PM_RUNTIME -static int coda_runtime_resume(struct device *dev) -{ - struct coda_dev *cdev = dev_get_drvdata(dev); - int ret = 0; - - if (dev->pm_domain) { - ret = coda_hw_init(cdev); - if (ret) - v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); - } - - return ret; -} -#endif - -static const struct dev_pm_ops coda_pm_ops = { - SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) -}; - -static struct platform_driver coda_driver = { - .probe = coda_probe, - .remove = coda_remove, - .driver = { - .name = CODA_NAME, - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(coda_dt_ids), - .pm = &coda_pm_ops, - }, - .id_table = coda_platform_ids, -}; - -module_platform_driver(coda_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); -MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile new file mode 100644 index 0000000..3543291 --- /dev/null +++ b/drivers/media/platform/coda/Makefile @@ -0,0 +1,3 @@ +coda-objs := coda-common.o coda-bit.o coda-h264.o + +obj-$(CONFIG_VIDEO_CODA) += coda.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c new file mode 100644 index 0000000..9b8ea8b --- /dev/null +++ b/drivers/media/platform/coda/coda-bit.c @@ -0,0 +1,1861 @@ +/* + * Coda multi-standard codec IP - BIT processor functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, <javier.martin@vista-silicon.com> + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> + +#include "coda.h" + +#define CODA7_PS_BUF_SIZE 0x28000 +#define CODA9_PS_SAVE_SIZE (512 * 1024) + +#define CODA_DEFAULT_GAMMA 4096 +#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ + +static inline int coda_is_initialized(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; +} + +static inline unsigned long coda_isbusy(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_BUSY); +} + +static int coda_wait_timeout(struct coda_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while (coda_isbusy(dev)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static void coda_command_async(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541) { + /* Restore context related registers to CODA */ + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + coda_write(dev, ctx->frame_mem_ctrl, + CODA_REG_BIT_FRAME_MEM_CTRL); + coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + } + + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + + coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); + + coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); +} + +static int coda_command_sync(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + + coda_command_async(ctx, cmd); + return coda_wait_timeout(dev); +} + +int coda_hw_reset(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + unsigned long timeout; + unsigned int idx; + int ret; + + if (!dev->rstc) + return -ENOENT; + + idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); + + if (dev->devtype->product == CODA_960) { + timeout = jiffies + msecs_to_jiffies(100); + coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); + while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + } + + ret = reset_control_reset(dev->rstc); + if (ret < 0) + return ret; + + if (dev->devtype->product == CODA_960) + coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + ret = coda_wait_timeout(dev); + coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); + + return ret; +} + +static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr; + + rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + kfifo->out = (kfifo->in & ~kfifo->mask) | + (rd_ptr - ctx->bitstream.paddr); + if (kfifo->out > kfifo->in) + kfifo->out -= kfifo->mask + 1; +} + +static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr, wr_ptr; + + rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); + coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr; + + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static int coda_bitstream_queue(struct coda_ctx *ctx, + struct vb2_buffer *src_buf) +{ + u32 src_size = vb2_get_plane_payload(src_buf, 0); + u32 n; + + n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), + src_size); + if (n < src_size) + return -ENOSPC; + + dma_sync_single_for_device(&ctx->dev->plat_dev->dev, + ctx->bitstream.paddr, ctx->bitstream.size, + DMA_TO_DEVICE); + + src_buf->v4l2_buf.sequence = ctx->qsequence++; + + return 0; +} + +static bool coda_bitstream_try_queue(struct coda_ctx *ctx, + struct vb2_buffer *src_buf) +{ + int ret; + + if (coda_get_bitstream_payload(ctx) + + vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size) + return false; + + if (vb2_plane_vaddr(src_buf, 0) == NULL) { + v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); + return true; + } + + ret = coda_bitstream_queue(ctx, src_buf); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); + return false; + } + /* Sync read pointer to device */ + if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) + coda_kfifo_sync_to_device_write(ctx); + + ctx->hold = false; + + return true; +} + +void coda_fill_bitstream(struct coda_ctx *ctx) +{ + struct vb2_buffer *src_buf; + struct coda_timestamp *ts; + + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + if (coda_bitstream_try_queue(ctx, src_buf)) { + /* + * Source buffer is queued in the bitstream ringbuffer; + * queue the timestamp and mark source buffer as done + */ + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + + ts = kmalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + ts->sequence = src_buf->v4l2_buf.sequence; + ts->timecode = src_buf->v4l2_buf.timecode; + ts->timestamp = src_buf->v4l2_buf.timestamp; + list_add_tail(&ts->list, &ctx->timestamp_list); + } + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } else { + break; + } + } +} + +void coda_bit_stream_end_flag(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + /* If this context is currently running, update the hardware flag */ + if ((dev->devtype->product == CODA_960) && + coda_isbusy(dev) && + (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } +} + +static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) +{ + struct coda_dev *dev = ctx->dev; + u32 *p = ctx->parabuf.vaddr; + + if (dev->devtype->product == CODA_DX6) + p[index] = value; + else + p[index ^ 1] = value; +} + +static void coda_free_framebuffers(struct coda_ctx *ctx) +{ + int i; + + for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) + coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]); +} + +static int coda_alloc_framebuffers(struct coda_ctx *ctx, + struct coda_q_data *q_data, u32 fourcc) +{ + struct coda_dev *dev = ctx->dev; + int width, height; + dma_addr_t paddr; + int ysize; + int ret; + int i; + + if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) { + width = round_up(q_data->width, 16); + height = round_up(q_data->height, 16); + } else { + width = round_up(q_data->width, 8); + height = q_data->height; + } + ysize = width * height; + + /* Allocate frame buffers */ + for (i = 0; i < ctx->num_internal_frames; i++) { + size_t size; + char *name; + + size = ysize + ysize / 2; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && + dev->devtype->product != CODA_DX6) + size += ysize / 4; + name = kasprintf(GFP_KERNEL, "fb%d", i); + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], + size, name); + kfree(name); + if (ret < 0) { + coda_free_framebuffers(ctx); + return ret; + } + } + + /* Register frame buffers in the parameter buffer */ + for (i = 0; i < ctx->num_internal_frames; i++) { + paddr = ctx->internal_frames[i].paddr; + /* Start addresses of Y, Cb, Cr planes */ + coda_parabuf_write(ctx, i * 3 + 0, paddr); + coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); + coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4); + + /* mvcol buffer for h.264 */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && + dev->devtype->product != CODA_DX6) + coda_parabuf_write(ctx, 96 + i, + ctx->internal_frames[i].paddr + + ysize + ysize/4 + ysize/4); + } + + /* mvcol buffer for mpeg4 */ + if ((dev->devtype->product != CODA_DX6) && + (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4)) + coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr + + ysize + ysize/4 + ysize/4); + + return 0; +} + +static void coda_free_context_buffers(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + coda_free_aux_buf(dev, &ctx->slicebuf); + coda_free_aux_buf(dev, &ctx->psbuf); + if (dev->devtype->product != CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); +} + +static int coda_alloc_context_buffers(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + struct coda_dev *dev = ctx->dev; + size_t size; + int ret; + + if (dev->devtype->product == CODA_DX6) + return 0; + + if (ctx->psbuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n"); + return -EBUSY; + } + if (ctx->slicebuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n"); + return -EBUSY; + } + if (ctx->workbuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n"); + ret = -EBUSY; + return -ENOMEM; + } + + if (q_data->fourcc == V4L2_PIX_FMT_H264) { + /* worst case slice size */ + size = (DIV_ROUND_UP(q_data->width, 16) * + DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; + ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, + "slicebuf"); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %d byte slice buffer", + ctx->slicebuf.size); + return ret; + } + } + + if (dev->devtype->product == CODA_7541) { + ret = coda_alloc_context_buf(ctx, &ctx->psbuf, + CODA7_PS_BUF_SIZE, "psbuf"); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate psmem buffer"); + goto err; + } + } + + size = dev->devtype->workbuf_size; + if (dev->devtype->product == CODA_960 && + q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf"); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %d byte context buffer", + ctx->workbuf.size); + goto err; + } + + return 0; + +err: + coda_free_context_buffers(ctx); + return ret; +} + +static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, + int header_code, u8 *header, int *size) +{ + struct coda_dev *dev = ctx->dev; + size_t bufsize; + int ret; + int i; + + if (dev->devtype->product == CODA_960) + memset(vb2_plane_vaddr(buf, 0), 0, 64); + + coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), + CODA_CMD_ENC_HEADER_BB_START); + bufsize = vb2_plane_size(buf, 0); + if (dev->devtype->product == CODA_960) + bufsize /= 1024; + coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); + coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); + ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); + return ret; + } + + if (dev->devtype->product == CODA_960) { + for (i = 63; i > 0; i--) + if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0) + break; + *size = i + 1; + } else { + *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - + coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + } + memcpy(header, vb2_plane_vaddr(buf, 0), *size); + + return 0; +} + +static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) +{ + phys_addr_t ret; + + size = round_up(size, 1024); + if (size > iram->remaining) + return 0; + iram->remaining -= size; + + ret = iram->next_paddr; + iram->next_paddr += size; + + return ret; +} + +static void coda_setup_iram(struct coda_ctx *ctx) +{ + struct coda_iram_info *iram_info = &ctx->iram_info; + struct coda_dev *dev = ctx->dev; + int w64, w128; + int mb_width; + int dbk_bits; + int bit_bits; + int ip_bits; + + memset(iram_info, 0, sizeof(*iram_info)); + iram_info->next_paddr = dev->iram.paddr; + iram_info->remaining = dev->iram.size; + + if (!dev->iram.vaddr) + return; + + switch (dev->devtype->product) { + case CODA_7541: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + break; + case CODA_960: + dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; + bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + break; + default: /* CODA_DX6 */ + return; + } + + if (ctx->inst_type == CODA_INST_ENCODER) { + struct coda_q_data *q_data_src; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + mb_width = DIV_ROUND_UP(q_data_src->width, 16); + w128 = mb_width * 128; + w64 = mb_width * 64; + + /* Prioritize in case IRAM is too small for everything */ + if (dev->devtype->product == CODA_7541) { + iram_info->search_ram_size = round_up(mb_width * 16 * + 36 + 2048, 1024); + iram_info->search_ram_paddr = coda_iram_alloc(iram_info, + iram_info->search_ram_size); + if (!iram_info->search_ram_paddr) { + pr_err("IRAM is smaller than the search ram size\n"); + goto out; + } + iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE | + CODA7_USE_ME_ENABLE; + } + + /* Only H.264BP and H.263P3 are considered */ + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64); + if (!iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP disabled for encoder */ + } else if (ctx->inst_type == CODA_INST_DECODER) { + struct coda_q_data *q_data_dst; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + mb_width = DIV_ROUND_UP(q_data_dst->width, 16); + w128 = mb_width * 128; + + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP unused as there is no VC1 support yet */ + } + +out: + if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "IRAM smaller than needed\n"); + + if (dev->devtype->product == CODA_7541) { + /* TODO - Enabling these causes picture errors on CODA7541 */ + if (ctx->inst_type == CODA_INST_DECODER) { + /* fw 1.4.50 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_IP_ENABLE); + } else { + /* fw 13.4.29 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_HOST_DBK_ENABLE | + CODA7_USE_IP_ENABLE | + CODA7_USE_DBK_ENABLE); + } + } +} + +static u32 coda_supported_firmwares[] = { + CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), + CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), +}; + +static bool coda_firmware_supported(u32 vernum) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) + if (vernum == coda_supported_firmwares[i]) + return true; + return false; +} + +int coda_check_firmware(struct coda_dev *dev) +{ + u16 product, major, minor, release; + u32 data; + int ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); + if (coda_wait_timeout(dev)) { + v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); + ret = -EIO; + goto err_run_cmd; + } + + if (dev->devtype->product == CODA_960) { + data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); + v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", + data); + } + + /* Check we are compatible with the loaded firmware */ + data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); + product = CODA_FIRMWARE_PRODUCT(data); + major = CODA_FIRMWARE_MAJOR(data); + minor = CODA_FIRMWARE_MINOR(data); + release = CODA_FIRMWARE_RELEASE(data); + + clk_disable_unprepare(dev->clk_per); + clk_disable_unprepare(dev->clk_ahb); + + if (product != dev->devtype->product) { + v4l2_err(&dev->v4l2_dev, + "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n", + coda_product_name(dev->devtype->product), + coda_product_name(product), major, minor, release); + return -EINVAL; + } + + v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", + coda_product_name(product)); + + if (coda_firmware_supported(data)) { + v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", + major, minor, release); + } else { + v4l2_warn(&dev->v4l2_dev, + "Unsupported firmware version: %u.%u.%u\n", + major, minor, release); + } + + return 0; + +err_run_cmd: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +/* + * Encoder context operations + */ + +static int coda_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct vb2_buffer *buf; + int gamma, ret, value; + u32 dst_fourcc; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + /* Allocate per-instance buffers */ + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); + bitstream_size = q_data_dst->sizeimage; + + if (!coda_is_initialized(dev)) { + v4l2_err(v4l2_dev, "coda is not initialized.\n"); + return -EFAULT; + } + + mutex_lock(&dev->coda_mutex); + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | + CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + case CODA_960: + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + /* fallthrough */ + case CODA_7541: + coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | + CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + } + + value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL); + value &= ~(1 << 2 | 0x7 << 9); + ctx->frame_mem_ctrl = value; + coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product == CODA_DX6) { + /* Configure the coda */ + coda_write(dev, dev->iram.paddr, + CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); + } + + /* Could set rotation here if needed */ + switch (dev->devtype->product) { + case CODA_DX6: + value = (q_data_src->width & CODADX6_PICWIDTH_MASK) + << CODADX6_PICWIDTH_OFFSET; + value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + break; + case CODA_7541: + if (dst_fourcc == V4L2_PIX_FMT_H264) { + value = (round_up(q_data_src->width, 16) & + CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; + value |= (round_up(q_data_src->height, 16) & + CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; + break; + } + /* fallthrough */ + case CODA_960: + value = (q_data_src->width & CODA7_PICWIDTH_MASK) + << CODA7_PICWIDTH_OFFSET; + value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_SEQ_SRC_F_RATE); + + ctx->params.codec_mode = ctx->codec->mode; + switch (dst_fourcc) { + case V4L2_PIX_FMT_MPEG4: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); + break; + case V4L2_PIX_FMT_H264: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + if (ctx->params.h264_deblk_enabled) { + value = ((ctx->params.h264_deblk_alpha & + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | + ((ctx->params.h264_deblk_beta & + CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET); + } else { + value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); + break; + default: + v4l2_err(v4l2_dev, + "dst format (0x%08x) invalid.\n", dst_fourcc); + ret = -EINVAL; + goto out; + } + + switch (ctx->params.slice_mode) { + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + value = 0; + break; + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: + value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) + << CODA_SLICING_SIZE_OFFSET; + value |= (1 & CODA_SLICING_UNIT_MASK) + << CODA_SLICING_UNIT_OFFSET; + value |= 1 & CODA_SLICING_MODE_MASK; + break; + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: + value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) + << CODA_SLICING_SIZE_OFFSET; + value |= (0 & CODA_SLICING_UNIT_MASK) + << CODA_SLICING_UNIT_OFFSET; + value |= 1 & CODA_SLICING_MODE_MASK; + break; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); + value = ctx->params.gop_size & CODA_GOP_SIZE_MASK; + coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); + + if (ctx->params.bitrate) { + /* Rate control enabled */ + value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) + << CODA_RATECONTROL_BITRATE_OFFSET; + value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + if (dev->devtype->product == CODA_960) + value |= BIT(31); /* disable autoskip */ + } else { + value = 0; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); + + coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_SEQ_INTRA_REFRESH); + + coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); + + + value = 0; + if (dev->devtype->product == CODA_960) + gamma = CODA9_DEFAULT_GAMMA; + else + gamma = CODA_DEFAULT_GAMMA; + if (gamma > 0) { + coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, + CODA_CMD_ENC_SEQ_RC_GAMMA); + } + + if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { + coda_write(dev, + ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | + ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, + CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); + } + if (dev->devtype->product == CODA_960) { + if (ctx->params.h264_max_qp) + value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; + if (CODA_DEFAULT_GAMMA > 0) + value |= 1 << CODA9_OPTION_GAMMA_OFFSET; + } else { + if (CODA_DEFAULT_GAMMA > 0) { + if (dev->devtype->product == CODA_DX6) + value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; + else + value |= 1 << CODA7_OPTION_GAMMA_OFFSET; + } + if (ctx->params.h264_min_qp) + value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; + if (ctx->params.h264_max_qp) + value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); + + coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); + + coda_setup_iram(ctx); + + if (dst_fourcc == V4L2_PIX_FMT_H264) { + switch (dev->devtype->product) { + case CODA_DX6: + value = FMO_SLICE_SAVE_BUF_SIZE << 7; + coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); + break; + case CODA_7541: + coda_write(dev, ctx->iram_info.search_ram_paddr, + CODA7_CMD_ENC_SEQ_SEARCH_BASE); + coda_write(dev, ctx->iram_info.search_ram_size, + CODA7_CMD_ENC_SEQ_SEARCH_SIZE); + break; + case CODA_960: + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + goto out; + } + + if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); + ret = -EFAULT; + goto out; + } + + if (dev->devtype->product == CODA_960) + ctx->num_internal_frames = 4; + else + ctx->num_internal_frames = 2; + ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); + goto out; + } + + coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, q_data_src->bytesperline, + CODA_CMD_SET_FRAME_BUF_STRIDE); + if (dev->devtype->product == CODA_7541) { + coda_write(dev, q_data_src->bytesperline, + CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); + } + if (dev->devtype->product != CODA_DX6) { + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + /* FIXME */ + coda_write(dev, ctx->internal_frames[2].paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_A); + coda_write(dev, ctx->internal_frames[3].paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_B); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + goto out; + } + + /* Save stream headers */ + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + /* + * Get SPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + /* + * Get PPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + /* + * 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: + /* + * Get VOS in the first frame and copy it to an + * intermediate buffer + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, + &ctx->vpu_header[2][0], + &ctx->vpu_header_size[2]); + if (ret < 0) + goto out; + break; + default: + /* No more formats need to save headers at the moment */ + break; + } + +out: + mutex_unlock(&dev->coda_mutex); + return ret; +} + +static int coda_prepare_encode(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + int force_ipicture; + int quant_param = 0; + u32 picture_y, picture_cb, picture_cr; + u32 pic_stream_buffer_addr, pic_stream_buffer_size; + u32 dst_fourcc; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + src_buf->v4l2_buf.sequence = ctx->osequence; + dst_buf->v4l2_buf.sequence = ctx->osequence; + ctx->osequence++; + + /* + * Workaround coda firmware BUG that only marks the first + * frame as IDR. This is a problem for some decoders that can't + * recover when a frame is lost. + */ + if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) { + src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } else { + src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + /* + * Copy headers at the beginning of the first frame for H.264 only. + * In MPEG4 they are already copied by the coda. + */ + if (src_buf->v4l2_buf.sequence == 0) { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]; + pic_stream_buffer_size = CODA_MAX_FRAME_SIZE - + ctx->vpu_header_size[0] - + ctx->vpu_header_size[1] - + ctx->vpu_header_size[2]; + memcpy(vb2_plane_vaddr(dst_buf, 0), + &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); + memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0], + &ctx->vpu_header[1][0], ctx->vpu_header_size[1]); + memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1], &ctx->vpu_header[2][0], + ctx->vpu_header_size[2]); + } else { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(dst_buf, 0); + pic_stream_buffer_size = CODA_MAX_FRAME_SIZE; + } + + if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { + force_ipicture = 1; + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_intra_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_intra_qp; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set intra qp, fmt not supported\n"); + break; + } + } else { + force_ipicture = 0; + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_inter_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_inter_qp; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set inter qp, fmt not supported\n"); + break; + } + } + + /* submit */ + coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, + CODA_CMD_ENC_PIC_ROT_MODE); + coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); + + + picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0); + switch (q_data_src->fourcc) { + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + picture_cr = picture_y + q_data_src->bytesperline * + q_data_src->height; + picture_cb = picture_cr + q_data_src->bytesperline / 2 * + q_data_src->height / 2; + break; + case V4L2_PIX_FMT_YUV420: + default: + picture_cb = picture_y + q_data_src->bytesperline * + q_data_src->height; + picture_cr = picture_cb + q_data_src->bytesperline / 2 * + q_data_src->height / 2; + break; + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); + coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); + + coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y); + coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB); + coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR); + } else { + coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); + coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); + coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR); + } + coda_write(dev, force_ipicture << 1 & 0x2, + CODA_CMD_ENC_PIC_OPTION); + + coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); + coda_write(dev, pic_stream_buffer_size / 1024, + CODA_CMD_ENC_PIC_BB_SIZE); + + if (!ctx->streamon_out) { + /* After streamoff on the output side, set stream end flag */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static void coda_finish_encode(struct coda_ctx *ctx) +{ + struct vb2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr, start_ptr; + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Get results from the coda */ + start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); + wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + + /* Calculate bytesused field */ + if (dst_buf->v4l2_buf.sequence == 0) { + vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]); + } else { + vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr); + } + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n", + wr_ptr - start_ptr); + + coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); + coda_read(dev, CODA_RET_ENC_PIC_FLAG); + + if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) { + dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; + } else { + dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } + + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->v4l2_buf.flags |= + src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + ctx->gopcounter--; + if (ctx->gopcounter < 0) + ctx->gopcounter = ctx->params.gop_size - 1; + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "job finished: encoding frame (%d) (%s)\n", + dst_buf->v4l2_buf.sequence, + (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? + "KEYFRAME" : "PFRAME"); +} + +static void coda_seq_end_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); + struct coda_dev *dev = ctx->dev; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, + __func__); + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_END failed\n"); + } + + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + coda_free_framebuffers(ctx); + coda_free_context_buffers(ctx); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static void coda_bit_release(struct coda_ctx *ctx) +{ + coda_free_framebuffers(ctx); + coda_free_context_buffers(ctx); +} + +const struct coda_context_ops coda_bit_encode_ops = { + .queue_init = coda_encoder_queue_init, + .start_streaming = coda_start_encoding, + .prepare_run = coda_prepare_encode, + .finish_run = coda_finish_encode, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +/* + * Decoder context operations + */ + +static int __coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct coda_dev *dev = ctx->dev; + int width, height; + u32 src_fourcc; + u32 val; + int ret; + + /* Start decoding */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + bitstream_buf = ctx->bitstream.paddr; + bitstream_size = ctx->bitstream.size; + src_fourcc = q_data_src->fourcc; + + /* Allocate per-instance buffers */ + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + + /* Update coda bitstream read and write pointers from kfifo */ + coda_kfifo_sync_to_device_full(ctx); + + ctx->display_idx = -1; + ctx->frm_dis_flg = 0; + coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE, + CODA_REG_BIT_BIT_STREAM_PARAM); + + coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); + val = 0; + if ((dev->devtype->product == CODA_7541) || + (dev->devtype->product == CODA_960)) + val |= CODA_REORDER_ENABLE; + coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); + + ctx->params.codec_mode = ctx->codec->mode; + if (dev->devtype->product == CODA_960 && + src_fourcc == V4L2_PIX_FMT_MPEG4) + ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; + else + ctx->params.codec_mode_aux = 0; + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (dev->devtype->product == CODA_7541) { + coda_write(dev, ctx->psbuf.paddr, + CODA_CMD_DEC_SEQ_PS_BB_START); + coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), + CODA_CMD_DEC_SEQ_PS_BB_SIZE); + } + if (dev->devtype->product == CODA_960) { + coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); + coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); + } + } + if (dev->devtype->product != CODA_960) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); + + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + return -ETIMEDOUT; + } + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + + if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_INIT failed, error code = %d\n", + coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); + return -EAGAIN; + } + + val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); + if (dev->devtype->product == CODA_DX6) { + width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; + height = val & CODADX6_PICHEIGHT_MASK; + } else { + width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; + height = val & CODA7_PICHEIGHT_MASK; + } + + if (width > q_data_dst->width || height > q_data_dst->height) { + v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", + width, height, q_data_dst->width, q_data_dst->height); + return -EINVAL; + } + + width = round_up(width, 16); + height = round_up(height, 16); + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n", + __func__, ctx->idx, width, height); + + ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); + if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { + v4l2_err(&dev->v4l2_dev, + "not enough framebuffers to decode (%d < %d)\n", + CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); + return -EINVAL; + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); + + q_data_dst->rect.left = (left_right >> 10) & 0x3ff; + q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; + q_data_dst->rect.width = width - q_data_dst->rect.left - + (left_right & 0x3ff); + q_data_dst->rect.height = height - q_data_dst->rect.top - + (top_bottom & 0x3ff); + } + + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); + return ret; + } + + /* Tell the decoder how many frame buffers we allocated. */ + coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product != CODA_DX6) { + /* Set secondary AXI IRAM */ + coda_setup_iram(ctx); + + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + + coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE); + coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET | + 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET, + CODA9_CMD_SET_FRAME_CACHE_CONFIG); + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + coda_write(dev, ctx->slicebuf.paddr, + CODA_CMD_SET_FRAME_SLICE_BB_START); + coda_write(dev, ctx->slicebuf.size / 1024, + CODA_CMD_SET_FRAME_SLICE_BB_SIZE); + } + + if (dev->devtype->product == CODA_7541) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); + } else if (dev->devtype->product == CODA_960) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); + } + + if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { + v4l2_err(&ctx->dev->v4l2_dev, + "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&dev->coda_mutex); + ret = __coda_start_decoding(ctx); + mutex_unlock(&dev->coda_mutex); + + return ret; +} + +static int coda_prepare_decode(struct coda_ctx *ctx) +{ + struct vb2_buffer *dst_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_dst; + u32 stridey, height; + u32 picture_y, picture_cb, picture_cr; + + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + if (ctx->params.rot_mode & CODA_ROT_90) { + stridey = q_data_dst->height; + height = q_data_dst->width; + } else { + stridey = q_data_dst->width; + height = q_data_dst->height; + } + + /* Try to copy source buffer contents into the bitstream ringbuffer */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx); + mutex_unlock(&ctx->bitstream_mutex); + + if (coda_get_bitstream_payload(ctx) < 512 && + (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "bitstream payload: %d, skipping\n", + coda_get_bitstream_payload(ctx)); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return -EAGAIN; + } + + /* Run coda_start_decoding (again) if not yet initialized */ + if (!ctx->initialized) { + int ret = __coda_start_decoding(ctx); + + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return -EAGAIN; + } else { + ctx->initialized = 1; + } + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + /* Set rotator output */ + picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) { + /* Switch Cr and Cb for YVU420 format */ + picture_cr = picture_y + stridey * height; + picture_cb = picture_cr + stridey / 2 * height / 2; + } else { + picture_cb = picture_y + stridey * height; + picture_cr = picture_cb + stridey / 2 * height / 2; + } + + if (dev->devtype->product == CODA_960) { + /* + * The CODA960 seems to have an internal list of buffers with + * 64 entries that includes the registered frame buffers as + * well as the rotator buffer output. + * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames. + */ + coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index, + CODA9_CMD_DEC_PIC_ROT_INDEX); + coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y); + coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB); + coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR); + coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE); + } else { + coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y); + coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB); + coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR); + coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE); + } + coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, + CODA_CMD_DEC_PIC_ROT_MODE); + + switch (dev->devtype->product) { + case CODA_DX6: + /* TBD */ + case CODA_7541: + coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); + break; + case CODA_960: + /* 'hardcode to use interrupt disable mode'? */ + coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); + break; + } + + coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); + + coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); + coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + coda_kfifo_sync_to_device_full(ctx); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static void coda_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct coda_q_data *q_data_dst; + struct vb2_buffer *dst_buf; + struct coda_timestamp *ts; + int width, height; + int decoded_idx; + int display_idx; + u32 src_fourcc; + int success; + u32 err_mb; + u32 val; + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * in stream-end mode, the read pointer can overshoot the write pointer + * by up to 512 bytes + */ + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { + if (coda_get_bitstream_payload(ctx) >= CODA_MAX_FRAME_SIZE - 512) + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_fourcc = q_data_src->fourcc; + + val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); + if (val != 1) + pr_err("DEC_PIC_SUCCESS = %d\n", val); + + success = val & 0x1; + if (!success) + v4l2_err(&dev->v4l2_dev, "decode failed\n"); + + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (val & (1 << 3)) + v4l2_err(&dev->v4l2_dev, + "insufficient PS buffer space (%d bytes)\n", + ctx->psbuf.size); + if (val & (1 << 2)) + v4l2_err(&dev->v4l2_dev, + "insufficient slice buffer space (%d bytes)\n", + ctx->slicebuf.size); + } + + val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); + width = (val >> 16) & 0xffff; + height = val & 0xffff; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* frame crop information */ + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); + + if (left_right == 0xffffffff && top_bottom == 0xffffffff) { + /* Keep current crop information */ + } else { + struct v4l2_rect *rect = &q_data_dst->rect; + + rect->left = left_right >> 16 & 0xffff; + rect->top = top_bottom >> 16 & 0xffff; + rect->width = width - rect->left - + (left_right & 0xffff); + rect->height = height - rect->top - + (top_bottom & 0xffff); + } + } else { + /* no cropping */ + } + + err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); + if (err_mb > 0) + v4l2_err(&dev->v4l2_dev, + "errors in %d macroblocks\n", err_mb); + + if (dev->devtype->product == CODA_7541) { + val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); + if (val == 0) { + /* not enough bitstream data */ + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "prescan failed: %d\n", val); + ctx->hold = true; + return; + } + } + + ctx->frm_dis_flg = coda_read(dev, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + /* + * The previous display frame was copied out by the rotator, + * now it can be overwritten again + */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + ctx->frm_dis_flg &= ~(1 << ctx->display_idx); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + } + + /* + * The index of the last decoded frame, not necessarily in + * display order, and the index of the next display frame. + * The latter could have been decoded in a previous run. + */ + decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); + display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); + + if (decoded_idx == -1) { + /* no frame was decoded, but we might have a display frame */ + if (display_idx >= 0 && display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + else if (ctx->display_idx < 0) + ctx->hold = true; + } else if (decoded_idx == -2) { + /* no frame was decoded, we still return remaining buffers */ + } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "decoded frame index out of range: %d\n", decoded_idx); + } else { + val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; + val -= ctx->sequence_offset; + mutex_lock(&ctx->bitstream_mutex); + if (!list_empty(&ctx->timestamp_list)) { + ts = list_first_entry(&ctx->timestamp_list, + struct coda_timestamp, list); + list_del(&ts->list); + if (val != (ts->sequence & 0xffff)) { + v4l2_err(&dev->v4l2_dev, + "sequence number mismatch (%d(%d) != %d)\n", + val, ctx->sequence_offset, + ts->sequence); + } + ctx->frame_timestamps[decoded_idx] = *ts; + kfree(ts); + } else { + v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); + memset(&ctx->frame_timestamps[decoded_idx], 0, + sizeof(struct coda_timestamp)); + ctx->frame_timestamps[decoded_idx].sequence = val; + } + mutex_unlock(&ctx->bitstream_mutex); + + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; + if (val == 0) + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; + else if (val == 1) + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME; + else + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME; + + ctx->frame_errors[decoded_idx] = err_mb; + } + + if (display_idx == -1) { + /* + * no more frames to be decoded, but there could still + * be rotator output to dequeue + */ + ctx->hold = true; + } else if (display_idx == -3) { + /* possibly prescan failure */ + } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "presentation frame index out of range: %d\n", + display_idx); + } + + /* If a frame was copied out, return it */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->v4l2_buf.sequence = ctx->osequence++; + + dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx]; + ts = &ctx->frame_timestamps[ctx->display_idx]; + dst_buf->v4l2_buf.timecode = ts->timecode; + dst_buf->v4l2_buf.timestamp = ts->timestamp; + + vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2); + + v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "job finished: decoding frame (%d) (%s)\n", + dst_buf->v4l2_buf.sequence, + (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? + "KEYFRAME" : "PFRAME"); + } else { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "job finished: no frame decoded\n"); + } + + /* The rotator will copy the current display frame next time */ + ctx->display_idx = display_idx; +} + +const struct coda_context_ops coda_bit_decode_ops = { + .queue_init = coda_decoder_queue_init, + .start_streaming = coda_start_decoding, + .prepare_run = coda_prepare_decode, + .finish_run = coda_finish_decode, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +irqreturn_t coda_irq_handler(int irq, void *data) +{ + struct coda_dev *dev = data; + struct coda_ctx *ctx; + + /* read status register to attend the IRQ */ + coda_read(dev, CODA_REG_BIT_INT_STATUS); + coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, + CODA_REG_BIT_INT_CLEAR); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (ctx == NULL) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + mutex_unlock(&dev->coda_mutex); + return IRQ_HANDLED; + } + + if (ctx->aborting) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "task has been aborted\n"); + } + + if (coda_isbusy(ctx->dev)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "coda is still busy!!!!\n"); + return IRQ_NONE; + } + + complete(&ctx->completion); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c new file mode 100644 index 0000000..ced4760 --- /dev/null +++ b/drivers/media/platform/coda/coda-common.c @@ -0,0 +1,2052 @@ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, <javier.martin@vista-silicon.com> + * Xavier Duret + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/genalloc.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kfifo.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/of.h> +#include <linux/platform_data/coda.h> +#include <linux/reset.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "coda.h" + +#define CODA_NAME "coda" + +#define CODADX6_MAX_INSTANCES 4 + +#define CODA_PARA_BUF_SIZE (10 * 1024) +#define CODA_ISRAM_SIZE (2048 * 2) + +#define MIN_W 176 +#define MIN_H 144 + +#define S_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN 1 /* multiple of 2 */ +#define H_ALIGN 1 /* multiple of 2 */ + +#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) + +int coda_debug; +module_param(coda_debug, int, 0644); +MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); + +struct coda_fmt { + char *name; + u32 fourcc; +}; + +void coda_write(struct coda_dev *dev, u32 data, u32 reg) +{ + v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + writel(data, dev->regs_base + reg); +} + +unsigned int coda_read(struct coda_dev *dev, u32 reg) +{ + u32 data; + + data = readl(dev->regs_base + reg); + v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + return data; +} + +/* + * Array of all formats supported by any version of Coda: + */ +static const struct coda_fmt coda_formats[] = { + { + .name = "YUV 4:2:0 Planar, YCbCr", + .fourcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:0 Planar, YCrCb", + .fourcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "H264 Encoded Stream", + .fourcc = V4L2_PIX_FMT_H264, + }, + { + .name = "MPEG4 Encoded Stream", + .fourcc = V4L2_PIX_FMT_MPEG4, + }, +}; + +#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ + { mode, src_fourcc, dst_fourcc, max_w, max_h } + +/* + * Arrays of codecs supported by each given version of Coda: + * i.MX27 -> codadx6 + * i.MX5x -> coda7 + * i.MX6 -> coda960 + * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants + */ +static const struct coda_codec codadx6_codecs[] = { + CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), +}; + +static const struct coda_codec coda7_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), +}; + +static const struct coda_codec coda9_codecs[] = { + CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), + CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), +}; + +static bool coda_format_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + return true; + default: + return false; + } +} + +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) +{ + return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc; +} + +static const struct coda_codec *coda_find_codec(struct coda_dev *dev, + int src_fourcc, int dst_fourcc) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + int k; + + src_fourcc = coda_format_normalize_yuv(src_fourcc); + dst_fourcc = coda_format_normalize_yuv(dst_fourcc); + if (src_fourcc == dst_fourcc) + return NULL; + + for (k = 0; k < num_codecs; k++) { + if (codecs[k].src_fourcc == src_fourcc && + codecs[k].dst_fourcc == dst_fourcc) + break; + } + + if (k == num_codecs) + return NULL; + + return &codecs[k]; +} + +static void coda_get_max_dimensions(struct coda_dev *dev, + const struct coda_codec *codec, + int *max_w, int *max_h) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + unsigned int w, h; + int k; + + if (codec) { + w = codec->max_w; + h = codec->max_h; + } else { + for (k = 0, w = 0, h = 0; k < num_codecs; k++) { + w = max(w, codecs[k].max_w); + h = max(h, codecs[k].max_h); + } + } + + if (max_w) + *max_w = w; + if (max_h) + *max_h = h; +} + +const char *coda_product_name(int product) +{ + static char buf[9]; + + switch (product) { + case CODA_DX6: + return "CodaDx6"; + case CODA_7541: + return "CODA7541"; + case CODA_960: + return "CODA960"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", product); + return buf; + } +} + +/* + * V4L2 ioctl() operations. + */ +static int coda_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver)); + strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product), + sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:" CODA_NAME, 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 coda_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_codec *codecs = ctx->dev->devtype->codecs; + const struct coda_fmt *formats = coda_formats; + const struct coda_fmt *fmt; + int num_codecs = ctx->dev->devtype->num_codecs; + int num_formats = ARRAY_SIZE(coda_formats); + int i, k, num = 0; + bool yuv; + + if (ctx->inst_type == CODA_INST_ENCODER) + yuv = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); + else + yuv = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE); + + for (i = 0; i < num_formats; i++) { + /* Skip either raw or compressed formats */ + if (yuv != coda_format_is_yuv(formats[i].fourcc)) + continue; + /* All uncompressed formats are always supported */ + if (yuv) { + if (num == f->index) + break; + ++num; + continue; + } + /* Compressed formats may be supported, check the codec list */ + for (k = 0; k < num_codecs; k++) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[i].fourcc == codecs[k].dst_fourcc) + break; + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + formats[i].fourcc == codecs[k].src_fourcc) + break; + } + if (k < num_codecs) { + if (num == f->index) + break; + ++num; + } + } + + if (i < num_formats) { + fmt = &formats[i]; + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + if (!yuv) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + return 0; + } + + /* Format not found */ + return -EINVAL; +} + +static int coda_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_q_data *q_data; + struct coda_ctx *ctx = fh_to_ctx(priv); + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fourcc; + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.bytesperline = q_data->bytesperline; + + f->fmt.pix.sizeimage = q_data->sizeimage; + f->fmt.pix.colorspace = ctx->colorspace; + + return 0; +} + +static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, + struct v4l2_format *f) +{ + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data; + unsigned int max_w, max_h; + enum v4l2_field field; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + coda_get_max_dimensions(dev, codec, &max_w, &max_h); + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, + &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, + S_ALIGN); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_JPEG: + break; + default: + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + f->fmt.pix.pixelformat = q_data->fourcc; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + /* Frame stride must be multiple of 8, but 16 for h.264 */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_JPEG: + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE; + break; + default: + BUG(); + } + + return 0; +} + +static int coda_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_codec *codec = NULL; + struct vb2_queue *src_vq; + int ret; + + /* + * If the source format is already fixed, try to find a codec that + * converts to the given destination format + */ + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (vb2_is_streaming(src_vq)) { + struct coda_q_data *q_data_src; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) + return -EINVAL; + + f->fmt.pix.width = q_data_src->width; + f->fmt.pix.height = q_data_src->height; + } else { + /* Otherwise determine codec by encoded format, if possible */ + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + f->fmt.pix.pixelformat); + } + + f->fmt.pix.colorspace = ctx->colorspace; + + ret = coda_try_fmt(ctx, codec, f); + if (ret < 0) + return ret; + + /* The h.264 decoder only returns complete 16x16 macroblocks */ + if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) { + f->fmt.pix.width = f->fmt.pix.width; + f->fmt.pix.height = round_up(f->fmt.pix.height, 16); + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + } + + return 0; +} + +static int coda_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_codec *codec = NULL; + + /* Determine codec by encoded format, returns NULL if raw or invalid */ + if (ctx->inst_type == CODA_INST_DECODER) { + codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, + V4L2_PIX_FMT_YUV420); + if (!codec) + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_H264, + V4L2_PIX_FMT_YUV420); + if (!codec) + return -EINVAL; + } + + if (!f->fmt.pix.colorspace) + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + + return coda_try_fmt(ctx, codec, f); +} + +static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) +{ + struct coda_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + q_data->fourcc = f->fmt.pix.pixelformat; + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->bytesperline = f->fmt.pix.bytesperline; + q_data->sizeimage = f->fmt.pix.sizeimage; + q_data->rect.left = 0; + q_data->rect.top = 0; + q_data->rect.width = f->fmt.pix.width; + q_data->rect.height = f->fmt.pix.height; + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "Setting format for type %d, wxh: %dx%d, fmt: %d\n", + f->type, q_data->width, q_data->height, q_data->fourcc); + + return 0; +} + +static int coda_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = coda_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return coda_s_fmt(ctx, f); +} + +static int coda_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + struct v4l2_format f_cap; + int ret; + + ret = coda_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = coda_s_fmt(ctx, f); + if (ret) + return ret; + + ctx->colorspace = f->fmt.pix.colorspace; + + f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + coda_g_fmt(file, priv, &f_cap); + f_cap.fmt.pix.width = f->fmt.pix.width; + f_cap.fmt.pix.height = f->fmt.pix.height; + + ret = coda_try_fmt_vid_cap(file, priv, &f_cap); + if (ret) + return ret; + + return coda_s_fmt(ctx, &f_cap); +} + +static int coda_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); +} + +static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, + struct v4l2_buffer *buf) +{ + struct vb2_queue *src_vq; + + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) && + (buf->sequence == (ctx->qsequence - 1))); +} + +static int coda_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); + + /* If this is the last capture buffer, emit an end-of-stream event */ + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + coda_buf_is_end_of_stream(ctx, buf)) { + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + + return ret; +} + +static int coda_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + struct v4l2_rect r, *rsel; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + r.left = 0; + r.top = 0; + r.width = q_data->width; + r.height = q_data->height; + rsel = &q_data->rect; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rsel = &r; + /* fallthrough */ + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + rsel = &r; + /* fallthrough */ + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + + s->r = *rsel; + + return 0; +} + +static int coda_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) + return -EINVAL; + + if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) + return -EINVAL; + + return 0; +} + +static int coda_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + int ret; + + ret = coda_try_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + /* Ignore decoder stop command silently in encoder context */ + if (ctx->inst_type != CODA_INST_DECODER) + return 0; + + /* Set the stream-end flag on this context */ + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); + + return 0; +} + +static int coda_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops coda_ioctl_ops = { + .vidioc_querycap = coda_querycap, + + .vidioc_enum_fmt_vid_cap = coda_enum_fmt, + .vidioc_g_fmt_vid_cap = coda_g_fmt, + .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = coda_enum_fmt, + .vidioc_g_fmt_vid_out = coda_g_fmt, + .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + + .vidioc_qbuf = coda_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = coda_dqbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = coda_g_selection, + + .vidioc_try_decoder_cmd = coda_try_decoder_cmd, + .vidioc_decoder_cmd = coda_decoder_cmd, + + .vidioc_subscribe_event = coda_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct gdi_tiled_map *tiled_map = &ctx->tiled_map; + struct coda_dev *dev = ctx->dev; + int i; + + for (i = 0; i < 16; i++) + coda_write(dev, tiled_map->xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, tiled_map->xy2ba_map[i], + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, tiled_map->xy2ra_map[i], + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + for (i = 0; i < 32; i++) + coda_write(dev, tiled_map->rbc2axi_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); +} + +/* + * Mem-to-mem operations. + */ + +static void coda_device_run(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *dev = ctx->dev; + + queue_work(dev->workqueue, &ctx->pic_run_work); +} + +static void coda_pic_run_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + ret = ctx->ops->prepare_run(ctx); + if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) { + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + /* job_finish scheduled by prepare_decode */ + return; + } + + if (!wait_for_completion_timeout(&ctx->completion, + msecs_to_jiffies(1000))) { + dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); + + ctx->hold = true; + + coda_hw_reset(ctx); + } else if (!ctx->aborting) { + ctx->ops->finish_run(ctx); + } + + if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) + queue_work(dev->workqueue, &ctx->seq_end_work); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static int coda_job_ready(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + + /* + * For both 'P' and 'key' frame cases 1 picture + * and 1 frame are needed. In the decoder case, + * the compressed frame can be in the bitstream. + */ + if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && + ctx->inst_type != CODA_INST_DECODER) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "not ready: not enough video buffers.\n"); + return 0; + } + + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "not ready: not enough video capture buffers.\n"); + return 0; + } + + if (ctx->hold || + ((ctx->inst_type == CODA_INST_DECODER) && + (coda_get_bitstream_payload(ctx) < 512) && + !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "%d: not ready: not enough bitstream data.\n", + ctx->idx); + return 0; + } + + if (ctx->aborting) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "not ready: aborting\n"); + return 0; + } + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "job ready\n"); + return 1; +} + +static void coda_job_abort(void *priv) +{ + struct coda_ctx *ctx = priv; + + ctx->aborting = 1; + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "Aborting task\n"); +} + +static void coda_lock(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *pcdev = ctx->dev; + + mutex_lock(&pcdev->dev_mutex); +} + +static void coda_unlock(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *pcdev = ctx->dev; + + mutex_unlock(&pcdev->dev_mutex); +} + +static const struct v4l2_m2m_ops coda_m2m_ops = { + .device_run = coda_device_run, + .job_ready = coda_job_ready, + .job_abort = coda_job_abort, + .lock = coda_lock, + .unlock = coda_unlock, +}; + +static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) +{ + struct gdi_tiled_map *tiled_map = &ctx->tiled_map; + int luma_map, chro_map, i; + + memset(tiled_map, 0, sizeof(*tiled_map)); + + luma_map = 64; + chro_map = 64; + tiled_map->map_type = tiled_map_type; + for (i = 0; i < 16; i++) + tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map; + for (i = 0; i < 4; i++) + tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map; + for (i = 0; i < 16; i++) + tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map; + + if (tiled_map_type == GDI_LINEAR_FRAME_MAP) { + tiled_map->xy2rbc_config = 0; + } else { + dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n", + tiled_map_type); + return; + } +} + +static void set_default_params(struct coda_ctx *ctx) +{ + u32 src_fourcc, dst_fourcc; + int max_w; + int max_h; + + if (ctx->inst_type == CODA_INST_ENCODER) { + src_fourcc = V4L2_PIX_FMT_YUV420; + dst_fourcc = V4L2_PIX_FMT_H264; + } else { + src_fourcc = V4L2_PIX_FMT_H264; + dst_fourcc = V4L2_PIX_FMT_YUV420; + } + ctx->codec = coda_find_codec(ctx->dev, src_fourcc, dst_fourcc); + max_w = ctx->codec->max_w; + max_h = ctx->codec->max_h; + + ctx->params.codec_mode = ctx->codec->mode; + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->params.framerate = 30; + ctx->aborting = 0; + + /* Default formats for output and input queues */ + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; + ctx->q_data[V4L2_M2M_SRC].width = max_w; + ctx->q_data[V4L2_M2M_SRC].height = max_h; + ctx->q_data[V4L2_M2M_DST].width = max_w; + ctx->q_data[V4L2_M2M_DST].height = max_h; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { + ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; + ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2; + ctx->q_data[V4L2_M2M_DST].bytesperline = 0; + ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; + } else { + ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; + ctx->q_data[V4L2_M2M_SRC].sizeimage = CODA_MAX_FRAME_SIZE; + ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; + ctx->q_data[V4L2_M2M_DST].sizeimage = (max_w * max_h * 3) / 2; + } + ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; + ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; + ctx->q_data[V4L2_M2M_DST].rect.width = max_w; + ctx->q_data[V4L2_M2M_DST].rect.height = max_h; + + if (ctx->dev->devtype->product == CODA_960) + coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); +} + +/* + * Queue operations + */ +static int coda_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(vq); + struct coda_q_data *q_data; + unsigned int size; + + q_data = get_q_data(ctx, vq->type); + size = q_data->sizeimage; + + *nplanes = 1; + sizes[0] = size; + + alloc_ctxs[0] = ctx->dev->alloc_ctx; + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "get %d buffer(s) of size %d each.\n", *nbuffers, size); + + return 0; +} + +static int coda_buf_prepare(struct vb2_buffer *vb) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + v4l2_warn(&ctx->dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + return 0; +} + +static void coda_buf_queue(struct vb2_buffer *vb) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + /* + * In the decoder case, immediately try to copy the buffer into the + * bitstream ringbuffer and mark it as ready to be dequeued. + */ + if (q_data->fourcc == V4L2_PIX_FMT_H264 && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* + * For backwards compatibility, queuing an empty buffer marks + * the stream end + */ + if (vb2_get_plane_payload(vb, 0) == 0) + coda_bit_stream_end_flag(ctx); + mutex_lock(&ctx->bitstream_mutex); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + if (vb2_is_streaming(vb->vb2_queue)) + coda_fill_bitstream(ctx); + mutex_unlock(&ctx->bitstream_mutex); + } else { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + } +} + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent) +{ + buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr, + GFP_KERNEL); + if (!buf->vaddr) { + v4l2_err(&dev->v4l2_dev, + "Failed to allocate %s buffer of size %u\n", + name, size); + return -ENOMEM; + } + + buf->size = size; + + if (name && parent) { + buf->blob.data = buf->vaddr; + buf->blob.size = size; + buf->dentry = debugfs_create_blob(name, 0644, parent, + &buf->blob); + if (!buf->dentry) + dev_warn(&dev->plat_dev->dev, + "failed to create debugfs entry %s\n", name); + } + + return 0; +} + +void coda_free_aux_buf(struct coda_dev *dev, + struct coda_aux_buf *buf) +{ + if (buf->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buf->size, + buf->vaddr, buf->paddr); + buf->vaddr = NULL; + buf->size = 0; + } + debugfs_remove(buf->dentry); +} + +static int coda_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_buffer *buf; + u32 dst_fourcc; + int ret = 0; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (q_data_src->fourcc == V4L2_PIX_FMT_H264) { + /* copy the buffers that where queued before streamon */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx); + mutex_unlock(&ctx->bitstream_mutex); + + if (coda_get_bitstream_payload(ctx) < 512) { + ret = -EINVAL; + goto err; + } + } else { + if (count < 1) { + ret = -EINVAL; + goto err; + } + } + + ctx->streamon_out = 1; + } else { + if (count < 1) { + ret = -EINVAL; + goto err; + } + + ctx->streamon_cap = 1; + } + + /* Don't start the coda unless both queues are on */ + if (!(ctx->streamon_out & ctx->streamon_cap)) + return 0; + + /* Allow decoder device_run with no new buffers queued */ + if (ctx->inst_type == CODA_INST_DECODER) + v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); + + ctx->gopcounter = ctx->params.gop_size - 1; + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + q_data_dst->fourcc); + if (!ctx->codec) { + v4l2_err(v4l2_dev, "couldn't tell instance type.\n"); + ret = -EINVAL; + goto err; + } + + ret = ctx->ops->start_streaming(ctx); + if (ctx->inst_type == CODA_INST_DECODER) { + if (ret == -EAGAIN) + return 0; + else if (ret < 0) + goto err; + } + + ctx->initialized = 1; + return ret; + +err: + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED); + } + return ret; +} + +static void coda_stop_streaming(struct vb2_queue *q) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct coda_dev *dev = ctx->dev; + struct vb2_buffer *buf; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "%s: output\n", __func__); + ctx->streamon_out = 0; + + coda_bit_stream_end_flag(ctx); + + ctx->isequence = 0; + + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } else { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "%s: capture\n", __func__); + ctx->streamon_cap = 0; + + ctx->osequence = 0; + ctx->sequence_offset = 0; + + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + if (!ctx->streamon_out && !ctx->streamon_cap) { + struct coda_timestamp *ts; + + mutex_lock(&ctx->bitstream_mutex); + while (!list_empty(&ctx->timestamp_list)) { + ts = list_first_entry(&ctx->timestamp_list, + struct coda_timestamp, list); + list_del(&ts->list); + kfree(ts); + } + mutex_unlock(&ctx->bitstream_mutex); + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + ctx->runcounter = 0; + } +} + +static const struct vb2_ops coda_qops = { + .queue_setup = coda_queue_setup, + .buf_prepare = coda_buf_prepare, + .buf_queue = coda_buf_queue, + .start_streaming = coda_start_streaming, + .stop_streaming = coda_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int coda_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct coda_ctx *ctx = + container_of(ctrl->handler, struct coda_ctx, ctrls); + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_HOR; + else + ctx->params.rot_mode &= ~CODA_MIR_HOR; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_VER; + else + ctx->params.rot_mode &= ~CODA_MIR_VER; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctx->params.bitrate = ctrl->val / 1000; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctx->params.gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + ctx->params.h264_intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + ctx->params.h264_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->params.h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->params.h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctx->params.h264_deblk_alpha = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctx->params.h264_deblk_beta = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctx->params.h264_deblk_enabled = (ctrl->val == + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: + ctx->params.mpeg4_intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: + ctx->params.mpeg4_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + ctx->params.slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + ctx->params.slice_max_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + ctx->params.slice_max_bits = ctrl->val * 8; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + ctx->params.intra_refresh = ctrl->val; + break; + default: + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops coda_ctrl_ops = { + .s_ctrl = coda_s_ctrl, +}; + +static int coda_ctrls_setup(struct coda_ctx *ctx) +{ + v4l2_ctrl_handler_init(&ctx->ctrls, 9); + + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); + if (ctx->dev->devtype->product != CODA_960) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, + 500); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, + 1920 * 1088 / 256, 1, 0); + + if (ctx->ctrls.error) { + v4l2_err(&ctx->dev->v4l2_dev, + "control initialization error (%d)", + ctx->ctrls.error); + return -EINVAL; + } + + return v4l2_ctrl_handler_setup(&ctx->ctrls); +} + +static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) +{ + vq->drv_priv = ctx; + vq->ops = &coda_qops; + vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + vq->lock = &ctx->dev->dev_mutex; + + return vb2_queue_init(vq); +} + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +static int coda_next_free_instance(struct coda_dev *dev) +{ + int idx = ffz(dev->instance_mask); + + if ((idx < 0) || + (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES)) + return -EBUSY; + + return idx; +} + +static int coda_open(struct file *file, enum coda_inst_type inst_type, + const struct coda_context_ops *ctx_ops) +{ + struct coda_dev *dev = video_drvdata(file); + struct coda_ctx *ctx = NULL; + char *name; + int ret; + int idx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + idx = coda_next_free_instance(dev); + if (idx < 0) { + ret = idx; + goto err_coda_max; + } + set_bit(idx, &dev->instance_mask); + + name = kasprintf(GFP_KERNEL, "context%d", idx); + ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); + kfree(name); + + ctx->inst_type = inst_type; + ctx->ops = ctx_ops; + init_completion(&ctx->completion); + INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); + INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->dev = dev; + ctx->idx = idx; + switch (dev->devtype->product) { + case CODA_7541: + case CODA_960: + ctx->reg_idx = 0; + break; + default: + ctx->reg_idx = idx; + } + + /* Power up and upload firmware if necessary */ + ret = pm_runtime_get_sync(&dev->plat_dev->dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); + goto err_pm_get; + } + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + set_default_params(ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + ctx->ops->queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", + __func__, ret); + goto err_ctx_init; + } + + ret = coda_ctrls_setup(ctx); + if (ret) { + v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); + goto err_ctrls_setup; + } + + ctx->fh.ctrl_handler = &ctx->ctrls; + + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE, + "parabuf"); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); + goto err_dma_alloc; + } + + ctx->bitstream.size = CODA_MAX_FRAME_SIZE; + ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev, + ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate bitstream ringbuffer"); + ret = -ENOMEM; + goto err_dma_writecombine; + } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + mutex_init(&ctx->bitstream_mutex); + mutex_init(&ctx->buffer_mutex); + INIT_LIST_HEAD(&ctx->timestamp_list); + + coda_lock(ctx); + list_add(&ctx->list, &dev->instances); + coda_unlock(ctx); + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", + ctx->idx, ctx); + + return 0; + +err_dma_writecombine: + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + coda_free_aux_buf(dev, &ctx->parabuf); +err_dma_alloc: + v4l2_ctrl_handler_free(&ctx->ctrls); +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +err_ctx_init: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + pm_runtime_put_sync(&dev->plat_dev->dev); +err_pm_get: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + clear_bit(ctx->idx, &dev->instance_mask); +err_coda_max: + kfree(ctx); + return ret; +} + +static int coda_encoder_open(struct file *file) +{ + return coda_open(file, CODA_INST_ENCODER, &coda_bit_encode_ops); +} + +static int coda_decoder_open(struct file *file) +{ + return coda_open(file, CODA_INST_DECODER, &coda_bit_decode_ops); +} + +static int coda_release(struct file *file) +{ + struct coda_dev *dev = video_drvdata(file); + struct coda_ctx *ctx = fh_to_ctx(file->private_data); + + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", + ctx); + + debugfs_remove_recursive(ctx->debugfs_entry); + + /* If this instance is running, call .job_abort and wait for it to end */ + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + /* In case the instance was not running, we still need to call SEQ_END */ + if (ctx->initialized) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } + + coda_lock(ctx); + list_del(&ctx->list); + coda_unlock(ctx); + + dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size, + ctx->bitstream.vaddr, ctx->bitstream.paddr); + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + + coda_free_aux_buf(dev, &ctx->parabuf); + v4l2_ctrl_handler_free(&ctx->ctrls); + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + pm_runtime_put_sync(&dev->plat_dev->dev); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + clear_bit(ctx->idx, &dev->instance_mask); + if (ctx->ops->release) + ctx->ops->release(ctx); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations coda_encoder_fops = { + .owner = THIS_MODULE, + .open = coda_encoder_open, + .release = coda_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct v4l2_file_operations coda_decoder_fops = { + .owner = THIS_MODULE, + .open = coda_decoder_open, + .release = coda_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int coda_hw_init(struct coda_dev *dev) +{ + u32 data; + u16 *p; + int i, ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + if (dev->rstc) + reset_control_reset(dev->rstc); + + /* + * Copy the first CODA_ISRAM_SIZE in the internal SRAM. + * The 16-bit chars in the code buffer are in memory access + * order, re-sort them to CODA order for register download. + * Data in this SRAM survives a reboot. + */ + p = (u16 *)dev->codebuf.vaddr; + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[i ^ 1]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } else { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[round_down(i, 4) + + 3 - (i % 4)]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } + + /* Clear registers */ + for (i = 0; i < 64; i++) + coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); + + /* Tell the BIT where to find everything it needs */ + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541) { + coda_write(dev, dev->tempbuf.paddr, + CODA_REG_BIT_TEMP_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + } else { + coda_write(dev, dev->workbuf.paddr, + CODA_REG_BIT_WORK_BUF_ADDR); + } + coda_write(dev, dev->codebuf.paddr, + CODA_REG_BIT_CODE_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); + + /* Set default values */ + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + break; + default: + coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + } + if (dev->devtype->product == CODA_960) + coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL); + else + coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); + + coda_write(dev, CODA_INT_INTERRUPT_ENABLE, + CODA_REG_BIT_INT_ENABLE); + + /* Reset VPU and start processor */ + data = coda_read(dev, CODA_REG_BIT_CODE_RESET); + data |= CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + udelay(10); + data &= ~CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + + return 0; + +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static int coda_register_device(struct coda_dev *dev, struct video_device *vfd) +{ + vfd->release = video_device_release_empty, + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + video_set_drvdata(vfd, dev); + + /* Not applicable, use the selection API instead */ + v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); + v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); + v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); + + return video_register_device(vfd, VFL_TYPE_GRABBER, 0); +} + +static void coda_fw_callback(const struct firmware *fw, void *context) +{ + struct coda_dev *dev = context; + struct platform_device *pdev = dev->plat_dev; + int ret; + + if (!fw) { + v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); + goto put_pm; + } + + /* allocate auxiliary per-device code buffer for the BIT processor */ + ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", + dev->debugfs_root); + if (ret < 0) { + dev_err(&pdev->dev, "failed to allocate code buffer\n"); + goto put_pm; + } + + /* Copy the whole firmware image to the code buffer */ + memcpy(dev->codebuf.vaddr, fw->data, fw->size); + release_firmware(fw); + + ret = coda_hw_init(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); + goto put_pm; + } + + ret = coda_check_firmware(dev); + if (ret < 0) + goto put_pm; + + dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(dev->alloc_ctx)) { + v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n"); + goto put_pm; + } + + dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + goto rel_ctx; + } + + dev->vfd[0].fops = &coda_encoder_fops, + dev->vfd[0].ioctl_ops = &coda_ioctl_ops; + snprintf(dev->vfd[0].name, sizeof(dev->vfd[0].name), "coda-encoder"); + ret = coda_register_device(dev, &dev->vfd[0]); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register encoder video device\n"); + goto rel_m2m; + } + + dev->vfd[1].fops = &coda_decoder_fops, + dev->vfd[1].ioctl_ops = &coda_ioctl_ops; + snprintf(dev->vfd[1].name, sizeof(dev->vfd[1].name), "coda-decoder"); + ret = coda_register_device(dev, &dev->vfd[1]); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register decoder video device\n"); + goto rel_m2m; + } + + v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n", + dev->vfd[0].num, dev->vfd[1].num); + + pm_runtime_put_sync(&pdev->dev); + return; + +rel_m2m: + v4l2_m2m_release(dev->m2m_dev); +rel_ctx: + vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); +put_pm: + pm_runtime_put_sync(&pdev->dev); +} + +static int coda_firmware_request(struct coda_dev *dev) +{ + char *fw = dev->devtype->firmware; + + dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, + coda_product_name(dev->devtype->product)); + + return request_firmware_nowait(THIS_MODULE, true, + fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback); +} + +enum coda_platform { + CODA_IMX27, + CODA_IMX53, + CODA_IMX6Q, + CODA_IMX6DL, +}; + +static const struct coda_devtype coda_devdata[] = { + [CODA_IMX27] = { + .firmware = "v4l-codadx6-imx27.bin", + .product = CODA_DX6, + .codecs = codadx6_codecs, + .num_codecs = ARRAY_SIZE(codadx6_codecs), + .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, + .iram_size = 0xb000, + }, + [CODA_IMX53] = { + .firmware = "v4l-coda7541-imx53.bin", + .product = CODA_7541, + .codecs = coda7_codecs, + .num_codecs = ARRAY_SIZE(coda7_codecs), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, + [CODA_IMX6Q] = { + .firmware = "v4l-coda960-imx6q.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x21000, + }, + [CODA_IMX6DL] = { + .firmware = "v4l-coda960-imx6dl.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x20000, + }, +}; + +static struct platform_device_id coda_platform_ids[] = { + { .name = "coda-imx27", .driver_data = CODA_IMX27 }, + { .name = "coda-imx53", .driver_data = CODA_IMX53 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, coda_platform_ids); + +#ifdef CONFIG_OF +static const struct of_device_id coda_dt_ids[] = { + { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, + { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, + { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, + { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, coda_dt_ids); +#endif + +static int coda_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev); + const struct platform_device_id *pdev_id; + struct coda_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + struct gen_pool *pool; + struct coda_dev *dev; + struct resource *res; + int ret, irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&pdev->dev, "Not enough memory for %s\n", + CODA_NAME); + return -ENOMEM; + } + + spin_lock_init(&dev->irqlock); + INIT_LIST_HEAD(&dev->instances); + + dev->plat_dev = pdev; + dev->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(dev->clk_per)) { + dev_err(&pdev->dev, "Could not get per clock\n"); + return PTR_ERR(dev->clk_per); + } + + dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(dev->clk_ahb)) { + dev_err(&pdev->dev, "Could not get ahb clock\n"); + return PTR_ERR(dev->clk_ahb); + } + + /* Get memory for physical registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); + + /* IRQ */ + irq = platform_get_irq_byname(pdev, "bit"); + if (irq < 0) + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + return irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, + IRQF_ONESHOT, dev_name(&pdev->dev), dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); + if (IS_ERR(dev->rstc)) { + ret = PTR_ERR(dev->rstc); + if (ret == -ENOENT || ret == -ENOSYS) { + dev->rstc = NULL; + } else { + dev_err(&pdev->dev, "failed get reset control: %d\n", + ret); + return ret; + } + } + + /* Get IRAM pool from device tree or platform data */ + pool = of_get_named_gen_pool(np, "iram", 0); + if (!pool && pdata) + pool = dev_get_gen_pool(pdata->iram_dev); + if (!pool) { + dev_err(&pdev->dev, "iram pool not available\n"); + return -ENOMEM; + } + dev->iram_pool = pool; + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + mutex_init(&dev->dev_mutex); + mutex_init(&dev->coda_mutex); + + pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); + + if (of_id) { + dev->devtype = of_id->data; + } else if (pdev_id) { + dev->devtype = &coda_devdata[pdev_id->driver_data]; + } else { + v4l2_device_unregister(&dev->v4l2_dev); + return -EINVAL; + } + + dev->debugfs_root = debugfs_create_dir("coda", NULL); + if (!dev->debugfs_root) + dev_warn(&pdev->dev, "failed to create debugfs root\n"); + + /* allocate auxiliary per-device buffers for the BIT processor */ + if (dev->devtype->product == CODA_DX6) { + ret = coda_alloc_aux_buf(dev, &dev->workbuf, + dev->devtype->workbuf_size, "workbuf", + dev->debugfs_root); + if (ret < 0) { + dev_err(&pdev->dev, "failed to allocate work buffer\n"); + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + } + + if (dev->devtype->tempbuf_size) { + ret = coda_alloc_aux_buf(dev, &dev->tempbuf, + dev->devtype->tempbuf_size, "tempbuf", + dev->debugfs_root); + if (ret < 0) { + dev_err(&pdev->dev, "failed to allocate temp buffer\n"); + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + } + + dev->iram.size = dev->devtype->iram_size; + dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, + &dev->iram.paddr); + if (!dev->iram.vaddr) { + dev_warn(&pdev->dev, "unable to alloc iram\n"); + } else { + dev->iram.blob.data = dev->iram.vaddr; + dev->iram.blob.size = dev->iram.size; + dev->iram.dentry = debugfs_create_blob("iram", 0644, + dev->debugfs_root, + &dev->iram.blob); + } + + dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!dev->workqueue) { + dev_err(&pdev->dev, "unable to alloc workqueue\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, dev); + + /* + * Start activated so we can directly call coda_hw_init in + * coda_fw_callback regardless of whether CONFIG_PM_RUNTIME is + * enabled or whether the device is associated with a PM domain. + */ + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return coda_firmware_request(dev); +} + +static int coda_remove(struct platform_device *pdev) +{ + struct coda_dev *dev = platform_get_drvdata(pdev); + + video_unregister_device(&dev->vfd[0]); + video_unregister_device(&dev->vfd[1]); + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + pm_runtime_disable(&pdev->dev); + if (dev->alloc_ctx) + vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + v4l2_device_unregister(&dev->v4l2_dev); + destroy_workqueue(dev->workqueue); + if (dev->iram.vaddr) + gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, + dev->iram.size); + coda_free_aux_buf(dev, &dev->codebuf); + coda_free_aux_buf(dev, &dev->tempbuf); + coda_free_aux_buf(dev, &dev->workbuf); + debugfs_remove_recursive(dev->debugfs_root); + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int coda_runtime_resume(struct device *dev) +{ + struct coda_dev *cdev = dev_get_drvdata(dev); + int ret = 0; + + if (dev->pm_domain && cdev->codebuf.vaddr) { + ret = coda_hw_init(cdev); + if (ret) + v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); + } + + return ret; +} +#endif + +static const struct dev_pm_ops coda_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) +}; + +static struct platform_driver coda_driver = { + .probe = coda_probe, + .remove = coda_remove, + .driver = { + .name = CODA_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(coda_dt_ids), + .pm = &coda_pm_ops, + }, + .id_table = coda_platform_ids, +}; + +module_platform_driver(coda_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c new file mode 100644 index 0000000..456773a --- /dev/null +++ b/drivers/media/platform/coda/coda-h264.c @@ -0,0 +1,37 @@ +/* + * Coda multi-standard codec IP - H.264 helper functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, <javier.martin@vista-silicon.com> + * Xavier Duret + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +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 }; + +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; +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h new file mode 100644 index 0000000..bbc18c0 --- /dev/null +++ b/drivers/media/platform/coda/coda.h @@ -0,0 +1,287 @@ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, <javier.martin@vista-silicon.com> + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + * + * 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. + */ + +#include <linux/debugfs.h> +#include <linux/irqreturn.h> +#include <linux/mutex.h> +#include <linux/kfifo.h> +#include <linux/videodev2.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/videobuf2-core.h> + +#include "coda_regs.h" + +#define CODA_MAX_FRAMEBUFFERS 8 +#define CODA_MAX_FRAME_SIZE 0x100000 +#define FMO_SLICE_SAVE_BUF_SIZE (32) + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +enum coda_inst_type { + CODA_INST_ENCODER, + CODA_INST_DECODER, +}; + +enum coda_product { + CODA_DX6 = 0xf001, + CODA_7541 = 0xf012, + CODA_960 = 0xf020, +}; + +struct coda_devtype { + char *firmware; + enum coda_product product; + const struct coda_codec *codecs; + unsigned int num_codecs; + size_t workbuf_size; + size_t tempbuf_size; + size_t iram_size; +}; + +struct coda_aux_buf { + void *vaddr; + dma_addr_t paddr; + u32 size; + struct debugfs_blob_wrapper blob; + struct dentry *dentry; +}; + +struct coda_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd[2]; + struct platform_device *plat_dev; + const struct coda_devtype *devtype; + + void __iomem *regs_base; + struct clk *clk_per; + struct clk *clk_ahb; + struct reset_control *rstc; + + struct coda_aux_buf codebuf; + struct coda_aux_buf tempbuf; + struct coda_aux_buf workbuf; + struct gen_pool *iram_pool; + struct coda_aux_buf iram; + + spinlock_t irqlock; + struct mutex dev_mutex; + struct mutex coda_mutex; + struct workqueue_struct *workqueue; + struct v4l2_m2m_dev *m2m_dev; + struct vb2_alloc_ctx *alloc_ctx; + struct list_head instances; + unsigned long instance_mask; + struct dentry *debugfs_root; +}; + +struct coda_codec { + u32 mode; + u32 src_fourcc; + u32 dst_fourcc; + u32 max_w; + u32 max_h; +}; + +struct coda_huff_tab; + +struct coda_params { + u8 rot_mode; + u8 h264_intra_qp; + u8 h264_inter_qp; + u8 h264_min_qp; + u8 h264_max_qp; + u8 h264_deblk_enabled; + u8 h264_deblk_alpha; + u8 h264_deblk_beta; + u8 mpeg4_intra_qp; + u8 mpeg4_inter_qp; + u8 gop_size; + int intra_refresh; + int codec_mode; + int codec_mode_aux; + enum v4l2_mpeg_video_multi_slice_mode slice_mode; + u32 framerate; + u16 bitrate; + u32 slice_max_bits; + u32 slice_max_mb; +}; + +struct coda_timestamp { + struct list_head list; + u32 sequence; + struct v4l2_timecode timecode; + struct timeval timestamp; +}; + +/* Per-queue, driver-specific private data */ +struct coda_q_data { + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int sizeimage; + unsigned int fourcc; + struct v4l2_rect rect; +}; + +struct coda_iram_info { + u32 axi_sram_use; + phys_addr_t buf_bit_use; + phys_addr_t buf_ip_ac_dc_use; + phys_addr_t buf_dbk_y_use; + phys_addr_t buf_dbk_c_use; + phys_addr_t buf_ovl_use; + phys_addr_t buf_btp_use; + phys_addr_t search_ram_paddr; + int search_ram_size; + int remaining; + phys_addr_t next_paddr; +}; + +struct gdi_tiled_map { + int xy2ca_map[16]; + int xy2ba_map[16]; + int xy2ra_map[16]; + int rbc2axi_map[32]; + int xy2rbc_config; + int map_type; +#define GDI_LINEAR_FRAME_MAP 0 +}; + +struct coda_ctx; + +struct coda_context_ops { + int (*queue_init)(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + int (*start_streaming)(struct coda_ctx *ctx); + int (*prepare_run)(struct coda_ctx *ctx); + void (*finish_run)(struct coda_ctx *ctx); + void (*seq_end_work)(struct work_struct *work); + void (*release)(struct coda_ctx *ctx); +}; + +struct coda_ctx { + struct coda_dev *dev; + struct mutex buffer_mutex; + struct list_head list; + struct work_struct pic_run_work; + struct work_struct seq_end_work; + struct completion completion; + const struct coda_context_ops *ops; + int aborting; + int initialized; + int streamon_out; + int streamon_cap; + u32 isequence; + u32 qsequence; + u32 osequence; + u32 sequence_offset; + struct coda_q_data q_data[2]; + enum coda_inst_type inst_type; + const struct coda_codec *codec; + enum v4l2_colorspace colorspace; + struct coda_params params; + struct v4l2_ctrl_handler ctrls; + struct v4l2_fh fh; + int gopcounter; + int runcounter; + char vpu_header[3][64]; + int vpu_header_size[3]; + struct kfifo bitstream_fifo; + struct mutex bitstream_mutex; + struct coda_aux_buf bitstream; + bool hold; + struct coda_aux_buf parabuf; + struct coda_aux_buf psbuf; + struct coda_aux_buf slicebuf; + struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; + u32 frame_types[CODA_MAX_FRAMEBUFFERS]; + struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS]; + u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; + struct list_head timestamp_list; + struct coda_aux_buf workbuf; + int num_internal_frames; + int idx; + int reg_idx; + struct coda_iram_info iram_info; + struct gdi_tiled_map tiled_map; + u32 bit_stream_param; + u32 frm_dis_flg; + u32 frame_mem_ctrl; + int display_idx; + struct dentry *debugfs_entry; +}; + +extern int coda_debug; + +void coda_write(struct coda_dev *dev, u32 data, u32 reg); +unsigned int coda_read(struct coda_dev *dev, u32 reg); + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent); +void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); + +static inline int coda_alloc_context_buf(struct coda_ctx *ctx, + struct coda_aux_buf *buf, size_t size, + const char *name) +{ + return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); +} + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +int coda_hw_reset(struct coda_ctx *ctx); + +void coda_fill_bitstream(struct coda_ctx *ctx); + +void coda_set_gdi_regs(struct coda_ctx *ctx); + +static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &(ctx->q_data[V4L2_M2M_SRC]); + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &(ctx->q_data[V4L2_M2M_DST]); + default: + return NULL; + } +} + +const char *coda_product_name(int product); + +int coda_check_firmware(struct coda_dev *dev); + +static inline int coda_get_bitstream_payload(struct coda_ctx *ctx) +{ + return kfifo_len(&ctx->bitstream_fifo); +} + +void coda_bit_stream_end_flag(struct coda_ctx *ctx); + +int coda_h264_padding(int size, char *p); + +extern const struct coda_context_ops coda_bit_encode_ops; +extern const struct coda_context_ops coda_bit_decode_ops; + +irqreturn_t coda_irq_handler(int irq, void *data); diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda/coda_regs.h index c791275..c791275 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda/coda_regs.h diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index afb3aec..d9e1ddb 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -1,6 +1,8 @@ config VIDEO_DAVINCI_VPIF_DISPLAY tristate "TI DaVinci VPIF V4L2-Display driver" - depends on VIDEO_DEV && ARCH_DAVINCI + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT @@ -14,7 +16,9 @@ config VIDEO_DAVINCI_VPIF_DISPLAY config VIDEO_DAVINCI_VPIF_CAPTURE tristate "TI DaVinci VPIF video capture driver" - depends on VIDEO_DEV && ARCH_DAVINCI + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG help Enables Davinci VPIF module used for capture devices. @@ -26,7 +30,9 @@ config VIDEO_DAVINCI_VPIF_CAPTURE config VIDEO_DM6446_CCDC tristate "TI DM6446 CCDC video capture driver" - depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + depends on VIDEO_V4L2 + depends on ARCH_DAVINCI || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF_DMA_CONTIG help Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces @@ -40,7 +46,9 @@ config VIDEO_DM6446_CCDC config VIDEO_DM355_CCDC tristate "TI DM355 CCDC video capture driver" - depends on VIDEO_V4L2 && ARCH_DAVINCI + depends on VIDEO_V4L2 + depends on ARCH_DAVINCI || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF_DMA_CONTIG help Enables DM355 CCD hw module. DM355 CCDC hw interfaces @@ -55,6 +63,7 @@ config VIDEO_DM355_CCDC config VIDEO_DM365_ISIF tristate "TI DM365 ISIF video capture driver" depends on VIDEO_V4L2 && ARCH_DAVINCI + depends on HAS_DMA select VIDEOBUF_DMA_CONTIG help Enables ISIF hw module. This is the hardware module for @@ -67,6 +76,7 @@ config VIDEO_DM365_ISIF config VIDEO_DAVINCI_VPBE_DISPLAY tristate "TI DaVinci VPBE V4L2-Display driver" depends on ARCH_DAVINCI + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG help Enables Davinci VPBE module used for display devices. diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index 05f8fb7..3f44deb 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -460,7 +460,7 @@ static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) * ccdc_write_dfc_entry() * write an entry in the dfc table. */ -int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) { /* TODO This is to be re-visited and adjusted */ #define DFC_WRITE_WAIT_COUNT 1000 diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 07e98df..62a0ebb 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -130,9 +130,9 @@ static void ccdc_enable_vport(int flag) * This function will configure the window size * to be capture in CCDC reg */ -void ccdc_setwin(struct v4l2_rect *image_win, - enum ccdc_frmfmt frm_fmt, - int ppc) +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) { int horz_start, horz_nr_pixels; int vert_start, vert_nr_lines; @@ -291,7 +291,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed"); return -EFAULT; } - config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; + config_params->fault_pxl.fpc_table_addr = (unsigned long)fpc_physaddr; return 0; } @@ -370,7 +370,7 @@ static int ccdc_set_params(void __user *params) * ccdc_config_ycbcr() * This function will configure CCDC for YCbCr video capture */ -void ccdc_config_ycbcr(void) +static void ccdc_config_ycbcr(void) { struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; u32 syn_mode; @@ -506,7 +506,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) /* Configure Fault pixel if needed */ regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%lx to FPC_ADDR...\n", (fpc->fpc_table_addr)); /* Write the FPC params with FPC disable */ val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; @@ -523,7 +523,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) * ccdc_config_raw() * This function will configure CCDC for Raw capture mode */ -void ccdc_config_raw(void) +static void ccdc_config_raw(void) { struct ccdc_params_raw *params = &ccdc_cfg.bayer; struct ccdc_config_params_raw *config_params = diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index ea7661a..de55f47 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -125,7 +125,7 @@ static DEFINE_MUTEX(ccdc_lock); /* ccdc configuration */ static struct ccdc_config *ccdc_cfg; -const struct vpfe_standard vpfe_standards[] = { +static const struct vpfe_standard vpfe_standards[] = { {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, }; @@ -442,11 +442,10 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, return ret; /* Update the values of sizeimage and bytesperline */ - if (!ret) { - pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); - pix->sizeimage = pix->bytesperline * pix->height; - } - return ret; + pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); + pix->sizeimage = pix->bytesperline * pix->height; + + return 0; } static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) @@ -943,12 +942,11 @@ static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct vpfe_device *vpfe_dev = video_drvdata(file); - int ret = 0; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); /* Fill in the information about format */ *fmt = vpfe_dev->fmt; - return ret; + return 0; } static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, @@ -1914,7 +1912,7 @@ static int vpfe_probe(struct platform_device *pdev) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "trying to register vpfe device.\n"); v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "video_dev=%x\n", (int)&vpfe_dev->video_dev); + "video_dev=%p\n", &vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = video_register_device(vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index cd08e52..3dad5bd 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -38,6 +38,7 @@ MODULE_LICENSE("GPL"); #define VPIF_CH3_MAX_MODES 2 spinlock_t vpif_lock; +EXPORT_SYMBOL_GPL(vpif_lock); void __iomem *vpif_base; EXPORT_SYMBOL_GPL(vpif_base); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index b054b7e..3ccb26f 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -213,8 +213,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) /* Remove buffer from the buffer queue */ list_del(&common->cur_frm->list); spin_unlock_irqrestore(&common->irqlock, flags); - /* Mark state of the current frame to active */ - common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); @@ -350,7 +348,6 @@ static void vpif_schedule_next_buffer(struct common_obj *common) /* Remove that buffer from the buffer queue */ list_del(&common->next_frm->list); spin_unlock(&common->irqlock); - common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); /* Set top and bottom field addresses in VPIF registers */ @@ -373,7 +370,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) struct vpif_device *dev = &vpif_obj; struct common_obj *common; struct channel_obj *ch; - enum v4l2_field field; int channel_id = 0; int fid = -1, i; @@ -383,8 +379,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) ch = dev->dev[channel_id]; - field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; - for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) { common = &ch->common[i]; /* skip If streaming is not started in this channel */ @@ -533,7 +527,7 @@ static int vpif_update_std_info(struct channel_obj *ch) */ static void vpif_calculate_offsets(struct channel_obj *ch) { - unsigned int hpitch, vpitch, sizeimage; + unsigned int hpitch, sizeimage; struct video_obj *vid_ch = &(ch->video); struct vpif_params *vpifparams = &ch->vpifparams; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -552,7 +546,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch) sizeimage = common->fmt.fmt.pix.sizeimage; hpitch = common->fmt.fmt.pix.bytesperline; - vpitch = sizeimage / (hpitch * 2); if ((V4L2_FIELD_NONE == vid_ch->buf_field) || (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { @@ -1603,7 +1596,7 @@ static int vpif_suspend(struct device *dev) ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - if (!vb2_is_streaming(&common->buffer_queue)) + if (!vb2_start_streaming_called(&common->buffer_queue)) continue; mutex_lock(&common->lock); @@ -1637,7 +1630,7 @@ static int vpif_resume(struct device *dev) ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - if (!vb2_is_streaming(&common->buffer_queue)) + if (!vb2_start_streaming_called(&common->buffer_queue)) continue; mutex_lock(&common->lock); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index a03ec73..8d6ced5 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -196,8 +196,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) list_del(&common->cur_frm->list); spin_unlock_irqrestore(&common->irqlock, flags); - /* Mark state of the current frame to active */ - common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); common->set_addr((addr + common->ytop_off), @@ -306,8 +304,6 @@ static void process_progressive_mode(struct common_obj *common) /* Remove that buffer from the buffer queue */ list_del(&common->next_frm->list); spin_unlock(&common->irqlock); - /* Mark status of the buffer as active */ - common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; /* Set top and bottom field addrs in VPIF registers */ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); @@ -360,7 +356,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) struct vpif_device *dev = &vpif_obj; struct channel_obj *ch; struct common_obj *common; - enum v4l2_field field; int fid = -1, i; int channel_id = 0; @@ -369,7 +364,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) return IRQ_NONE; ch = dev->dev[channel_id]; - field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; for (i = 0; i < VPIF_NUMOBJECTS; i++) { common = &ch->common[i]; /* If streaming is started in this channel */ @@ -502,7 +496,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch) struct vpif_params *vpifparams = &ch->vpifparams; enum v4l2_field field = common->fmt.fmt.pix.field; struct video_obj *vid_ch = &ch->video; - unsigned int hpitch, vpitch, sizeimage; + unsigned int hpitch, sizeimage; if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { if (ch->vpifparams.std_info.frm_fmt) @@ -516,7 +510,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch) sizeimage = common->fmt.fmt.pix.sizeimage; hpitch = common->fmt.fmt.pix.bytesperline; - vpitch = sizeimage / (hpitch * 2); if ((V4L2_FIELD_NONE == vid_ch->buf_field) || (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { common->ytop_off = 0; @@ -813,17 +806,14 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg, { struct vpif_display_chan_config *chan_cfg = &vpif_cfg->chan_config[ch->channel_id]; - struct vpif_subdev_info *subdev_info = NULL; struct v4l2_subdev *sd = NULL; u32 input = 0, output = 0; int sd_index; int ret; sd_index = vpif_output_to_subdev(vpif_cfg, chan_cfg, index); - if (sd_index >= 0) { + if (sd_index >= 0) sd = vpif_obj.sd[sd_index]; - subdev_info = &vpif_cfg->subdevinfo[sd_index]; - } if (sd) { input = chan_cfg->outputs[index].input_route; @@ -1210,8 +1200,8 @@ static int vpif_probe_complete(void) INIT_LIST_HEAD(&common->dma_queue); /* register video device */ - vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", - (int)ch, (int)&ch->video_dev); + vpif_dbg(1, debug, "channel=%p,channel->video_dev=%p\n", + ch, &ch->video_dev); /* Initialize the video_device structure */ vdev = ch->video_dev; @@ -1410,7 +1400,7 @@ static int vpif_suspend(struct device *dev) ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - if (!vb2_is_streaming(&common->buffer_queue)) + if (!vb2_start_streaming_called(&common->buffer_queue)) continue; mutex_lock(&common->lock); @@ -1442,7 +1432,7 @@ static int vpif_resume(struct device *dev) ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - if (!vb2_is_streaming(&common->buffer_queue)) + if (!vb2_start_streaming_called(&common->buffer_queue)) continue; mutex_lock(&common->lock); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 9d0cc04..b4c9f1d 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -852,8 +852,8 @@ int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) swap(addr->cb, addr->cr); - pr_debug("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", - addr->y, addr->cb, addr->cr, ret); + pr_debug("ADDR: y= %pad cb= %pad cr= %pad ret= %d", + &addr->y, &addr->cb, &addr->cr, ret); return ret; } @@ -1086,7 +1086,7 @@ static int gsc_probe(struct platform_device *pdev) else gsc->id = pdev->id; - if (gsc->id < 0 || gsc->id >= drv_data->num_entities) { + if (gsc->id >= drv_data->num_entities) { dev_err(dev, "Invalid platform device id: %d\n", gsc->id); return -EINVAL; } diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e434f1f0..74e1de6 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -362,7 +362,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh, { struct gsc_ctx *ctx = fh_to_ctx(fh); struct gsc_dev *gsc = ctx->gsc_dev; - struct gsc_frame *frame; u32 max_cnt; max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? @@ -376,8 +375,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh, gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx); } - frame = ctx_get_frame(ctx, reqbufs->type); - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index e22d147..ce12a11 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -90,8 +90,8 @@ void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, int index) { - pr_debug("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index, - addr->y, addr->cb, addr->cr); + pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", index, + &addr->y, &addr->cb, &addr->cr); writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index)); writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index)); writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index)); @@ -101,8 +101,8 @@ void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr, int index) { - pr_debug("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", - index, addr->y, addr->cb, addr->cr); + pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad", + index, &addr->y, &addr->cb, &addr->cr); writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index)); writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index)); writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index)); diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 5dcaa0a..77c9512 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -2,7 +2,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on (PLAT_S5P || ARCH_EXYNOS) + depends on (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST) depends on OF && COMMON_CLK help Say Y here to enable camera host interface devices for @@ -16,6 +16,7 @@ config VIDEO_EXYNOS4_IS_COMMON config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" depends on I2C + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select MFD_SYSCON @@ -43,6 +44,7 @@ if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250 config VIDEO_EXYNOS_FIMC_LITE tristate "EXYNOS FIMC-LITE camera interface driver" depends on I2C + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select VIDEO_EXYNOS4_IS_COMMON help @@ -55,6 +57,7 @@ endif config VIDEO_EXYNOS4_FIMC_IS tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG depends on OF select FW_LOADER diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c index e8519e1..e050e63 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-errno.c +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c @@ -15,7 +15,7 @@ #include "fimc-is-errno.h" -const char * const fimc_is_param_strerr(unsigned int error) +const char *fimc_is_param_strerr(unsigned int error) { switch (error) { case ERROR_COMMON_CMD: @@ -146,7 +146,7 @@ const char * const fimc_is_param_strerr(unsigned int error) } } -const char * const fimc_is_strerr(unsigned int error) +const char *fimc_is_strerr(unsigned int error) { error &= ~IS_ERROR_TIME_OUT_FLAG; diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h index 3de6f6d..ef981e7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h @@ -242,7 +242,7 @@ enum fimc_is_error { ERROR_SCALER_FLIP = 521, }; -const char * const fimc_is_strerr(unsigned int error); -const char * const fimc_is_param_strerr(unsigned int error); +const char *fimc_is_strerr(unsigned int error); +const char *fimc_is_param_strerr(unsigned int error); #endif /* FIMC_IS_ERR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index bf1465d..72b9b43 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -667,7 +667,6 @@ void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val) void fimc_is_set_initial_params(struct fimc_is *is) { struct global_param *global; - struct sensor_param *sensor; struct isp_param *isp; struct drc_param *drc; struct fd_param *fd; @@ -676,7 +675,6 @@ void fimc_is_set_initial_params(struct fimc_is *is) index = is->config_index; global = &is->config[index].global; - sensor = &is->config[index].sensor; isp = &is->config[index].isp; drc = &is->config[index].drc; fd = &is->config[index].fd; diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 5476dce..22162b2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -388,7 +388,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context) mutex_lock(&is->lock); if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) { - dev_err(dev, "wrong firmware size: %d\n", fw->size); + dev_err(dev, "wrong firmware size: %zu\n", fw->size); goto done; } @@ -416,7 +416,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context) dev_info(dev, "loaded firmware: %s, rev. %s\n", is->fw.info, is->fw.version); - dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr); + dev_dbg(dev, "FW size: %zu, paddr: %pad\n", fw->size, &is->memory.paddr); is->is_shared_region->chip_id = 0xe4412; is->is_shared_region->chip_rev_no = 1; @@ -693,9 +693,9 @@ int fimc_is_hw_initialize(struct fimc_is *is) return -EIO; } - pr_debug("shared region: %#x, parameter region: %#x\n", - is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, - is->is_dma_p_region); + pr_debug("shared region: %pad, parameter region: %pad\n", + &is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, + &is->is_dma_p_region); is->setfile.sub_index = 0; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 93f9cf2..76b6b4d 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -219,9 +219,9 @@ static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) ivb->dma_addr[i]; isp_dbg(2, &video->ve.vdev, - "dma_buf %d (%d/%d/%d) addr: %#x\n", - buf_index, ivb->index, i, vb->v4l2_buf.index, - ivb->dma_addr[i]); + "dma_buf %pad (%d/%d/%d) addr: %pad\n", + &buf_index, ivb->index, i, vb->v4l2_buf.index, + &ivb->dma_addr[i]); } if (++video->buf_count < video->reqbufs_count) @@ -313,7 +313,6 @@ static int isp_video_release(struct file *file) struct fimc_is_video *ivc = &isp->video_capture; struct media_entity *entity = &ivc->ve.vdev.entity; struct media_device *mdev = entity->parent; - int ret = 0; mutex_lock(&isp->video_lock); @@ -335,7 +334,7 @@ static int isp_video_release(struct file *file) pm_runtime_put(&isp->pdev->dev); mutex_unlock(&isp->video_lock); - return ret; + return 0; } static const struct v4l2_file_operations isp_video_fops = { diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 344718d..54c49d5 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1098,8 +1098,10 @@ static int fimc_md_link_notify(struct media_link *link, unsigned int flags, if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { if (!(flags & MEDIA_LNK_FL_ENABLED)) ret = __fimc_md_modify_pipelines(sink, false); +#if 0 else - ; /* TODO: Link state change validation */ + /* TODO: Link state change validation */ +#endif /* After link activation */ } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && (link->flags & MEDIA_LNK_FL_ENABLED)) { diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index ae54ef5..db6fd14 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -25,6 +25,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/videodev2.h> @@ -752,7 +753,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, v4l2_of_parse_endpoint(node, &endpoint); state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; - if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES) + if (state->index >= CSIS_MAX_ENTITIES) return -ENXIO; /* Get MIPI CSI-2 bus configration from the endpoint node. */ diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index bf739e3..6265d36 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -1,6 +1,7 @@ config VIDEO_CAFE_CCIC tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" depends on PCI && I2C && VIDEO_V4L2 + depends on HAS_DMA select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG @@ -12,6 +13,7 @@ config VIDEO_CAFE_CCIC config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" depends on ARCH_MMP && I2C && VIDEO_V4L2 + depends on HAS_DMA select VIDEO_OV7670 select I2C_GPIO select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index be4b512..7a86c77 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(dma_buf_size, "parameters require larger buffers, an attempt to reallocate " "will be made."); #else /* MCAM_MODE_VMALLOC */ -static const bool alloc_bufs_at_read = 0; +static const bool alloc_bufs_at_read; static const int n_dma_bufs = 3; /* Used by S/G_PARM */ #endif /* MCAM_MODE_VMALLOC */ diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index fa8f7ca..4971ff2 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -27,7 +27,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-dma-contig.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define EMMAPRP_MODULE_NAME "mem2mem-emmaprp" diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 37ad446..05de442 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -3,7 +3,7 @@ config VIDEO_OMAP2_VOUT_VRFB config VIDEO_OMAP2_VOUT tristate "OMAP2/OMAP3 V4L2-Display driver" - depends on ARCH_OMAP2 || ARCH_OMAP3 + depends on ARCH_OMAP2 || ARCH_OMAP3 || (COMPILE_TEST && HAS_MMU) select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 2d177fa..64ab6fb 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -369,7 +369,7 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout, { int ret = 0; struct omap_overlay_info info; - int cropheight, cropwidth, pixheight, pixwidth; + int cropheight, cropwidth, pixwidth; if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && (outw != vout->pix.width || outh != vout->pix.height)) { @@ -389,12 +389,10 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout, if (is_rotation_90_or_270(vout)) { cropheight = vout->crop.width; cropwidth = vout->crop.height; - pixheight = vout->pix.width; pixwidth = vout->pix.height; } else { cropheight = vout->crop.height; cropwidth = vout->crop.width; - pixheight = vout->pix.height; pixwidth = vout->pix.width; } @@ -991,7 +989,7 @@ static int omap_vout_release(struct file *file) mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2; omap_dispc_unregister_isr(omap_vout_isr, vout, mask); - vout->streaming = 0; + vout->streaming = false; videobuf_streamoff(q); videobuf_queue_cancel(q); @@ -1451,12 +1449,10 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) } case V4L2_CID_VFLIP: { - struct omap_overlay *ovl; struct omapvideo_info *ovid; unsigned int mirror = a->value; ovid = &vout->vid_info; - ovl = ovid->overlays[0]; mutex_lock(&vout->lock); if (mirror && ovid->rotation_type == VOUT_ROT_NONE) { @@ -1489,7 +1485,7 @@ static int vidioc_reqbufs(struct file *file, void *fh, struct omap_vout_device *vout = fh; struct videobuf_queue *q = &vout->vbq; - if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0)) + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; /* if memory is not mmp or userptr return error */ @@ -1648,7 +1644,7 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) vout->field_id = 0; /* set flag here. Next QBUF will start DMA */ - vout->streaming = 1; + vout->streaming = true; vout->first_int = 1; @@ -1708,7 +1704,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) if (!vout->streaming) return -EINVAL; - vout->streaming = 0; + vout->streaming = false; mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2; @@ -1916,7 +1912,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) control[0].id = V4L2_CID_ROTATE; control[0].value = 0; vout->rotation = 0; - vout->mirror = 0; + vout->mirror = false; vout->control[2].id = V4L2_CID_HFLIP; vout->control[2].value = 0; if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 62e7e57..aa39306 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -148,7 +148,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, ret = -ENOMEM; goto release_vrfb_ctx; } - vout->vrfb_static_allocation = 1; + vout->vrfb_static_allocation = true; } return 0; @@ -336,7 +336,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) offset = vout->vrfb_context[0].yoffset * vout->vrfb_context[0].bytespp; temp_ps = ps / vr_ps; - if (mirroring == 0) { + if (!mirroring) { *cropped_offset = offset + line_length * temp_ps * cleft + crop->top * temp_ps; } else { @@ -350,7 +350,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) vout->vrfb_context[0].bytespp) + (vout->vrfb_context[0].xoffset * vout->vrfb_context[0].bytespp)); - if (mirroring == 0) { + if (!mirroring) { *cropped_offset = offset + (line_length * ps * ctop) + (cleft / vr_ps) * ps; @@ -364,7 +364,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset * vout->vrfb_context[0].bytespp; temp_ps = ps / vr_ps; - if (mirroring == 0) { + if (!mirroring) { *cropped_offset = offset + line_length * temp_ps * crop->left + ctop * ps; } else { @@ -375,7 +375,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) } break; case dss_rotation_0_degree: - if (mirroring == 0) { + if (!mirroring) { *cropped_offset = (line_length * ps) * crop->top + (crop->left / vr_ps) * ps; } else { diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/omap/omap_vout_vrfb.h index ffde741..4c23148 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.h +++ b/drivers/media/platform/omap/omap_vout_vrfb.h @@ -23,18 +23,18 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, struct videobuf_buffer *vb); void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout); #else -void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { } -int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, +static inline void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }; +static inline int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, u32 static_vrfb_allocation) - { return 0; } -void omap_vout_release_vrfb(struct omap_vout_device *vout) { } -int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, + { return 0; }; +static inline void omap_vout_release_vrfb(struct omap_vout_device *vout) { }; +static inline int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, unsigned int *count, unsigned int startindex) - { return 0; } -int omap_vout_prepare_vrfb(struct omap_vout_device *vout, + { return 0; }; +static inline int omap_vout_prepare_vrfb(struct omap_vout_device *vout, struct videobuf_buffer *vb) - { return 0; } -void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { } + { return 0; }; +static inline void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }; #endif #endif diff --git a/drivers/media/platform/omap3isp/cfa_coef_table.h b/drivers/media/platform/omap3isp/cfa_coef_table.h index c84df07..e75b0eb 100644 --- a/drivers/media/platform/omap3isp/cfa_coef_table.h +++ b/drivers/media/platform/omap3isp/cfa_coef_table.h @@ -11,16 +11,6 @@ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ { 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, diff --git a/drivers/media/platform/omap3isp/gamma_table.h b/drivers/media/platform/omap3isp/gamma_table.h index 78deebf..3b507078 100644 --- a/drivers/media/platform/omap3isp/gamma_table.h +++ b/drivers/media/platform/omap3isp/gamma_table.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 2c7aa67..72265e5 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -40,16 +40,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <asm/cacheflush.h> @@ -999,16 +989,14 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) video, s_stream, 0); } - v4l2_subdev_call(subdev, video, s_stream, 0); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); if (subdev == &isp->isp_res.subdev) - ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer); + ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer); else if (subdev == &isp->isp_prev.subdev) - ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview); + ret |= isp_pipeline_wait(isp, isp_pipeline_wait_preview); else if (subdev == &isp->isp_ccdc.subdev) - ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); - else - ret = 0; + ret |= isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); /* Handle stop failures. An entity that fails to stop can * usually just be restarted. Flag the stop failure nonetheless diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 2c314ee..cfdfc87 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_CORE_H diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 9f727d2..81a9dc0 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/module.h> @@ -491,14 +481,13 @@ done: static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) { unsigned long flags; + int ret; spin_lock_irqsave(&ccdc->lsc.req_lock, flags); - if (ccdc->lsc.active) { - spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); - return 1; - } + ret = ccdc->lsc.active != NULL; spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); - return 0; + + return ret; } static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) @@ -818,29 +807,48 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc) struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); struct isp_device *isp = to_isp_device(ccdc); const struct isp_format_info *info; + struct v4l2_mbus_framefmt *format; unsigned long l3_ick = pipe->l3_ick; unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; unsigned int div = 0; - u32 fmtcfg_vp; + u32 fmtcfg = ISPCCDC_FMTCFG_VPEN; + + format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; + + if (!format->code) { + /* Disable the video port when the input format isn't supported. + * This is indicated by a pixel code set to 0. + */ + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); + return; + } + + isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | + (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); + isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | + ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); - fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) - & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); + isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | + (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code); switch (info->width) { case 8: case 10: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_9_0; break; case 11: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_10_1; break; case 12: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_11_2; break; case 13: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_12_3; break; } @@ -850,75 +858,59 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc) div = l3_ick / pipe->external_rate; div = clamp(div, 2U, max_div); - fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; + fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; - isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); -} - -/* - * ccdc_enable_vp - Enable Video Port. - * @ccdc: Pointer to ISP CCDC device. - * @enable: 0 Disables VP, 1 Enables VP - * - * This is needed for outputting image to Preview, H3A and HIST ISP submodules. - */ -static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) -{ - struct isp_device *isp = to_isp_device(ccdc); - - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, - ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); + isp_reg_writel(isp, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); } /* * ccdc_config_outlineoffset - Configure memory saving output line offset * @ccdc: Pointer to ISP CCDC device. - * @offset: Address offset to start a new line. Must be twice the - * Output width and aligned on 32 byte boundary - * @oddeven: Specifies the odd/even line pattern to be chosen to store the - * output. - * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. + * @bpl: Number of bytes per line when stored in memory. + * @field: Field order when storing interlaced formats in memory. + * + * Configure the offsets for the line output control: + * + * - The horizontal line offset is defined as the number of bytes between the + * start of two consecutive lines in memory. Set it to the given bytes per + * line value. + * + * - The field offset value is defined as the number of lines to offset the + * start of the field identified by FID = 1. Set it to one. * - * - Configures the output line offset when stored in memory - * - Sets the odd/even line pattern to store the output - * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) - * - Configures the number of even and odd line fields in case of rearranging - * the lines. + * - The line offset values are defined as the number of lines (as defined by + * the horizontal line offset) between the start of two consecutive lines for + * all combinations of odd/even lines in odd/even fields. When interleaving + * fields set them all to two lines, and to one line otherwise. */ static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, - u32 offset, u8 oddeven, u8 numlines) + unsigned int bpl, + enum v4l2_field field) { struct isp_device *isp = to_isp_device(ccdc); + u32 sdofst = 0; - isp_reg_writel(isp, offset & 0xffff, - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); + isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HSIZE_OFF); - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - ISPCCDC_SDOFST_FINV); - - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - ISPCCDC_SDOFST_FOFST_4L); - - switch (oddeven) { - case EVENEVEN: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); - break; - case ODDEVEN: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); - break; - case EVENODD: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); - break; - case ODDODD: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + /* When interleaving fields in memory offset field one by one + * line and set the line offset to two lines. + */ + sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST1_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST2_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST3_SHIFT); break; + default: + /* In all other cases set the line offsets to one line. */ break; } + + isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST); } /* @@ -981,10 +973,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 || format->code == V4L2_MBUS_FMT_UYVY8_2X8) { - /* The bridge is enabled for YUV8 formats. Configure the input - * mode accordingly. + /* According to the OMAP3 TRM the input mode only affects SYNC + * mode, enabling BT.656 mode should take precedence. However, + * in practice setting the input mode to YCbCr data on 8 bits + * seems to be required in BT.656 mode. In SYNC mode set it to + * YCbCr on 16 bits as the bridge is enabled in that case. */ - syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; + if (ccdc->bt656) + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; + else + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; } switch (data_size) { @@ -1008,9 +1006,15 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, if (pdata && pdata->hs_pol) syn_mode |= ISPCCDC_SYN_MODE_HDPOL; - if (pdata && pdata->vs_pol) + /* The polarity of the vertical sync signal output by the BT.656 + * decoder is not documented and seems to be active low. + */ + if ((pdata && pdata->vs_pol) || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_VDPOL; + if (pdata && pdata->fld_pol) + syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); /* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The @@ -1023,8 +1027,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_Y8POS); - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, - ISPCCDC_REC656IF_R656ON); + /* Enable or disable BT.656 mode, including error correction for the + * synchronization codes. + */ + if (ccdc->bt656) + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, + ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, + ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH); + } /* CCDC formats descriptions */ @@ -1115,17 +1127,33 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) unsigned long flags; unsigned int bridge; unsigned int shift; + unsigned int nph; + unsigned int sph; u32 syn_mode; u32 ccdc_pattern; + ccdc->bt656 = false; + ccdc->fields = 0; + pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - if (ccdc->input == CCDC_INPUT_PARALLEL) + if (ccdc->input == CCDC_INPUT_PARALLEL) { + struct v4l2_mbus_config cfg; + int ret; + + ret = v4l2_subdev_call(sensor, video, g_mbus_config, &cfg); + if (!ret) + ccdc->bt656 = cfg.type == V4L2_MBUS_BT656; + pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) ->bus.parallel; + } + + /* CCDC_PAD_SINK */ + format = &ccdc->formats[CCDC_PAD_SINK]; /* Compute the lane shifter shift value and enable the bridge when the - * input format is YUV. + * input format is a non-BT.656 YUV variant. */ fmt_src.pad = pad->index; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1134,12 +1162,13 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) depth_in = fmt_info->width; } - fmt_info = omap3isp_video_format_info - (isp->isp_ccdc.formats[CCDC_PAD_SINK].code); + fmt_info = omap3isp_video_format_info(format->code); depth_out = fmt_info->width; shift = depth_in - depth_out; - if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8) + if (ccdc->bt656) + bridge = ISPCTRL_PAR_BRIDGE_DISABLE; + else if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8) bridge = ISPCTRL_PAR_BRIDGE_LENDIAN; else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8) bridge = ISPCTRL_PAR_BRIDGE_BENDIAN; @@ -1148,6 +1177,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge); + /* Configure the sync interface. */ ccdc_config_sync_if(ccdc, pdata, depth_out); syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -1167,9 +1197,6 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) else syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; - /* CCDC_PAD_SINK */ - format = &ccdc->formats[CCDC_PAD_SINK]; - /* Mosaic filter */ switch (format->code) { case V4L2_MBUS_FMT_SRGGB10_1X10: @@ -1202,16 +1229,40 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; crop = &ccdc->crop; - isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | - ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + /* The horizontal coordinates are expressed in pixel clock cycles. We + * need two cycles per pixel in BT.656 mode, and one cycle per pixel in + * SYNC mode regardless of the format as the bridge is enabled for YUV + * formats in that case. + */ + if (ccdc->bt656) { + sph = crop->left * 2; + nph = crop->width * 2 - 1; + } else { + sph = crop->left; + nph = crop->width - 1; + } + + isp_reg_writel(isp, (sph << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); - isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, + isp_reg_writel(isp, (crop->top << ISPCCDC_VERT_START_SLV0_SHIFT) | + (crop->top << ISPCCDC_VERT_START_SLV1_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); isp_reg_writel(isp, (crop->height - 1) << ISPCCDC_VERT_LINES_NLV_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); - ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); + ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, + format->field); + + /* When interleaving fields enable processing of the field input signal. + * This will cause the line output control module to apply the field + * offset to field 1. + */ + if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE && + (format->field == V4L2_FIELD_INTERLACED_TB || + format->field == V4L2_FIELD_INTERLACED_BT)) + syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; /* The CCDC outputs data in UYVY order by default. Swap bytes to get * YUYV. @@ -1223,8 +1274,11 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_BSWD); - /* Use PACK8 mode for 1byte per pixel formats. */ - if (omap3isp_video_format_info(format->code)->width <= 8) + /* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode + * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad + * for simplicity. + */ + if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_PACK8; else syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; @@ -1232,18 +1286,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); /* CCDC_PAD_SOURCE_VP */ - format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; - - isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | - (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); - isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | - ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); - - isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | - (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); + ccdc_config_vp(ccdc); /* Lens shading correction. */ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); @@ -1277,6 +1320,8 @@ static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); + + ccdc->running = enable; } static int ccdc_disable(struct isp_ccdc_device *ccdc) @@ -1287,6 +1332,8 @@ static int ccdc_disable(struct isp_ccdc_device *ccdc) spin_lock_irqsave(&ccdc->lock, flags); if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) ccdc->stopping = CCDC_STOP_REQUEST; + if (!ccdc->running) + ccdc->stopping = CCDC_STOP_FINISHED; spin_unlock_irqrestore(&ccdc->lock, flags); ret = wait_event_timeout(ccdc->wait, @@ -1369,14 +1416,14 @@ static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, return -EBUSY; } -/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence +/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence * @ccdc: Pointer to ISP CCDC device. * @event: Pointing which event trigger handler * * Return 1 when the event and stopping request combination is satisfied, * zero otherwise. */ -static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) +static int ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) { int rval = 0; @@ -1458,7 +1505,7 @@ static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) if (ccdc->lsc.state == LSC_STATE_STOPPING) ccdc->lsc.state = LSC_STATE_STOPPED; - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) goto done; if (ccdc->lsc.state != LSC_STATE_RECONFIG) @@ -1486,12 +1533,59 @@ done: spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); } +/* + * Check whether the CCDC has captured all fields necessary to complete the + * buffer. + */ +static bool ccdc_has_all_fields(struct isp_ccdc_device *ccdc) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); + struct isp_device *isp = to_isp_device(ccdc); + enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field; + enum v4l2_field field; + + /* When the input is progressive fields don't matter. */ + if (of_field == V4L2_FIELD_NONE) + return true; + + /* Read the current field identifier. */ + field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE) + & ISPCCDC_SYN_MODE_FLDSTAT + ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; + + /* When capturing fields in alternate order just store the current field + * identifier in the pipeline. + */ + if (of_field == V4L2_FIELD_ALTERNATE) { + pipe->field = field; + return true; + } + + /* The format is interlaced. Make sure we've captured both fields. */ + ccdc->fields |= field == V4L2_FIELD_BOTTOM + ? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP; + + if (ccdc->fields != CCDC_FIELD_BOTH) + return false; + + /* Verify that the field just captured corresponds to the last field + * needed based on the desired field order. + */ + if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) || + (of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM)) + return false; + + /* The buffer can be completed, reset the fields for the next buffer. */ + ccdc->fields = 0; + + return true; +} + static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) { struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); struct isp_device *isp = to_isp_device(ccdc); struct isp_buffer *buffer; - int restart = 0; /* The CCDC generates VD0 interrupts even when disabled (the datasheet * doesn't explicitly state if that's supposed to happen or not, so it @@ -1500,30 +1594,31 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) * would thus not be enough, we need to handle the situation explicitly. */ if (list_empty(&ccdc->video_out.dmaqueue)) - goto done; + return 0; /* We're in continuous mode, and memory writes were disabled due to a * buffer underrun. Reenable them now that we have a buffer. The buffer * address has been set in ccdc_video_queue. */ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { - restart = 1; ccdc->underrun = 0; - goto done; + return 1; } + /* Wait for the CCDC to become idle. */ if (ccdc_sbl_wait_idle(ccdc, 1000)) { dev_info(isp->dev, "CCDC won't become idle!\n"); isp->crashed |= 1U << ccdc->subdev.entity.id; omap3isp_pipeline_cancel_stream(pipe); - goto done; + return 0; } + if (!ccdc_has_all_fields(ccdc)) + return 1; + buffer = omap3isp_video_buffer_next(&ccdc->video_out); - if (buffer != NULL) { + if (buffer != NULL) ccdc_set_outaddr(ccdc, buffer->dma); - restart = 1; - } pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; @@ -1532,8 +1627,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_SINGLESHOT); -done: - return restart; + return buffer != NULL; } /* @@ -1547,11 +1641,38 @@ static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) unsigned long flags; int restart = 0; + /* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus + * need to increment the frame counter here. + */ + if (ccdc->bt656) { + struct isp_pipeline *pipe = + to_isp_pipeline(&ccdc->subdev.entity); + + atomic_inc(&pipe->frame_number); + } + + /* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in + * the VD1 interrupt handler in that mode without risking a CCDC stall + * if a short frame is received. + */ + if (ccdc->bt656) { + spin_lock_irqsave(&ccdc->lock, flags); + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && + ccdc->output & CCDC_OUTPUT_MEMORY) { + if (ccdc->lsc.state != LSC_STATE_STOPPED) + __ccdc_lsc_enable(ccdc, 0); + __ccdc_enable(ccdc, 0); + } + ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1); + spin_unlock_irqrestore(&ccdc->lock, flags); + } + if (ccdc->output & CCDC_OUTPUT_MEMORY) restart = ccdc_isr_buffer(ccdc); spin_lock_irqsave(&ccdc->lock, flags); - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { + + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { spin_unlock_irqrestore(&ccdc->lock, flags); return; } @@ -1572,6 +1693,18 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) { unsigned long flags; + /* In BT.656 mode the synchronization signals are generated by the CCDC + * from the embedded sync codes. The VD0 and VD1 interrupts are thus + * only triggered when the CCDC is enabled, unlike external sync mode + * where the line counter runs even when the CCDC is stopped. We can't + * disable the CCDC at VD1 time, as no VD0 interrupt would be generated + * for a short frame, which would result in the CCDC being stopped and + * no VD interrupt generated anymore. The CCDC is stopped from the VD0 + * interrupt handler instead for BT.656. + */ + if (ccdc->bt656) + return; + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); /* @@ -1601,7 +1734,7 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) break; } - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) goto done; if (ccdc->lsc.request == NULL) @@ -1656,6 +1789,8 @@ int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) { struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; + unsigned long flags; + bool restart = false; if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) return -ENODEV; @@ -1664,9 +1799,20 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) /* We now have a buffer queued on the output, restart the pipeline * on the next CCDC interrupt if running in continuous mode (or when - * starting the stream). + * starting the stream) in external sync mode, or immediately in BT.656 + * sync mode as no CCDC interrupt is generated when the CCDC is stopped + * in that case. */ - ccdc->underrun = 1; + spin_lock_irqsave(&ccdc->lock, flags); + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running && + ccdc->bt656) + restart = true; + else + ccdc->underrun = 1; + spin_unlock_irqrestore(&ccdc->lock, flags); + + if (restart) + ccdc_enable(ccdc); return 0; } @@ -1753,11 +1899,6 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) ccdc_configure(ccdc); - /* TODO: Don't configure the video port if all of its output - * links are inactive. - */ - ccdc_config_vp(ccdc); - ccdc_enable_vp(ccdc, 1); ccdc_print_status(ccdc); } @@ -1830,6 +1971,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, unsigned int width = fmt->width; unsigned int height = fmt->height; struct v4l2_rect *crop; + enum v4l2_field field; unsigned int i; switch (pad) { @@ -1846,14 +1988,24 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, /* Clamp the input size. */ fmt->width = clamp_t(u32, width, 32, 4096); fmt->height = clamp_t(u32, height, 32, 4096); + + /* Default to progressive field order. */ + if (fmt->field == V4L2_FIELD_ANY) + fmt->field = V4L2_FIELD_NONE; + break; case CCDC_PAD_SOURCE_OF: pixelcode = fmt->code; + field = fmt->field; *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); - /* YUV formats are converted from 2X8 to 1X16 by the bridge and - * can be byte-swapped. + /* In SYNC mode the bridge converts YUV formats from 2X8 to + * 1X16. In BT.656 no such conversion occurs. As we don't know + * at this point whether the source will use SYNC or BT.656 mode + * let's pretend the conversion always occurs. The CCDC will be + * configured to pack bytes in BT.656, hiding the inaccuracy. + * In all cases bytes can be swapped. */ if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 || fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) { @@ -1874,6 +2026,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, crop = __ccdc_get_crop(ccdc, fh, which); fmt->width = crop->width; fmt->height = crop->height; + + /* When input format is interlaced with alternating fields the + * CCDC can interleave the fields. + */ + if (fmt->field == V4L2_FIELD_ALTERNATE && + (field == V4L2_FIELD_INTERLACED_TB || + field == V4L2_FIELD_INTERLACED_BT)) { + fmt->field = field; + fmt->height *= 2; + } + break; case CCDC_PAD_SOURCE_VP: @@ -1901,7 +2064,6 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, * stored on 2 bytes. */ fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->field = V4L2_FIELD_NONE; } /* diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h index f650616..3440a70 100644 --- a/drivers/media/platform/omap3isp/ispccdc.h +++ b/drivers/media/platform/omap3isp/ispccdc.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_CCDC_H @@ -103,6 +93,10 @@ struct ispccdc_lsc { #define CCDC_PAD_SOURCE_VP 2 #define CCDC_PADS_NUM 3 +#define CCDC_FIELD_TOP 1 +#define CCDC_FIELD_BOTTOM 2 +#define CCDC_FIELD_BOTH 3 + /* * struct isp_ccdc_device - Structure for the CCDC module to store its own * information @@ -123,11 +117,14 @@ struct ispccdc_lsc { * @lsc: Lens shading compensation configuration * @update: Bitmask of controls to update during the next interrupt * @shadow_update: Controls update in progress by userspace + * @bt656: Whether the input interface uses BT.656 synchronization + * @fields: The fields (CCDC_FIELD_*) stored in the current buffer * @underrun: A buffer underrun occurred and a new buffer has been queued * @state: Streaming state * @lock: Serializes shadow_update with interrupt handler * @wait: Wait queue used to stop the module * @stopping: Stopping state + * @running: Is the CCDC hardware running * @ioctl_lock: Serializes ioctl calls and LSC requests freeing */ struct isp_ccdc_device { @@ -151,11 +148,15 @@ struct isp_ccdc_device { unsigned int update; unsigned int shadow_update; + bool bt656; + unsigned int fields; + unsigned int underrun:1; enum isp_pipeline_stream_state state; spinlock_t lock; wait_queue_head_t wait; unsigned int stopping; + bool running; struct mutex ioctl_lock; }; diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index f3801db..9cb49b3 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/delay.h> diff --git a/drivers/media/platform/omap3isp/ispccp2.h b/drivers/media/platform/omap3isp/ispccp2.h index 76d65f4..4662bff 100644 --- a/drivers/media/platform/omap3isp/ispccp2.h +++ b/drivers/media/platform/omap3isp/ispccp2.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_CCP2_H diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 5a2e47e..6530b25 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/delay.h> #include <media/v4l2-common.h> diff --git a/drivers/media/platform/omap3isp/ispcsi2.h b/drivers/media/platform/omap3isp/ispcsi2.h index c57729b..453ed62 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.h +++ b/drivers/media/platform/omap3isp/ispcsi2.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_CSI2_H diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index c09de32..e033f22 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/delay.h> diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h index 14551fd..e17c88b 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/drivers/media/platform/omap3isp/ispcsiphy.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_CSI_PHY_H diff --git a/drivers/media/platform/omap3isp/isph3a.h b/drivers/media/platform/omap3isp/isph3a.h index fb09fd4..e5b28d0 100644 --- a/drivers/media/platform/omap3isp/isph3a.h +++ b/drivers/media/platform/omap3isp/isph3a.h @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_H3A_H diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index d6811ce..b208c54 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/slab.h> diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index 6fc960c..8a83e19 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ /* Linux specific include files */ diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index 06a5f81..ce822c3 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/delay.h> diff --git a/drivers/media/platform/omap3isp/isphist.h b/drivers/media/platform/omap3isp/isphist.h index 0b2a38e..3b54155 100644 --- a/drivers/media/platform/omap3isp/isphist.h +++ b/drivers/media/platform/omap3isp/isphist.h @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_HIST_H diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 720809b..605f57e 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/device.h> diff --git a/drivers/media/platform/omap3isp/isppreview.h b/drivers/media/platform/omap3isp/isppreview.h index f669234..16fdc03 100644 --- a/drivers/media/platform/omap3isp/isppreview.h +++ b/drivers/media/platform/omap3isp/isppreview.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_PREVIEW_H diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h index b7d90e6..b5ea8da 100644 --- a/drivers/media/platform/omap3isp/ispreg.h +++ b/drivers/media/platform/omap3isp/ispreg.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_REG_H @@ -740,17 +730,13 @@ #define ISPCCDC_HSIZE_OFF_SHIFT 0 -#define ISPCCDC_SDOFST_FINV (1 << 14) -#define ISPCCDC_SDOFST_FOFST_1L 0 -#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) +#define ISPCCDC_SDOFST_FIINV (1 << 14) +#define ISPCCDC_SDOFST_FOFST_SHIFT 12 +#define ISPCCDC_SDOFST_FOFST_MASK (3 << 12) #define ISPCCDC_SDOFST_LOFST3_SHIFT 0 #define ISPCCDC_SDOFST_LOFST2_SHIFT 3 #define ISPCCDC_SDOFST_LOFST1_SHIFT 6 #define ISPCCDC_SDOFST_LOFST0_SHIFT 9 -#define EVENEVEN 1 -#define ODDEVEN 2 -#define EVENODD 3 -#define ODDODD 4 #define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 #define ISPCCDC_CLAMP_OBST_SHIFT 10 diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index 6f077c2..05d1ace 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/device.h> @@ -239,7 +229,7 @@ static void resizer_set_phase(struct isp_res_device *res, u32 h_phase, u32 v_phase) { struct isp_device *isp = to_isp_device(res); - u32 rgval = 0; + u32 rgval; rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); @@ -275,7 +265,7 @@ static void resizer_set_luma(struct isp_res_device *res, struct resizer_luma_yenh *luma) { struct isp_device *isp = to_isp_device(res); - u32 rgval = 0; + u32 rgval; rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT) & ISPRSZ_YENH_ALGO_MASK; @@ -322,7 +312,7 @@ static void resizer_set_ratio(struct isp_res_device *res, { struct isp_device *isp = to_isp_device(res); const u16 *h_filter, *v_filter; - u32 rgval = 0; + u32 rgval; rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); @@ -365,9 +355,8 @@ static void resizer_set_output_size(struct isp_res_device *res, u32 width, u32 height) { struct isp_device *isp = to_isp_device(res); - u32 rgval = 0; + u32 rgval; - dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height); rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT) & ISPRSZ_OUT_SIZE_HORZ_MASK; rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT) @@ -409,7 +398,7 @@ static void resizer_set_output_offset(struct isp_res_device *res, u32 offset) static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top) { struct isp_device *isp = to_isp_device(res); - u32 rgval = 0; + u32 rgval; rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT) & ISPRSZ_IN_START_HORZ_ST_MASK; @@ -429,9 +418,7 @@ static void resizer_set_input_size(struct isp_res_device *res, u32 width, u32 height) { struct isp_device *isp = to_isp_device(res); - u32 rgval = 0; - - dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height); + u32 rgval; rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT) & ISPRSZ_IN_SIZE_HORZ_MASK; @@ -1075,10 +1062,13 @@ static void resizer_isr_buffer(struct isp_res_device *res) void omap3isp_resizer_isr(struct isp_res_device *res) { struct v4l2_mbus_framefmt *informat, *outformat; + unsigned long flags; if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) return; + spin_lock_irqsave(&res->lock, flags); + if (res->applycrop) { outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, V4L2_SUBDEV_FORMAT_ACTIVE); @@ -1088,6 +1078,8 @@ void omap3isp_resizer_isr(struct isp_res_device *res) res->applycrop = 0; } + spin_unlock_irqrestore(&res->lock, flags); + resizer_isr_buffer(res); } @@ -1290,8 +1282,10 @@ static int resizer_set_selection(struct v4l2_subdev *sd, { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(res); - struct v4l2_mbus_framefmt *format_sink, *format_source; + const struct v4l2_mbus_framefmt *format_sink; + struct v4l2_mbus_framefmt format_source; struct resizer_ratio ratio; + unsigned long flags; if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) @@ -1299,16 +1293,14 @@ static int resizer_set_selection(struct v4l2_subdev *sd, format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, sel->which); - format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, - sel->which); - - dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, - sel->r.left, sel->r.top, sel->r.width, sel->r.height, - sel->which); + format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, + sel->which); - dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, + dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", + __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act", format_sink->width, format_sink->height, - format_source->width, format_source->height); + sel->r.left, sel->r.top, sel->r.width, sel->r.height, + format_source.width, format_source.height); /* Clamp the crop rectangle to the bounds, and then mangle it further to * fulfill the TRM equations. Store the clamped but otherwise unmangled @@ -1318,23 +1310,39 @@ static int resizer_set_selection(struct v4l2_subdev *sd, * smaller input crop rectangle every time the output size is set if we * stored the mangled rectangle. */ - resizer_try_crop(format_sink, format_source, &sel->r); + resizer_try_crop(format_sink, &format_source, &sel->r); *__resizer_get_crop(res, fh, sel->which) = sel->r; - resizer_calc_ratios(res, &sel->r, format_source, &ratio); + resizer_calc_ratios(res, &sel->r, &format_source, &ratio); - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", + __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act", + format_sink->width, format_sink->height, + sel->r.left, sel->r.top, sel->r.width, sel->r.height, + format_source.width, format_source.height); + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + format_source; return 0; + } + + /* Update the source format, resizing ratios and crop rectangle. If + * streaming is on the IRQ handler will reprogram the resizer after the + * current frame. We thus we need to protect against race conditions. + */ + spin_lock_irqsave(&res->lock, flags); + + *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + format_source; res->ratio = ratio; res->crop.active = sel->r; - /* - * set_selection can be called while streaming is on. In this case the - * crop values will be set in the next IRQ. - */ if (res->state != ISP_PIPELINE_STREAM_STOPPED) res->applycrop = 1; + spin_unlock_irqrestore(&res->lock, flags); + return 0; } @@ -1781,6 +1789,8 @@ int omap3isp_resizer_init(struct isp_device *isp) init_waitqueue_head(&res->wait); atomic_set(&res->stopping, 0); + spin_lock_init(&res->lock); + return resizer_init_entities(res); } diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h index 9b01e90..5414542 100644 --- a/drivers/media/platform/omap3isp/ispresizer.h +++ b/drivers/media/platform/omap3isp/ispresizer.h @@ -12,21 +12,12 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_RESIZER_H #define OMAP3_ISP_RESIZER_H +#include <linux/spinlock.h> #include <linux/types.h> /* @@ -96,6 +87,7 @@ enum resizer_input_entity { /* * struct isp_res_device - OMAP3 ISP resizer module + * @lock: Protects formats and crop rectangles between set_selection and IRQ * @crop.request: Crop rectangle requested by the user * @crop.active: Active crop rectangle (based on hardware requirements) */ @@ -116,6 +108,7 @@ struct isp_res_device { enum isp_pipeline_stream_state state; wait_queue_head_t wait; atomic_t stopping; + spinlock_t lock; struct { struct v4l2_rect request; diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index e6cbc1e..a94e834 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/dma-mapping.h> diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index 58d6ac7..b32b296 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -13,16 +13,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_STAT_H diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index e36bac2..bc38c88 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -11,16 +11,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <asm/cacheflush.h> @@ -319,10 +309,11 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) vfh->format.fmt.pix.height != format.fmt.pix.height || vfh->format.fmt.pix.width != format.fmt.pix.width || vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || - vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) + vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage || + vfh->format.fmt.pix.field != format.fmt.pix.field) return -EINVAL; - return ret; + return 0; } /* ----------------------------------------------------------------------------- @@ -491,6 +482,11 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) else buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); + if (pipe->field != V4L2_FIELD_NONE) + buf->vb.v4l2_buf.sequence /= 2; + + buf->vb.v4l2_buf.field = pipe->field; + /* Report pipeline errors to userspace on the capture device side. */ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { state = VB2_BUF_STATE_ERROR; @@ -641,7 +637,40 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) if (format->type != video->type) return -EINVAL; - mutex_lock(&video->mutex); + /* Replace unsupported field orders with sane defaults. */ + switch (format->fmt.pix.field) { + case V4L2_FIELD_NONE: + /* Progressive is supported everywhere. */ + break; + case V4L2_FIELD_ALTERNATE: + /* ALTERNATE is not supported on output nodes. */ + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_INTERLACED: + /* The ISP has no concept of video standard, select the + * top-bottom order when the unqualified interlaced order is + * requested. + */ + format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; + /* Fall-through */ + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + /* Interlaced orders are only supported at the CCDC output. */ + if (video != &video->isp->isp_ccdc.video_out) + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + default: + /* All other field orders are currently unsupported, default to + * progressive. + */ + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + } /* Fill the bytesperline and sizeimage fields by converting to media bus * format and back to pixel format. @@ -649,9 +678,10 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) isp_video_pix_to_mbus(&format->fmt.pix, &fmt); isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); + mutex_lock(&video->mutex); vfh->format = *format; - mutex_unlock(&video->mutex); + return 0; } @@ -1039,6 +1069,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = &vfh->queue; INIT_LIST_HEAD(&video->dmaqueue); atomic_set(&pipe->frame_number, -1); + pipe->field = vfh->format.fmt.pix.field; mutex_lock(&video->queue_lock); ret = vb2_streamon(&vfh->queue, type); diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 7d2e821..0b7efed 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -11,16 +11,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef OMAP3_ISP_VIDEO_H @@ -88,6 +78,7 @@ enum isp_pipeline_state { /* * struct isp_pipeline - An ISP hardware pipeline + * @field: The field being processed by the pipeline * @error: A hardware error occurred during capture * @entities: Bitmask of entities in the pipeline (indexed by entity ID) */ @@ -101,6 +92,7 @@ struct isp_pipeline { u32 entities; unsigned long l3_ick; unsigned int max_rate; + enum v4l2_field field; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; diff --git a/drivers/media/platform/omap3isp/luma_enhance_table.h b/drivers/media/platform/omap3isp/luma_enhance_table.h index 098b45e..81c5b15 100644 --- a/drivers/media/platform/omap3isp/luma_enhance_table.h +++ b/drivers/media/platform/omap3isp/luma_enhance_table.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, diff --git a/drivers/media/platform/omap3isp/noise_filter_table.h b/drivers/media/platform/omap3isp/noise_filter_table.h index d50451a..5073f98 100644 --- a/drivers/media/platform/omap3isp/noise_filter_table.h +++ b/drivers/media/platform/omap3isp/noise_filter_table.h @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA */ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index f336413..4f81b4c 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -280,8 +280,8 @@ static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb, return -EINVAL; } - pr_debug("DMA address: y: %#x cb: %#x cr: %#x\n", - paddr->y, paddr->cb, paddr->cr); + pr_debug("DMA address: y: %pad cb: %pad cr: %pad\n", + &paddr->y, &paddr->cb, &paddr->cr); return 0; } diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c index ebf5b18..6e0c998 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.c +++ b/drivers/media/platform/s3c-camif/camif-regs.c @@ -214,8 +214,8 @@ void camif_hw_set_output_addr(struct camif_vp *vp, paddr->cr); } - pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n", - i, paddr->y, paddr->cb, paddr->cr); + pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad\n", + i, &paddr->y, &paddr->cb, &paddr->cr); } static void camif_hw_set_out_dma_size(struct camif_vp *vp) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 357af1e..d79e214 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -490,14 +490,13 @@ static void job_abort(void *prv) { struct g2d_ctx *ctx = prv; struct g2d_dev *dev = ctx->dev; - int ret; if (dev->curr == NULL) /* No job currently running */ return; - ret = wait_event_timeout(dev->irq_queue, - dev->curr == NULL, - msecs_to_jiffies(G2D_TIMEOUT)); + wait_event_timeout(dev->irq_queue, + dev->curr == NULL, + msecs_to_jiffies(G2D_TIMEOUT)); } static void device_run(void *prv) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index e66acbc..e525a7c 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -729,7 +729,7 @@ static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality) ARRAY_SIZE(qtbl_chrominance[quality])); } -void exynos4_jpeg_set_huff_tbl(void __iomem *base) +static void exynos4_jpeg_set_huff_tbl(void __iomem *base) { exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL, ARRAY_SIZE(hdctbl0)); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c index d26e1f8..e8c2cad 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c @@ -233,6 +233,7 @@ void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x) writel(reg, regs + EXYNOS3250_JPGX); } +#if 0 /* Currently unused */ unsigned int exynos3250_jpeg_get_y(void __iomem *regs) { return readl(regs + EXYNOS3250_JPGY); @@ -242,6 +243,7 @@ unsigned int exynos3250_jpeg_get_x(void __iomem *regs) { return readl(regs + EXYNOS3250_JPGX); } +#endif void exynos3250_jpeg_interrupts_enable(void __iomem *regs) { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c index da8d6a1..ab6d6f4 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c @@ -23,7 +23,7 @@ void exynos4_jpeg_sw_reset(void __iomem *base) reg = readl(base + EXYNOS4_JPEG_CNTL_REG); writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); - ndelay(100000); + udelay(100); writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); } @@ -151,9 +151,6 @@ void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt) void exynos4_jpeg_set_interrupt(void __iomem *base) { - unsigned int reg; - - reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); } @@ -185,7 +182,7 @@ void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) writel(reg | EXYNOS4_HUF_TBL_EN, base + EXYNOS4_JPEG_CNTL_REG); else - writel(reg | ~EXYNOS4_HUF_TBL_EN, + writel(reg & ~EXYNOS4_HUF_TBL_EN, base + EXYNOS4_JPEG_CNTL_REG); } @@ -196,9 +193,9 @@ void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value) reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN); if (value == 1) - writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); + writel(reg | EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); else - writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); + writel(reg & ~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); } void exynos4_jpeg_set_stream_buf_address(void __iomem *base, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index 52407d7..e3b8e67 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -324,11 +324,9 @@ int s5p_jpeg_stream_stat_ok(void __iomem *regs) void s5p_jpeg_clear_int(void __iomem *regs) { - unsigned long reg; - - reg = readl(regs + S5P_JPGINTST); + readl(regs + S5P_JPGINTST); writel(S5P_INT_RELEASE, regs + S5P_JPGCOM); - reg = readl(regs + S5P_JPGOPR); + readl(regs + S5P_JPGOPR); } unsigned int s5p_jpeg_compressed_size(void __iomem *regs) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index d35b041..165bc86 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -37,8 +37,8 @@ #define S5P_MFC_DEC_NAME "s5p-mfc-dec" #define S5P_MFC_ENC_NAME "s5p-mfc-enc" -int debug; -module_param(debug, int, S_IRUGO | S_IWUSR); +int mfc_debug_level; +module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages"); /* Helper functions for interrupt processing */ @@ -150,10 +150,10 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work) if (!ctx) continue; ctx->state = MFCINST_ERROR; - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, - &ctx->vq_dst); - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, - &ctx->vq_src); + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, + &ctx->dst_queue, &ctx->vq_dst); + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, + &ctx->src_queue, &ctx->vq_src); clear_work_bit(ctx); wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0); } @@ -264,7 +264,12 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) unsigned int frame_type; 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_disp_frame_type, ctx); + if (IS_MFCV6_PLUS(dev)) + frame_type = s5p_mfc_hw_call(dev->mfc_ops, + get_disp_frame_type, ctx); + else + frame_type = s5p_mfc_hw_call(dev->mfc_ops, + get_dec_frame_type, dev); /* If frame is same as previous then skip and do not dequeue */ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) { @@ -327,12 +332,12 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, if (res_change == S5P_FIMV_RES_INCREASE || res_change == S5P_FIMV_RES_DECREASE) { ctx->state = MFCINST_RES_CHANGE_INIT; - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); wake_up_ctx(ctx, reason, err); if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); return; } if (ctx->dpb_flush_flag) @@ -400,7 +405,7 @@ leave_handle_frame: if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING) || ctx->dst_queue_cnt < ctx->pb_count) clear_work_bit(ctx); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); wake_up_ctx(ctx, reason, err); if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); @@ -409,7 +414,7 @@ leave_handle_frame: if (test_bit(0, &dev->enter_suspend)) wake_up_dev(dev, reason, err); else - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } /* Error handling for interrupt */ @@ -435,10 +440,10 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, ctx->state = MFCINST_ERROR; /* Mark all dst buffers as having an error */ spin_lock_irqsave(&dev->irqlock, flags); - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, &ctx->vq_dst); /* Mark all src buffers as having an error */ - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->src_queue, &ctx->vq_src); spin_unlock_irqrestore(&dev->irqlock, flags); wake_up_ctx(ctx, reason, err); @@ -452,7 +457,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, } if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); s5p_mfc_clock_off(); wake_up_dev(dev, reason, err); return; @@ -476,7 +481,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height, dev); - s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, dec_calc_dpb_size, ctx); ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count, dev); @@ -503,12 +508,12 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, ctx->head_processed = 1; } } - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); clear_work_bit(ctx); if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); wake_up_ctx(ctx, reason, err); } @@ -523,7 +528,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, if (ctx == NULL) return; dev = ctx->dev; - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); ctx->int_type = reason; ctx->int_err = err; ctx->int_cond = 1; @@ -550,7 +555,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, s5p_mfc_clock_off(); wake_up(&ctx->queue); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } else { if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); @@ -591,7 +596,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, s5p_mfc_clock_off(); wake_up(&ctx->queue); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } /* Interrupt processing */ @@ -628,12 +633,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) if (ctx->c_ops->post_frame_start) { if (ctx->c_ops->post_frame_start(ctx)) mfc_err("post_frame_start() failed\n"); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); wake_up_ctx(ctx, reason, err); if (test_and_clear_bit(0, &dev->hw_lock) == 0) BUG(); s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } else { s5p_mfc_handle_frame(ctx, reason, err); } @@ -663,7 +668,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) case S5P_MFC_R2H_CMD_WAKEUP_RET: if (ctx) clear_work_bit(ctx); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); wake_up_dev(dev, reason, err); clear_bit(0, &dev->hw_lock); clear_bit(0, &dev->enter_suspend); @@ -685,12 +690,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) default: mfc_debug(2, "Unknown int reason\n"); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); } mfc_debug_leave(); return IRQ_HANDLED; irq_cleanup_hw: - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); ctx->int_type = reason; ctx->int_err = err; ctx->int_cond = 1; @@ -699,7 +704,7 @@ irq_cleanup_hw: s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); mfc_debug(2, "Exit via irq_cleanup_hw\n"); return IRQ_HANDLED; } @@ -1311,11 +1316,9 @@ static int s5p_mfc_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); - int pre_power; if (!m_dev->alloc_ctx) return 0; - pre_power = atomic_read(&m_dev->pm.power); atomic_set(&m_dev->pm.power, 1); return 0; } @@ -1328,20 +1331,20 @@ static const struct dev_pm_ops s5p_mfc_pm_ops = { NULL) }; -struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = { +static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = { .h264_ctx = MFC_H264_CTX_BUF_SIZE, .non_h264_ctx = MFC_CTX_BUF_SIZE, .dsc = DESC_BUF_SIZE, .shm = SHARED_BUF_SIZE, }; -struct s5p_mfc_buf_size buf_size_v5 = { +static struct s5p_mfc_buf_size buf_size_v5 = { .fw = MAX_FW_SIZE, .cpb = MAX_CPB_SIZE, .priv = &mfc_buf_size_v5, }; -struct s5p_mfc_buf_align mfc_buf_align_v5 = { +static struct s5p_mfc_buf_align mfc_buf_align_v5 = { .base = MFC_BASE_ALIGN_ORDER, }; @@ -1354,7 +1357,7 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = { .fw_name[0] = "s5p-mfc.fw", }; -struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = { +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = { .dev_ctx = MFC_CTX_BUF_SIZE_V6, .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V6, .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V6, @@ -1362,13 +1365,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = { .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V6, }; -struct s5p_mfc_buf_size buf_size_v6 = { +static struct s5p_mfc_buf_size buf_size_v6 = { .fw = MAX_FW_SIZE_V6, .cpb = MAX_CPB_SIZE_V6, .priv = &mfc_buf_size_v6, }; -struct s5p_mfc_buf_align mfc_buf_align_v6 = { +static struct s5p_mfc_buf_align mfc_buf_align_v6 = { .base = 0, }; @@ -1386,7 +1389,7 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = { .fw_name[1] = "s5p-mfc-v6-v2.fw", }; -struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { .dev_ctx = MFC_CTX_BUF_SIZE_V7, .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7, .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7, @@ -1394,13 +1397,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7, }; -struct s5p_mfc_buf_size buf_size_v7 = { +static struct s5p_mfc_buf_size buf_size_v7 = { .fw = MAX_FW_SIZE_V7, .cpb = MAX_CPB_SIZE_V7, .priv = &mfc_buf_size_v7, }; -struct s5p_mfc_buf_align mfc_buf_align_v7 = { +static struct s5p_mfc_buf_align mfc_buf_align_v7 = { .base = 0, }; @@ -1413,7 +1416,7 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = { .fw_name[0] = "s5p-mfc-v7.fw", }; -struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { .dev_ctx = MFC_CTX_BUF_SIZE_V8, .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8, .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8, @@ -1421,13 +1424,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8, }; -struct s5p_mfc_buf_size buf_size_v8 = { +static struct s5p_mfc_buf_size buf_size_v8 = { .fw = MAX_FW_SIZE_V8, .cpb = MAX_CPB_SIZE_V8, .priv = &mfc_buf_size_v8, }; -struct s5p_mfc_buf_align mfc_buf_align_v8 = { +static struct s5p_mfc_buf_align mfc_buf_align_v8 = { .base = 0, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c index 9a6efd6..8c4739c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c @@ -14,6 +14,7 @@ #include "s5p_mfc_cmd.h" #include "s5p_mfc_common.h" #include "s5p_mfc_debug.h" +#include "s5p_mfc_cmd_v5.h" /* This function is used to send a command to the MFC */ static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index ec1a594..f176096 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -16,6 +16,7 @@ #include "s5p_mfc_debug.h" #include "s5p_mfc_intr.h" #include "s5p_mfc_opr.h" +#include "s5p_mfc_cmd_v6.h" static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, struct s5p_mfc_cmd_args *args) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 01816ff..3e41ca1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -698,6 +698,12 @@ struct mfc_control { #define s5p_mfc_hw_call(f, op, args...) \ ((f && f->op) ? f->op(args) : -ENODEV) +#define s5p_mfc_hw_call_void(f, op, args...) \ +do { \ + if (f && f->op) \ + f->op(args); \ +} while (0) + #define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh) #define ctrl_to_ctx(__ctrl) \ container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index ca9f789..0c885a8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -21,6 +21,7 @@ #include "s5p_mfc_intr.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_pm.h" +#include "s5p_mfc_ctrl.h" /* Allocate memory for firmware */ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) @@ -188,12 +189,12 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev) { if (IS_MFCV6_PLUS(dev)) { mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6); - mfc_debug(2, "Base Address : %08x\n", dev->bank1); + mfc_debug(2, "Base Address : %pad\n", &dev->bank1); } else { mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A); mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B); - mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", - dev->bank1, dev->bank2); + mfc_debug(2, "Bank1: %pad, Bank2: %pad\n", + &dev->bank1, &dev->bank2); } } @@ -257,9 +258,9 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) s5p_mfc_clock_off(); return ret; } - mfc_debug(2, "Ok, now will write a command to init the system\n"); + mfc_debug(2, "Ok, now will wait for completion of hardware init\n"); if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SYS_INIT_RET)) { - mfc_err("Failed to load firmware\n"); + mfc_err("Failed to init hardware\n"); s5p_mfc_reset(dev); s5p_mfc_clock_off(); return -EIO; @@ -293,7 +294,7 @@ void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev) s5p_mfc_clock_on(); s5p_mfc_reset(dev); - s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, release_dev_context_buffer, dev); s5p_mfc_clock_off(); } @@ -396,7 +397,7 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) set_work_bit_irqsave(ctx); s5p_mfc_clean_ctx_int_flags(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); if (s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) { /* Error or timeout */ @@ -410,9 +411,9 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) err_free_desc_buf: if (ctx->type == MFCINST_DECODER) - s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx); err_free_inst_buf: - s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx); err: return ret; } @@ -422,17 +423,17 @@ void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) ctx->state = MFCINST_RETURN_INST; set_work_bit_irqsave(ctx); s5p_mfc_clean_ctx_int_flags(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); /* Wait until instance is returned or timeout occurred */ if (s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) mfc_err("Err returning instance\n"); /* Free resources */ - s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); - s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx); if (ctx->type == MFCINST_DECODER) - s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx); ctx->inst_no = MFC_NO_INSTANCE_SET; ctx->state = MFCINST_FREE; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h index 8e608f5..5936923 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h @@ -1,5 +1,5 @@ /* - * drivers/media/platform/samsung/mfc5/s5p_mfc_debug.h + * drivers/media/platform/s5p-mfc/s5p_mfc_debug.h * * Header file for Samsung MFC (Multi Function Codec - FIMV) driver * This file contains debug macros @@ -18,11 +18,11 @@ #define DEBUG #ifdef DEBUG -extern int debug; +extern int mfc_debug_level; #define mfc_debug(level, fmt, args...) \ do { \ - if (debug >= level) \ + if (mfc_debug_level >= level) \ printk(KERN_DEBUG "%s:%d: " fmt, \ __func__, __LINE__, ##args); \ } while (0) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 9103258..a98fe02 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -283,17 +283,13 @@ static int vidioc_querycap(struct file *file, void *priv, /* Enumerate format */ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, - bool mplane, bool out) + bool out) { struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_fmt *fmt; int i, j = 0; for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (mplane && formats[i].num_planes == 1) - continue; - else if (!mplane && formats[i].num_planes > 1) - continue; if (out && formats[i].type != MFC_FMT_DEC) continue; else if (!out && formats[i].type != MFC_FMT_RAW) @@ -313,28 +309,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return 0; } -static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, f, false, false); -} - static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(file, f, true, false); -} - -static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, f, false, true); + return vidioc_enum_fmt(file, f, false); } static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(file, f, true, true); + return vidioc_enum_fmt(file, f, true); } /* Get format */ @@ -543,7 +527,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); if (ret) goto out; - s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx); ctx->dst_bufs_cnt = 0; } else if (ctx->capture_state == QUEUE_FREE) { WARN_ON(ctx->dst_bufs_cnt != 0); @@ -571,7 +555,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0); } else { @@ -823,8 +807,8 @@ static int vidioc_g_crop(struct file *file, void *priv, return 0; } -int vidioc_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int vidioc_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -846,7 +830,7 @@ int vidioc_decoder_cmd(struct file *file, void *priv, if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); spin_unlock_irqrestore(&dev->irqlock, flags); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } else { mfc_err("EOS: marking last buffer of stream"); buf = list_entry(ctx->src_queue.prev, @@ -881,9 +865,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, /* v4l2_ioctl_ops */ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, @@ -990,7 +972,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (ctx->capture_state == QUEUE_BUFS_MMAPED) return 0; - for (i = 0; i <= ctx->src_fmt->num_planes ; i++) { + for (i = 0; i < ctx->dst_fmt->num_planes; i++) { if (IS_ERR_OR_NULL(ERR_PTR( vb2_dma_contig_plane_dma_addr(vb, i)))) { mfc_err("Plane mem not allocated\n"); @@ -1044,7 +1026,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) /* If context is ready then dev = work->data;schedule it to run */ if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); return 0; } @@ -1065,8 +1047,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q) } 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); + s5p_mfc_hw_call_void(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; @@ -1076,7 +1058,7 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q) 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); + s5p_mfc_hw_call_void(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"); @@ -1084,8 +1066,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q) } 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); + s5p_mfc_hw_call_void(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); @@ -1124,7 +1106,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) } if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } static struct vb2_ops s5p_mfc_dec_qops = { @@ -1220,7 +1202,7 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx) else f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT; ctx->dst_fmt = find_format(&f, MFC_FMT_RAW); - mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n", - (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt); + mfc_debug(2, "Default src_fmt is %p, dest_fmt is %p\n", + ctx->src_fmt, ctx->dst_fmt); } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index d26b248..a904a1c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -739,14 +739,11 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx) static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_buf *mb_entry; - unsigned long mb_y_addr, mb_c_addr; /* move buffers in ref queue to src queue */ while (!list_empty(&ctx->ref_queue)) { mb_entry = list_entry((&ctx->ref_queue)->next, struct s5p_mfc_buf, list); - mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); list_del(&mb_entry->list); ctx->ref_queue_cnt--; list_add_tail(&mb_entry->list, &ctx->src_queue); @@ -770,7 +767,7 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx) dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); - s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, + s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); return 0; @@ -803,7 +800,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) ctx->state = MFCINST_RUNNING; if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } else { enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_enc_dpb_count, dev); @@ -828,15 +825,15 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx) src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); - s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx, src_y_addr, - src_c_addr); + s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_frame_buffer, ctx, + src_y_addr, src_c_addr); spin_unlock_irqrestore(&dev->irqlock, flags); spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); - s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, + s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -861,7 +858,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT)); spin_lock_irqsave(&dev->irqlock, flags); if (slice_type >= 0) { - s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx, + s5p_mfc_hw_call_void(dev->mfc_ops, get_enc_frame_buffer, ctx, &enc_y_addr, &enc_c_addr); list_for_each_entry(mb_entry, &ctx->src_queue, list) { mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); @@ -954,17 +951,13 @@ static int vidioc_querycap(struct file *file, void *priv, } static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, - bool mplane, bool out) + bool out) { struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_fmt *fmt; int i, j = 0; for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (mplane && formats[i].num_planes == 1) - continue; - else if (!mplane && formats[i].num_planes > 1) - continue; if (out && formats[i].type != MFC_FMT_RAW) continue; else if (!out && formats[i].type != MFC_FMT_ENC) @@ -984,28 +977,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return -EINVAL; } -static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, f, false, false); -} - static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(file, f, true, false); -} - -static int vidioc_enum_fmt_vid_out(struct file *file, void *prov, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, f, false, true); + return vidioc_enum_fmt(file, f, false); } static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(file, f, true, true); + return vidioc_enum_fmt(file, f, true); } static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) @@ -1127,7 +1108,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt_mp->width, pix_fmt_mp->height, ctx->img_width, ctx->img_height); - s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx); + s5p_mfc_hw_call_void(dev->mfc_ops, enc_calc_src_size, ctx); pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size; pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width; pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size; @@ -1681,8 +1662,8 @@ static int vidioc_g_parm(struct file *file, void *priv, return 0; } -int vidioc_encoder_cmd(struct file *file, void *priv, - struct v4l2_encoder_cmd *cmd) +static int vidioc_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *cmd) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -1704,7 +1685,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv, if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); spin_unlock_irqrestore(&dev->irqlock, flags); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } else { mfc_debug(2, "EOS: marking last buffer of stream\n"); buf = list_entry(ctx->src_queue.prev, @@ -1736,9 +1717,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, @@ -1771,13 +1750,13 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) return -EINVAL; } for (i = 0; i < fmt->num_planes; i++) { - if (!vb2_dma_contig_plane_dma_addr(vb, i)) { + dma_addr_t dma = vb2_dma_contig_plane_dma_addr(vb, i); + if (!dma) { mfc_err("failed to get plane cookie\n"); return -EINVAL; } - mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx\n", - vb->v4l2_buf.index, i, - vb2_dma_contig_plane_dma_addr(vb, i)); + mfc_debug(2, "index: %d, plane[%d] cookie: %pad\n", + vb->v4l2_buf.index, i, &dma); } return 0; } @@ -1897,7 +1876,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb) ret = check_vb_with_fmt(ctx->dst_fmt, vb); if (ret < 0) return ret; - mfc_debug(2, "plane size: %ld, dst size: %d\n", + mfc_debug(2, "plane size: %ld, dst size: %zu\n", vb2_plane_size(vb, 0), ctx->enc_dst_buf_size); if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) { mfc_err("plane size is too small for capture\n"); @@ -1948,7 +1927,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) /* If context is ready then dev = work->data;schedule it to run */ if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); return 0; } @@ -1969,14 +1948,14 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q) ctx->state = MFCINST_FINISHED; spin_lock_irqsave(&dev->irqlock, flags); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, - &ctx->vq_dst); + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, + &ctx->dst_queue, &ctx->vq_dst); INIT_LIST_HEAD(&ctx->dst_queue); ctx->dst_queue_cnt = 0; } if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { cleanup_ref_queue(ctx); - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, + s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->src_queue, &ctx->vq_src); INIT_LIST_HEAD(&ctx->src_queue); ctx->src_queue_cnt = 0; @@ -2017,7 +1996,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) } if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev); } static struct vb2_ops s5p_mfc_enc_qops = { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index c9a2274..00a1d8b 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -41,7 +41,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, struct s5p_mfc_priv_buf *b) { - mfc_debug(3, "Allocating priv: %d\n", b->size); + mfc_debug(3, "Allocating priv: %zu\n", b->size); b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); @@ -50,7 +50,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, return -ENOMEM; } - mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma); + mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma); return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 7a7ad32..de2b8c6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -20,254 +20,254 @@ struct s5p_mfc_regs { /* codec common registers */ - void *risc_on; - void *risc2host_int; - void *host2risc_int; - void *risc_base_address; - void *mfc_reset; - void *host2risc_command; - void *risc2host_command; - void *mfc_bus_reset_ctrl; - void *firmware_version; - void *instance_id; - void *codec_type; - void *context_mem_addr; - void *context_mem_size; - void *pixel_format; - void *metadata_enable; - void *mfc_version; - void *dbg_info_enable; - void *dbg_buffer_addr; - void *dbg_buffer_size; - void *hed_control; - void *mfc_timeout_value; - void *hed_shared_mem_addr; - void *dis_shared_mem_addr;/* only v7 */ - void *ret_instance_id; - void *error_code; - void *dbg_buffer_output_size; - void *metadata_status; - void *metadata_addr_mb_info; - void *metadata_size_mb_info; - void *dbg_info_stage_counter; + volatile void __iomem *risc_on; + volatile void __iomem *risc2host_int; + volatile void __iomem *host2risc_int; + volatile void __iomem *risc_base_address; + volatile void __iomem *mfc_reset; + volatile void __iomem *host2risc_command; + volatile void __iomem *risc2host_command; + volatile void __iomem *mfc_bus_reset_ctrl; + volatile void __iomem *firmware_version; + volatile void __iomem *instance_id; + volatile void __iomem *codec_type; + volatile void __iomem *context_mem_addr; + volatile void __iomem *context_mem_size; + volatile void __iomem *pixel_format; + volatile void __iomem *metadata_enable; + volatile void __iomem *mfc_version; + volatile void __iomem *dbg_info_enable; + volatile void __iomem *dbg_buffer_addr; + volatile void __iomem *dbg_buffer_size; + volatile void __iomem *hed_control; + volatile void __iomem *mfc_timeout_value; + volatile void __iomem *hed_shared_mem_addr; + volatile void __iomem *dis_shared_mem_addr;/* only v7 */ + volatile void __iomem *ret_instance_id; + volatile void __iomem *error_code; + volatile void __iomem *dbg_buffer_output_size; + volatile void __iomem *metadata_status; + volatile void __iomem *metadata_addr_mb_info; + volatile void __iomem *metadata_size_mb_info; + volatile void __iomem *dbg_info_stage_counter; /* decoder registers */ - void *d_crc_ctrl; - void *d_dec_options; - void *d_display_delay; - void *d_set_frame_width; - void *d_set_frame_height; - void *d_sei_enable; - void *d_min_num_dpb; - void *d_min_first_plane_dpb_size; - void *d_min_second_plane_dpb_size; - void *d_min_third_plane_dpb_size;/* only v8 */ - void *d_min_num_mv; - void *d_mvc_num_views; - void *d_min_num_dis;/* only v7 */ - void *d_min_first_dis_size;/* only v7 */ - void *d_min_second_dis_size;/* only v7 */ - void *d_min_third_dis_size;/* only v7 */ - void *d_post_filter_luma_dpb0;/* v7 and v8 */ - void *d_post_filter_luma_dpb1;/* v7 and v8 */ - void *d_post_filter_luma_dpb2;/* only v7 */ - void *d_post_filter_chroma_dpb0;/* v7 and v8 */ - void *d_post_filter_chroma_dpb1;/* v7 and v8 */ - void *d_post_filter_chroma_dpb2;/* only v7 */ - void *d_num_dpb; - void *d_num_mv; - void *d_init_buffer_options; - void *d_first_plane_dpb_stride_size;/* only v8 */ - void *d_second_plane_dpb_stride_size;/* only v8 */ - void *d_third_plane_dpb_stride_size;/* only v8 */ - void *d_first_plane_dpb_size; - void *d_second_plane_dpb_size; - void *d_third_plane_dpb_size;/* only v8 */ - void *d_mv_buffer_size; - void *d_first_plane_dpb; - void *d_second_plane_dpb; - void *d_third_plane_dpb; - void *d_mv_buffer; - void *d_scratch_buffer_addr; - void *d_scratch_buffer_size; - void *d_metadata_buffer_addr; - void *d_metadata_buffer_size; - void *d_nal_start_options;/* v7 and v8 */ - void *d_cpb_buffer_addr; - void *d_cpb_buffer_size; - void *d_available_dpb_flag_upper; - void *d_available_dpb_flag_lower; - void *d_cpb_buffer_offset; - void *d_slice_if_enable; - void *d_picture_tag; - void *d_stream_data_size; - void *d_dynamic_dpb_flag_upper;/* v7 and v8 */ - void *d_dynamic_dpb_flag_lower;/* v7 and v8 */ - void *d_display_frame_width; - void *d_display_frame_height; - void *d_display_status; - void *d_display_first_plane_addr; - void *d_display_second_plane_addr; - void *d_display_third_plane_addr;/* only v8 */ - void *d_display_frame_type; - void *d_display_crop_info1; - void *d_display_crop_info2; - void *d_display_picture_profile; - void *d_display_luma_crc;/* v7 and v8 */ - void *d_display_chroma0_crc;/* v7 and v8 */ - void *d_display_chroma1_crc;/* only v8 */ - void *d_display_luma_crc_top;/* only v6 */ - void *d_display_chroma_crc_top;/* only v6 */ - void *d_display_luma_crc_bot;/* only v6 */ - void *d_display_chroma_crc_bot;/* only v6 */ - void *d_display_aspect_ratio; - void *d_display_extended_ar; - void *d_decoded_frame_width; - void *d_decoded_frame_height; - void *d_decoded_status; - void *d_decoded_first_plane_addr; - void *d_decoded_second_plane_addr; - void *d_decoded_third_plane_addr;/* only v8 */ - void *d_decoded_frame_type; - void *d_decoded_crop_info1; - void *d_decoded_crop_info2; - void *d_decoded_picture_profile; - void *d_decoded_nal_size; - void *d_decoded_luma_crc; - void *d_decoded_chroma0_crc; - void *d_decoded_chroma1_crc;/* only v8 */ - void *d_ret_picture_tag_top; - void *d_ret_picture_tag_bot; - void *d_ret_picture_time_top; - void *d_ret_picture_time_bot; - void *d_chroma_format; - void *d_vc1_info;/* v7 and v8 */ - void *d_mpeg4_info; - void *d_h264_info; - void *d_metadata_addr_concealed_mb; - void *d_metadata_size_concealed_mb; - void *d_metadata_addr_vc1_param; - void *d_metadata_size_vc1_param; - void *d_metadata_addr_sei_nal; - void *d_metadata_size_sei_nal; - void *d_metadata_addr_vui; - void *d_metadata_size_vui; - void *d_metadata_addr_mvcvui;/* v7 and v8 */ - void *d_metadata_size_mvcvui;/* v7 and v8 */ - void *d_mvc_view_id; - void *d_frame_pack_sei_avail; - void *d_frame_pack_arrgment_id; - void *d_frame_pack_sei_info; - void *d_frame_pack_grid_pos; - void *d_display_recovery_sei_info;/* v7 and v8 */ - void *d_decoded_recovery_sei_info;/* v7 and v8 */ - void *d_display_first_addr;/* only v7 */ - void *d_display_second_addr;/* only v7 */ - void *d_display_third_addr;/* only v7 */ - void *d_decoded_first_addr;/* only v7 */ - void *d_decoded_second_addr;/* only v7 */ - void *d_decoded_third_addr;/* only v7 */ - void *d_used_dpb_flag_upper;/* v7 and v8 */ - void *d_used_dpb_flag_lower;/* v7 and v8 */ + volatile void __iomem *d_crc_ctrl; + volatile void __iomem *d_dec_options; + volatile void __iomem *d_display_delay; + volatile void __iomem *d_set_frame_width; + volatile void __iomem *d_set_frame_height; + volatile void __iomem *d_sei_enable; + volatile void __iomem *d_min_num_dpb; + volatile void __iomem *d_min_first_plane_dpb_size; + volatile void __iomem *d_min_second_plane_dpb_size; + volatile void __iomem *d_min_third_plane_dpb_size;/* only v8 */ + volatile void __iomem *d_min_num_mv; + volatile void __iomem *d_mvc_num_views; + volatile void __iomem *d_min_num_dis;/* only v7 */ + volatile void __iomem *d_min_first_dis_size;/* only v7 */ + volatile void __iomem *d_min_second_dis_size;/* only v7 */ + volatile void __iomem *d_min_third_dis_size;/* only v7 */ + volatile void __iomem *d_post_filter_luma_dpb0;/* v7 and v8 */ + volatile void __iomem *d_post_filter_luma_dpb1;/* v7 and v8 */ + volatile void __iomem *d_post_filter_luma_dpb2;/* only v7 */ + volatile void __iomem *d_post_filter_chroma_dpb0;/* v7 and v8 */ + volatile void __iomem *d_post_filter_chroma_dpb1;/* v7 and v8 */ + volatile void __iomem *d_post_filter_chroma_dpb2;/* only v7 */ + volatile void __iomem *d_num_dpb; + volatile void __iomem *d_num_mv; + volatile void __iomem *d_init_buffer_options; + volatile void __iomem *d_first_plane_dpb_stride_size;/* only v8 */ + volatile void __iomem *d_second_plane_dpb_stride_size;/* only v8 */ + volatile void __iomem *d_third_plane_dpb_stride_size;/* only v8 */ + volatile void __iomem *d_first_plane_dpb_size; + volatile void __iomem *d_second_plane_dpb_size; + volatile void __iomem *d_third_plane_dpb_size;/* only v8 */ + volatile void __iomem *d_mv_buffer_size; + volatile void __iomem *d_first_plane_dpb; + volatile void __iomem *d_second_plane_dpb; + volatile void __iomem *d_third_plane_dpb; + volatile void __iomem *d_mv_buffer; + volatile void __iomem *d_scratch_buffer_addr; + volatile void __iomem *d_scratch_buffer_size; + volatile void __iomem *d_metadata_buffer_addr; + volatile void __iomem *d_metadata_buffer_size; + volatile void __iomem *d_nal_start_options;/* v7 and v8 */ + volatile void __iomem *d_cpb_buffer_addr; + volatile void __iomem *d_cpb_buffer_size; + volatile void __iomem *d_available_dpb_flag_upper; + volatile void __iomem *d_available_dpb_flag_lower; + volatile void __iomem *d_cpb_buffer_offset; + volatile void __iomem *d_slice_if_enable; + volatile void __iomem *d_picture_tag; + volatile void __iomem *d_stream_data_size; + volatile void __iomem *d_dynamic_dpb_flag_upper;/* v7 and v8 */ + volatile void __iomem *d_dynamic_dpb_flag_lower;/* v7 and v8 */ + volatile void __iomem *d_display_frame_width; + volatile void __iomem *d_display_frame_height; + volatile void __iomem *d_display_status; + volatile void __iomem *d_display_first_plane_addr; + volatile void __iomem *d_display_second_plane_addr; + volatile void __iomem *d_display_third_plane_addr;/* only v8 */ + volatile void __iomem *d_display_frame_type; + volatile void __iomem *d_display_crop_info1; + volatile void __iomem *d_display_crop_info2; + volatile void __iomem *d_display_picture_profile; + volatile void __iomem *d_display_luma_crc;/* v7 and v8 */ + volatile void __iomem *d_display_chroma0_crc;/* v7 and v8 */ + volatile void __iomem *d_display_chroma1_crc;/* only v8 */ + volatile void __iomem *d_display_luma_crc_top;/* only v6 */ + volatile void __iomem *d_display_chroma_crc_top;/* only v6 */ + volatile void __iomem *d_display_luma_crc_bot;/* only v6 */ + volatile void __iomem *d_display_chroma_crc_bot;/* only v6 */ + volatile void __iomem *d_display_aspect_ratio; + volatile void __iomem *d_display_extended_ar; + volatile void __iomem *d_decoded_frame_width; + volatile void __iomem *d_decoded_frame_height; + volatile void __iomem *d_decoded_status; + volatile void __iomem *d_decoded_first_plane_addr; + volatile void __iomem *d_decoded_second_plane_addr; + volatile void __iomem *d_decoded_third_plane_addr;/* only v8 */ + volatile void __iomem *d_decoded_frame_type; + volatile void __iomem *d_decoded_crop_info1; + volatile void __iomem *d_decoded_crop_info2; + volatile void __iomem *d_decoded_picture_profile; + volatile void __iomem *d_decoded_nal_size; + volatile void __iomem *d_decoded_luma_crc; + volatile void __iomem *d_decoded_chroma0_crc; + volatile void __iomem *d_decoded_chroma1_crc;/* only v8 */ + volatile void __iomem *d_ret_picture_tag_top; + volatile void __iomem *d_ret_picture_tag_bot; + volatile void __iomem *d_ret_picture_time_top; + volatile void __iomem *d_ret_picture_time_bot; + volatile void __iomem *d_chroma_format; + volatile void __iomem *d_vc1_info;/* v7 and v8 */ + volatile void __iomem *d_mpeg4_info; + volatile void __iomem *d_h264_info; + volatile void __iomem *d_metadata_addr_concealed_mb; + volatile void __iomem *d_metadata_size_concealed_mb; + volatile void __iomem *d_metadata_addr_vc1_param; + volatile void __iomem *d_metadata_size_vc1_param; + volatile void __iomem *d_metadata_addr_sei_nal; + volatile void __iomem *d_metadata_size_sei_nal; + volatile void __iomem *d_metadata_addr_vui; + volatile void __iomem *d_metadata_size_vui; + volatile void __iomem *d_metadata_addr_mvcvui;/* v7 and v8 */ + volatile void __iomem *d_metadata_size_mvcvui;/* v7 and v8 */ + volatile void __iomem *d_mvc_view_id; + volatile void __iomem *d_frame_pack_sei_avail; + volatile void __iomem *d_frame_pack_arrgment_id; + volatile void __iomem *d_frame_pack_sei_info; + volatile void __iomem *d_frame_pack_grid_pos; + volatile void __iomem *d_display_recovery_sei_info;/* v7 and v8 */ + volatile void __iomem *d_decoded_recovery_sei_info;/* v7 and v8 */ + volatile void __iomem *d_display_first_addr;/* only v7 */ + volatile void __iomem *d_display_second_addr;/* only v7 */ + volatile void __iomem *d_display_third_addr;/* only v7 */ + volatile void __iomem *d_decoded_first_addr;/* only v7 */ + volatile void __iomem *d_decoded_second_addr;/* only v7 */ + volatile void __iomem *d_decoded_third_addr;/* only v7 */ + volatile void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */ + volatile void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */ /* encoder registers */ - void *e_frame_width; - void *e_frame_height; - void *e_cropped_frame_width; - void *e_cropped_frame_height; - void *e_frame_crop_offset; - void *e_enc_options; - void *e_picture_profile; - void *e_vbv_buffer_size; - void *e_vbv_init_delay; - void *e_fixed_picture_qp; - void *e_rc_config; - void *e_rc_qp_bound; - void *e_rc_qp_bound_pb;/* v7 and v8 */ - void *e_rc_mode; - void *e_mb_rc_config; - void *e_padding_ctrl; - void *e_air_threshold; - void *e_mv_hor_range; - void *e_mv_ver_range; - void *e_num_dpb; - void *e_luma_dpb; - void *e_chroma_dpb; - void *e_me_buffer; - void *e_scratch_buffer_addr; - void *e_scratch_buffer_size; - void *e_tmv_buffer0; - void *e_tmv_buffer1; - void *e_ir_buffer_addr;/* v7 and v8 */ - void *e_source_first_plane_addr; - void *e_source_second_plane_addr; - void *e_source_third_plane_addr;/* v7 and v8 */ - void *e_source_first_plane_stride;/* v7 and v8 */ - void *e_source_second_plane_stride;/* v7 and v8 */ - void *e_source_third_plane_stride;/* v7 and v8 */ - void *e_stream_buffer_addr; - void *e_stream_buffer_size; - void *e_roi_buffer_addr; - void *e_param_change; - void *e_ir_size; - void *e_gop_config; - void *e_mslice_mode; - void *e_mslice_size_mb; - void *e_mslice_size_bits; - void *e_frame_insertion; - void *e_rc_frame_rate; - void *e_rc_bit_rate; - void *e_rc_roi_ctrl; - void *e_picture_tag; - void *e_bit_count_enable; - void *e_max_bit_count; - void *e_min_bit_count; - void *e_metadata_buffer_addr; - void *e_metadata_buffer_size; - void *e_encoded_source_first_plane_addr; - void *e_encoded_source_second_plane_addr; - void *e_encoded_source_third_plane_addr;/* v7 and v8 */ - void *e_stream_size; - void *e_slice_type; - void *e_picture_count; - void *e_ret_picture_tag; - void *e_stream_buffer_write_pointer; /* only v6 */ - void *e_recon_luma_dpb_addr; - void *e_recon_chroma_dpb_addr; - void *e_metadata_addr_enc_slice; - void *e_metadata_size_enc_slice; - void *e_mpeg4_options; - void *e_mpeg4_hec_period; - void *e_aspect_ratio; - void *e_extended_sar; - void *e_h264_options; - void *e_h264_options_2;/* v7 and v8 */ - void *e_h264_lf_alpha_offset; - void *e_h264_lf_beta_offset; - void *e_h264_i_period; - void *e_h264_fmo_slice_grp_map_type; - void *e_h264_fmo_num_slice_grp_minus1; - void *e_h264_fmo_slice_grp_change_dir; - void *e_h264_fmo_slice_grp_change_rate_minus1; - void *e_h264_fmo_run_length_minus1_0; - void *e_h264_aso_slice_order_0; - void *e_h264_chroma_qp_offset; - void *e_h264_num_t_layer; - void *e_h264_hierarchical_qp_layer0; - void *e_h264_frame_packing_sei_info; - void *e_h264_nal_control;/* v7 and v8 */ - void *e_mvc_frame_qp_view1; - void *e_mvc_rc_bit_rate_view1; - void *e_mvc_rc_qbound_view1; - void *e_mvc_rc_mode_view1; - void *e_mvc_inter_view_prediction_on; - void *e_vp8_options;/* v7 and v8 */ - void *e_vp8_filter_options;/* v7 and v8 */ - void *e_vp8_golden_frame_option;/* v7 and v8 */ - void *e_vp8_num_t_layer;/* v7 and v8 */ - void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ - void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ - void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ + volatile void __iomem *e_frame_width; + volatile void __iomem *e_frame_height; + volatile void __iomem *e_cropped_frame_width; + volatile void __iomem *e_cropped_frame_height; + volatile void __iomem *e_frame_crop_offset; + volatile void __iomem *e_enc_options; + volatile void __iomem *e_picture_profile; + volatile void __iomem *e_vbv_buffer_size; + volatile void __iomem *e_vbv_init_delay; + volatile void __iomem *e_fixed_picture_qp; + volatile void __iomem *e_rc_config; + volatile void __iomem *e_rc_qp_bound; + volatile void __iomem *e_rc_qp_bound_pb;/* v7 and v8 */ + volatile void __iomem *e_rc_mode; + volatile void __iomem *e_mb_rc_config; + volatile void __iomem *e_padding_ctrl; + volatile void __iomem *e_air_threshold; + volatile void __iomem *e_mv_hor_range; + volatile void __iomem *e_mv_ver_range; + volatile void __iomem *e_num_dpb; + volatile void __iomem *e_luma_dpb; + volatile void __iomem *e_chroma_dpb; + volatile void __iomem *e_me_buffer; + volatile void __iomem *e_scratch_buffer_addr; + volatile void __iomem *e_scratch_buffer_size; + volatile void __iomem *e_tmv_buffer0; + volatile void __iomem *e_tmv_buffer1; + volatile void __iomem *e_ir_buffer_addr;/* v7 and v8 */ + volatile void __iomem *e_source_first_plane_addr; + volatile void __iomem *e_source_second_plane_addr; + volatile void __iomem *e_source_third_plane_addr;/* v7 and v8 */ + volatile void __iomem *e_source_first_plane_stride;/* v7 and v8 */ + volatile void __iomem *e_source_second_plane_stride;/* v7 and v8 */ + volatile void __iomem *e_source_third_plane_stride;/* v7 and v8 */ + volatile void __iomem *e_stream_buffer_addr; + volatile void __iomem *e_stream_buffer_size; + volatile void __iomem *e_roi_buffer_addr; + volatile void __iomem *e_param_change; + volatile void __iomem *e_ir_size; + volatile void __iomem *e_gop_config; + volatile void __iomem *e_mslice_mode; + volatile void __iomem *e_mslice_size_mb; + volatile void __iomem *e_mslice_size_bits; + volatile void __iomem *e_frame_insertion; + volatile void __iomem *e_rc_frame_rate; + volatile void __iomem *e_rc_bit_rate; + volatile void __iomem *e_rc_roi_ctrl; + volatile void __iomem *e_picture_tag; + volatile void __iomem *e_bit_count_enable; + volatile void __iomem *e_max_bit_count; + volatile void __iomem *e_min_bit_count; + volatile void __iomem *e_metadata_buffer_addr; + volatile void __iomem *e_metadata_buffer_size; + volatile void __iomem *e_encoded_source_first_plane_addr; + volatile void __iomem *e_encoded_source_second_plane_addr; + volatile void __iomem *e_encoded_source_third_plane_addr;/* v7 and v8 */ + volatile void __iomem *e_stream_size; + volatile void __iomem *e_slice_type; + volatile void __iomem *e_picture_count; + volatile void __iomem *e_ret_picture_tag; + volatile void __iomem *e_stream_buffer_write_pointer; /* only v6 */ + volatile void __iomem *e_recon_luma_dpb_addr; + volatile void __iomem *e_recon_chroma_dpb_addr; + volatile void __iomem *e_metadata_addr_enc_slice; + volatile void __iomem *e_metadata_size_enc_slice; + volatile void __iomem *e_mpeg4_options; + volatile void __iomem *e_mpeg4_hec_period; + volatile void __iomem *e_aspect_ratio; + volatile void __iomem *e_extended_sar; + volatile void __iomem *e_h264_options; + volatile void __iomem *e_h264_options_2;/* v7 and v8 */ + volatile void __iomem *e_h264_lf_alpha_offset; + volatile void __iomem *e_h264_lf_beta_offset; + volatile void __iomem *e_h264_i_period; + volatile void __iomem *e_h264_fmo_slice_grp_map_type; + volatile void __iomem *e_h264_fmo_num_slice_grp_minus1; + volatile void __iomem *e_h264_fmo_slice_grp_change_dir; + volatile void __iomem *e_h264_fmo_slice_grp_change_rate_minus1; + volatile void __iomem *e_h264_fmo_run_length_minus1_0; + volatile void __iomem *e_h264_aso_slice_order_0; + volatile void __iomem *e_h264_chroma_qp_offset; + volatile void __iomem *e_h264_num_t_layer; + volatile void __iomem *e_h264_hierarchical_qp_layer0; + volatile void __iomem *e_h264_frame_packing_sei_info; + volatile void __iomem *e_h264_nal_control;/* v7 and v8 */ + volatile void __iomem *e_mvc_frame_qp_view1; + volatile void __iomem *e_mvc_rc_bit_rate_view1; + volatile void __iomem *e_mvc_rc_qbound_view1; + volatile void __iomem *e_mvc_rc_mode_view1; + volatile void __iomem *e_mvc_inter_view_prediction_on; + volatile void __iomem *e_vp8_options;/* v7 and v8 */ + volatile void __iomem *e_vp8_filter_options;/* v7 and v8 */ + volatile void __iomem *e_vp8_golden_frame_option;/* v7 and v8 */ + volatile void __iomem *e_vp8_num_t_layer;/* v7 and v8 */ + volatile void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ + volatile void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ + volatile void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ }; struct s5p_mfc_hw_ops { 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 58ec7bb..7cf0796 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -228,6 +228,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); if (ret) { mfc_err("Failed to allocate shared memory buffer\n"); + s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx); return ret; } @@ -262,7 +263,7 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { - writel(data, (ctx->shm.virt + ofs)); + writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs)); wmb(); } @@ -270,7 +271,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, unsigned int ofs) { rmb(); - return readl(ctx->shm.virt + ofs); + return readl((volatile void __iomem *)(ctx->shm.virt + ofs)); } static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) @@ -377,7 +378,7 @@ static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, /* Set decoding frame buffer */ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) { - unsigned int frame_size, i; + unsigned int frame_size_lu, i; unsigned int frame_size_ch, frame_size_mv; struct s5p_mfc_dev *dev = ctx->dev; unsigned int dpb; @@ -465,23 +466,23 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) ctx->codec_mode); return -EINVAL; } - frame_size = ctx->luma_size; + frame_size_lu = ctx->luma_size; frame_size_ch = ctx->chroma_size; frame_size_mv = ctx->mv_size; - mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch, + mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size_lu, frame_size_ch, frame_size_mv); for (i = 0; i < ctx->total_dpb_count; i++) { /* Bank2 */ - mfc_debug(2, "Luma %d: %x\n", i, + mfc_debug(2, "Luma %d: %zx\n", i, ctx->dst_bufs[i].cookie.raw.luma); mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma), S5P_FIMV_DEC_LUMA_ADR + i * 4); - mfc_debug(2, "\tChroma %d: %x\n", i, + mfc_debug(2, "\tChroma %d: %zx\n", i, ctx->dst_bufs[i].cookie.raw.chroma); mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma), S5P_FIMV_DEC_CHROMA_ADR + i * 4); if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC) { - mfc_debug(2, "\tBuf2: %x, size: %d\n", + mfc_debug(2, "\tBuf2: %zx, size: %d\n", buf_addr2, buf_size2); mfc_write(dev, OFFSETB(buf_addr2), S5P_FIMV_H264_MV_ADR + i * 4); @@ -489,14 +490,14 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) buf_size2 -= frame_size_mv; } } - mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1); + mfc_debug(2, "Buf1: %zu, buf_size1: %d\n", buf_addr1, buf_size1); mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n", buf_size1, buf_size2, ctx->total_dpb_count); if (buf_size1 < 0 || buf_size2 < 0) { mfc_debug(2, "Not enough memory has been allocated\n"); return -ENOMEM; } - s5p_mfc_write_info_v5(ctx, frame_size, ALLOC_LUMA_DPB_SIZE); + s5p_mfc_write_info_v5(ctx, frame_size_lu, ALLOC_LUMA_DPB_SIZE); s5p_mfc_write_info_v5(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE); if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC) s5p_mfc_write_info_v5(ctx, frame_size_mv, ALLOC_MV_SIZE); @@ -566,7 +567,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) enc_ref_c_size = ALIGN(guard_width * guard_height, S5P_FIMV_NV12MT_SALIGN); } - mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2); + mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2); switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_ENC: for (i = 0; i < 2; i++) { @@ -605,7 +606,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) S5P_FIMV_H264_NBOR_INFO_ADR); buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE; buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE; - mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", + mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2); break; case S5P_MFC_CODEC_MPEG4_ENC: @@ -636,7 +637,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) S5P_FIMV_MPEG4_ACDC_COEF_ADR); buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE; buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE; - mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", + mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2); break; case S5P_MFC_CODEC_H263_ENC: @@ -662,7 +663,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR); buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE; buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE; - mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", + mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2); break; default: @@ -1186,7 +1187,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *temp_vb; unsigned long flags; - unsigned int index; if (ctx->state == MFCINST_FINISHING) { last_frame = MFC_DEC_LAST_FRAME; @@ -1211,7 +1211,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream, 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) { 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 c1c12f8..8798b14 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -43,11 +43,6 @@ } while (0) #endif /* S5P_MFC_DEBUG_REGWRITE */ -#define READL(reg) \ - (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg)) -#define WRITEL(data, reg) \ - (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg))) - #define IS_MFCV6_V2(dev) (!IS_MFCV7_PLUS(dev) && dev->fw_ver == MFC_FW_V2) /* Allocate temporary buffers for decoding */ @@ -105,7 +100,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_width, mb_height), S5P_FIMV_ME_BUFFER_ALIGN_V6); - mfc_debug(2, "recon luma size: %d chroma size: %d\n", + mfc_debug(2, "recon luma size: %zu chroma size: %zu\n", ctx->luma_dpb_size, ctx->chroma_dpb_size); } else { return -EINVAL; @@ -416,10 +411,10 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n" "buf_size: 0x%08x (%d)\n", ctx->inst_no, buf_addr, strm_size, strm_size); - WRITEL(strm_size, mfc_regs->d_stream_data_size); - WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr); - WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size); - WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset); + writel(strm_size, mfc_regs->d_stream_data_size); + writel(buf_addr, mfc_regs->d_cpb_buffer_addr); + writel(buf_size->cpb, mfc_regs->d_cpb_buffer_size); + writel(start_num_byte, mfc_regs->d_cpb_buffer_offset); mfc_debug_leave(); return 0; @@ -443,17 +438,17 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count); mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay); - WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb); - WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); - WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); + writel(ctx->total_dpb_count, mfc_regs->d_num_dpb); + writel(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); + writel(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); - WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr); - WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); + writel(buf_addr1, mfc_regs->d_scratch_buffer_addr); + writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); if (IS_MFCV8(dev)) { - WRITEL(ctx->img_width, + writel(ctx->img_width, mfc_regs->d_first_plane_dpb_stride_size); - WRITEL(ctx->img_width, + writel(ctx->img_width, mfc_regs->d_second_plane_dpb_stride_size); } @@ -462,8 +457,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC || ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){ - WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size); - WRITEL(ctx->mv_count, mfc_regs->d_num_mv); + writel(ctx->mv_size, mfc_regs->d_mv_buffer_size); + writel(ctx->mv_count, mfc_regs->d_num_mv); } frame_size = ctx->luma_size; @@ -474,13 +469,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) for (i = 0; i < ctx->total_dpb_count; i++) { /* Bank2 */ - mfc_debug(2, "Luma %d: %x\n", i, + mfc_debug(2, "Luma %d: %zx\n", i, ctx->dst_bufs[i].cookie.raw.luma); - WRITEL(ctx->dst_bufs[i].cookie.raw.luma, + writel(ctx->dst_bufs[i].cookie.raw.luma, mfc_regs->d_first_plane_dpb + i * 4); - mfc_debug(2, "\tChroma %d: %x\n", i, + mfc_debug(2, "\tChroma %d: %zx\n", i, ctx->dst_bufs[i].cookie.raw.chroma); - WRITEL(ctx->dst_bufs[i].cookie.raw.chroma, + writel(ctx->dst_bufs[i].cookie.raw.chroma, mfc_regs->d_second_plane_dpb + i * 4); } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || @@ -492,23 +487,23 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) align_gap = buf_addr1 - align_gap; buf_size1 -= align_gap; - mfc_debug(2, "\tBuf1: %x, size: %d\n", + mfc_debug(2, "\tBuf1: %zx, size: %d\n", buf_addr1, buf_size1); - WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4); + writel(buf_addr1, mfc_regs->d_mv_buffer + i * 4); buf_addr1 += frame_size_mv; buf_size1 -= frame_size_mv; } } - mfc_debug(2, "Buf1: %u, buf_size1: %d (frames %d)\n", + mfc_debug(2, "Buf1: %zu, buf_size1: %d (frames %d)\n", buf_addr1, buf_size1, ctx->total_dpb_count); if (buf_size1 < 0) { mfc_debug(2, "Not enough memory has been allocated.\n"); return -ENOMEM; } - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_INIT_BUFS_V6, NULL); mfc_debug(2, "After setting buffers.\n"); @@ -522,8 +517,8 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */ - WRITEL(size, mfc_regs->e_stream_buffer_size); + writel(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */ + writel(size, mfc_regs->e_stream_buffer_size); mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n", addr, size); @@ -537,8 +532,8 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(y_addr, mfc_regs->e_source_first_plane_addr); - WRITEL(c_addr, mfc_regs->e_source_second_plane_addr); + writel(y_addr, mfc_regs->e_source_first_plane_addr); + writel(c_addr, mfc_regs->e_source_second_plane_addr); mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr); mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); @@ -551,11 +546,11 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; unsigned long enc_recon_y_addr, enc_recon_c_addr; - *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr); - *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr); + *y_addr = readl(mfc_regs->e_encoded_source_first_plane_addr); + *c_addr = readl(mfc_regs->e_encoded_source_second_plane_addr); - enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr); - enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr); + enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr); + enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr); mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr); mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); @@ -577,36 +572,36 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); for (i = 0; i < ctx->pb_count; i++) { - WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); + writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); buf_addr1 += ctx->luma_dpb_size; - WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); + writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); buf_addr1 += ctx->chroma_dpb_size; - WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); + writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); buf_addr1 += ctx->me_buffer_size; buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size); } - WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr); - WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size); + writel(buf_addr1, mfc_regs->e_scratch_buffer_addr); + writel(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size); buf_addr1 += ctx->scratch_buf_size; buf_size1 -= ctx->scratch_buf_size; - WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0); + writel(buf_addr1, mfc_regs->e_tmv_buffer0); buf_addr1 += ctx->tmv_buffer_size >> 1; - WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1); + writel(buf_addr1, mfc_regs->e_tmv_buffer1); buf_addr1 += ctx->tmv_buffer_size >> 1; buf_size1 -= ctx->tmv_buffer_size; - mfc_debug(2, "Buf1: %u, buf_size1: %d (ref frames %d)\n", + mfc_debug(2, "Buf1: %zu, buf_size1: %d (ref frames %d)\n", buf_addr1, buf_size1, ctx->pb_count); if (buf_size1 < 0) { mfc_debug(2, "Not enough memory has been allocated.\n"); return -ENOMEM; } - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_INIT_BUFS_V6, NULL); mfc_debug_leave(); @@ -621,15 +616,15 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx) /* multi-slice control */ /* multi-slice MB number or bit size */ - WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode); + writel(ctx->slice_mode, mfc_regs->e_mslice_mode); if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { - WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb); + writel(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb); } else if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { - WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits); + writel(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits); } else { - WRITEL(0x0, mfc_regs->e_mslice_size_mb); - WRITEL(0x0, mfc_regs->e_mslice_size_bits); + writel(0x0, mfc_regs->e_mslice_size_mb); + writel(0x0, mfc_regs->e_mslice_size_bits); } return 0; @@ -645,21 +640,21 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) mfc_debug_enter(); /* width */ - WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */ + writel(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */ /* height */ - WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */ + writel(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */ /* cropped width */ - WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width); + writel(ctx->img_width, mfc_regs->e_cropped_frame_width); /* cropped height */ - WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height); + writel(ctx->img_height, mfc_regs->e_cropped_frame_height); /* cropped offset */ - WRITEL(0x0, mfc_regs->e_frame_crop_offset); + writel(0x0, mfc_regs->e_frame_crop_offset); /* pictype : IDR period */ reg = 0; reg |= p->gop_size & 0xFFFF; - WRITEL(reg, mfc_regs->e_gop_config); + writel(reg, mfc_regs->e_gop_config); /* multi-slice control */ /* multi-slice MB number or bit size */ @@ -667,65 +662,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) reg = 0; if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { reg |= (0x1 << 3); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); ctx->slice_size.mb = p->slice_mb; } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { reg |= (0x1 << 3); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); ctx->slice_size.bits = p->slice_bit; } else { reg &= ~(0x1 << 3); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); } s5p_mfc_set_slice_mode(ctx); /* cyclic intra refresh */ - WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size); - reg = READL(mfc_regs->e_enc_options); + writel(p->intra_refresh_mb, mfc_regs->e_ir_size); + reg = readl(mfc_regs->e_enc_options); if (p->intra_refresh_mb == 0) reg &= ~(0x1 << 4); else reg |= (0x1 << 4); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* 'NON_REFERENCE_STORE_ENABLE' for debugging */ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg &= ~(0x1 << 9); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* memory structure cur. frame */ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg &= ~(0x1 << 7); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x0, mfc_regs->pixel_format); + writel(0x0, mfc_regs->pixel_format); } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg &= ~(0x1 << 7); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x1, mfc_regs->pixel_format); + writel(0x1, mfc_regs->pixel_format); } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg |= (0x1 << 7); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x0, mfc_regs->pixel_format); + writel(0x0, mfc_regs->pixel_format); } /* memory structure recon. frame */ /* 0: Linear, 1: 2D tiled */ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg |= (0x1 << 8); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* padding control & value */ - WRITEL(0x0, mfc_regs->e_padding_ctrl); + writel(0x0, mfc_regs->e_padding_ctrl); if (p->pad) { reg = 0; /** enable */ @@ -736,64 +731,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) reg |= ((p->pad_cb & 0xFF) << 8); /** y value */ reg |= p->pad_luma & 0xFF; - WRITEL(reg, mfc_regs->e_padding_ctrl); + writel(reg, mfc_regs->e_padding_ctrl); } /* rate control config. */ reg = 0; /* frame-level rate control */ reg |= ((p->rc_frame & 0x1) << 9); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* bit rate */ if (p->rc_frame) - WRITEL(p->rc_bitrate, + writel(p->rc_bitrate, mfc_regs->e_rc_bit_rate); else - WRITEL(1, mfc_regs->e_rc_bit_rate); + writel(1, mfc_regs->e_rc_bit_rate); /* reaction coefficient */ if (p->rc_frame) { if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */ - WRITEL(1, mfc_regs->e_rc_mode); + writel(1, mfc_regs->e_rc_mode); else /* loose CBR */ - WRITEL(2, mfc_regs->e_rc_mode); + writel(2, mfc_regs->e_rc_mode); } /* seq header ctrl */ - reg = READL(mfc_regs->e_enc_options); + reg = readl(mfc_regs->e_enc_options); reg &= ~(0x1 << 2); reg |= ((p->seq_hdr_mode & 0x1) << 2); /* frame skip mode */ reg &= ~(0x3); reg |= (p->frame_skip_mode & 0x3); - WRITEL(reg, mfc_regs->e_enc_options); + writel(reg, mfc_regs->e_enc_options); /* 'DROP_CONTROL_ENABLE', disable */ - reg = READL(mfc_regs->e_rc_config); + reg = readl(mfc_regs->e_rc_config); reg &= ~(0x1 << 10); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* setting for MV range [16, 256] */ reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK); - WRITEL(reg, mfc_regs->e_mv_hor_range); + writel(reg, mfc_regs->e_mv_hor_range); reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK); - WRITEL(reg, mfc_regs->e_mv_ver_range); + writel(reg, mfc_regs->e_mv_ver_range); - WRITEL(0x0, mfc_regs->e_frame_insertion); - WRITEL(0x0, mfc_regs->e_roi_buffer_addr); - WRITEL(0x0, mfc_regs->e_param_change); - WRITEL(0x0, mfc_regs->e_rc_roi_ctrl); - WRITEL(0x0, mfc_regs->e_picture_tag); + writel(0x0, mfc_regs->e_frame_insertion); + writel(0x0, mfc_regs->e_roi_buffer_addr); + writel(0x0, mfc_regs->e_param_change); + writel(0x0, mfc_regs->e_rc_roi_ctrl); + writel(0x0, mfc_regs->e_picture_tag); - WRITEL(0x0, mfc_regs->e_bit_count_enable); - WRITEL(0x0, mfc_regs->e_max_bit_count); - WRITEL(0x0, mfc_regs->e_min_bit_count); + writel(0x0, mfc_regs->e_bit_count_enable); + writel(0x0, mfc_regs->e_max_bit_count); + writel(0x0, mfc_regs->e_min_bit_count); - WRITEL(0x0, mfc_regs->e_metadata_buffer_addr); - WRITEL(0x0, mfc_regs->e_metadata_buffer_size); + writel(0x0, mfc_regs->e_metadata_buffer_addr); + writel(0x0, mfc_regs->e_metadata_buffer_size); mfc_debug_leave(); @@ -814,10 +809,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(mfc_regs->e_gop_config); + reg = readl(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, mfc_regs->e_gop_config); + writel(reg, mfc_regs->e_gop_config); /* profile & level */ reg = 0; @@ -825,19 +820,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->level & 0xFF) << 8); /** profile - 0 ~ 3 */ reg |= p_h264->profile & 0x3F; - WRITEL(reg, mfc_regs->e_picture_profile); + writel(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(mfc_regs->e_rc_config); + reg = readl(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /** frame QP */ reg &= ~(0x3F); reg |= p_h264->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -845,16 +840,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_h264->rc_min_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_qp_bound); + writel(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, mfc_regs->e_fixed_picture_qp); + writel(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8); reg |= p_h264->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_fixed_picture_qp); + writel(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -862,38 +857,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, mfc_regs->e_rc_frame_rate); + writel(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p_h264->cpb_size & 0xFFFF, + writel(p_h264->cpb_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); + writel(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* interlace */ reg = 0; reg |= ((p_h264->interlace & 0x1) << 3); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* height */ if (p_h264->interlace) { - WRITEL(ctx->img_height >> 1, + writel(ctx->img_height >> 1, mfc_regs->e_frame_height); /* 32 align */ /* cropped height */ - WRITEL(ctx->img_height >> 1, + writel(ctx->img_height >> 1, mfc_regs->e_cropped_frame_height); } /* loop filter ctrl */ - reg = READL(mfc_regs->e_h264_options); + reg = readl(mfc_regs->e_h264_options); reg &= ~(0x3 << 1); reg |= ((p_h264->loop_filter_mode & 0x3) << 1); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* loopfilter alpha offset */ if (p_h264->loop_filter_alpha < 0) { @@ -903,7 +898,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0x00; reg |= (p_h264->loop_filter_alpha & 0xF); } - WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset); + writel(reg, mfc_regs->e_h264_lf_alpha_offset); /* loopfilter beta offset */ if (p_h264->loop_filter_beta < 0) { @@ -913,28 +908,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0x00; reg |= (p_h264->loop_filter_beta & 0xF); } - WRITEL(reg, mfc_regs->e_h264_lf_beta_offset); + writel(reg, mfc_regs->e_h264_lf_beta_offset); /* entropy coding mode */ - reg = READL(mfc_regs->e_h264_options); + reg = readl(mfc_regs->e_h264_options); reg &= ~(0x1); reg |= p_h264->entropy_mode & 0x1; - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* number of ref. picture */ - reg = READL(mfc_regs->e_h264_options); + reg = readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 7); reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* 8x8 transform enable */ - reg = READL(mfc_regs->e_h264_options); + reg = readl(mfc_regs->e_h264_options); reg &= ~(0x3 << 12); reg |= ((p_h264->_8x8_transform & 0x3) << 12); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* macroblock adaptive scaling features */ - WRITEL(0x0, mfc_regs->e_mb_rc_config); + writel(0x0, mfc_regs->e_mb_rc_config); if (p->rc_mb) { reg = 0; /** dark region */ @@ -945,95 +940,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->rc_mb_static & 0x1) << 1); /** high activity region */ reg |= p_h264->rc_mb_activity & 0x1; - WRITEL(reg, mfc_regs->e_mb_rc_config); + writel(reg, mfc_regs->e_mb_rc_config); } /* aspect ratio VUI */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 5); reg |= ((p_h264->vui_sar & 0x1) << 5); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); - WRITEL(0x0, mfc_regs->e_aspect_ratio); - WRITEL(0x0, mfc_regs->e_extended_sar); + writel(0x0, mfc_regs->e_aspect_ratio); + writel(0x0, mfc_regs->e_extended_sar); if (p_h264->vui_sar) { /* aspect ration IDC */ reg = 0; reg |= p_h264->vui_sar_idc & 0xFF; - WRITEL(reg, mfc_regs->e_aspect_ratio); + writel(reg, mfc_regs->e_aspect_ratio); if (p_h264->vui_sar_idc == 0xFF) { /* extended SAR */ reg = 0; reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16; reg |= p_h264->vui_ext_sar_height & 0xFFFF; - WRITEL(reg, mfc_regs->e_extended_sar); + writel(reg, mfc_regs->e_extended_sar); } } /* intra picture period for H.264 open GOP */ /* control */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 4); reg |= ((p_h264->open_gop & 0x1) << 4); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* value */ - WRITEL(0x0, mfc_regs->e_h264_i_period); + writel(0x0, mfc_regs->e_h264_i_period); if (p_h264->open_gop) { reg = 0; reg |= p_h264->open_gop_size & 0xFFFF; - WRITEL(reg, mfc_regs->e_h264_i_period); + writel(reg, mfc_regs->e_h264_i_period); } /* 'WEIGHTED_BI_PREDICTION' for B is disable */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x3 << 9); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 14); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* ASO */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 6); reg |= ((p_h264->aso & 0x1) << 6); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); /* hier qp enable */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 8); reg |= ((p_h264->open_gop & 0x1) << 8); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); reg = 0; if (p_h264->hier_qp && p_h264->hier_qp_layer) { reg |= (p_h264->hier_qp_type & 0x1) << 0x3; reg |= p_h264->hier_qp_layer & 0x7; - WRITEL(reg, mfc_regs->e_h264_num_t_layer); + writel(reg, mfc_regs->e_h264_num_t_layer); /* QP value for each layer */ for (i = 0; i < p_h264->hier_qp_layer && i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) { - WRITEL(p_h264->hier_qp_layer_qp[i], + writel(p_h264->hier_qp_layer_qp[i], mfc_regs->e_h264_hierarchical_qp_layer0 + i * 4); } } /* number of coding layer should be zero when hierarchical is disable */ - WRITEL(reg, mfc_regs->e_h264_num_t_layer); + writel(reg, mfc_regs->e_h264_num_t_layer); /* frame packing SEI generation */ - READL(mfc_regs->e_h264_options); + readl(mfc_regs->e_h264_options); reg &= ~(0x1 << 25); reg |= ((p_h264->sei_frame_packing & 0x1) << 25); - WRITEL(reg, mfc_regs->e_h264_options); + writel(reg, mfc_regs->e_h264_options); if (p_h264->sei_frame_packing) { reg = 0; /** current frame0 flag */ reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2); /** arrangement type */ reg |= p_h264->sei_fp_arrangement_type & 0x3; - WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info); + writel(reg, mfc_regs->e_h264_frame_packing_sei_info); } if (p_h264->fmo) { @@ -1042,7 +1037,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) if (p_h264->fmo_slice_grp > 4) p_h264->fmo_slice_grp = 4; for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++) - WRITEL(p_h264->fmo_run_len[i] - 1, + writel(p_h264->fmo_run_len[i] - 1, mfc_regs->e_h264_fmo_run_length_minus1_0 + i * 4); break; @@ -1054,10 +1049,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN: if (p_h264->fmo_slice_grp > 2) p_h264->fmo_slice_grp = 2; - WRITEL(p_h264->fmo_chg_dir & 0x1, + writel(p_h264->fmo_chg_dir & 0x1, mfc_regs->e_h264_fmo_slice_grp_change_dir); /* the valid range is 0 ~ number of macroblocks -1 */ - WRITEL(p_h264->fmo_chg_rate, + writel(p_h264->fmo_chg_rate, mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1); break; default: @@ -1068,12 +1063,12 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) break; } - WRITEL(p_h264->fmo_map_type, + writel(p_h264->fmo_map_type, mfc_regs->e_h264_fmo_slice_grp_map_type); - WRITEL(p_h264->fmo_slice_grp - 1, + writel(p_h264->fmo_slice_grp - 1, mfc_regs->e_h264_fmo_num_slice_grp_minus1); } else { - WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1); + writel(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1); } mfc_debug_leave(); @@ -1094,10 +1089,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(mfc_regs->e_gop_config); + reg = readl(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, mfc_regs->e_gop_config); + writel(reg, mfc_regs->e_gop_config); /* profile & level */ reg = 0; @@ -1105,19 +1100,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg |= ((p_mpeg4->level & 0xFF) << 8); /** profile - 0 ~ 1 */ reg |= p_mpeg4->profile & 0x3F; - WRITEL(reg, mfc_regs->e_picture_profile); + writel(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(mfc_regs->e_rc_config); + reg = readl(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /** frame QP */ reg &= ~(0x3F); reg |= p_mpeg4->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -1125,16 +1120,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_mpeg4->rc_min_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_qp_bound); + writel(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, mfc_regs->e_fixed_picture_qp); + writel(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8); reg |= p_mpeg4->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_fixed_picture_qp); + writel(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -1142,21 +1137,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, mfc_regs->e_rc_frame_rate); + writel(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); + writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); + writel(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* Disable HEC */ - WRITEL(0x0, mfc_regs->e_mpeg4_options); - WRITEL(0x0, mfc_regs->e_mpeg4_hec_period); + writel(0x0, mfc_regs->e_mpeg4_options); + writel(0x0, mfc_regs->e_mpeg4_hec_period); mfc_debug_leave(); @@ -1179,19 +1174,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg = 0; /** profile */ reg |= (0x1 << 4); - WRITEL(reg, mfc_regs->e_picture_profile); + writel(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(mfc_regs->e_rc_config); + reg = readl(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /** frame QP */ reg &= ~(0x3F); reg |= p_h263->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -1199,16 +1194,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg |= ((p_h263->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_h263->rc_min_qp & 0x3F; - WRITEL(reg, mfc_regs->e_rc_qp_bound); + writel(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, mfc_regs->e_fixed_picture_qp); + writel(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8); reg |= p_h263->rc_frame_qp & 0x3F; - WRITEL(reg, mfc_regs->e_fixed_picture_qp); + writel(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -1216,16 +1211,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, mfc_regs->e_rc_frame_rate); + writel(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); + writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); + writel(p->vbv_delay, mfc_regs->e_vbv_init_delay); } mfc_debug_leave(); @@ -1247,57 +1242,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(mfc_regs->e_gop_config); + reg = readl(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, mfc_regs->e_gop_config); + writel(reg, mfc_regs->e_gop_config); /* profile - 0 ~ 3 */ reg = p_vp8->profile & 0x3; - WRITEL(reg, mfc_regs->e_picture_profile); + writel(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(mfc_regs->e_rc_config); + reg = readl(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* frame rate */ if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) { reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, mfc_regs->e_rc_frame_rate); + writel(reg, mfc_regs->e_rc_frame_rate); } /* frame QP */ reg &= ~(0x7F); reg |= p_vp8->rc_frame_qp & 0x7F; - WRITEL(reg, mfc_regs->e_rc_config); + writel(reg, mfc_regs->e_rc_config); /* other QPs */ - WRITEL(0x0, mfc_regs->e_fixed_picture_qp); + writel(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8); reg |= p_vp8->rc_frame_qp & 0x7F; - WRITEL(reg, mfc_regs->e_fixed_picture_qp); + writel(reg, mfc_regs->e_fixed_picture_qp); } /* max QP */ reg = ((p_vp8->rc_max_qp & 0x7F) << 8); /* min QP */ reg |= p_vp8->rc_min_qp & 0x7F; - WRITEL(reg, mfc_regs->e_rc_qp_bound); + writel(reg, mfc_regs->e_rc_qp_bound); /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); + writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); + writel(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* VP8 specific params */ @@ -1319,7 +1314,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) } reg |= (val & 0xF) << 3; reg |= (p_vp8->num_ref & 0x2); - WRITEL(reg, mfc_regs->e_vp8_options); + writel(reg, mfc_regs->e_vp8_options); mfc_debug_leave(); @@ -1338,9 +1333,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no, S5P_FIMV_CH_SEQ_HEADER_V6); mfc_debug(2, "BUFs: %08x %08x %08x\n", - READL(mfc_regs->d_cpb_buffer_addr), - READL(mfc_regs->d_cpb_buffer_addr), - READL(mfc_regs->d_cpb_buffer_addr)); + readl(mfc_regs->d_cpb_buffer_addr), + readl(mfc_regs->d_cpb_buffer_addr), + readl(mfc_regs->d_cpb_buffer_addr)); /* FMO_ASO_CTRL - 0: Enable, 1: Disable */ reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6); @@ -1351,11 +1346,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) * set to negative value. */ if (ctx->display_delay >= 0) { reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6); - WRITEL(ctx->display_delay, mfc_regs->d_display_delay); + writel(ctx->display_delay, mfc_regs->d_display_delay); } if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) { - WRITEL(reg, mfc_regs->d_dec_options); + writel(reg, mfc_regs->d_dec_options); reg = 0; } @@ -1370,22 +1365,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6); if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) - WRITEL(reg, mfc_regs->d_init_buffer_options); + writel(reg, mfc_regs->d_init_buffer_options); else - WRITEL(reg, mfc_regs->d_dec_options); + writel(reg, mfc_regs->d_dec_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) - WRITEL(0x1, mfc_regs->pixel_format); + writel(0x1, mfc_regs->pixel_format); else - WRITEL(0x0, mfc_regs->pixel_format); + writel(0x0, mfc_regs->pixel_format); /* sei parse */ - WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable); + writel(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable); - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_SEQ_HEADER_V6, NULL); mfc_debug_leave(); @@ -1400,8 +1395,8 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) if (flush) { dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_H2R_CMD_FLUSH_V6, NULL); } } @@ -1413,19 +1408,19 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower); - WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable); + writel(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower); + writel(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable); - WRITEL(ctx->inst_no, mfc_regs->instance_id); + writel(ctx->inst_no, mfc_regs->instance_id); /* Issue different commands to instance basing on whether it * is the last frame or not. */ switch (last_frame) { case 0: - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_FRAME_START_V6, NULL); break; case 1: - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_LAST_FRAME_V6, NULL); break; default: @@ -1458,12 +1453,12 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) /* Set stride lengths for v7 & above */ if (IS_MFCV7_PLUS(dev)) { - WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride); - WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride); + writel(ctx->img_width, mfc_regs->e_source_first_plane_stride); + writel(ctx->img_width, mfc_regs->e_source_second_plane_stride); } - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_SEQ_HEADER_V6, NULL); return 0; @@ -1479,7 +1474,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) if (p_h264->aso) { for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) { - WRITEL(p_h264->aso_slice_order[i], + writel(p_h264->aso_slice_order[i], mfc_regs->e_h264_aso_slice_order_0 + i * 4); } } @@ -1501,8 +1496,8 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) s5p_mfc_set_slice_mode(ctx); - WRITEL(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + writel(ctx->inst_no, mfc_regs->instance_id); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_FRAME_START_V6, NULL); mfc_debug(2, "--\n"); @@ -1877,15 +1872,15 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) { const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(0, mfc_regs->risc2host_command); - WRITEL(0, mfc_regs->risc2host_int); + writel(0, mfc_regs->risc2host_command); + writel(0, mfc_regs->risc2host_int); } static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { s5p_mfc_clock_on(); - WRITEL(data, (void *)ofs); + writel(data, (volatile void __iomem *)((unsigned long)ofs)); s5p_mfc_clock_off(); } @@ -1895,7 +1890,7 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) int ret; s5p_mfc_clock_on(); - ret = READL((void *)ofs); + ret = readl((volatile void __iomem *)((unsigned long)ofs)); s5p_mfc_clock_off(); return ret; @@ -1903,51 +1898,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_display_first_plane_addr); + return readl(dev->mfc_regs->d_display_first_plane_addr); } static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_decoded_first_plane_addr); + return readl(dev->mfc_regs->d_decoded_first_plane_addr); } static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_display_status); + return readl(dev->mfc_regs->d_display_status); } static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_decoded_status); + return readl(dev->mfc_regs->d_decoded_status); } static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_decoded_frame_type) & + return readl(dev->mfc_regs->d_decoded_frame_type) & S5P_FIMV_DECODE_FRAME_MASK_V6; } static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - return READL(dev->mfc_regs->d_display_frame_type) & + return readl(dev->mfc_regs->d_display_frame_type) & S5P_FIMV_DECODE_FRAME_MASK_V6; } static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_decoded_nal_size); + return readl(dev->mfc_regs->d_decoded_nal_size); } static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->risc2host_command) & + return readl(dev->mfc_regs->risc2host_command) & S5P_FIMV_RISC2HOST_CMD_MASK; } static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->error_code); + return readl(dev->mfc_regs->error_code); } static int s5p_mfc_err_dec_v6(unsigned int err) @@ -1962,87 +1957,87 @@ static int s5p_mfc_err_dspl_v6(unsigned int err) static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_display_frame_width); + return readl(dev->mfc_regs->d_display_frame_width); } static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_display_frame_height); + return readl(dev->mfc_regs->d_display_frame_height); } static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_min_num_dpb); + return readl(dev->mfc_regs->d_min_num_dpb); } static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_min_num_mv); + return readl(dev->mfc_regs->d_min_num_mv); } static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->ret_instance_id); + return readl(dev->mfc_regs->ret_instance_id); } static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->e_num_dpb); + return readl(dev->mfc_regs->e_num_dpb); } static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->e_stream_size); + return readl(dev->mfc_regs->e_stream_size); } static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->e_slice_type); + return readl(dev->mfc_regs->e_slice_type); } static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->e_picture_count); + return readl(dev->mfc_regs->e_picture_count); } static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - return READL(dev->mfc_regs->d_frame_pack_sei_avail); + return readl(dev->mfc_regs->d_frame_pack_sei_avail); } static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_mvc_num_views); + return readl(dev->mfc_regs->d_mvc_num_views); } static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) { - return READL(dev->mfc_regs->d_mvc_view_id); + return readl(dev->mfc_regs->d_mvc_view_id); } static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, - (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top); + (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_top); } static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, - (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot); + (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_bot); } static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, - (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1); + (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info1); } static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, - (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2); + (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info2); } static struct s5p_mfc_regs mfc_regs; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index b6a8be9..826c489 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -21,7 +21,7 @@ #include "s5p_mfc_pm.h" #define MFC_GATE_CLK_NAME "mfc" -#define MFC_SCLK_NAME "sclk-mfc" +#define MFC_SCLK_NAME "sclk_mfc" #define MFC_SCLK_RATE (200 * 1000000) #define CLK_DEBUG diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig index 369a4c1..a9d56f8 100644 --- a/drivers/media/platform/s5p-tv/Kconfig +++ b/drivers/media/platform/s5p-tv/Kconfig @@ -8,7 +8,8 @@ config VIDEO_SAMSUNG_S5P_TV bool "Samsung TV driver for S5P platform" - depends on (PLAT_S5P || ARCH_EXYNOS) && PM_RUNTIME + depends on PM_RUNTIME + depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST default n ---help--- Say Y here to enable selecting the TV output devices for @@ -70,6 +71,7 @@ config VIDEO_SAMSUNG_S5P_MIXER tristate "Samsung Mixer and Video Processor Driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on VIDEO_SAMSUNG_S5P_TV + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG help Say Y here if you want support for the Mixer in Samsung S5P SoCs. diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 754740f..37c8bd6 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -615,7 +615,7 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on) else ret = pm_runtime_put_sync(hdev->dev); /* only values < 0 indicate errors */ - return IS_ERR_VALUE(ret) ? ret : 0; + return ret < 0 ? ret : 0; } static int hdmi_s_dv_timings(struct v4l2_subdev *sd, diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 5a7c379..72cf892 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -190,7 +190,7 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on) ret = pm_runtime_put_sync(dev); /* only values < 0 indicate errors */ - return IS_ERR_VALUE(ret) ? ret : 0; + return ret < 0 ? ret : 0; } static int sdo_streamon(struct sdo_device *sdev) diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 3dd762e..db8c17b 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -289,7 +289,7 @@ static int sii9234_s_power(struct v4l2_subdev *sd, int on) else ret = pm_runtime_put(&ctx->client->dev); /* only values < 0 indicate errors */ - return IS_ERR_VALUE(ret) ? ret : 0; + return ret < 0 ? ret : 0; } static int sii9234_s_stream(struct v4l2_subdev *sd, int enable) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 8dc279d..be3b3bc 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -26,6 +26,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> +#include <media/v4l2-image-sizes.h> #include <media/videobuf2-dma-contig.h> #define VEU_STR 0x00 /* start register */ @@ -135,9 +136,6 @@ enum sh_veu_fmt_idx { 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 diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 6540847..f2776cd 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -20,6 +20,8 @@ config SOC_CAMERA_PLATFORM config VIDEO_MX3 tristate "i.MX3x Camera Sensor Interface driver" depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA + depends on MX3_IPU || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for the i.MX3x Camera Sensor Interface @@ -35,6 +37,7 @@ config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) support" depends on VIDEO_DEV && SOC_CAMERA depends on ARCH_SHMOBILE || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP ---help--- @@ -51,6 +54,7 @@ config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP ---help--- @@ -58,7 +62,9 @@ config VIDEO_SH_MOBILE_CEU config VIDEO_OMAP1 tristate "OMAP1 Camera Interface driver" - depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA + depends on VIDEO_DEV && SOC_CAMERA + depends on ARCH_OMAP1 + depends on HAS_DMA select VIDEOBUF_DMA_CONTIG select VIDEOBUF_DMA_SG ---help--- @@ -66,14 +72,18 @@ config VIDEO_OMAP1 config VIDEO_MX2 tristate "i.MX27 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && SOC_IMX27 + depends on VIDEO_DEV && SOC_CAMERA + depends on SOC_IMX27 || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for the i.MX27 Camera Sensor Interface config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" - depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91 + depends on VIDEO_DEV && SOC_CAMERA + depends on ARCH_AT91 || COMPILE_TEST + depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- This module makes the ATMEL Image Sensor Interface available diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 3408b04..c5291b0 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -54,7 +54,7 @@ static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl) struct isi_dma_desc { struct list_head list; struct fbd *p_fbd; - u32 fbd_phys; + dma_addr_t fbd_phys; }; /* Frame buffer data */ @@ -75,7 +75,7 @@ struct atmel_isi { /* Allocate descriptors for dma buffer use */ struct fbd *p_fb_descriptors; - u32 fb_descriptors_phys; + dma_addr_t fb_descriptors_phys; struct list_head dma_desc_head; struct isi_dma_desc dma_desc[MAX_BUFFER_NUM]; @@ -169,7 +169,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) isi->active = list_entry(isi->video_buffer_list.next, struct frame_buffer, list); isi_writel(isi, ISI_DMA_C_DSCR, - isi->active->p_dma_desc->fbd_phys); + (u32)isi->active->p_dma_desc->fbd_phys); isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); @@ -346,7 +346,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) return; } - isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys); + isi_writel(isi, ISI_DMA_C_DSCR, (u32)buffer->p_dma_desc->fbd_phys); isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); @@ -384,7 +384,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - u32 sr = 0; int ret; /* Reset ISI */ @@ -394,11 +393,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return ret; } /* Disable all interrupts */ - isi_writel(isi, ISI_INTDIS, ~0UL); + isi_writel(isi, ISI_INTDIS, (u32)~0UL); spin_lock_irq(&isi->lock); /* Clear any pending interrupt */ - sr = isi_readl(isi, ISI_STATUS); + isi_readl(isi, ISI_STATUS); if (count) start_dma(isi, isi->active); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index b40bc2e..2347612a 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -809,10 +809,9 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q, static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) { - u32 cntl; int count = 0; - cntl = readl(pcdev->base_emma + PRP_CNTL); + readl(pcdev->base_emma + PRP_CNTL); writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); while (count++ < 100) { if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) @@ -1003,7 +1002,7 @@ static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, struct v4l2_mbus_framefmt *mf_in, struct v4l2_pix_format *pix_out, bool apply) { - int num, den; + unsigned int num, den; unsigned long m; int i, dir; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 64dc80c..66178fc 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1694,7 +1694,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, break; default: break; - }; + } if (ep.bus.parallel.flags & V4L2_MBUS_MASTER) pcdev->platform_flags |= PXA_CAMERA_MASTER; diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 85d579f..20defcb 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -981,7 +981,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, if (shift == 3) { dev_err(dev, - "Failed to configure the client below %ux%x\n", + "Failed to configure the client below %ux%u\n", mf.width, mf.height); return -EIO; } @@ -1502,7 +1502,7 @@ static int rcar_vin_probe(struct platform_device *pdev) } else { priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin"); priv->chip = (enum chip_id)match->data; - }; + } spin_lock_init(&priv->lock); INIT_LIST_HEAD(&priv->capture); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index f4308fe..8e61b97 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -437,6 +437,22 @@ static int soc_camera_prepare_buf(struct file *file, void *priv, return vb2_prepare_buf(&icd->vb2_vidq, b); } +static int soc_camera_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *p) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (icd->streamer != file) + return -EBUSY; + + /* videobuf2 only */ + if (ici->ops->init_videobuf) + return -EINVAL; + else + return vb2_expbuf(&icd->vb2_vidq, p); +} + /* Always entered with .host_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { @@ -1347,13 +1363,11 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd, return -ENODEV; } - ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL); + ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); if (!ssdd) { ret = -ENOMEM; goto ealloc; } - - memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd)); /* * In synchronous case we request regulators ourselves in * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try @@ -2085,6 +2099,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_dqbuf = soc_camera_dqbuf, .vidioc_create_bufs = soc_camera_create_bufs, .vidioc_prepare_buf = soc_camera_prepare_buf, + .vidioc_expbuf = soc_camera_expbuf, .vidioc_streamon = soc_camera_streamon, .vidioc_streamoff = soc_camera_streamoff, .vidioc_cropcap = soc_camera_cropcap, diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index a51a013..3e2e3a3 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -329,7 +329,7 @@ int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size) if (!buf->addr) return -ENOMEM; - WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN); + WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0); return 0; } @@ -584,7 +584,7 @@ static void dump_dtd(struct vpdma_dtd *dtd) pr_debug("word1: line_length = %d, xfer_height = %d\n", dtd_get_line_length(dtd), dtd_get_xfer_height(dtd)); - pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr); + pr_debug("word2: start_addr = %pad\n", &dtd->start_addr); pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, " "pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd), diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 972f43f..9a081c2 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/log2.h> +#include <linux/sizes.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> @@ -138,12 +139,12 @@ struct vpe_dei_regs { * default expert DEI register values, unlikely to be modified. */ static const struct vpe_dei_regs dei_regs = { - 0x020C0804u, - 0x0118100Fu, - 0x08040200u, - 0x1010100Cu, - 0x10101010u, - 0x10101010u, + .mdt_spacial_freq_thr_reg = 0x020C0804u, + .edi_config_reg = 0x0118100Fu, + .edi_lut_reg0 = 0x08040200u, + .edi_lut_reg1 = 0x1010100Cu, + .edi_lut_reg2 = 0x10101010u, + .edi_lut_reg3 = 0x10101010u, }; /* @@ -834,10 +835,10 @@ static int set_srcdst_params(struct vpe_ctx *ctx) VPDMA_STRIDE_ALIGN); mv_buf_size = bytes_per_line * s_q_data->height; - ctx->deinterlacing = 1; + ctx->deinterlacing = true; src_h <<= 1; } else { - ctx->deinterlacing = 0; + ctx->deinterlacing = false; mv_buf_size = 0; } @@ -2343,8 +2344,7 @@ v4l2_dev_unreg: static int vpe_remove(struct platform_device *pdev) { - struct vpe_dev *dev = - (struct vpe_dev *) platform_get_drvdata(pdev); + struct vpe_dev *dev = platform_get_drvdata(pdev); v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index b4f9d03..ae6870c 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -18,6 +18,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-image-sizes.h> #include <media/ov7670.h> #include <media/videobuf-dma-sg.h> #include <linux/delay.h> @@ -49,14 +50,6 @@ MODULE_PARM_DESC(override_serial, "to force-enable the camera."); /* - * Basic window sizes. - */ -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 - -/* * The structure describing our camera. */ enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 }; @@ -89,7 +82,7 @@ struct via_camera { * live in frame buffer memory, so we don't call them "DMA". */ unsigned int cb_offsets[3]; /* offsets into fb mem */ - u8 *cb_addrs[3]; /* Kernel-space addresses */ + u8 __iomem *cb_addrs[3]; /* Kernel-space addresses */ int n_cap_bufs; /* How many are we using? */ int next_buf; struct videobuf_queue vb_queue; @@ -1283,7 +1276,7 @@ static bool viacam_serial_is_enabled(void) VIACAM_SERIAL_CREG, &cbyte); if ((cbyte & VIACAM_SERIAL_BIT) == 0) return false; /* Not enabled */ - if (override_serial == 0) { + if (!override_serial) { printk(KERN_NOTICE "Via camera: serial port is enabled, " \ "refusing to load.\n"); printk(KERN_NOTICE "Specify override_serial=1 to force " \ diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c deleted file mode 100644 index 8033371..0000000 --- a/drivers/media/platform/vivi.c +++ /dev/null @@ -1,1542 +0,0 @@ -/* - * Virtual Video driver - This code emulates a real video device with v4l2 api - * - * Copyright (c) 2006 by: - * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> - * Ted Walther <ted--a.t--enumera.com> - * John Sokol <sokol--a.t--videotechnology.com> - * http://v4l.videotechnology.com/ - * - * Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski - * Copyright (c) 2010 Samsung Electronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the BSD Licence, GNU General Public License - * as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version - */ -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/font.h> -#include <linux/mutex.h> -#include <linux/videodev2.h> -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <media/videobuf2-vmalloc.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> -#include <media/v4l2-common.h> - -#define VIVI_MODULE_NAME "vivi" - -/* 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 - -#define VIVI_VERSION "0.8.1" - -MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); -MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_VERSION(VIVI_VERSION); - -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); - -static unsigned n_devs = 1; -module_param(n_devs, uint, 0644); -MODULE_PARM_DESC(n_devs, "number of video devices to create"); - -static unsigned debug; -module_param(debug, uint, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -/* 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) - -/* ------------------------------------------------------------------ - Basic structures - ------------------------------------------------------------------*/ - -struct vivi_fmt { - const char *name; - u32 fourcc; /* v4l2 format id */ - u8 depth; - bool is_yuv; -}; - -static const struct vivi_fmt formats[] = { - { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .is_yuv = true, - }, - { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .is_yuv = true, - }, - { - .name = "4:2:2, packed, YVYU", - .fourcc = V4L2_PIX_FMT_YVYU, - .depth = 16, - .is_yuv = true, - }, - { - .name = "4:2:2, packed, VYUY", - .fourcc = V4L2_PIX_FMT_VYUY, - .depth = 16, - .is_yuv = true, - }, - { - .name = "RGB565 (LE)", - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .depth = 16, - }, - { - .name = "RGB565 (BE)", - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .depth = 16, - }, - { - .name = "RGB555 (LE)", - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, - }, - { - .name = "RGB555 (BE)", - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .depth = 16, - }, - { - .name = "RGB24 (LE)", - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .depth = 24, - }, - { - .name = "RGB24 (BE)", - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .depth = 24, - }, - { - .name = "RGB32 (LE)", - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .depth = 32, - }, - { - .name = "RGB32 (BE)", - .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ - .depth = 32, - }, -}; - -static const struct vivi_fmt *__get_format(u32 pixelformat) -{ - const struct vivi_fmt *fmt; - unsigned int k; - - for (k = 0; k < ARRAY_SIZE(formats); k++) { - fmt = &formats[k]; - if (fmt->fourcc == pixelformat) - break; - } - - if (k == ARRAY_SIZE(formats)) - return NULL; - - 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_dmaqueue { - struct list_head active; - - /* thread for generating video stream*/ - struct task_struct *kthread; - wait_queue_head_t wq; - /* Counters to control fps rate */ - int frame; - int ini_jiffies; -}; - -static LIST_HEAD(vivi_devlist); - -struct vivi_dev { - struct list_head vivi_devlist; - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - - /* controls */ - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *hue; - struct { - /* autogain/gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *volume; - struct v4l2_ctrl *alpha; - struct v4l2_ctrl *button; - struct v4l2_ctrl *boolean; - struct v4l2_ctrl *int32; - struct v4l2_ctrl *int64; - struct v4l2_ctrl *menu; - struct v4l2_ctrl *string; - struct v4l2_ctrl *bitmask; - struct v4l2_ctrl *int_menu; - - spinlock_t slock; - struct mutex mutex; - - struct vivi_dmaqueue vidq; - - /* Several counters */ - unsigned ms; - unsigned long jiffies; - unsigned button_pressed; - - int mv_count; /* Controls bars movement */ - - /* Input Number */ - int input; - - /* video capture */ - const struct vivi_fmt *fmt; - struct v4l2_fract timeperframe; - unsigned int width, height; - struct vb2_queue vb_vidq; - unsigned int seq_count; - - u8 bars[9][3]; - u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); - unsigned int pixelsize; - u8 alpha_component; - u32 textfg, textbg; -}; - -/* ------------------------------------------------------------------ - DMA and thread functions - ------------------------------------------------------------------*/ - -/* Bars and Colors should match positions */ - -enum colors { - WHITE, - AMBER, - CYAN, - GREEN, - MAGENTA, - RED, - BLUE, - BLACK, - TEXT_BLACK, -}; - -/* R G B */ -#define COLOR_WHITE {204, 204, 204} -#define COLOR_AMBER {208, 208, 0} -#define COLOR_CYAN { 0, 206, 206} -#define COLOR_GREEN { 0, 239, 0} -#define COLOR_MAGENTA {239, 0, 239} -#define COLOR_RED {205, 0, 0} -#define COLOR_BLUE { 0, 0, 255} -#define COLOR_BLACK { 0, 0, 0} - -struct bar_std { - u8 bar[9][3]; -}; - -/* Maximum number of bars are 10 - otherwise, the input print code - should be modified */ -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 } - }, { - { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, - COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK } - }, { - { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, - COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK } - }, { - { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, - COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK } - }, -}; - -#define NUM_INPUTS ARRAY_SIZE(bars) - -#define TO_Y(r, g, b) \ - (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16) -/* RGB to V(Cr) Color transform */ -#define TO_V(r, g, b) \ - (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128) -/* RGB to U(Cb) Color transform */ -#define TO_U(r, g, b) \ - (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128) - -/* precalculate color bar values to speed up rendering */ -static void precalculate_bars(struct vivi_dev *dev) -{ - u8 r, g, b; - int k, is_yuv; - - for (k = 0; k < 9; k++) { - r = bars[dev->input].bar[k][0]; - g = bars[dev->input].bar[k][1]; - b = bars[dev->input].bar[k][2]; - is_yuv = dev->fmt->is_yuv; - - switch (dev->fmt->fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - r >>= 3; - g >>= 2; - b >>= 3; - break; - case V4L2_PIX_FMT_RGB555: - case V4L2_PIX_FMT_RGB555X: - r >>= 3; - g >>= 3; - b >>= 3; - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32: - break; - } - - if (is_yuv) { - dev->bars[k][0] = TO_Y(r, g, b); /* Luma */ - dev->bars[k][1] = TO_U(r, g, b); /* Cb */ - dev->bars[k][2] = TO_V(r, g, b); /* Cr */ - } else { - dev->bars[k][0] = r; - dev->bars[k][1] = g; - dev->bars[k][2] = b; - } - } -} - -/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ -static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) -{ - u8 r_y, g_u, b_v; - u8 alpha = dev->alpha_component; - int color; - u8 *p; - - r_y = dev->bars[colorpos][0]; /* R or precalculated Y */ - g_u = dev->bars[colorpos][1]; /* G or precalculated U */ - b_v = dev->bars[colorpos][2]; /* B or precalculated V */ - - for (color = 0; color < dev->pixelsize; color++) { - p = buf + color; - - switch (dev->fmt->fourcc) { - case V4L2_PIX_FMT_YUYV: - switch (color) { - case 0: - *p = r_y; - break; - case 1: - *p = odd ? b_v : g_u; - break; - } - break; - case V4L2_PIX_FMT_UYVY: - switch (color) { - case 0: - *p = odd ? b_v : g_u; - break; - case 1: - *p = r_y; - break; - } - break; - case V4L2_PIX_FMT_YVYU: - switch (color) { - case 0: - *p = r_y; - break; - case 1: - *p = odd ? g_u : b_v; - break; - } - break; - case V4L2_PIX_FMT_VYUY: - switch (color) { - case 0: - *p = odd ? g_u : b_v; - break; - case 1: - *p = r_y; - break; - } - break; - case V4L2_PIX_FMT_RGB565: - switch (color) { - case 0: - *p = (g_u << 5) | b_v; - break; - case 1: - *p = (r_y << 3) | (g_u >> 3); - break; - } - break; - case V4L2_PIX_FMT_RGB565X: - switch (color) { - case 0: - *p = (r_y << 3) | (g_u >> 3); - break; - case 1: - *p = (g_u << 5) | b_v; - break; - } - break; - case V4L2_PIX_FMT_RGB555: - switch (color) { - case 0: - *p = (g_u << 5) | b_v; - break; - case 1: - *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); - break; - } - break; - case V4L2_PIX_FMT_RGB555X: - switch (color) { - case 0: - *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); - break; - case 1: - *p = (g_u << 5) | b_v; - break; - } - break; - case V4L2_PIX_FMT_RGB24: - switch (color) { - case 0: - *p = r_y; - break; - case 1: - *p = g_u; - break; - case 2: - *p = b_v; - break; - } - break; - case V4L2_PIX_FMT_BGR24: - switch (color) { - case 0: - *p = b_v; - break; - case 1: - *p = g_u; - break; - case 2: - *p = r_y; - break; - } - break; - case V4L2_PIX_FMT_RGB32: - switch (color) { - case 0: - *p = alpha; - break; - case 1: - *p = r_y; - break; - case 2: - *p = g_u; - break; - case 3: - *p = b_v; - break; - } - break; - case V4L2_PIX_FMT_BGR32: - switch (color) { - case 0: - *p = b_v; - break; - case 1: - *p = g_u; - break; - case 2: - *p = r_y; - break; - case 3: - *p = alpha; - break; - } - break; - } - } -} - -static void precalculate_line(struct vivi_dev *dev) -{ - 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 >= width) - return; - - /* Print stream time */ -#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 stride = dev->width * dev->pixelsize; - int hmax = dev->height; - 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 * 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; - snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ", - (ms / (60 * 60 * 1000)) % 24, - (ms / (60 * 1000)) % 60, - (ms / 1000) % 60, - ms % 1000); - gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " %dx%d, input %d ", - dev->width, dev->height, dev->input); - gen_text(dev, vbuf, line++ * 16, 16, str); - - gain = v4l2_ctrl_g_ctrl(dev->gain); - mutex_lock(dev->ctrl_handler.lock); - snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", - dev->brightness->cur.val, - dev->contrast->cur.val, - dev->saturation->cur.val, - dev->hue->cur.val); - gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ", - dev->autogain->cur.val, gain, dev->volume->cur.val, - dev->alpha->cur.val); - gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", - dev->int32->cur.val, - *dev->int64->p_cur.p_s64, - dev->bitmask->cur.val); - gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", - dev->boolean->cur.val, - dev->menu->qmenu[dev->menu->cur.val], - dev->string->p_cur.p_char); - gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " integer_menu %lld, value %d ", - dev->int_menu->qmenu_int[dev->int_menu->cur.val], - dev->int_menu->cur.val); - gen_text(dev, vbuf, line++ * 16, 16, str); - mutex_unlock(dev->ctrl_handler.lock); - if (dev->button_pressed) { - dev->button_pressed--; - snprintf(str, sizeof(str), " button pressed!"); - gen_text(dev, vbuf, line++ * 16, 16, str); - } - - dev->mv_count += 2; - - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; - buf->vb.v4l2_buf.sequence = dev->seq_count++; - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); -} - -static void vivi_thread_tick(struct vivi_dev *dev) -{ - struct vivi_dmaqueue *dma_q = &dev->vidq; - struct vivi_buffer *buf; - unsigned long flags = 0; - - dprintk(dev, 1, "Thread tick\n"); - - spin_lock_irqsave(&dev->slock, flags); - if (list_empty(&dma_q->active)) { - dprintk(dev, 1, "No active queue to serve\n"); - spin_unlock_irqrestore(&dev->slock, flags); - return; - } - - buf = list_entry(dma_q->active.next, struct vivi_buffer, list); - list_del(&buf->list); - spin_unlock_irqrestore(&dev->slock, flags); - - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - - /* Fill buffer */ - vivi_fillbuff(dev, buf); - dprintk(dev, 1, "filled buffer %p\n", buf); - - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); - dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); -} - -#define frames_to_ms(dev, frames) \ - ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator) - -static void vivi_sleep(struct vivi_dev *dev) -{ - struct vivi_dmaqueue *dma_q = &dev->vidq; - int timeout; - DECLARE_WAITQUEUE(wait, current); - - dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__, - (unsigned long)dma_q); - - add_wait_queue(&dma_q->wq, &wait); - if (kthread_should_stop()) - goto stop_task; - - /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); - - vivi_thread_tick(dev); - - schedule_timeout_interruptible(timeout); - -stop_task: - remove_wait_queue(&dma_q->wq, &wait); - try_to_freeze(); -} - -static int vivi_thread(void *data) -{ - struct vivi_dev *dev = data; - - dprintk(dev, 1, "thread started\n"); - - set_freezable(); - - for (;;) { - vivi_sleep(dev); - - if (kthread_should_stop()) - break; - } - dprintk(dev, 1, "thread: exit\n"); - return 0; -} - -static int vivi_start_generating(struct vivi_dev *dev) -{ - struct vivi_dmaqueue *dma_q = &dev->vidq; - - dprintk(dev, 1, "%s\n", __func__); - - /* Resets frame counters */ - dev->ms = 0; - dev->mv_count = 0; - dev->jiffies = jiffies; - - dma_q->frame = 0; - dma_q->ini_jiffies = jiffies; - dma_q->kthread = kthread_run(vivi_thread, dev, "%s", - dev->v4l2_dev.name); - - if (IS_ERR(dma_q->kthread)) { - v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); - return PTR_ERR(dma_q->kthread); - } - /* Wakes thread */ - wake_up_interruptible(&dma_q->wq); - - dprintk(dev, 1, "returning from %s\n", __func__); - return 0; -} - -static void vivi_stop_generating(struct vivi_dev *dev) -{ - struct vivi_dmaqueue *dma_q = &dev->vidq; - - dprintk(dev, 1, "%s\n", __func__); - - /* shutdown control thread */ - if (dma_q->kthread) { - kthread_stop(dma_q->kthread); - dma_q->kthread = NULL; - } - - /* - * Typical driver might need to wait here until dma engine stops. - * In this case we can abort imiedetly, so it's just a noop. - */ - - /* Release all active buffers */ - while (!list_empty(&dma_q->active)) { - struct vivi_buffer *buf; - buf = list_entry(dma_q->active.next, struct vivi_buffer, list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); - } -} -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ -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 vivi_dev *dev = vb2_get_drv_priv(vq); - unsigned long size; - - size = dev->width * dev->height * dev->pixelsize; - if (fmt) { - if (fmt->fmt.pix.sizeimage < size) - return -EINVAL; - size = fmt->fmt.pix.sizeimage; - /* check against insane over 8K resolution buffers */ - if (size > 7680 * 4320 * dev->pixelsize) - return -EINVAL; - } - - *nplanes = 1; - - sizes[0] = size; - - /* - * videobuf2-vmalloc allocator is context-less so no need to set - * alloc_ctxs array. - */ - - dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__, - *nbuffers, size); - - return 0; -} - -static int buffer_prepare(struct vb2_buffer *vb) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - unsigned long size; - - dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field); - - BUG_ON(NULL == dev->fmt); - - /* - * Theses properties only change when queue is idle, see s_fmt. - * The below checks should not be performed here, on each - * buffer_prepare (i.e. on each qbuf). Most of the code in this function - * should thus be moved to buffer_init and s_fmt. - */ - if (dev->width < 48 || dev->width > MAX_WIDTH || - dev->height < 32 || dev->height > MAX_HEIGHT) - return -EINVAL; - - size = dev->width * dev->height * dev->pixelsize; - if (vb2_plane_size(vb, 0) < size) { - dprintk(dev, 1, "%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); - - precalculate_bars(dev); - precalculate_line(dev); - - return 0; -} - -static void buffer_queue(struct vb2_buffer *vb) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - struct vivi_dmaqueue *vidq = &dev->vidq; - unsigned long flags = 0; - - dprintk(dev, 1, "%s\n", __func__); - - spin_lock_irqsave(&dev->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&dev->slock, flags); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vq); - int err; - - dprintk(dev, 1, "%s\n", __func__); - dev->seq_count = 0; - err = vivi_start_generating(dev); - if (err) { - struct vivi_buffer *buf, *tmp; - - list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); - } - } - return err; -} - -/* abort streaming and wait for last buffer */ -static void stop_streaming(struct vb2_queue *vq) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vq); - dprintk(dev, 1, "%s\n", __func__); - vivi_stop_generating(dev); -} - -static void vivi_lock(struct vb2_queue *vq) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vq); - mutex_lock(&dev->mutex); -} - -static void vivi_unlock(struct vb2_queue *vq) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vq); - mutex_unlock(&dev->mutex); -} - - -static const struct vb2_ops vivi_video_qops = { - .queue_setup = queue_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, - .wait_prepare = vivi_unlock, - .wait_finish = vivi_lock, -}; - -/* ------------------------------------------------------------------ - IOCTL vidioc handling - ------------------------------------------------------------------*/ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct vivi_dev *dev = video_drvdata(file); - - strcpy(cap->driver, "vivi"); - strcpy(cap->card, "vivi"); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev->v4l2_dev.name); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - const struct vivi_fmt *fmt; - - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - - fmt = &formats[f->index]; - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vivi_dev *dev = video_drvdata(file); - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.pixelformat = dev->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * dev->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - if (dev->fmt->is_yuv) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - else - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vivi_dev *dev = video_drvdata(file); - const struct vivi_fmt *fmt; - - fmt = get_format(f); - if (!fmt) { - dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", - f->fmt.pix.pixelformat); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - fmt = get_format(f); - } - - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0); - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - if (fmt->is_yuv) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - else - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vivi_dev *dev = video_drvdata(file); - struct vb2_queue *q = &dev->vb_vidq; - - int ret = vidioc_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - if (vb2_is_busy(q)) { - dprintk(dev, 1, "%s device busy\n", __func__); - return -EBUSY; - } - - dev->fmt = get_format(f); - dev->pixelsize = dev->fmt->depth / 8; - dev->width = f->fmt.pix.width; - dev->height = f->fmt.pix.height; - - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - static const struct v4l2_frmsize_stepwise sizes = { - 48, MAX_WIDTH, 4, - 32, MAX_HEIGHT, 1 - }; - int i; - - if (fsize->index) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(formats); i++) - if (formats[i].fourcc == fsize->pixel_format) - break; - if (i == ARRAY_SIZE(formats)) - return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = sizes; - return 0; -} - -/* only one input in this sample driver */ -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index >= NUM_INPUTS) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct vivi_dev *dev = video_drvdata(file); - - *i = dev->input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct vivi_dev *dev = video_drvdata(file); - - if (i >= NUM_INPUTS) - return -EINVAL; - - if (i == dev->input) - return 0; - - dev->input = i; - /* - * Modify the brightness range depending on the input. - * This makes it easy to use vivi to test if applications can - * handle control range modifications and is also how this is - * typically used in practice as different inputs may be hooked - * up to different receivers with different control ranges. - */ - v4l2_ctrl_modify_range(dev->brightness, - 128 * i, 255 + 128 * i, 1, 127 + 128 * i); - precalculate_bars(dev); - precalculate_line(dev); - return 0; -} - -/* timeperframe is arbitrary and continuous */ -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; - - /* check for valid width/height */ - if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3)) - return -EINVAL; - if (fival->height < 32 || fival->height > MAX_HEIGHT) - return -EINVAL; - - fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - - /* fill in stepwise (step=1.0 is required 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) -{ - struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - - if (ctrl == dev->autogain) - dev->gain->val = jiffies & 0xff; - return 0; -} - -static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_ALPHA_COMPONENT: - dev->alpha_component = ctrl->val; - break; - default: - if (ctrl == dev->button) - dev->button_pressed = 30; - break; - } - return 0; -} - -/* ------------------------------------------------------------------ - File operations for the device - ------------------------------------------------------------------*/ - -static const struct v4l2_ctrl_ops vivi_ctrl_ops = { - .g_volatile_ctrl = vivi_g_volatile_ctrl, - .s_ctrl = vivi_s_ctrl, -}; - -#define VIVI_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) - -static const struct v4l2_ctrl_config vivi_ctrl_button = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 0, - .name = "Button", - .type = V4L2_CTRL_TYPE_BUTTON, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_boolean = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 1, - .name = "Boolean", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = 0, - .max = 1, - .step = 1, - .def = 1, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_int32 = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 2, - .name = "Integer 32 Bits", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = -0x80000000LL, - .max = 0x7fffffff, - .step = 1, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_int64 = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 3, - .name = "Integer 64 Bits", - .type = V4L2_CTRL_TYPE_INTEGER64, - .min = LLONG_MIN, - .max = LLONG_MAX, - .step = 1, -}; - -static const char * const vivi_ctrl_menu_strings[] = { - "Menu Item 0 (Skipped)", - "Menu Item 1", - "Menu Item 2 (Skipped)", - "Menu Item 3", - "Menu Item 4", - "Menu Item 5 (Skipped)", - NULL, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_menu = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 4, - .name = "Menu", - .type = V4L2_CTRL_TYPE_MENU, - .min = 1, - .max = 4, - .def = 3, - .menu_skip_mask = 0x04, - .qmenu = vivi_ctrl_menu_strings, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_string = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 5, - .name = "String", - .type = V4L2_CTRL_TYPE_STRING, - .min = 2, - .max = 4, - .step = 1, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_bitmask = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 6, - .name = "Bitmask", - .type = V4L2_CTRL_TYPE_BITMASK, - .def = 0x80002000, - .min = 0, - .max = 0x80402010, - .step = 0, -}; - -static const s64 vivi_ctrl_int_menu_values[] = { - 1, 1, 2, 3, 5, 8, 13, 21, 42, -}; - -static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { - .ops = &vivi_ctrl_ops, - .id = VIVI_CID_CUSTOM_BASE + 7, - .name = "Integer menu", - .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .min = 1, - .max = 8, - .def = 4, - .menu_skip_mask = 0x02, - .qmenu_int = vivi_ctrl_int_menu_values, -}; - -static const struct v4l2_file_operations vivi_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops vivi_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .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_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, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device vivi_template = { - .name = "vivi", - .fops = &vivi_fops, - .ioctl_ops = &vivi_ioctl_ops, - .release = video_device_release_empty, -}; - -/* ----------------------------------------------------------------- - Initialization and module stuff - ------------------------------------------------------------------*/ - -static int vivi_release(void) -{ - struct vivi_dev *dev; - struct list_head *list; - - while (!list_empty(&vivi_devlist)) { - list = vivi_devlist.next; - list_del(list); - dev = list_entry(list, struct vivi_dev, vivi_devlist); - - v4l2_info(&dev->v4l2_dev, "unregistering %s\n", - video_device_node_name(&dev->vdev)); - video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); - v4l2_ctrl_handler_free(&dev->ctrl_handler); - kfree(dev); - } - - return 0; -} - -static int __init vivi_create_instance(int inst) -{ - struct vivi_dev *dev; - struct video_device *vfd; - struct v4l2_ctrl_handler *hdl; - struct vb2_queue *q; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), - "%s-%03d", VIVI_MODULE_NAME, inst); - ret = v4l2_device_register(NULL, &dev->v4l2_dev); - if (ret) - goto free_dev; - - dev->fmt = &formats[0]; - dev->timeperframe = tpf_default; - dev->width = 640; - dev->height = 480; - dev->pixelsize = dev->fmt->depth / 8; - hdl = &dev->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 11); - dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); - dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 16); - dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 127); - dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 100); - dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, - V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); - dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); - dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); - dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); - dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL); - dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); - dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); - dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL); - dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL); - if (hdl->error) { - ret = hdl->error; - goto unreg_dev; - } - v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true); - dev->v4l2_dev.ctrl_handler = hdl; - - /* initialize locks */ - spin_lock_init(&dev->slock); - - /* initialize queue */ - q = &dev->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivi_buffer); - q->ops = &vivi_video_qops; - q->mem_ops = &vb2_vmalloc_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - ret = vb2_queue_init(q); - if (ret) - goto unreg_dev; - - mutex_init(&dev->mutex); - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - init_waitqueue_head(&dev->vidq.wq); - - vfd = &dev->vdev; - *vfd = vivi_template; - vfd->debug = debug; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->queue = q; - - /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. - */ - vfd->lock = &dev->mutex; - video_set_drvdata(vfd, dev); - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) - goto unreg_dev; - - /* Now that everything is fine, let's add it to device list */ - list_add_tail(&dev->vivi_devlist, &vivi_devlist); - - v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - return 0; - -unreg_dev: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); -free_dev: - kfree(dev); - return ret; -} - -/* This routine allocates from 1 to n_devs virtual drivers. - - The real maximum number of virtual drivers will depend on how many drivers - will succeed. This is limited to the maximum number of devices that - videodev supports, which is equal to VIDEO_NUM_DEVICES. - */ -static int __init vivi_init(void) -{ - const struct font_desc *font = find_font("VGA8x16"); - int ret = 0, i; - - if (font == NULL) { - printk(KERN_ERR "vivi: could not find font\n"); - return -ENODEV; - } - font8x16 = font->data; - - if (n_devs <= 0) - n_devs = 1; - - for (i = 0; i < n_devs; i++) { - ret = vivi_create_instance(i); - if (ret) { - /* If some instantiations succeeded, keep driver */ - if (i) - ret = 0; - break; - } - } - - if (ret < 0) { - printk(KERN_ERR "vivi: error %d while loading driver\n", ret); - return ret; - } - - printk(KERN_INFO "Video Technology Magazine Virtual Video " - "Capture Board ver %s successfully loaded.\n", - VIVI_VERSION); - - /* n_devs will reflect the actual number of allocated devices */ - n_devs = i; - - return ret; -} - -static void __exit vivi_exit(void) -{ - vivi_release(); -} - -module_init(vivi_init); -module_exit(vivi_exit); diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig new file mode 100644 index 0000000..d71139a --- /dev/null +++ b/drivers/media/platform/vivid/Kconfig @@ -0,0 +1,19 @@ +config VIDEO_VIVID + tristate "Virtual Video Test Driver" + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + select FONT_SUPPORT + select FONT_8x16 + select VIDEOBUF2_VMALLOC + default n + ---help--- + Enables a virtual video driver. This driver emulates a webcam, + TV, S-Video and HDMI capture hardware, including VBI support for + the SDTV inputs. Also video output, VBI output, radio receivers, + transmitters and software defined radio capture is emulated. + + It is highly configurable and is ideal for testing applications. + Error injection is supported to test rare errors that are hard + to reproduce in real hardware. + + Say Y here if you want to test video apps or debug V4L devices. + When in doubt, say N. diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile new file mode 100644 index 0000000..756fc12 --- /dev/null +++ b/drivers/media/platform/vivid/Makefile @@ -0,0 +1,6 @@ +vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ + vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ + vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ + vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ + vivid-osd.o vivid-tpg.o vivid-tpg-colors.o +obj-$(CONFIG_VIDEO_VIVID) += vivid.o diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c new file mode 100644 index 0000000..2c61a62 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-core.c @@ -0,0 +1,1390 @@ +/* + * vivid-core.c - A Virtual Video Test Driver, core initialization + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/font.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/videobuf2-vmalloc.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-vid-cap.h" +#include "vivid-vid-out.h" +#include "vivid-radio-common.h" +#include "vivid-radio-rx.h" +#include "vivid-radio-tx.h" +#include "vivid-sdr-cap.h" +#include "vivid-vbi-cap.h" +#include "vivid-vbi-out.h" +#include "vivid-osd.h" +#include "vivid-ctrls.h" + +#define VIVID_MODULE_NAME "vivid" + +/* The maximum number of vivid devices */ +#define VIVID_MAX_DEVS 64 + +MODULE_DESCRIPTION("Virtual Video Test Driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static unsigned n_devs = 1; +module_param(n_devs, uint, 0444); +MODULE_PARM_DESC(n_devs, " number of driver instances to create"); + +static int vid_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vid_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(vid_cap_nr, " videoX start number, -1 is autodetect"); + +static int vid_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vid_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(vid_out_nr, " videoX start number, -1 is autodetect"); + +static int vbi_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vbi_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_cap_nr, " vbiX start number, -1 is autodetect"); + +static int vbi_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(vbi_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_out_nr, " vbiX start number, -1 is autodetect"); + +static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(sdr_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect"); + +static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(radio_rx_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect"); + +static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(radio_tx_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect"); + +static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(ccs_cap_mode, int, NULL, 0444); +MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n" + "\t\t bit 0=crop, 1=compose, 2=scale,\n" + "\t\t -1=user-controlled (default)"); + +static int ccs_out_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(ccs_out_mode, int, NULL, 0444); +MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n" + "\t\t bit 0=crop, 1=compose, 2=scale,\n" + "\t\t -1=user-controlled (default)"); + +static unsigned multiplanar[VIVID_MAX_DEVS]; +module_param_array(multiplanar, uint, NULL, 0444); +MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n" + "\t\t 1 is single planar devices,\n" + "\t\t 2 is multiplanar devices"); + +/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */ +static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d }; +module_param_array(node_types, uint, NULL, 0444); +MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n" + "\t\t bit 0: Video Capture node\n" + "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" + "\t\t bit 4: Radio Receiver node\n" + "\t\t bit 5: Software Defined Radio Receiver node\n" + "\t\t bit 8: Video Output node\n" + "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" + "\t\t bit 12: Radio Transmitter node\n" + "\t\t bit 16: Framebuffer for testing overlays"); + +/* Default: 4 inputs */ +static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 }; +module_param_array(num_inputs, uint, NULL, 0444); +MODULE_PARM_DESC(num_inputs, " number of inputs, default is 4"); + +/* Default: input 0 = WEBCAM, 1 = TV, 2 = SVID, 3 = HDMI */ +static unsigned input_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0xe4 }; +module_param_array(input_types, uint, NULL, 0444); +MODULE_PARM_DESC(input_types, " input types, default is 0xe4. Two bits per input,\n" + "\t\t bits 0-1 == input 0, bits 31-30 == input 15.\n" + "\t\t Type 0 == webcam, 1 == TV, 2 == S-Video, 3 == HDMI"); + +/* Default: 2 outputs */ +static unsigned num_outputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 }; +module_param_array(num_outputs, uint, NULL, 0444); +MODULE_PARM_DESC(num_outputs, " number of outputs, default is 2"); + +/* Default: output 0 = SVID, 1 = HDMI */ +static unsigned output_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 }; +module_param_array(output_types, uint, NULL, 0444); +MODULE_PARM_DESC(output_types, " output types, default is 0x02. One bit per output,\n" + "\t\t bit 0 == output 0, bit 15 == output 15.\n" + "\t\t Type 0 == S-Video, 1 == HDMI"); + +unsigned vivid_debug; +module_param(vivid_debug, uint, 0644); +MODULE_PARM_DESC(vivid_debug, " activates debug info"); + +static bool no_error_inj; +module_param(no_error_inj, bool, 0444); +MODULE_PARM_DESC(no_error_inj, " if set disable the error injecting controls"); + +static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; + +const struct v4l2_rect vivid_min_rect = { + 0, 0, MIN_WIDTH, MIN_HEIGHT +}; + +const struct v4l2_rect vivid_max_rect = { + 0, 0, MAX_WIDTH * MAX_ZOOM, MAX_HEIGHT * MAX_ZOOM +}; + +static const u8 vivid_hdmi_edid[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, + 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, + 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', + '4', 'l', '2', '-', 'h', 'd', 'm', 'i', + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, + + 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, + 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, + 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, + 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, + 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, + 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 +}; + +void vivid_lock(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + mutex_lock(&dev->mutex); +} + +void vivid_unlock(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + mutex_unlock(&dev->mutex); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + strcpy(cap->driver, "vivid"); + strcpy(cap->card, "vivid"); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev->v4l2_dev.name); + + if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->vid_cap_caps; + if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->vid_out_caps; + else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->vbi_cap_caps; + else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->vbi_out_caps; + else if (vdev->vfl_type == VFL_TYPE_SDR) + cap->device_caps = dev->sdr_cap_caps; + else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = dev->radio_rx_caps; + else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->radio_tx_caps; + cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | + dev->vbi_cap_caps | dev->vbi_out_caps | + dev->radio_rx_caps | dev->radio_tx_caps | + dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_s_hw_freq_seek(file, fh, a); + return -ENOTTY; +} + +static int vidioc_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_enum_freq_bands(file, fh, band); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_enum_freq_bands(file, fh, band); + return -ENOTTY; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_g_tuner(file, fh, vt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_g_tuner(file, fh, vt); + return vivid_video_g_tuner(file, fh, vt); +} + +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_rx_s_tuner(file, fh, vt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_s_tuner(file, fh, vt); + return vivid_video_s_tuner(file, fh, vt); +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_g_frequency(file, + vdev->vfl_dir == VFL_DIR_RX ? + &dev->radio_rx_freq : &dev->radio_tx_freq, vf); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_g_frequency(file, fh, vf); + return vivid_video_g_frequency(file, fh, vf); +} + +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_s_frequency(file, + vdev->vfl_dir == VFL_DIR_RX ? + &dev->radio_rx_freq : &dev->radio_tx_freq, vf); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_s_frequency(file, fh, vf); + return vivid_video_s_frequency(file, fh, vf); +} + +static int vidioc_overlay(struct file *file, void *fh, unsigned i) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_overlay(file, fh, i); + return vivid_vid_out_overlay(file, fh, i); +} + +static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_fbuf(file, fh, a); + return vivid_vid_out_g_fbuf(file, fh, a); +} + +static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_fbuf(file, fh, a); + return vivid_vid_out_s_fbuf(file, fh, a); +} + +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_std(file, fh, id); + return vivid_vid_out_s_std(file, fh, id); +} + +static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_dv_timings(file, fh, timings); + return vivid_vid_out_s_dv_timings(file, fh, timings); +} + +static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cc) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_cropcap(file, fh, cc); + return vivid_vid_out_cropcap(file, fh, cc); +} + +static int vidioc_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_selection(file, fh, sel); + return vivid_vid_out_g_selection(file, fh, sel); +} + +static int vidioc_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_selection(file, fh, sel); + return vivid_vid_out_s_selection(file, fh, sel); +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_g_parm(file, fh, parm); + return vivid_vid_out_g_parm(file, fh, parm); +} + +static int vidioc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_vid_cap_s_parm(file, fh, parm); + return vivid_vid_out_g_parm(file, fh, parm); +} + +static ssize_t vivid_radio_read(struct file *file, char __user *buf, + size_t size, loff_t *offset) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_TX) + return -EINVAL; + return vivid_radio_rx_read(file, buf, size, offset); +} + +static ssize_t vivid_radio_write(struct file *file, const char __user *buf, + size_t size, loff_t *offset) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return -EINVAL; + return vivid_radio_tx_write(file, buf, size, offset); +} + +static unsigned int vivid_radio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) + return vivid_radio_rx_poll(file, wait); + return vivid_radio_tx_poll(file, wait); +} + +static bool vivid_is_in_use(struct video_device *vdev) +{ + unsigned long flags; + bool res; + + spin_lock_irqsave(&vdev->fh_lock, flags); + res = !list_empty(&vdev->fh_list); + spin_unlock_irqrestore(&vdev->fh_lock, flags); + return res; +} + +static bool vivid_is_last_user(struct vivid_dev *dev) +{ + unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) + + vivid_is_in_use(&dev->vid_out_dev) + + vivid_is_in_use(&dev->vbi_cap_dev) + + vivid_is_in_use(&dev->vbi_out_dev) + + vivid_is_in_use(&dev->sdr_cap_dev) + + vivid_is_in_use(&dev->radio_rx_dev) + + vivid_is_in_use(&dev->radio_tx_dev); + + return uses == 1; +} + +static int vivid_fop_release(struct file *file) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + mutex_lock(&dev->mutex); + if (!no_error_inj && v4l2_fh_is_singular_file(file) && + !video_is_registered(vdev) && vivid_is_last_user(dev)) { + /* + * I am the last user of this driver, and a disconnect + * was forced (since this video_device is unregistered), + * so re-register all video_device's again. + */ + v4l2_info(&dev->v4l2_dev, "reconnect\n"); + set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + } + mutex_unlock(&dev->mutex); + if (file->private_data == dev->overlay_cap_owner) + dev->overlay_cap_owner = NULL; + if (file->private_data == dev->radio_rx_rds_owner) { + dev->radio_rx_rds_last_block = 0; + dev->radio_rx_rds_owner = NULL; + } + if (file->private_data == dev->radio_tx_rds_owner) { + dev->radio_tx_rds_last_block = 0; + dev->radio_tx_rds_owner = NULL; + } + if (vdev->queue) + return vb2_fop_release(file); + return v4l2_fh_release(file); +} + +static const struct v4l2_file_operations vivid_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vivid_fop_release, + .read = vb2_fop_read, + .write = vb2_fop_write, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_file_operations vivid_radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vivid_fop_release, + .read = vivid_radio_read, + .write = vivid_radio_write, + .poll = vivid_radio_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops vivid_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, + + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, + .vidioc_cropcap = vidioc_cropcap, + + .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_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_fmt_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_s_fmt_sliced_vbi_cap, + .vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap, + + .vidioc_g_fmt_vbi_out = vidioc_g_fmt_vbi_out, + .vidioc_try_fmt_vbi_out = vidioc_g_fmt_vbi_out, + .vidioc_s_fmt_vbi_out = vidioc_s_fmt_vbi_out, + + .vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out, + .vidioc_try_fmt_sliced_vbi_out = vidioc_try_fmt_sliced_vbi_out, + .vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out, + + .vidioc_enum_fmt_sdr_cap = vidioc_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + + .vidioc_overlay = vidioc_overlay, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_out_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_out_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_out_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + + .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, +/* Not yet .vidioc_expbuf = vb2_ioctl_expbuf,*/ + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_modulator = vidioc_s_modulator, + .vidioc_g_modulator = vidioc_g_modulator, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_enum_freq_bands = vidioc_enum_freq_bands, + + .vidioc_enum_output = vidioc_enum_output, + .vidioc_g_output = vidioc_g_output, + .vidioc_s_output = vidioc_s_output, + .vidioc_s_audout = vidioc_s_audout, + .vidioc_g_audout = vidioc_g_audout, + .vidioc_enumaudout = vidioc_enumaudout, + + .vidioc_querystd = vidioc_querystd, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_s_dv_timings = vidioc_s_dv_timings, + .vidioc_g_dv_timings = vidioc_g_dv_timings, + .vidioc_query_dv_timings = vidioc_query_dv_timings, + .vidioc_enum_dv_timings = vidioc_enum_dv_timings, + .vidioc_dv_timings_cap = vidioc_dv_timings_cap, + .vidioc_g_edid = vidioc_g_edid, + .vidioc_s_edid = vidioc_s_edid, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +static int __init vivid_create_instance(int inst) +{ + static const struct v4l2_dv_timings def_dv_timings = + V4L2_DV_BT_CEA_1280X720P60; + unsigned in_type_counter[4] = { 0, 0, 0, 0 }; + unsigned out_type_counter[4] = { 0, 0, 0, 0 }; + int ccs_cap = ccs_cap_mode[inst]; + int ccs_out = ccs_out_mode[inst]; + bool has_tuner; + bool has_modulator; + struct vivid_dev *dev; + struct video_device *vfd; + struct vb2_queue *q; + unsigned node_type = node_types[inst]; + v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; + int ret; + int i; + + /* allocate main vivid state structure */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->inst = inst; + + /* register v4l2_device */ + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), + "%s-%03d", VIVID_MODULE_NAME, inst); + ret = v4l2_device_register(NULL, &dev->v4l2_dev); + if (ret) + goto free_dev; + + /* start detecting feature set */ + + /* do we use single- or multi-planar? */ + if (multiplanar[inst] == 0) + dev->multiplanar = inst & 1; + else + dev->multiplanar = multiplanar[inst] > 1; + v4l2_info(&dev->v4l2_dev, "using %splanar format API\n", + dev->multiplanar ? "multi" : "single "); + + /* how many inputs do we have and of what type? */ + dev->num_inputs = num_inputs[inst]; + if (dev->num_inputs < 1) + dev->num_inputs = 1; + if (dev->num_inputs >= MAX_INPUTS) + dev->num_inputs = MAX_INPUTS; + for (i = 0; i < dev->num_inputs; i++) { + dev->input_type[i] = (input_types[inst] >> (i * 2)) & 0x3; + dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++; + } + dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID]; + + /* how many outputs do we have and of what type? */ + dev->num_outputs = num_outputs[inst]; + if (dev->num_outputs < 1) + dev->num_outputs = 1; + if (dev->num_outputs >= MAX_OUTPUTS) + dev->num_outputs = MAX_OUTPUTS; + for (i = 0; i < dev->num_outputs; i++) { + dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; + dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; + } + dev->has_audio_outputs = out_type_counter[SVID]; + + /* do we create a video capture device? */ + dev->has_vid_cap = node_type & 0x0001; + + /* do we create a vbi capture device? */ + if (in_type_counter[TV] || in_type_counter[SVID]) { + dev->has_raw_vbi_cap = node_type & 0x0004; + dev->has_sliced_vbi_cap = node_type & 0x0008; + dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap; + } + + /* do we create a video output device? */ + dev->has_vid_out = node_type & 0x0100; + + /* do we create a vbi output device? */ + if (out_type_counter[SVID]) { + dev->has_raw_vbi_out = node_type & 0x0400; + dev->has_sliced_vbi_out = node_type & 0x0800; + dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out; + } + + /* do we create a radio receiver device? */ + dev->has_radio_rx = node_type & 0x0010; + + /* do we create a radio transmitter device? */ + dev->has_radio_tx = node_type & 0x1000; + + /* do we create a software defined radio capture device? */ + dev->has_sdr_cap = node_type & 0x0020; + + /* do we have a tuner? */ + has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || + dev->has_radio_rx || dev->has_sdr_cap; + + /* do we have a modulator? */ + has_modulator = dev->has_radio_tx; + + if (dev->has_vid_cap) + /* do we have a framebuffer for overlay testing? */ + dev->has_fb = node_type & 0x10000; + + /* can we do crop/compose/scaling while capturing? */ + if (no_error_inj && ccs_cap == -1) + ccs_cap = 7; + + /* if ccs_cap == -1, then the use can select it using controls */ + if (ccs_cap != -1) { + dev->has_crop_cap = ccs_cap & 1; + dev->has_compose_cap = ccs_cap & 2; + dev->has_scaler_cap = ccs_cap & 4; + v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n", + dev->has_crop_cap ? 'Y' : 'N', + dev->has_compose_cap ? 'Y' : 'N', + dev->has_scaler_cap ? 'Y' : 'N'); + } + + /* can we do crop/compose/scaling with video output? */ + if (no_error_inj && ccs_out == -1) + ccs_out = 7; + + /* if ccs_out == -1, then the use can select it using controls */ + if (ccs_out != -1) { + dev->has_crop_out = ccs_out & 1; + dev->has_compose_out = ccs_out & 2; + dev->has_scaler_out = ccs_out & 4; + v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n", + dev->has_crop_out ? 'Y' : 'N', + dev->has_compose_out ? 'Y' : 'N', + dev->has_scaler_out ? 'Y' : 'N'); + } + + /* end detecting feature set */ + + if (dev->has_vid_cap) { + /* set up the capabilities of the video capture device */ + dev->vid_cap_caps = dev->multiplanar ? + V4L2_CAP_VIDEO_CAPTURE_MPLANE : + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY; + dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->vid_cap_caps |= V4L2_CAP_AUDIO; + if (in_type_counter[TV]) + dev->vid_cap_caps |= V4L2_CAP_TUNER; + } + if (dev->has_vid_out) { + /* set up the capabilities of the video output device */ + dev->vid_out_caps = dev->multiplanar ? + V4L2_CAP_VIDEO_OUTPUT_MPLANE : + V4L2_CAP_VIDEO_OUTPUT; + if (dev->has_fb) + dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; + dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->vid_out_caps |= V4L2_CAP_AUDIO; + } + if (dev->has_vbi_cap) { + /* set up the capabilities of the vbi capture device */ + dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) | + (dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0); + dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->vbi_cap_caps |= V4L2_CAP_AUDIO; + if (in_type_counter[TV]) + dev->vbi_cap_caps |= V4L2_CAP_TUNER; + } + if (dev->has_vbi_out) { + /* set up the capabilities of the vbi output device */ + dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) | + (dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0); + dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->vbi_out_caps |= V4L2_CAP_AUDIO; + } + if (dev->has_sdr_cap) { + /* set up the capabilities of the sdr capture device */ + dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; + dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + } + /* set up the capabilities of the radio receiver device */ + if (dev->has_radio_rx) + dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE | + V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | + V4L2_CAP_READWRITE; + /* set up the capabilities of the radio transmitter device */ + if (dev->has_radio_tx) + dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | + V4L2_CAP_READWRITE; + + /* initialize the test pattern generator */ + tpg_init(&dev->tpg, 640, 360); + if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) + goto free_dev; + dev->scaled_line = vzalloc(MAX_ZOOM * MAX_WIDTH); + if (!dev->scaled_line) + goto free_dev; + dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH); + if (!dev->blended_line) + goto free_dev; + + /* load the edid */ + dev->edid = vmalloc(256 * 128); + if (!dev->edid) + goto free_dev; + + /* create a string array containing the names of all the preset timings */ + while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) + dev->query_dv_timings_size++; + dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size * + (sizeof(void *) + 32), GFP_KERNEL); + if (dev->query_dv_timings_qmenu == NULL) + goto free_dev; + for (i = 0; i < dev->query_dv_timings_size; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size]; + u32 htot, vtot; + + p += i * 32; + dev->query_dv_timings_qmenu[i] = p; + + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + snprintf(p, 32, "%ux%u%s%u", + bt->width, bt->height, bt->interlaced ? "i" : "p", + (u32)bt->pixelclock / (htot * vtot)); + } + + /* disable invalid ioctls based on the feature set */ + if (!dev->has_audio_inputs) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO); + } + if (!dev->has_audio_outputs) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT); + } + if (!in_type_counter[TV] && !in_type_counter[SVID]) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD); + } + if (!out_type_counter[SVID]) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD); + } + if (!has_tuner && !has_modulator) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY); + } + if (!has_tuner) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER); + } + if (in_type_counter[HDMI] == 0) { + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS); + } + if (out_type_counter[HDMI] == 0) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS); + } + if (!dev->has_fb) { + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY); + } + v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES); + v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY); + + /* configure internal data */ + dev->fmt_cap = &vivid_formats[0]; + dev->fmt_out = &vivid_formats[0]; + if (!dev->multiplanar) + vivid_formats[0].data_offset[0] = 0; + dev->webcam_size_idx = 1; + dev->webcam_ival_idx = 3; + tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); + dev->std_cap = V4L2_STD_PAL; + dev->std_out = V4L2_STD_PAL; + if (dev->input_type[0] == TV || dev->input_type[0] == SVID) + tvnorms_cap = V4L2_STD_ALL; + if (dev->output_type[0] == SVID) + tvnorms_out = V4L2_STD_ALL; + dev->dv_timings_cap = def_dv_timings; + dev->dv_timings_out = def_dv_timings; + dev->tv_freq = 2804 /* 175.25 * 16 */; + dev->tv_audmode = V4L2_TUNER_MODE_STEREO; + dev->tv_field_cap = V4L2_FIELD_INTERLACED; + dev->tv_field_out = V4L2_FIELD_INTERLACED; + dev->radio_rx_freq = 95000 * 16; + dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO; + if (dev->has_radio_tx) { + dev->radio_tx_freq = 95500 * 16; + dev->radio_rds_loop = false; + } + dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; + dev->sdr_adc_freq = 300000; + dev->sdr_fm_freq = 50000000; + dev->edid_max_blocks = dev->edid_blocks = 2; + memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); + ktime_get_ts(&dev->radio_rds_init_ts); + + /* create all controls */ + ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, + in_type_counter[TV] || in_type_counter[SVID] || + out_type_counter[SVID], + in_type_counter[HDMI] || out_type_counter[HDMI]); + if (ret) + goto unreg_dev; + + /* + * update the capture and output formats to do a proper initial + * configuration. + */ + vivid_update_format_cap(dev, false); + vivid_update_format_out(dev); + + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + + /* initialize overlay */ + dev->fb_cap.fmt.width = dev->src_rect.width; + dev->fb_cap.fmt.height = dev->src_rect.height; + dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc; + dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2; + dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline; + + /* initialize locks */ + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + /* init dma queues */ + INIT_LIST_HEAD(&dev->vid_cap_active); + INIT_LIST_HEAD(&dev->vid_out_active); + INIT_LIST_HEAD(&dev->vbi_cap_active); + INIT_LIST_HEAD(&dev->vbi_out_active); + INIT_LIST_HEAD(&dev->sdr_cap_active); + + /* start creating the vb2 queues */ + if (dev->has_vid_cap) { + /* initialize vid_cap queue */ + q = &dev->vb_vid_cap_q; + q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vid_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vid_out) { + /* initialize vid_out queue */ + q = &dev->vb_vid_out_q; + q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vid_out_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_cap) { + /* initialize vbi_cap queue */ + q = &dev->vb_vbi_cap_q; + q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE : + V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vbi_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_vbi_out) { + /* initialize vbi_out queue */ + q = &dev->vb_vbi_out_q; + q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT : + V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_vbi_out_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_sdr_cap) { + /* initialize sdr_cap queue */ + q = &dev->vb_sdr_cap_q; + q->type = V4L2_BUF_TYPE_SDR_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_sdr_cap_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 8; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + + if (dev->has_fb) { + /* Create framebuffer for testing capture/output overlay */ + ret = vivid_fb_init(dev); + if (ret) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", + dev->fb_info.node); + } + + /* finally start creating the device nodes */ + if (dev->has_vid_cap) { + vfd = &dev->vid_cap_dev; + strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vid_cap_q; + vfd->tvnorms = tvnorms_cap; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_vid_out) { + vfd = &dev->vid_out_dev; + strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vid_out_q; + vfd->tvnorms = tvnorms_out; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_vbi_cap) { + vfd = &dev->vbi_cap_dev; + strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vbi_cap_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_cap; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n", + video_device_node_name(vfd), + (dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ? + "raw and sliced" : + (dev->has_raw_vbi_cap ? "raw" : "sliced")); + } + + if (dev->has_vbi_out) { + vfd = &dev->vbi_out_dev; + strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_vbi_out_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_out; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n", + video_device_node_name(vfd), + (dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ? + "raw and sliced" : + (dev->has_raw_vbi_out ? "raw" : "sliced")); + } + + if (dev->has_sdr_cap) { + vfd = &dev->sdr_cap_dev; + strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name)); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_sdr_cap_q; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_radio_rx) { + vfd = &dev->radio_rx_dev; + strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name)); + vfd->fops = &vivid_radio_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n", + video_device_node_name(vfd)); + } + + if (dev->has_radio_tx) { + vfd = &dev->radio_tx_dev; + strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_radio_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n", + video_device_node_name(vfd)); + } + + /* Now that everything is fine, let's add it to device list */ + vivid_devs[inst] = dev; + + return 0; + +unreg_dev: + video_unregister_device(&dev->radio_tx_dev); + video_unregister_device(&dev->radio_rx_dev); + video_unregister_device(&dev->sdr_cap_dev); + video_unregister_device(&dev->vbi_out_dev); + video_unregister_device(&dev->vbi_cap_dev); + video_unregister_device(&dev->vid_out_dev); + video_unregister_device(&dev->vid_cap_dev); + vivid_free_controls(dev); + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + vfree(dev->scaled_line); + vfree(dev->blended_line); + vfree(dev->edid); + tpg_free(&dev->tpg); + kfree(dev->query_dv_timings_qmenu); + kfree(dev); + return ret; +} + +/* This routine allocates from 1 to n_devs virtual drivers. + + The real maximum number of virtual drivers will depend on how many drivers + will succeed. This is limited to the maximum number of devices that + videodev supports, which is equal to VIDEO_NUM_DEVICES. + */ +static int __init vivid_init(void) +{ + const struct font_desc *font = find_font("VGA8x16"); + int ret = 0, i; + + if (font == NULL) { + pr_err("vivid: could not find font\n"); + return -ENODEV; + } + + tpg_set_font(font->data); + + n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS); + + for (i = 0; i < n_devs; i++) { + ret = vivid_create_instance(i); + if (ret) { + /* If some instantiations succeeded, keep driver */ + if (i) + ret = 0; + break; + } + } + + if (ret < 0) { + pr_err("vivid: error %d while loading driver\n", ret); + return ret; + } + + /* n_devs will reflect the actual number of allocated devices */ + n_devs = i; + + return ret; +} + +static void __exit vivid_exit(void) +{ + struct vivid_dev *dev; + unsigned i; + + for (i = 0; vivid_devs[i]; i++) { + dev = vivid_devs[i]; + + if (dev->has_vid_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vid_cap_dev)); + video_unregister_device(&dev->vid_cap_dev); + } + if (dev->has_vid_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vid_out_dev)); + video_unregister_device(&dev->vid_out_dev); + } + if (dev->has_vbi_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vbi_cap_dev)); + video_unregister_device(&dev->vbi_cap_dev); + } + if (dev->has_vbi_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->vbi_out_dev)); + video_unregister_device(&dev->vbi_out_dev); + } + if (dev->has_sdr_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->sdr_cap_dev)); + video_unregister_device(&dev->sdr_cap_dev); + } + if (dev->has_radio_rx) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->radio_rx_dev)); + video_unregister_device(&dev->radio_rx_dev); + } + if (dev->has_radio_tx) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->radio_tx_dev)); + video_unregister_device(&dev->radio_tx_dev); + } + if (dev->has_fb) { + v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n", + dev->fb_info.node); + unregister_framebuffer(&dev->fb_info); + vivid_fb_release_buffers(dev); + } + v4l2_device_unregister(&dev->v4l2_dev); + vivid_free_controls(dev); + vfree(dev->scaled_line); + vfree(dev->blended_line); + vfree(dev->edid); + vfree(dev->bitmap_cap); + vfree(dev->bitmap_out); + tpg_free(&dev->tpg); + kfree(dev->query_dv_timings_qmenu); + kfree(dev); + vivid_devs[i] = NULL; + } +} + +module_init(vivid_init); +module_exit(vivid_exit); diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h new file mode 100644 index 0000000..811c286 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-core.h @@ -0,0 +1,520 @@ +/* + * vivid-core.h - core datastructures + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_CORE_H_ +#define _VIVID_CORE_H_ + +#include <linux/fb.h> +#include <media/videobuf2-core.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ctrls.h> +#include "vivid-tpg.h" +#include "vivid-rds-gen.h" +#include "vivid-vbi-gen.h" + +#define dprintk(dev, level, fmt, arg...) \ + v4l2_dbg(level, vivid_debug, &dev->v4l2_dev, fmt, ## arg) + +/* Maximum allowed frame rate + * + * vivid 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 < 10ms image-generation logic should be changed, to avoid + * producing frames with equal content. + */ +#define FPS_MAX 100 + +/* The maximum number of clip rectangles */ +#define MAX_CLIPS 16 +/* The maximum number of inputs */ +#define MAX_INPUTS 16 +/* The maximum number of outputs */ +#define MAX_OUTPUTS 16 +/* The maximum up or down scaling factor is 4 */ +#define MAX_ZOOM 4 +/* The maximum image width/height are set to 4K DMT */ +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 2160 +/* The minimum image width/height */ +#define MIN_WIDTH 16 +#define MIN_HEIGHT 16 +/* The data_offset of plane 0 for the multiplanar formats */ +#define PLANE0_DATA_OFFSET 128 + +/* The supported TV frequency range in MHz */ +#define MIN_TV_FREQ (44U * 16U) +#define MAX_TV_FREQ (958U * 16U) + +/* The number of samples returned in every SDR buffer */ +#define SDR_CAP_SAMPLES_PER_BUF 0x4000 + +/* used by the threads to know when to resync internal counters */ +#define JIFFIES_PER_DAY (3600U * 24U * HZ) +#define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY)) + +extern const struct v4l2_rect vivid_min_rect; +extern const struct v4l2_rect vivid_max_rect; +extern unsigned vivid_debug; + +struct vivid_fmt { + const char *name; + u32 fourcc; /* v4l2 format id */ + u8 depth; + bool is_yuv; + bool can_do_overlay; + u32 alpha_mask; + u8 planes; + u32 data_offset[2]; +}; + +extern struct vivid_fmt vivid_formats[]; + +/* buffer for one video frame */ +struct vivid_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + struct list_head list; +}; + +enum vivid_input { + WEBCAM, + TV, + SVID, + HDMI, +}; + +enum vivid_signal_mode { + CURRENT_DV_TIMINGS, + CURRENT_STD = CURRENT_DV_TIMINGS, + NO_SIGNAL, + NO_LOCK, + OUT_OF_RANGE, + SELECTED_DV_TIMINGS, + SELECTED_STD = SELECTED_DV_TIMINGS, + CYCLE_DV_TIMINGS, + CYCLE_STD = CYCLE_DV_TIMINGS, + CUSTOM_DV_TIMINGS, +}; + +#define VIVID_INVALID_SIGNAL(mode) \ + ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE) + +struct vivid_dev { + unsigned inst; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_hdl_user_gen; + struct v4l2_ctrl_handler ctrl_hdl_user_vid; + struct v4l2_ctrl_handler ctrl_hdl_user_aud; + struct v4l2_ctrl_handler ctrl_hdl_streaming; + struct v4l2_ctrl_handler ctrl_hdl_sdtv_cap; + struct v4l2_ctrl_handler ctrl_hdl_loop_out; + struct video_device vid_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_vid_cap; + struct video_device vid_out_dev; + struct v4l2_ctrl_handler ctrl_hdl_vid_out; + struct video_device vbi_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_vbi_cap; + struct video_device vbi_out_dev; + struct v4l2_ctrl_handler ctrl_hdl_vbi_out; + struct video_device radio_rx_dev; + struct v4l2_ctrl_handler ctrl_hdl_radio_rx; + struct video_device radio_tx_dev; + struct v4l2_ctrl_handler ctrl_hdl_radio_tx; + struct video_device sdr_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_sdr_cap; + spinlock_t slock; + struct mutex mutex; + + /* capabilities */ + u32 vid_cap_caps; + u32 vid_out_caps; + u32 vbi_cap_caps; + u32 vbi_out_caps; + u32 sdr_cap_caps; + u32 radio_rx_caps; + u32 radio_tx_caps; + + /* supported features */ + bool multiplanar; + unsigned num_inputs; + u8 input_type[MAX_INPUTS]; + u8 input_name_counter[MAX_INPUTS]; + unsigned num_outputs; + u8 output_type[MAX_OUTPUTS]; + u8 output_name_counter[MAX_OUTPUTS]; + bool has_audio_inputs; + bool has_audio_outputs; + bool has_vid_cap; + bool has_vid_out; + bool has_vbi_cap; + bool has_raw_vbi_cap; + bool has_sliced_vbi_cap; + bool has_vbi_out; + bool has_raw_vbi_out; + bool has_sliced_vbi_out; + bool has_radio_rx; + bool has_radio_tx; + bool has_sdr_cap; + bool has_fb; + + bool can_loop_video; + + /* controls */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + struct { + /* autogain/gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *alpha; + struct v4l2_ctrl *button; + struct v4l2_ctrl *boolean; + struct v4l2_ctrl *int32; + struct v4l2_ctrl *int64; + struct v4l2_ctrl *menu; + struct v4l2_ctrl *string; + struct v4l2_ctrl *bitmask; + struct v4l2_ctrl *int_menu; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *colorspace; + struct v4l2_ctrl *rgb_range_cap; + struct v4l2_ctrl *real_rgb_range_cap; + struct { + /* std_signal_mode/standard cluster */ + struct v4l2_ctrl *ctrl_std_signal_mode; + struct v4l2_ctrl *ctrl_standard; + }; + struct { + /* dv_timings_signal_mode/timings cluster */ + struct v4l2_ctrl *ctrl_dv_timings_signal_mode; + struct v4l2_ctrl *ctrl_dv_timings; + }; + struct v4l2_ctrl *ctrl_has_crop_cap; + struct v4l2_ctrl *ctrl_has_compose_cap; + struct v4l2_ctrl *ctrl_has_scaler_cap; + struct v4l2_ctrl *ctrl_has_crop_out; + struct v4l2_ctrl *ctrl_has_compose_out; + struct v4l2_ctrl *ctrl_has_scaler_out; + struct v4l2_ctrl *ctrl_tx_mode; + struct v4l2_ctrl *ctrl_tx_rgb_range; + + struct v4l2_ctrl *radio_tx_rds_pi; + struct v4l2_ctrl *radio_tx_rds_pty; + struct v4l2_ctrl *radio_tx_rds_mono_stereo; + struct v4l2_ctrl *radio_tx_rds_art_head; + struct v4l2_ctrl *radio_tx_rds_compressed; + struct v4l2_ctrl *radio_tx_rds_dyn_pty; + struct v4l2_ctrl *radio_tx_rds_ta; + struct v4l2_ctrl *radio_tx_rds_tp; + struct v4l2_ctrl *radio_tx_rds_ms; + struct v4l2_ctrl *radio_tx_rds_psname; + struct v4l2_ctrl *radio_tx_rds_radiotext; + + struct v4l2_ctrl *radio_rx_rds_pty; + struct v4l2_ctrl *radio_rx_rds_ta; + struct v4l2_ctrl *radio_rx_rds_tp; + struct v4l2_ctrl *radio_rx_rds_ms; + struct v4l2_ctrl *radio_rx_rds_psname; + struct v4l2_ctrl *radio_rx_rds_radiotext; + + unsigned input_brightness[MAX_INPUTS]; + unsigned osd_mode; + unsigned button_pressed; + bool sensor_hflip; + bool sensor_vflip; + bool hflip; + bool vflip; + bool vbi_cap_interlaced; + bool loop_video; + + /* Framebuffer */ + unsigned long video_pbase; + void *video_vbase; + u32 video_buffer_size; + int display_width; + int display_height; + int display_byte_stride; + int bits_per_pixel; + int bytes_per_pixel; + struct fb_info fb_info; + struct fb_var_screeninfo fb_defined; + struct fb_fix_screeninfo fb_fix; + + /* Error injection */ + bool queue_setup_error; + bool buf_prepare_error; + bool start_streaming_error; + bool dqbuf_error; + bool seq_wrap; + bool time_wrap; + __kernel_time_t time_wrap_offset; + unsigned perc_dropped_buffers; + enum vivid_signal_mode std_signal_mode; + unsigned query_std_last; + v4l2_std_id query_std; + enum tpg_video_aspect std_aspect_ratio; + + enum vivid_signal_mode dv_timings_signal_mode; + char **query_dv_timings_qmenu; + unsigned query_dv_timings_size; + unsigned query_dv_timings_last; + unsigned query_dv_timings; + enum tpg_video_aspect dv_timings_aspect_ratio; + + /* Input */ + unsigned input; + v4l2_std_id std_cap; + struct v4l2_dv_timings dv_timings_cap; + u32 service_set_cap; + struct vivid_vbi_gen_data vbi_gen; + u8 *edid; + unsigned edid_blocks; + unsigned edid_max_blocks; + unsigned webcam_size_idx; + unsigned webcam_ival_idx; + unsigned tv_freq; + unsigned tv_audmode; + unsigned tv_field_cap; + unsigned tv_audio_input; + + /* Capture Overlay */ + struct v4l2_framebuffer fb_cap; + struct v4l2_fh *overlay_cap_owner; + void *fb_vbase_cap; + int overlay_cap_top, overlay_cap_left; + enum v4l2_field overlay_cap_field; + void *bitmap_cap; + struct v4l2_clip clips_cap[MAX_CLIPS]; + struct v4l2_clip try_clips_cap[MAX_CLIPS]; + unsigned clipcount_cap; + + /* Output */ + unsigned output; + v4l2_std_id std_out; + struct v4l2_dv_timings dv_timings_out; + u32 colorspace_out; + u32 service_set_out; + u32 bytesperline_out[2]; + unsigned tv_field_out; + unsigned tv_audio_output; + bool vbi_out_have_wss; + u8 vbi_out_wss[2]; + bool vbi_out_have_cc[2]; + u8 vbi_out_cc[2][2]; + bool dvi_d_out; + u8 *scaled_line; + u8 *blended_line; + unsigned cur_scaled_line; + + /* Output Overlay */ + void *fb_vbase_out; + bool overlay_out_enabled; + int overlay_out_top, overlay_out_left; + void *bitmap_out; + struct v4l2_clip clips_out[MAX_CLIPS]; + struct v4l2_clip try_clips_out[MAX_CLIPS]; + unsigned clipcount_out; + unsigned fbuf_out_flags; + u32 chromakey_out; + u8 global_alpha_out; + + /* video capture */ + struct tpg_data tpg; + unsigned ms_vid_cap; + bool must_blank[VIDEO_MAX_FRAME]; + + const struct vivid_fmt *fmt_cap; + struct v4l2_fract timeperframe_vid_cap; + enum v4l2_field field_cap; + struct v4l2_rect src_rect; + struct v4l2_rect fmt_cap_rect; + struct v4l2_rect crop_cap; + struct v4l2_rect compose_cap; + struct v4l2_rect crop_bounds_cap; + struct vb2_queue vb_vid_cap_q; + struct list_head vid_cap_active; + struct vb2_queue vb_vbi_cap_q; + struct list_head vbi_cap_active; + + /* thread for generating video capture stream */ + struct task_struct *kthread_vid_cap; + unsigned long jiffies_vid_cap; + u32 cap_seq_offset; + u32 cap_seq_count; + bool cap_seq_resync; + u32 vid_cap_seq_start; + u32 vid_cap_seq_count; + bool vid_cap_streaming; + u32 vbi_cap_seq_start; + u32 vbi_cap_seq_count; + bool vbi_cap_streaming; + bool stream_sliced_vbi_cap; + + /* video output */ + const struct vivid_fmt *fmt_out; + struct v4l2_fract timeperframe_vid_out; + enum v4l2_field field_out; + struct v4l2_rect sink_rect; + struct v4l2_rect fmt_out_rect; + struct v4l2_rect crop_out; + struct v4l2_rect compose_out; + struct v4l2_rect compose_bounds_out; + struct vb2_queue vb_vid_out_q; + struct list_head vid_out_active; + struct vb2_queue vb_vbi_out_q; + struct list_head vbi_out_active; + + /* video loop precalculated rectangles */ + + /* + * Intersection between what the output side composes and the capture side + * crops. I.e., what actually needs to be copied from the output buffer to + * the capture buffer. + */ + struct v4l2_rect loop_vid_copy; + /* The part of the output buffer that (after scaling) corresponds to loop_vid_copy. */ + struct v4l2_rect loop_vid_out; + /* The part of the capture buffer that (after scaling) corresponds to loop_vid_copy. */ + struct v4l2_rect loop_vid_cap; + /* + * The intersection of the framebuffer, the overlay output window and + * loop_vid_copy. I.e., the part of the framebuffer that actually should be + * blended with the compose_out rectangle. This uses the framebuffer origin. + */ + struct v4l2_rect loop_fb_copy; + /* The same as loop_fb_copy but with compose_out origin. */ + struct v4l2_rect loop_vid_overlay; + /* + * The part of the capture buffer that (after scaling) corresponds + * to loop_vid_overlay. + */ + struct v4l2_rect loop_vid_overlay_cap; + + /* thread for generating video output stream */ + struct task_struct *kthread_vid_out; + unsigned long jiffies_vid_out; + u32 out_seq_offset; + u32 out_seq_count; + bool out_seq_resync; + u32 vid_out_seq_start; + u32 vid_out_seq_count; + bool vid_out_streaming; + u32 vbi_out_seq_start; + u32 vbi_out_seq_count; + bool vbi_out_streaming; + bool stream_sliced_vbi_out; + + /* SDR capture */ + struct vb2_queue vb_sdr_cap_q; + struct list_head sdr_cap_active; + unsigned sdr_adc_freq; + unsigned sdr_fm_freq; + int sdr_fixp_src_phase; + int sdr_fixp_mod_phase; + + bool tstamp_src_is_soe; + bool has_crop_cap; + bool has_compose_cap; + bool has_scaler_cap; + bool has_crop_out; + bool has_compose_out; + bool has_scaler_out; + + /* thread for generating SDR stream */ + struct task_struct *kthread_sdr_cap; + unsigned long jiffies_sdr_cap; + u32 sdr_cap_seq_offset; + u32 sdr_cap_seq_count; + bool sdr_cap_seq_resync; + + /* RDS generator */ + struct vivid_rds_gen rds_gen; + + /* Radio receiver */ + unsigned radio_rx_freq; + unsigned radio_rx_audmode; + int radio_rx_sig_qual; + unsigned radio_rx_hw_seek_mode; + bool radio_rx_hw_seek_prog_lim; + bool radio_rx_rds_controls; + bool radio_rx_rds_enabled; + unsigned radio_rx_rds_use_alternates; + unsigned radio_rx_rds_last_block; + struct v4l2_fh *radio_rx_rds_owner; + + /* Radio transmitter */ + unsigned radio_tx_freq; + unsigned radio_tx_subchans; + bool radio_tx_rds_controls; + unsigned radio_tx_rds_last_block; + struct v4l2_fh *radio_tx_rds_owner; + + /* Shared between radio receiver and transmitter */ + bool radio_rds_loop; + struct timespec radio_rds_init_ts; +}; + +static inline bool vivid_is_webcam(const struct vivid_dev *dev) +{ + return dev->input_type[dev->input] == WEBCAM; +} + +static inline bool vivid_is_tv_cap(const struct vivid_dev *dev) +{ + return dev->input_type[dev->input] == TV; +} + +static inline bool vivid_is_svid_cap(const struct vivid_dev *dev) +{ + return dev->input_type[dev->input] == SVID; +} + +static inline bool vivid_is_hdmi_cap(const struct vivid_dev *dev) +{ + return dev->input_type[dev->input] == HDMI; +} + +static inline bool vivid_is_sdtv_cap(const struct vivid_dev *dev) +{ + return vivid_is_tv_cap(dev) || vivid_is_svid_cap(dev); +} + +static inline bool vivid_is_svid_out(const struct vivid_dev *dev) +{ + return dev->output_type[dev->output] == SVID; +} + +static inline bool vivid_is_hdmi_out(const struct vivid_dev *dev) +{ + return dev->output_type[dev->output] == HDMI; +} + +void vivid_lock(struct vb2_queue *vq); +void vivid_unlock(struct vb2_queue *vq); + +#endif diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c new file mode 100644 index 0000000..d5cbf00 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -0,0 +1,1502 @@ +/* + * vivid-ctrls.c - control support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/videodev2.h> +#include <media/v4l2-event.h> +#include <media/v4l2-common.h> + +#include "vivid-core.h" +#include "vivid-vid-cap.h" +#include "vivid-vid-out.h" +#include "vivid-vid-common.h" +#include "vivid-radio-common.h" +#include "vivid-osd.h" +#include "vivid-ctrls.h" + +#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) +#define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0) +#define VIVID_CID_BOOLEAN (VIVID_CID_CUSTOM_BASE + 1) +#define VIVID_CID_INTEGER (VIVID_CID_CUSTOM_BASE + 2) +#define VIVID_CID_INTEGER64 (VIVID_CID_CUSTOM_BASE + 3) +#define VIVID_CID_MENU (VIVID_CID_CUSTOM_BASE + 4) +#define VIVID_CID_STRING (VIVID_CID_CUSTOM_BASE + 5) +#define VIVID_CID_BITMASK (VIVID_CID_CUSTOM_BASE + 6) +#define VIVID_CID_INTMENU (VIVID_CID_CUSTOM_BASE + 7) + +#define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) +#define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) +#define VIVID_CID_TEST_PATTERN (VIVID_CID_VIVID_BASE + 0) +#define VIVID_CID_OSD_TEXT_MODE (VIVID_CID_VIVID_BASE + 1) +#define VIVID_CID_HOR_MOVEMENT (VIVID_CID_VIVID_BASE + 2) +#define VIVID_CID_VERT_MOVEMENT (VIVID_CID_VIVID_BASE + 3) +#define VIVID_CID_SHOW_BORDER (VIVID_CID_VIVID_BASE + 4) +#define VIVID_CID_SHOW_SQUARE (VIVID_CID_VIVID_BASE + 5) +#define VIVID_CID_INSERT_SAV (VIVID_CID_VIVID_BASE + 6) +#define VIVID_CID_INSERT_EAV (VIVID_CID_VIVID_BASE + 7) +#define VIVID_CID_VBI_CAP_INTERLACED (VIVID_CID_VIVID_BASE + 8) + +#define VIVID_CID_HFLIP (VIVID_CID_VIVID_BASE + 20) +#define VIVID_CID_VFLIP (VIVID_CID_VIVID_BASE + 21) +#define VIVID_CID_STD_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 22) +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 23) +#define VIVID_CID_TSTAMP_SRC (VIVID_CID_VIVID_BASE + 24) +#define VIVID_CID_COLORSPACE (VIVID_CID_VIVID_BASE + 25) +#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 26) +#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 27) +#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 28) +#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 29) +#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 30) +#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 31) +#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 32) +#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 33) +#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 34) +#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 35) +#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 36) +#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 37) +#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 38) + +#define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) +#define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 62) +#define VIVID_CID_DV_TIMINGS (VIVID_CID_VIVID_BASE + 63) +#define VIVID_CID_PERC_DROPPED (VIVID_CID_VIVID_BASE + 64) +#define VIVID_CID_DISCONNECT (VIVID_CID_VIVID_BASE + 65) +#define VIVID_CID_DQBUF_ERROR (VIVID_CID_VIVID_BASE + 66) +#define VIVID_CID_QUEUE_SETUP_ERROR (VIVID_CID_VIVID_BASE + 67) +#define VIVID_CID_BUF_PREPARE_ERROR (VIVID_CID_VIVID_BASE + 68) +#define VIVID_CID_START_STR_ERROR (VIVID_CID_VIVID_BASE + 69) +#define VIVID_CID_QUEUE_ERROR (VIVID_CID_VIVID_BASE + 70) +#define VIVID_CID_CLEAR_FB (VIVID_CID_VIVID_BASE + 71) + +#define VIVID_CID_RADIO_SEEK_MODE (VIVID_CID_VIVID_BASE + 90) +#define VIVID_CID_RADIO_SEEK_PROG_LIM (VIVID_CID_VIVID_BASE + 91) +#define VIVID_CID_RADIO_RX_RDS_RBDS (VIVID_CID_VIVID_BASE + 92) +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 93) + +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 94) + + +/* General User Controls */ + +static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_gen); + + switch (ctrl->id) { + case VIVID_CID_DISCONNECT: + v4l2_info(&dev->v4l2_dev, "disconnect\n"); + clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + break; + case VIVID_CID_CLEAR_FB: + vivid_clear_fb(dev); + break; + case VIVID_CID_BUTTON: + dev->button_pressed = 30; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_user_gen_ctrl_ops = { + .s_ctrl = vivid_user_gen_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_button = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_BUTTON, + .name = "Button", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_boolean = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_BOOLEAN, + .name = "Boolean", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_int32 = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_INTEGER, + .name = "Integer 32 Bits", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0xffffffff80000000ULL, + .max = 0x7fffffff, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_int64 = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_INTEGER64, + .name = "Integer 64 Bits", + .type = V4L2_CTRL_TYPE_INTEGER64, + .min = 0x8000000000000000ULL, + .max = 0x7fffffffffffffffLL, + .step = 1, +}; + +static const char * const vivid_ctrl_menu_strings[] = { + "Menu Item 0 (Skipped)", + "Menu Item 1", + "Menu Item 2 (Skipped)", + "Menu Item 3", + "Menu Item 4", + "Menu Item 5 (Skipped)", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_menu = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_MENU, + .name = "Menu", + .type = V4L2_CTRL_TYPE_MENU, + .min = 1, + .max = 4, + .def = 3, + .menu_skip_mask = 0x04, + .qmenu = vivid_ctrl_menu_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_string = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_STRING, + .name = "String", + .type = V4L2_CTRL_TYPE_STRING, + .min = 2, + .max = 4, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_bitmask = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_BITMASK, + .name = "Bitmask", + .type = V4L2_CTRL_TYPE_BITMASK, + .def = 0x80002000, + .min = 0, + .max = 0x80402010, + .step = 0, +}; + +static const s64 vivid_ctrl_int_menu_values[] = { + 1, 1, 2, 3, 5, 8, 13, 21, 42, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_int_menu = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_INTMENU, + .name = "Integer Menu", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 1, + .max = 8, + .def = 4, + .menu_skip_mask = 0x02, + .qmenu_int = vivid_ctrl_int_menu_values, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_disconnect = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_DISCONNECT, + .name = "Disconnect", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_clear_fb = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_CLEAR_FB, + .name = "Clear Framebuffer", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + + +/* Video User Controls */ + +static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid); + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + dev->gain->val = dev->jiffies_vid_cap & 0xff; + break; + } + return 0; +} + +static int vivid_user_vid_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + dev->input_brightness[dev->input] = ctrl->val - dev->input * 128; + tpg_s_brightness(&dev->tpg, dev->input_brightness[dev->input]); + break; + case V4L2_CID_CONTRAST: + tpg_s_contrast(&dev->tpg, ctrl->val); + break; + case V4L2_CID_SATURATION: + tpg_s_saturation(&dev->tpg, ctrl->val); + break; + case V4L2_CID_HUE: + tpg_s_hue(&dev->tpg, ctrl->val); + break; + case V4L2_CID_HFLIP: + dev->hflip = ctrl->val; + tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip); + break; + case V4L2_CID_VFLIP: + dev->vflip = ctrl->val; + tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip); + break; + case V4L2_CID_ALPHA_COMPONENT: + tpg_s_alpha_component(&dev->tpg, ctrl->val); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = { + .g_volatile_ctrl = vivid_user_vid_g_volatile_ctrl, + .s_ctrl = vivid_user_vid_s_ctrl, +}; + + +/* Video Capture Controls */ + +static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); + unsigned i; + + switch (ctrl->id) { + case VIVID_CID_TEST_PATTERN: + vivid_update_quality(dev); + tpg_s_pattern(&dev->tpg, ctrl->val); + break; + case VIVID_CID_COLORSPACE: + tpg_s_colorspace(&dev->tpg, ctrl->val); + vivid_send_source_change(dev, TV); + vivid_send_source_change(dev, SVID); + vivid_send_source_change(dev, HDMI); + vivid_send_source_change(dev, WEBCAM); + break; + case V4L2_CID_DV_RX_RGB_RANGE: + if (!vivid_is_hdmi_cap(dev)) + break; + tpg_s_rgb_range(&dev->tpg, ctrl->val); + break; + case VIVID_CID_LIMITED_RGB_RANGE: + tpg_s_real_rgb_range(&dev->tpg, ctrl->val ? + V4L2_DV_RGB_RANGE_LIMITED : V4L2_DV_RGB_RANGE_FULL); + break; + case VIVID_CID_ALPHA_MODE: + tpg_s_alpha_mode(&dev->tpg, ctrl->val); + break; + case VIVID_CID_HOR_MOVEMENT: + tpg_s_mv_hor_mode(&dev->tpg, ctrl->val); + break; + case VIVID_CID_VERT_MOVEMENT: + tpg_s_mv_vert_mode(&dev->tpg, ctrl->val); + break; + case VIVID_CID_OSD_TEXT_MODE: + dev->osd_mode = ctrl->val; + break; + case VIVID_CID_PERCENTAGE_FILL: + tpg_s_perc_fill(&dev->tpg, ctrl->val); + for (i = 0; i < VIDEO_MAX_FRAME; i++) + dev->must_blank[i] = ctrl->val < 100; + break; + case VIVID_CID_INSERT_SAV: + tpg_s_insert_sav(&dev->tpg, ctrl->val); + break; + case VIVID_CID_INSERT_EAV: + tpg_s_insert_eav(&dev->tpg, ctrl->val); + break; + case VIVID_CID_HFLIP: + dev->sensor_hflip = ctrl->val; + tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip); + break; + case VIVID_CID_VFLIP: + dev->sensor_vflip = ctrl->val; + tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip); + break; + case VIVID_CID_HAS_CROP_CAP: + dev->has_crop_cap = ctrl->val; + vivid_update_format_cap(dev, true); + break; + case VIVID_CID_HAS_COMPOSE_CAP: + dev->has_compose_cap = ctrl->val; + vivid_update_format_cap(dev, true); + break; + case VIVID_CID_HAS_SCALER_CAP: + dev->has_scaler_cap = ctrl->val; + vivid_update_format_cap(dev, true); + break; + case VIVID_CID_SHOW_BORDER: + tpg_s_show_border(&dev->tpg, ctrl->val); + break; + case VIVID_CID_SHOW_SQUARE: + tpg_s_show_square(&dev->tpg, ctrl->val); + break; + case VIVID_CID_STD_ASPECT_RATIO: + dev->std_aspect_ratio = ctrl->val; + tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); + break; + case VIVID_CID_DV_TIMINGS_SIGNAL_MODE: + dev->dv_timings_signal_mode = dev->ctrl_dv_timings_signal_mode->val; + if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) + dev->query_dv_timings = dev->ctrl_dv_timings->val; + v4l2_ctrl_activate(dev->ctrl_dv_timings, + dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS); + vivid_update_quality(dev); + vivid_send_source_change(dev, HDMI); + break; + case VIVID_CID_DV_TIMINGS_ASPECT_RATIO: + dev->dv_timings_aspect_ratio = ctrl->val; + tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); + break; + case VIVID_CID_TSTAMP_SRC: + dev->tstamp_src_is_soe = ctrl->val; + dev->vb_vid_cap_q.timestamp_flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + if (dev->tstamp_src_is_soe) + dev->vb_vid_cap_q.timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_SOE; + break; + case VIVID_CID_MAX_EDID_BLOCKS: + dev->edid_max_blocks = ctrl->val; + if (dev->edid_blocks > dev->edid_max_blocks) + dev->edid_blocks = dev->edid_max_blocks; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_vid_cap_ctrl_ops = { + .s_ctrl = vivid_vid_cap_s_ctrl, +}; + +static const char * const vivid_ctrl_hor_movement_strings[] = { + "Move Left Fast", + "Move Left", + "Move Left Slow", + "No Movement", + "Move Right Slow", + "Move Right", + "Move Right Fast", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_hor_movement = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HOR_MOVEMENT, + .name = "Horizontal Movement", + .type = V4L2_CTRL_TYPE_MENU, + .max = TPG_MOVE_POS_FAST, + .def = TPG_MOVE_NONE, + .qmenu = vivid_ctrl_hor_movement_strings, +}; + +static const char * const vivid_ctrl_vert_movement_strings[] = { + "Move Up Fast", + "Move Up", + "Move Up Slow", + "No Movement", + "Move Down Slow", + "Move Down", + "Move Down Fast", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_vert_movement = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_VERT_MOVEMENT, + .name = "Vertical Movement", + .type = V4L2_CTRL_TYPE_MENU, + .max = TPG_MOVE_POS_FAST, + .def = TPG_MOVE_NONE, + .qmenu = vivid_ctrl_vert_movement_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_show_border = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_SHOW_BORDER, + .name = "Show Border", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_show_square = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_SHOW_SQUARE, + .name = "Show Square", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const char * const vivid_ctrl_osd_mode_strings[] = { + "All", + "Counters Only", + "None", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_osd_mode = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_OSD_TEXT_MODE, + .name = "OSD Text Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = 2, + .qmenu = vivid_ctrl_osd_mode_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_perc_fill = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_PERCENTAGE_FILL, + .name = "Fill Percentage of Frame", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .def = 100, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_insert_sav = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_INSERT_SAV, + .name = "Insert SAV Code in Image", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_insert_eav = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_INSERT_EAV, + .name = "Insert EAV Code in Image", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_hflip = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HFLIP, + .name = "Sensor Flipped Horizontally", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_vflip = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_VFLIP, + .name = "Sensor Flipped Vertically", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_crop_cap = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HAS_CROP_CAP, + .name = "Enable Capture Cropping", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_compose_cap = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HAS_COMPOSE_CAP, + .name = "Enable Capture Composing", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_cap = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HAS_SCALER_CAP, + .name = "Enable Capture Scaler", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const char * const vivid_ctrl_tstamp_src_strings[] = { + "End of Frame", + "Start of Exposure", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_tstamp_src = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_TSTAMP_SRC, + .name = "Timestamp Source", + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = vivid_ctrl_tstamp_src_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_std_aspect_ratio = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_STD_ASPECT_RATIO, + .name = "Standard Aspect Ratio", + .type = V4L2_CTRL_TYPE_MENU, + .min = 1, + .max = 4, + .def = 1, + .qmenu = tpg_aspect_strings, +}; + +static const char * const vivid_ctrl_dv_timings_signal_mode_strings[] = { + "Current DV Timings", + "No Signal", + "No Lock", + "Out of Range", + "Selected DV Timings", + "Cycle Through All DV Timings", + "Custom DV Timings", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_signal_mode = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_DV_TIMINGS_SIGNAL_MODE, + .name = "DV Timings Signal Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = 5, + .qmenu = vivid_ctrl_dv_timings_signal_mode_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_aspect_ratio = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_DV_TIMINGS_ASPECT_RATIO, + .name = "DV Timings Aspect Ratio", + .type = V4L2_CTRL_TYPE_MENU, + .max = 3, + .qmenu = tpg_aspect_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_max_edid_blocks = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_MAX_EDID_BLOCKS, + .name = "Maximum EDID Blocks", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 256, + .def = 2, + .step = 1, +}; + +static const char * const vivid_ctrl_colorspace_strings[] = { + "", + "SMPTE 170M", + "SMPTE 240M", + "REC 709", + "", /* Skip Bt878 entry */ + "470 System M", + "470 System BG", + "", /* Skip JPEG entry */ + "sRGB", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_colorspace = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_COLORSPACE, + .name = "Colorspace", + .type = V4L2_CTRL_TYPE_MENU, + .min = 1, + .max = 8, + .menu_skip_mask = (1 << 4) | (1 << 7), + .def = 8, + .qmenu = vivid_ctrl_colorspace_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_alpha_mode = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_ALPHA_MODE, + .name = "Apply Alpha To Red Only", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_LIMITED_RGB_RANGE, + .name = "Limited RGB Range (16-235)", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + +/* VBI Capture Control */ + +static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vbi_cap); + + switch (ctrl->id) { + case VIVID_CID_VBI_CAP_INTERLACED: + dev->vbi_cap_interlaced = ctrl->val; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_vbi_cap_ctrl_ops = { + .s_ctrl = vivid_vbi_cap_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_vbi_cap_interlaced = { + .ops = &vivid_vbi_cap_ctrl_ops, + .id = VIVID_CID_VBI_CAP_INTERLACED, + .name = "Interlaced VBI Format", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + +/* Video Output Controls */ + +static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out); + struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; + + switch (ctrl->id) { + case VIVID_CID_HAS_CROP_OUT: + dev->has_crop_out = ctrl->val; + vivid_update_format_out(dev); + break; + case VIVID_CID_HAS_COMPOSE_OUT: + dev->has_compose_out = ctrl->val; + vivid_update_format_out(dev); + break; + case VIVID_CID_HAS_SCALER_OUT: + dev->has_scaler_out = ctrl->val; + vivid_update_format_out(dev); + break; + case V4L2_CID_DV_TX_MODE: + dev->dvi_d_out = ctrl->val == V4L2_DV_TX_MODE_DVI_D; + if (!vivid_is_hdmi_out(dev)) + break; + if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) { + if (bt->width == 720 && bt->height <= 576) + dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; + else + dev->colorspace_out = V4L2_COLORSPACE_REC709; + } else { + dev->colorspace_out = V4L2_COLORSPACE_SRGB; + } + if (dev->loop_video) + vivid_send_source_change(dev, HDMI); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_vid_out_ctrl_ops = { + .s_ctrl = vivid_vid_out_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_crop_out = { + .ops = &vivid_vid_out_ctrl_ops, + .id = VIVID_CID_HAS_CROP_OUT, + .name = "Enable Output Cropping", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_compose_out = { + .ops = &vivid_vid_out_ctrl_ops, + .id = VIVID_CID_HAS_COMPOSE_OUT, + .name = "Enable Output Composing", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = { + .ops = &vivid_vid_out_ctrl_ops, + .id = VIVID_CID_HAS_SCALER_OUT, + .name = "Enable Output Scaler", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + + +/* Streaming Controls */ + +static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_streaming); + struct timeval tv; + + switch (ctrl->id) { + case VIVID_CID_DQBUF_ERROR: + dev->dqbuf_error = true; + break; + case VIVID_CID_PERC_DROPPED: + dev->perc_dropped_buffers = ctrl->val; + break; + case VIVID_CID_QUEUE_SETUP_ERROR: + dev->queue_setup_error = true; + break; + case VIVID_CID_BUF_PREPARE_ERROR: + dev->buf_prepare_error = true; + break; + case VIVID_CID_START_STR_ERROR: + dev->start_streaming_error = true; + break; + case VIVID_CID_QUEUE_ERROR: + if (dev->vb_vid_cap_q.start_streaming_called) + vb2_queue_error(&dev->vb_vid_cap_q); + if (dev->vb_vbi_cap_q.start_streaming_called) + vb2_queue_error(&dev->vb_vbi_cap_q); + if (dev->vb_vid_out_q.start_streaming_called) + vb2_queue_error(&dev->vb_vid_out_q); + if (dev->vb_vbi_out_q.start_streaming_called) + vb2_queue_error(&dev->vb_vbi_out_q); + if (dev->vb_sdr_cap_q.start_streaming_called) + vb2_queue_error(&dev->vb_sdr_cap_q); + break; + case VIVID_CID_SEQ_WRAP: + dev->seq_wrap = ctrl->val; + break; + case VIVID_CID_TIME_WRAP: + dev->time_wrap = ctrl->val; + if (ctrl->val == 0) { + dev->time_wrap_offset = 0; + break; + } + v4l2_get_timestamp(&tv); + dev->time_wrap_offset = -tv.tv_sec - 16; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_streaming_ctrl_ops = { + .s_ctrl = vivid_streaming_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_dqbuf_error = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_DQBUF_ERROR, + .name = "Inject V4L2_BUF_FLAG_ERROR", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_perc_dropped = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_PERC_DROPPED, + .name = "Percentage of Dropped Buffers", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_queue_setup_error = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_QUEUE_SETUP_ERROR, + .name = "Inject VIDIOC_REQBUFS Error", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_buf_prepare_error = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_BUF_PREPARE_ERROR, + .name = "Inject VIDIOC_QBUF Error", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_start_streaming_error = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_START_STR_ERROR, + .name = "Inject VIDIOC_STREAMON Error", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_queue_error = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_QUEUE_ERROR, + .name = "Inject Fatal Streaming Error", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_seq_wrap = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_SEQ_WRAP, + .name = "Wrap Sequence Number", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_time_wrap = { + .ops = &vivid_streaming_ctrl_ops, + .id = VIVID_CID_TIME_WRAP, + .name = "Wrap Timestamp", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + +/* SDTV Capture Controls */ + +static int vivid_sdtv_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_sdtv_cap); + + switch (ctrl->id) { + case VIVID_CID_STD_SIGNAL_MODE: + dev->std_signal_mode = dev->ctrl_std_signal_mode->val; + if (dev->std_signal_mode == SELECTED_STD) + dev->query_std = vivid_standard[dev->ctrl_standard->val]; + v4l2_ctrl_activate(dev->ctrl_standard, dev->std_signal_mode == SELECTED_STD); + vivid_update_quality(dev); + vivid_send_source_change(dev, TV); + vivid_send_source_change(dev, SVID); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_sdtv_cap_ctrl_ops = { + .s_ctrl = vivid_sdtv_cap_s_ctrl, +}; + +static const char * const vivid_ctrl_std_signal_mode_strings[] = { + "Current Standard", + "No Signal", + "No Lock", + "", + "Selected Standard", + "Cycle Through All Standards", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_std_signal_mode = { + .ops = &vivid_sdtv_cap_ctrl_ops, + .id = VIVID_CID_STD_SIGNAL_MODE, + .name = "Standard Signal Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = 5, + .menu_skip_mask = 1 << 3, + .qmenu = vivid_ctrl_std_signal_mode_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_standard = { + .ops = &vivid_sdtv_cap_ctrl_ops, + .id = VIVID_CID_STANDARD, + .name = "Standard", + .type = V4L2_CTRL_TYPE_MENU, + .max = 14, + .qmenu = vivid_ctrl_standard_strings, +}; + + + +/* Radio Receiver Controls */ + +static int vivid_radio_rx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_rx); + + switch (ctrl->id) { + case VIVID_CID_RADIO_SEEK_MODE: + dev->radio_rx_hw_seek_mode = ctrl->val; + break; + case VIVID_CID_RADIO_SEEK_PROG_LIM: + dev->radio_rx_hw_seek_prog_lim = ctrl->val; + break; + case VIVID_CID_RADIO_RX_RDS_RBDS: + dev->rds_gen.use_rbds = ctrl->val; + break; + case VIVID_CID_RADIO_RX_RDS_BLOCKIO: + dev->radio_rx_rds_controls = ctrl->val; + dev->radio_rx_caps &= ~V4L2_CAP_READWRITE; + dev->radio_rx_rds_use_alternates = false; + if (!dev->radio_rx_rds_controls) { + dev->radio_rx_caps |= V4L2_CAP_READWRITE; + __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, 0); + __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, 0); + __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, 0); + __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, 0); + __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, ""); + __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, ""); + } + v4l2_ctrl_activate(dev->radio_rx_rds_pty, dev->radio_rx_rds_controls); + v4l2_ctrl_activate(dev->radio_rx_rds_psname, dev->radio_rx_rds_controls); + v4l2_ctrl_activate(dev->radio_rx_rds_radiotext, dev->radio_rx_rds_controls); + v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls); + v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls); + v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls); + break; + case V4L2_CID_RDS_RECEPTION: + dev->radio_rx_rds_enabled = ctrl->val; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_radio_rx_ctrl_ops = { + .s_ctrl = vivid_radio_rx_s_ctrl, +}; + +static const char * const vivid_ctrl_radio_rds_mode_strings[] = { + "Block I/O", + "Controls", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_blockio = { + .ops = &vivid_radio_rx_ctrl_ops, + .id = VIVID_CID_RADIO_RX_RDS_BLOCKIO, + .name = "RDS Rx I/O Mode", + .type = V4L2_CTRL_TYPE_MENU, + .qmenu = vivid_ctrl_radio_rds_mode_strings, + .max = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_rbds = { + .ops = &vivid_radio_rx_ctrl_ops, + .id = VIVID_CID_RADIO_RX_RDS_RBDS, + .name = "Generate RBDS Instead of RDS", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +static const char * const vivid_ctrl_radio_hw_seek_mode_strings[] = { + "Bounded", + "Wrap Around", + "Both", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_mode = { + .ops = &vivid_radio_rx_ctrl_ops, + .id = VIVID_CID_RADIO_SEEK_MODE, + .name = "Radio HW Seek Mode", + .type = V4L2_CTRL_TYPE_MENU, + .max = 2, + .qmenu = vivid_ctrl_radio_hw_seek_mode_strings, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_prog_lim = { + .ops = &vivid_radio_rx_ctrl_ops, + .id = VIVID_CID_RADIO_SEEK_PROG_LIM, + .name = "Radio Programmable HW Seek", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + +/* Radio Transmitter Controls */ + +static int vivid_radio_tx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_tx); + + switch (ctrl->id) { + case VIVID_CID_RADIO_TX_RDS_BLOCKIO: + dev->radio_tx_rds_controls = ctrl->val; + dev->radio_tx_caps &= ~V4L2_CAP_READWRITE; + if (!dev->radio_tx_rds_controls) + dev->radio_tx_caps |= V4L2_CAP_READWRITE; + break; + case V4L2_CID_RDS_TX_PTY: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, ctrl->val); + break; + case V4L2_CID_RDS_TX_PS_NAME: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, ctrl->p_new.p_char); + break; + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, ctrl->p_new.p_char); + break; + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, ctrl->val); + break; + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, ctrl->val); + break; + case V4L2_CID_RDS_TX_MUSIC_SPEECH: + if (dev->radio_rx_rds_controls) + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, ctrl->val); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_radio_tx_ctrl_ops = { + .s_ctrl = vivid_radio_tx_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_radio_tx_rds_blockio = { + .ops = &vivid_radio_tx_ctrl_ops, + .id = VIVID_CID_RADIO_TX_RDS_BLOCKIO, + .name = "RDS Tx I/O Mode", + .type = V4L2_CTRL_TYPE_MENU, + .qmenu = vivid_ctrl_radio_rds_mode_strings, + .max = 1, + .def = 1, +}; + + + +/* Video Loop Control */ + +static int vivid_loop_out_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_out); + + switch (ctrl->id) { + case VIVID_CID_LOOP_VIDEO: + dev->loop_video = ctrl->val; + vivid_update_quality(dev); + vivid_send_source_change(dev, SVID); + vivid_send_source_change(dev, HDMI); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_loop_out_ctrl_ops = { + .s_ctrl = vivid_loop_out_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { + .ops = &vivid_loop_out_ctrl_ops, + .id = VIVID_CID_LOOP_VIDEO, + .name = "Loop Video", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + +static const struct v4l2_ctrl_config vivid_ctrl_class = { + .ops = &vivid_user_gen_ctrl_ops, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, + .id = VIVID_CID_VIVID_CLASS, + .name = "Vivid Controls", + .type = V4L2_CTRL_TYPE_CTRL_CLASS, +}; + +int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, + bool show_ccs_out, bool no_error_inj, + bool has_sdtv, bool has_hdmi) +{ + struct v4l2_ctrl_handler *hdl_user_gen = &dev->ctrl_hdl_user_gen; + struct v4l2_ctrl_handler *hdl_user_vid = &dev->ctrl_hdl_user_vid; + struct v4l2_ctrl_handler *hdl_user_aud = &dev->ctrl_hdl_user_aud; + struct v4l2_ctrl_handler *hdl_streaming = &dev->ctrl_hdl_streaming; + struct v4l2_ctrl_handler *hdl_sdtv_cap = &dev->ctrl_hdl_sdtv_cap; + struct v4l2_ctrl_handler *hdl_loop_out = &dev->ctrl_hdl_loop_out; + struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap; + struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out; + struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap; + struct v4l2_ctrl_handler *hdl_vbi_out = &dev->ctrl_hdl_vbi_out; + struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx; + struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx; + struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap; + struct v4l2_ctrl_config vivid_ctrl_dv_timings = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_DV_TIMINGS, + .name = "DV Timings", + .type = V4L2_CTRL_TYPE_MENU, + }; + int i; + + v4l2_ctrl_handler_init(hdl_user_gen, 10); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_user_vid, 9); + v4l2_ctrl_new_custom(hdl_user_vid, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_user_aud, 2); + v4l2_ctrl_new_custom(hdl_user_aud, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_streaming, 8); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_sdtv_cap, 2); + v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_loop_out, 1); + v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_vid_cap, 55); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_vid_out, 26); + v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_vbi_cap, 21); + v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_vbi_out, 19); + v4l2_ctrl_new_custom(hdl_vbi_out, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_radio_rx, 17); + v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_radio_tx, 17); + v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_sdr_cap, 18); + v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL); + + /* User Controls */ + dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL, + V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); + dev->mute = v4l2_ctrl_new_std(hdl_user_aud, NULL, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + if (dev->has_vid_cap) { + dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + for (i = 0; i < MAX_INPUTS; i++) + dev->input_brightness[i] = 128; + dev->contrast = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + dev->saturation = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + dev->hue = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_HUE, -128, 128, 1, 0); + v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + dev->autogain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + dev->gain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 100); + dev->alpha = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); + } + dev->button = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_button, NULL); + dev->int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32, NULL); + dev->int64 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64, NULL); + dev->boolean = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_boolean, NULL); + dev->menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_menu, NULL); + dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL); + dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL); + dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL); + + if (dev->has_vid_cap) { + /* Image Processing Controls */ + struct v4l2_ctrl_config vivid_ctrl_test_pattern = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_TEST_PATTERN, + .name = "Test Pattern", + .type = V4L2_CTRL_TYPE_MENU, + .max = TPG_PAT_NOISE, + .qmenu = tpg_pattern_strings, + }; + + dev->test_pattern = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_test_pattern, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_perc_fill, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hor_movement, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vert_movement, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_osd_mode, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_border, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_square, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hflip, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vflip, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_sav, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL); + if (show_ccs_cap) { + dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_has_crop_cap, NULL); + dev->ctrl_has_compose_cap = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_has_compose_cap, NULL); + dev->ctrl_has_scaler_cap = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_has_scaler_cap, NULL); + } + + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_tstamp_src, NULL); + dev->colorspace = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_colorspace, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_alpha_mode, NULL); + } + + if (dev->has_vid_out && show_ccs_out) { + dev->ctrl_has_crop_out = v4l2_ctrl_new_custom(hdl_vid_out, + &vivid_ctrl_has_crop_out, NULL); + dev->ctrl_has_compose_out = v4l2_ctrl_new_custom(hdl_vid_out, + &vivid_ctrl_has_compose_out, NULL); + dev->ctrl_has_scaler_out = v4l2_ctrl_new_custom(hdl_vid_out, + &vivid_ctrl_has_scaler_out, NULL); + } + + /* + * Testing this driver with v4l2-compliance will trigger the error + * injection controls, and after that nothing will work as expected. + * So we have a module option to drop these error injecting controls + * allowing us to run v4l2_compliance again. + */ + if (!no_error_inj) { + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_disconnect, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_dqbuf_error, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_perc_dropped, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_setup_error, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_buf_prepare_error, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_start_streaming_error, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_error, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_seq_wrap, NULL); + v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_time_wrap, NULL); + } + + if (has_sdtv && (dev->has_vid_cap || dev->has_vbi_cap)) { + if (dev->has_vid_cap) + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_std_aspect_ratio, NULL); + dev->ctrl_std_signal_mode = v4l2_ctrl_new_custom(hdl_sdtv_cap, + &vivid_ctrl_std_signal_mode, NULL); + dev->ctrl_standard = v4l2_ctrl_new_custom(hdl_sdtv_cap, + &vivid_ctrl_standard, NULL); + if (dev->ctrl_std_signal_mode) + v4l2_ctrl_cluster(2, &dev->ctrl_std_signal_mode); + if (dev->has_raw_vbi_cap) + v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_vbi_cap_interlaced, NULL); + } + + if (has_hdmi && dev->has_vid_cap) { + dev->ctrl_dv_timings_signal_mode = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_dv_timings_signal_mode, NULL); + + vivid_ctrl_dv_timings.max = dev->query_dv_timings_size - 1; + vivid_ctrl_dv_timings.qmenu = + (const char * const *)dev->query_dv_timings_qmenu; + dev->ctrl_dv_timings = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_dv_timings, NULL); + if (dev->ctrl_dv_timings_signal_mode) + v4l2_ctrl_cluster(2, &dev->ctrl_dv_timings_signal_mode); + + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_dv_timings_aspect_ratio, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_max_edid_blocks, NULL); + dev->real_rgb_range_cap = v4l2_ctrl_new_custom(hdl_vid_cap, + &vivid_ctrl_limited_rgb_range, NULL); + dev->rgb_range_cap = v4l2_ctrl_new_std_menu(hdl_vid_cap, + &vivid_vid_cap_ctrl_ops, + V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + } + if (has_hdmi && dev->has_vid_out) { + /* + * We aren't doing anything with this at the moment, but + * HDMI outputs typically have this controls. + */ + dev->ctrl_tx_rgb_range = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, + V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, + V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, + 0, V4L2_DV_TX_MODE_HDMI); + } + if ((dev->has_vid_cap && dev->has_vid_out) || + (dev->has_vbi_cap && dev->has_vbi_out)) + v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_loop_video, NULL); + + if (dev->has_fb) + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_clear_fb, NULL); + + if (dev->has_radio_rx) { + v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_mode, NULL); + v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_prog_lim, NULL); + v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_blockio, NULL); + v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_rbds, NULL); + v4l2_ctrl_new_std(hdl_radio_rx, &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RECEPTION, 0, 1, 1, 1); + dev->radio_rx_rds_pty = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_PTY, 0, 31, 1, 0); + dev->radio_rx_rds_psname = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0); + dev->radio_rx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0); + dev->radio_rx_rds_ta = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0); + dev->radio_rx_rds_tp = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0); + dev->radio_rx_rds_ms = v4l2_ctrl_new_std(hdl_radio_rx, + &vivid_radio_rx_ctrl_ops, + V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1); + } + if (dev->has_radio_tx) { + v4l2_ctrl_new_custom(hdl_radio_tx, + &vivid_ctrl_radio_tx_rds_blockio, NULL); + dev->radio_tx_rds_pi = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, 0x8088); + dev->radio_tx_rds_pty = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_PTY, 0, 31, 1, 3); + dev->radio_tx_rds_psname = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_PS_NAME, 0, 8, 8, 0); + if (dev->radio_tx_rds_psname) + v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_psname, "VIVID-TX"); + dev->radio_tx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_RADIO_TEXT, 0, 64 * 2, 64, 0); + if (dev->radio_tx_rds_radiotext) + v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_radiotext, + "This is a VIVID default Radio Text template text, change at will"); + dev->radio_tx_rds_mono_stereo = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_MONO_STEREO, 0, 1, 1, 1); + dev->radio_tx_rds_art_head = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_ARTIFICIAL_HEAD, 0, 1, 1, 0); + dev->radio_tx_rds_compressed = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_COMPRESSED, 0, 1, 1, 0); + dev->radio_tx_rds_dyn_pty = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_DYNAMIC_PTY, 0, 1, 1, 0); + dev->radio_tx_rds_ta = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0); + dev->radio_tx_rds_tp = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_TRAFFIC_PROGRAM, 0, 1, 1, 1); + dev->radio_tx_rds_ms = v4l2_ctrl_new_std(hdl_radio_tx, + &vivid_radio_tx_ctrl_ops, + V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1); + } + if (hdl_user_gen->error) + return hdl_user_gen->error; + if (hdl_user_vid->error) + return hdl_user_vid->error; + if (hdl_user_aud->error) + return hdl_user_aud->error; + if (hdl_streaming->error) + return hdl_streaming->error; + if (hdl_sdr_cap->error) + return hdl_sdr_cap->error; + if (hdl_loop_out->error) + return hdl_loop_out->error; + + if (dev->autogain) + v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true); + + if (dev->has_vid_cap) { + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL); + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL); + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL); + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL); + if (hdl_vid_cap->error) + return hdl_vid_cap->error; + dev->vid_cap_dev.ctrl_handler = hdl_vid_cap; + } + if (dev->has_vid_out) { + v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL); + v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL); + v4l2_ctrl_add_handler(hdl_vid_out, hdl_loop_out, NULL); + if (hdl_vid_out->error) + return hdl_vid_out->error; + dev->vid_out_dev.ctrl_handler = hdl_vid_out; + } + if (dev->has_vbi_cap) { + v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL); + v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL); + if (hdl_vbi_cap->error) + return hdl_vbi_cap->error; + dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap; + } + if (dev->has_vbi_out) { + v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL); + v4l2_ctrl_add_handler(hdl_vbi_out, hdl_loop_out, NULL); + if (hdl_vbi_out->error) + return hdl_vbi_out->error; + dev->vbi_out_dev.ctrl_handler = hdl_vbi_out; + } + if (dev->has_radio_rx) { + v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL); + if (hdl_radio_rx->error) + return hdl_radio_rx->error; + dev->radio_rx_dev.ctrl_handler = hdl_radio_rx; + } + if (dev->has_radio_tx) { + v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL); + if (hdl_radio_tx->error) + return hdl_radio_tx->error; + dev->radio_tx_dev.ctrl_handler = hdl_radio_tx; + } + if (dev->has_sdr_cap) { + v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL); + v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL); + if (hdl_sdr_cap->error) + return hdl_sdr_cap->error; + dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap; + } + return 0; +} + +void vivid_free_controls(struct vivid_dev *dev) +{ + v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_out); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_out); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_rx); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_tx); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdr_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_gen); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_vid); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_aud); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_out); +} diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h new file mode 100644 index 0000000..9bcca9d --- /dev/null +++ b/drivers/media/platform/vivid/vivid-ctrls.h @@ -0,0 +1,34 @@ +/* + * vivid-ctrls.h - control support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_CTRLS_H_ +#define _VIVID_CTRLS_H_ + +enum vivid_hw_seek_modes { + VIVID_HW_SEEK_BOUNDED, + VIVID_HW_SEEK_WRAP, + VIVID_HW_SEEK_BOTH, +}; + +int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, + bool show_ccs_out, bool no_error_inj, + bool has_sdtv, bool has_hdmi); +void vivid_free_controls(struct vivid_dev *dev); + +#endif diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c new file mode 100644 index 0000000..39a67cf --- /dev/null +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -0,0 +1,886 @@ +/* + * vivid-kthread-cap.h - video/vbi capture thread support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/font.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/random.h> +#include <linux/v4l2-dv-timings.h> +#include <asm/div64.h> +#include <media/videobuf2-vmalloc.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-vid-cap.h" +#include "vivid-vid-out.h" +#include "vivid-radio-common.h" +#include "vivid-radio-rx.h" +#include "vivid-radio-tx.h" +#include "vivid-sdr-cap.h" +#include "vivid-vbi-cap.h" +#include "vivid-vbi-out.h" +#include "vivid-osd.h" +#include "vivid-ctrls.h" +#include "vivid-kthread-cap.h" + +static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev) +{ + if (vivid_is_sdtv_cap(dev)) + return dev->std_cap; + return 0; +} + +static void copy_pix(struct vivid_dev *dev, int win_y, int win_x, + u16 *cap, const u16 *osd) +{ + u16 out; + int left = dev->overlay_out_left; + int top = dev->overlay_out_top; + int fb_x = win_x + left; + int fb_y = win_y + top; + int i; + + out = *cap; + *cap = *osd; + if (dev->bitmap_out) { + const u8 *p = dev->bitmap_out; + unsigned stride = (dev->compose_out.width + 7) / 8; + + win_x -= dev->compose_out.left; + win_y -= dev->compose_out.top; + if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7)))) + return; + } + + for (i = 0; i < dev->clipcount_out; i++) { + struct v4l2_rect *r = &dev->clips_out[i].c; + + if (fb_y >= r->top && fb_y < r->top + r->height && + fb_x >= r->left && fb_x < r->left + r->width) + return; + } + if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_CHROMAKEY) && + *osd != dev->chromakey_out) + return; + if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) && + out == dev->chromakey_out) + return; + if (dev->fmt_cap->alpha_mask) { + if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) && + dev->global_alpha_out) + return; + if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) && + *cap & dev->fmt_cap->alpha_mask) + return; + if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) && + !(*cap & dev->fmt_cap->alpha_mask)) + return; + } + *cap = out; +} + +static void blend_line(struct vivid_dev *dev, unsigned y_offset, unsigned x_offset, + u8 *vcapbuf, const u8 *vosdbuf, + unsigned width, unsigned pixsize) +{ + unsigned x; + + for (x = 0; x < width; x++, vcapbuf += pixsize, vosdbuf += pixsize) { + copy_pix(dev, y_offset, x_offset + x, + (u16 *)vcapbuf, (const u16 *)vosdbuf); + } +} + +static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, unsigned twopixsize) +{ + /* Coarse scaling with Bresenham */ + unsigned int_part; + unsigned fract_part; + unsigned src_x = 0; + unsigned error = 0; + unsigned x; + + /* + * We always combine two pixels to prevent color bleed in the packed + * yuv case. + */ + srcw /= 2; + dstw /= 2; + int_part = srcw / dstw; + fract_part = srcw % dstw; + for (x = 0; x < dstw; x++, dst += twopixsize) { + memcpy(dst, src + src_x * twopixsize, twopixsize); + src_x += int_part; + error += fract_part; + if (error >= dstw) { + error -= dstw; + src_x++; + } + } +} + +/* + * Precalculate the rectangles needed to perform video looping: + * + * The nominal pipeline is that the video output buffer is cropped by + * crop_out, scaled to compose_out, overlaid with the output overlay, + * cropped on the capture side by crop_cap and scaled again to the video + * capture buffer using compose_cap. + * + * To keep things efficient we calculate the intersection of compose_out + * and crop_cap (since that's the only part of the video that will + * actually end up in the capture buffer), determine which part of the + * video output buffer that is and which part of the video capture buffer + * so we can scale the video straight from the output buffer to the capture + * buffer without any intermediate steps. + * + * If we need to deal with an output overlay, then there is no choice and + * that intermediate step still has to be taken. For the output overlay + * support we calculate the intersection of the framebuffer and the overlay + * window (which may be partially or wholly outside of the framebuffer + * itself) and the intersection of that with loop_vid_copy (i.e. the part of + * the actual looped video that will be overlaid). The result is calculated + * both in framebuffer coordinates (loop_fb_copy) and compose_out coordinates + * (loop_vid_overlay). Finally calculate the part of the capture buffer that + * will receive that overlaid video. + */ +static void vivid_precalc_copy_rects(struct vivid_dev *dev) +{ + /* Framebuffer rectangle */ + struct v4l2_rect r_fb = { + 0, 0, dev->display_width, dev->display_height + }; + /* Overlay window rectangle in framebuffer coordinates */ + struct v4l2_rect r_overlay = { + dev->overlay_out_left, dev->overlay_out_top, + dev->compose_out.width, dev->compose_out.height + }; + + dev->loop_vid_copy = rect_intersect(&dev->crop_cap, &dev->compose_out); + + dev->loop_vid_out = dev->loop_vid_copy; + rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); + dev->loop_vid_out.left += dev->crop_out.left; + dev->loop_vid_out.top += dev->crop_out.top; + + dev->loop_vid_cap = dev->loop_vid_copy; + rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); + + dprintk(dev, 1, + "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n", + dev->loop_vid_copy.width, dev->loop_vid_copy.height, + dev->loop_vid_copy.left, dev->loop_vid_copy.top, + dev->loop_vid_out.width, dev->loop_vid_out.height, + dev->loop_vid_out.left, dev->loop_vid_out.top, + dev->loop_vid_cap.width, dev->loop_vid_cap.height, + dev->loop_vid_cap.left, dev->loop_vid_cap.top); + + r_overlay = rect_intersect(&r_fb, &r_overlay); + + /* shift r_overlay to the same origin as compose_out */ + r_overlay.left += dev->compose_out.left - dev->overlay_out_left; + r_overlay.top += dev->compose_out.top - dev->overlay_out_top; + + dev->loop_vid_overlay = rect_intersect(&r_overlay, &dev->loop_vid_copy); + dev->loop_fb_copy = dev->loop_vid_overlay; + + /* shift dev->loop_fb_copy back again to the fb origin */ + dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left; + dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top; + + dev->loop_vid_overlay_cap = dev->loop_vid_overlay; + rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); + + dprintk(dev, 1, + "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n", + dev->loop_fb_copy.width, dev->loop_fb_copy.height, + dev->loop_fb_copy.left, dev->loop_fb_copy.top, + dev->loop_vid_overlay.width, dev->loop_vid_overlay.height, + dev->loop_vid_overlay.left, dev->loop_vid_overlay.top, + dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height, + dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top); +} + +static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, + struct vivid_buffer *vid_cap_buf) +{ + bool blank = dev->must_blank[vid_cap_buf->vb.v4l2_buf.index]; + struct tpg_data *tpg = &dev->tpg; + struct vivid_buffer *vid_out_buf = NULL; + unsigned pixsize = tpg_g_twopixelsize(tpg, p) / 2; + unsigned img_width = dev->compose_cap.width; + unsigned img_height = dev->compose_cap.height; + unsigned stride_cap = tpg->bytesperline[p]; + unsigned stride_out = dev->bytesperline_out[p]; + unsigned stride_osd = dev->display_byte_stride; + unsigned hmax = (img_height * tpg->perc_fill) / 100; + u8 *voutbuf; + u8 *vosdbuf = NULL; + unsigned y; + bool blend = dev->bitmap_out || dev->clipcount_out || dev->fbuf_out_flags; + /* Coarse scaling with Bresenham */ + unsigned vid_out_int_part; + unsigned vid_out_fract_part; + unsigned vid_out_y = 0; + unsigned vid_out_error = 0; + unsigned vid_overlay_int_part = 0; + unsigned vid_overlay_fract_part = 0; + unsigned vid_overlay_y = 0; + unsigned vid_overlay_error = 0; + unsigned vid_cap_right; + bool quick; + + vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height; + vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height; + + if (!list_empty(&dev->vid_out_active)) + vid_out_buf = list_entry(dev->vid_out_active.next, + struct vivid_buffer, list); + if (vid_out_buf == NULL) + return -ENODATA; + + vid_cap_buf->vb.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field; + + voutbuf = vb2_plane_vaddr(&vid_out_buf->vb, p) + + vid_out_buf->vb.v4l2_planes[p].data_offset; + voutbuf += dev->loop_vid_out.left * pixsize + dev->loop_vid_out.top * stride_out; + vcapbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride_cap; + + if (dev->loop_vid_copy.width == 0 || dev->loop_vid_copy.height == 0) { + /* + * If there is nothing to copy, then just fill the capture window + * with black. + */ + for (y = 0; y < hmax; y++, vcapbuf += stride_cap) + memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize); + return 0; + } + + if (dev->overlay_out_enabled && + dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) { + vosdbuf = dev->video_vbase; + vosdbuf += dev->loop_fb_copy.left * pixsize + + dev->loop_fb_copy.top * stride_osd; + vid_overlay_int_part = dev->loop_vid_overlay.height / + dev->loop_vid_overlay_cap.height; + vid_overlay_fract_part = dev->loop_vid_overlay.height % + dev->loop_vid_overlay_cap.height; + } + + vid_cap_right = dev->loop_vid_cap.left + dev->loop_vid_cap.width; + /* quick is true if no video scaling is needed */ + quick = dev->loop_vid_out.width == dev->loop_vid_cap.width; + + dev->cur_scaled_line = dev->loop_vid_out.height; + for (y = 0; y < hmax; y++, vcapbuf += stride_cap) { + /* osdline is true if this line requires overlay blending */ + bool osdline = vosdbuf && y >= dev->loop_vid_overlay_cap.top && + y < dev->loop_vid_overlay_cap.top + dev->loop_vid_overlay_cap.height; + + /* + * If this line of the capture buffer doesn't get any video, then + * just fill with black. + */ + if (y < dev->loop_vid_cap.top || + y >= dev->loop_vid_cap.top + dev->loop_vid_cap.height) { + memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize); + continue; + } + + /* fill the left border with black */ + if (dev->loop_vid_cap.left) + memcpy(vcapbuf, tpg->black_line[p], dev->loop_vid_cap.left * pixsize); + + /* fill the right border with black */ + if (vid_cap_right < img_width) + memcpy(vcapbuf + vid_cap_right * pixsize, + tpg->black_line[p], (img_width - vid_cap_right) * pixsize); + + if (quick && !osdline) { + memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, + voutbuf + vid_out_y * stride_out, + dev->loop_vid_cap.width * pixsize); + goto update_vid_out_y; + } + if (dev->cur_scaled_line == vid_out_y) { + memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, + dev->scaled_line, + dev->loop_vid_cap.width * pixsize); + goto update_vid_out_y; + } + if (!osdline) { + scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line, + dev->loop_vid_out.width, dev->loop_vid_cap.width, + tpg_g_twopixelsize(tpg, p)); + } else { + /* + * Offset in bytes within loop_vid_copy to the start of the + * loop_vid_overlay rectangle. + */ + unsigned offset = + (dev->loop_vid_overlay.left - dev->loop_vid_copy.left) * pixsize; + u8 *osd = vosdbuf + vid_overlay_y * stride_osd; + + scale_line(voutbuf + vid_out_y * stride_out, dev->blended_line, + dev->loop_vid_out.width, dev->loop_vid_copy.width, + tpg_g_twopixelsize(tpg, p)); + if (blend) + blend_line(dev, vid_overlay_y + dev->loop_vid_overlay.top, + dev->loop_vid_overlay.left, + dev->blended_line + offset, osd, + dev->loop_vid_overlay.width, pixsize); + else + memcpy(dev->blended_line + offset, + osd, dev->loop_vid_overlay.width * pixsize); + scale_line(dev->blended_line, dev->scaled_line, + dev->loop_vid_copy.width, dev->loop_vid_cap.width, + tpg_g_twopixelsize(tpg, p)); + } + dev->cur_scaled_line = vid_out_y; + memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, + dev->scaled_line, + dev->loop_vid_cap.width * pixsize); + +update_vid_out_y: + if (osdline) { + vid_overlay_y += vid_overlay_int_part; + vid_overlay_error += vid_overlay_fract_part; + if (vid_overlay_error >= dev->loop_vid_overlay_cap.height) { + vid_overlay_error -= dev->loop_vid_overlay_cap.height; + vid_overlay_y++; + } + } + vid_out_y += vid_out_int_part; + vid_out_error += vid_out_fract_part; + if (vid_out_error >= dev->loop_vid_cap.height) { + vid_out_error -= dev->loop_vid_cap.height; + vid_out_y++; + } + } + + if (!blank) + return 0; + for (; y < img_height; y++, vcapbuf += stride_cap) + memcpy(vcapbuf, tpg->contrast_line[p], img_width * pixsize); + return 0; +} + +static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; + unsigned line_height = 16 / factor; + bool is_tv = vivid_is_sdtv_cap(dev); + bool is_60hz = is_tv && (dev->std_cap & V4L2_STD_525_60); + unsigned p; + int line = 1; + u8 *basep[TPG_MAX_PLANES][2]; + unsigned ms; + char str[100]; + s32 gain; + bool is_loop = false; + + if (dev->loop_video && dev->can_loop_video && + ((vivid_is_svid_cap(dev) && !VIVID_INVALID_SIGNAL(dev->std_signal_mode)) || + (vivid_is_hdmi_cap(dev) && !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)))) + is_loop = true; + + buf->vb.v4l2_buf.sequence = dev->vid_cap_seq_count; + /* + * Take the timestamp now if the timestamp source is set to + * "Start of Exposure". + */ + if (dev->tstamp_src_is_soe) + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + if (dev->field_cap == V4L2_FIELD_ALTERNATE) { + /* + * 60 Hz standards start with the bottom field, 50 Hz standards + * with the top field. So if the 0-based seq_count is even, + * then the field is TOP for 50 Hz and BOTTOM for 60 Hz + * standards. + */ + buf->vb.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + /* + * The sequence counter counts frames, not fields. So divide + * by two. + */ + buf->vb.v4l2_buf.sequence /= 2; + } else { + buf->vb.v4l2_buf.field = dev->field_cap; + } + tpg_s_field(&dev->tpg, buf->vb.v4l2_buf.field); + tpg_s_perc_fill_blank(&dev->tpg, dev->must_blank[buf->vb.v4l2_buf.index]); + + vivid_precalc_copy_rects(dev); + + for (p = 0; p < tpg_g_planes(&dev->tpg); p++) { + void *vbuf = vb2_plane_vaddr(&buf->vb, p); + + /* + * The first plane of a multiplanar format has a non-zero + * data_offset. This helps testing whether the application + * correctly supports non-zero data offsets. + */ + if (dev->fmt_cap->data_offset[p]) { + memset(vbuf, dev->fmt_cap->data_offset[p] & 0xff, + dev->fmt_cap->data_offset[p]); + vbuf += dev->fmt_cap->data_offset[p]; + } + tpg_calc_text_basep(&dev->tpg, basep, p, vbuf); + if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) + tpg_fillbuffer(&dev->tpg, vivid_get_std_cap(dev), p, vbuf); + } + dev->must_blank[buf->vb.v4l2_buf.index] = false; + + /* Updates stream time, only update at the start of a new frame. */ + if (dev->field_cap != V4L2_FIELD_ALTERNATE || (buf->vb.v4l2_buf.sequence & 1) == 0) + dev->ms_vid_cap = jiffies_to_msecs(jiffies - dev->jiffies_vid_cap); + + ms = dev->ms_vid_cap; + if (dev->osd_mode <= 1) { + snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d %u%s", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000, + buf->vb.v4l2_buf.sequence, + (dev->field_cap == V4L2_FIELD_ALTERNATE) ? + (buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ? + " top" : " bottom") : ""); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + } + if (dev->osd_mode == 0) { + snprintf(str, sizeof(str), " %dx%d, input %d ", + dev->src_rect.width, dev->src_rect.height, dev->input); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + + gain = v4l2_ctrl_g_ctrl(dev->gain); + mutex_lock(dev->ctrl_hdl_user_vid.lock); + snprintf(str, sizeof(str), + " brightness %3d, contrast %3d, saturation %3d, hue %d ", + dev->brightness->cur.val, + dev->contrast->cur.val, + dev->saturation->cur.val, + dev->hue->cur.val); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), + " autogain %d, gain %3d, alpha 0x%02x ", + dev->autogain->cur.val, gain, dev->alpha->cur.val); + mutex_unlock(dev->ctrl_hdl_user_vid.lock); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + mutex_lock(dev->ctrl_hdl_user_aud.lock); + snprintf(str, sizeof(str), + " volume %3d, mute %d ", + dev->volume->cur.val, dev->mute->cur.val); + mutex_unlock(dev->ctrl_hdl_user_aud.lock); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + mutex_lock(dev->ctrl_hdl_user_gen.lock); + snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", + dev->int32->cur.val, + *dev->int64->p_cur.p_s64, + dev->bitmask->cur.val); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", + dev->boolean->cur.val, + dev->menu->qmenu[dev->menu->cur.val], + dev->string->p_cur.p_char); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), " integer_menu %lld, value %d ", + dev->int_menu->qmenu_int[dev->int_menu->cur.val], + dev->int_menu->cur.val); + mutex_unlock(dev->ctrl_hdl_user_gen.lock); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + if (dev->button_pressed) { + dev->button_pressed--; + snprintf(str, sizeof(str), " button pressed!"); + tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + } + } + + /* + * If "End of Frame" is specified at the timestamp source, then take + * the timestamp now. + */ + if (!dev->tstamp_src_is_soe) + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; +} + +/* + * Return true if this pixel coordinate is a valid video pixel. + */ +static bool valid_pix(struct vivid_dev *dev, int win_y, int win_x, int fb_y, int fb_x) +{ + int i; + + if (dev->bitmap_cap) { + /* + * Only if the corresponding bit in the bitmap is set can + * the video pixel be shown. Coordinates are relative to + * the overlay window set by VIDIOC_S_FMT. + */ + const u8 *p = dev->bitmap_cap; + unsigned stride = (dev->compose_cap.width + 7) / 8; + + if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7)))) + return false; + } + + for (i = 0; i < dev->clipcount_cap; i++) { + /* + * Only if the framebuffer coordinate is not in any of the + * clip rectangles will be video pixel be shown. + */ + struct v4l2_rect *r = &dev->clips_cap[i].c; + + if (fb_y >= r->top && fb_y < r->top + r->height && + fb_x >= r->left && fb_x < r->left + r->width) + return false; + } + return true; +} + +/* + * Draw the image into the overlay buffer. + * Note that the combination of overlay and multiplanar is not supported. + */ +static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + struct tpg_data *tpg = &dev->tpg; + unsigned pixsize = tpg_g_twopixelsize(tpg, 0) / 2; + void *vbase = dev->fb_vbase_cap; + void *vbuf = vb2_plane_vaddr(&buf->vb, 0); + unsigned img_width = dev->compose_cap.width; + unsigned img_height = dev->compose_cap.height; + unsigned stride = tpg->bytesperline[0]; + /* if quick is true, then valid_pix() doesn't have to be called */ + bool quick = dev->bitmap_cap == NULL && dev->clipcount_cap == 0; + int x, y, w, out_x = 0; + + if ((dev->overlay_cap_field == V4L2_FIELD_TOP || + dev->overlay_cap_field == V4L2_FIELD_BOTTOM) && + dev->overlay_cap_field != buf->vb.v4l2_buf.field) + return; + + vbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride; + x = dev->overlay_cap_left; + w = img_width; + if (x < 0) { + out_x = -x; + w = w - out_x; + x = 0; + } else { + w = dev->fb_cap.fmt.width - x; + if (w > img_width) + w = img_width; + } + if (w <= 0) + return; + if (dev->overlay_cap_top >= 0) + vbase += dev->overlay_cap_top * dev->fb_cap.fmt.bytesperline; + for (y = dev->overlay_cap_top; + y < dev->overlay_cap_top + (int)img_height; + y++, vbuf += stride) { + int px; + + if (y < 0 || y > dev->fb_cap.fmt.height) + continue; + if (quick) { + memcpy(vbase + x * pixsize, + vbuf + out_x * pixsize, w * pixsize); + vbase += dev->fb_cap.fmt.bytesperline; + continue; + } + for (px = 0; px < w; px++) { + if (!valid_pix(dev, y - dev->overlay_cap_top, + px + out_x, y, px + x)) + continue; + memcpy(vbase + (px + x) * pixsize, + vbuf + (px + out_x) * pixsize, + pixsize); + } + vbase += dev->fb_cap.fmt.bytesperline; + } +} + +static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs) +{ + struct vivid_buffer *vid_cap_buf = NULL; + struct vivid_buffer *vbi_cap_buf = NULL; + + dprintk(dev, 1, "Video Capture Thread Tick\n"); + + while (dropped_bufs-- > 1) + tpg_update_mv_count(&dev->tpg, + dev->field_cap == V4L2_FIELD_NONE || + dev->field_cap == V4L2_FIELD_ALTERNATE); + + /* Drop a certain percentage of buffers. */ + if (dev->perc_dropped_buffers && + prandom_u32_max(100) < dev->perc_dropped_buffers) + goto update_mv; + + spin_lock(&dev->slock); + if (!list_empty(&dev->vid_cap_active)) { + vid_cap_buf = list_entry(dev->vid_cap_active.next, struct vivid_buffer, list); + list_del(&vid_cap_buf->list); + } + if (!list_empty(&dev->vbi_cap_active)) { + if (dev->field_cap != V4L2_FIELD_ALTERNATE || + (dev->vbi_cap_seq_count & 1)) { + vbi_cap_buf = list_entry(dev->vbi_cap_active.next, + struct vivid_buffer, list); + list_del(&vbi_cap_buf->list); + } + } + spin_unlock(&dev->slock); + + if (!vid_cap_buf && !vbi_cap_buf) + goto update_mv; + + if (vid_cap_buf) { + /* Fill buffer */ + vivid_fillbuff(dev, vid_cap_buf); + dprintk(dev, 1, "filled buffer %d\n", + vid_cap_buf->vb.v4l2_buf.index); + + /* Handle overlay */ + if (dev->overlay_cap_owner && dev->fb_cap.base && + dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc) + vivid_overlay(dev, vid_cap_buf); + + vb2_buffer_done(&vid_cap_buf->vb, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "vid_cap buffer %d done\n", + vid_cap_buf->vb.v4l2_buf.index); + } + + if (vbi_cap_buf) { + if (dev->stream_sliced_vbi_cap) + vivid_sliced_vbi_cap_process(dev, vbi_cap_buf); + else + vivid_raw_vbi_cap_process(dev, vbi_cap_buf); + vb2_buffer_done(&vbi_cap_buf->vb, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "vbi_cap %d done\n", + vbi_cap_buf->vb.v4l2_buf.index); + } + dev->dqbuf_error = false; + +update_mv: + /* Update the test pattern movement counters */ + tpg_update_mv_count(&dev->tpg, dev->field_cap == V4L2_FIELD_NONE || + dev->field_cap == V4L2_FIELD_ALTERNATE); +} + +static int vivid_thread_vid_cap(void *data) +{ + struct vivid_dev *dev = data; + u64 numerators_since_start; + u64 buffers_since_start; + u64 next_jiffies_since_start; + unsigned long jiffies_since_start; + unsigned long cur_jiffies; + unsigned wait_jiffies; + unsigned numerator; + unsigned denominator; + int dropped_bufs; + + dprintk(dev, 1, "Video Capture Thread Start\n"); + + set_freezable(); + + /* Resets frame counters */ + dev->cap_seq_offset = 0; + dev->cap_seq_count = 0; + dev->cap_seq_resync = false; + dev->jiffies_vid_cap = jiffies; + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + mutex_lock(&dev->mutex); + cur_jiffies = jiffies; + if (dev->cap_seq_resync) { + dev->jiffies_vid_cap = cur_jiffies; + dev->cap_seq_offset = dev->cap_seq_count + 1; + dev->cap_seq_count = 0; + dev->cap_seq_resync = false; + } + numerator = dev->timeperframe_vid_cap.numerator; + denominator = dev->timeperframe_vid_cap.denominator; + + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + denominator *= 2; + + /* Calculate the number of jiffies since we started streaming */ + jiffies_since_start = cur_jiffies - dev->jiffies_vid_cap; + /* Get the number of buffers streamed since the start */ + buffers_since_start = (u64)jiffies_since_start * denominator + + (HZ * numerator) / 2; + do_div(buffers_since_start, HZ * numerator); + + /* + * After more than 0xf0000000 (rounded down to a multiple of + * 'jiffies-per-day' to ease jiffies_to_msecs calculation) + * jiffies have passed since we started streaming reset the + * counters and keep track of the sequence offset. + */ + if (jiffies_since_start > JIFFIES_RESYNC) { + dev->jiffies_vid_cap = cur_jiffies; + dev->cap_seq_offset = buffers_since_start; + buffers_since_start = 0; + } + dropped_bufs = buffers_since_start + dev->cap_seq_offset - dev->cap_seq_count; + dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset; + dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start; + dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start; + + vivid_thread_vid_cap_tick(dev, dropped_bufs); + + /* + * Calculate the number of 'numerators' streamed since we started, + * including the current buffer. + */ + numerators_since_start = ++buffers_since_start * numerator; + + /* And the number of jiffies since we started */ + jiffies_since_start = jiffies - dev->jiffies_vid_cap; + + mutex_unlock(&dev->mutex); + + /* + * Calculate when that next buffer is supposed to start + * in jiffies since we started streaming. + */ + next_jiffies_since_start = numerators_since_start * HZ + + denominator / 2; + do_div(next_jiffies_since_start, denominator); + /* If it is in the past, then just schedule asap */ + if (next_jiffies_since_start < jiffies_since_start) + next_jiffies_since_start = jiffies_since_start; + + wait_jiffies = next_jiffies_since_start - jiffies_since_start; + schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + } + dprintk(dev, 1, "Video Capture Thread End\n"); + return 0; +} + +static void vivid_grab_controls(struct vivid_dev *dev, bool grab) +{ + v4l2_ctrl_grab(dev->ctrl_has_crop_cap, grab); + v4l2_ctrl_grab(dev->ctrl_has_compose_cap, grab); + v4l2_ctrl_grab(dev->ctrl_has_scaler_cap, grab); +} + +int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) +{ + dprintk(dev, 1, "%s\n", __func__); + + if (dev->kthread_vid_cap) { + u32 seq_count = dev->cap_seq_count + dev->seq_wrap * 128; + + if (pstreaming == &dev->vid_cap_streaming) + dev->vid_cap_seq_start = seq_count; + else + dev->vbi_cap_seq_start = seq_count; + *pstreaming = true; + return 0; + } + + /* Resets frame counters */ + tpg_init_mv_count(&dev->tpg); + + dev->vid_cap_seq_start = dev->seq_wrap * 128; + dev->vbi_cap_seq_start = dev->seq_wrap * 128; + + dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev, + "%s-vid-cap", dev->v4l2_dev.name); + + if (IS_ERR(dev->kthread_vid_cap)) { + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + return PTR_ERR(dev->kthread_vid_cap); + } + *pstreaming = true; + vivid_grab_controls(dev, true); + + dprintk(dev, 1, "returning from %s\n", __func__); + return 0; +} + +void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) +{ + dprintk(dev, 1, "%s\n", __func__); + + if (dev->kthread_vid_cap == NULL) + return; + + *pstreaming = false; + if (pstreaming == &dev->vid_cap_streaming) { + /* Release all active buffers */ + while (!list_empty(&dev->vid_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->vid_cap_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "vid_cap buffer %d done\n", + buf->vb.v4l2_buf.index); + } + } + + if (pstreaming == &dev->vbi_cap_streaming) { + while (!list_empty(&dev->vbi_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->vbi_cap_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "vbi_cap buffer %d done\n", + buf->vb.v4l2_buf.index); + } + } + + if (dev->vid_cap_streaming || dev->vbi_cap_streaming) + return; + + /* shutdown control thread */ + vivid_grab_controls(dev, false); + mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_vid_cap); + dev->kthread_vid_cap = NULL; + mutex_lock(&dev->mutex); +} diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h new file mode 100644 index 0000000..5b92fc9 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-kthread-cap.h @@ -0,0 +1,26 @@ +/* + * vivid-kthread-cap.h - video/vbi capture thread support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_KTHREAD_CAP_H_ +#define _VIVID_KTHREAD_CAP_H_ + +int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming); +void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming); + +#endif diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c new file mode 100644 index 0000000..d9f36cc --- /dev/null +++ b/drivers/media/platform/vivid/vivid-kthread-out.c @@ -0,0 +1,305 @@ +/* + * vivid-kthread-out.h - video/vbi output thread support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/font.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/random.h> +#include <linux/v4l2-dv-timings.h> +#include <asm/div64.h> +#include <media/videobuf2-vmalloc.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-vid-cap.h" +#include "vivid-vid-out.h" +#include "vivid-radio-common.h" +#include "vivid-radio-rx.h" +#include "vivid-radio-tx.h" +#include "vivid-sdr-cap.h" +#include "vivid-vbi-cap.h" +#include "vivid-vbi-out.h" +#include "vivid-osd.h" +#include "vivid-ctrls.h" +#include "vivid-kthread-out.h" + +static void vivid_thread_vid_out_tick(struct vivid_dev *dev) +{ + struct vivid_buffer *vid_out_buf = NULL; + struct vivid_buffer *vbi_out_buf = NULL; + + dprintk(dev, 1, "Video Output Thread Tick\n"); + + /* Drop a certain percentage of buffers. */ + if (dev->perc_dropped_buffers && + prandom_u32_max(100) < dev->perc_dropped_buffers) + return; + + spin_lock(&dev->slock); + /* + * Only dequeue buffer if there is at least one more pending. + * This makes video loopback possible. + */ + if (!list_empty(&dev->vid_out_active) && + !list_is_singular(&dev->vid_out_active)) { + vid_out_buf = list_entry(dev->vid_out_active.next, + struct vivid_buffer, list); + list_del(&vid_out_buf->list); + } + if (!list_empty(&dev->vbi_out_active) && + (dev->field_out != V4L2_FIELD_ALTERNATE || + (dev->vbi_out_seq_count & 1))) { + vbi_out_buf = list_entry(dev->vbi_out_active.next, + struct vivid_buffer, list); + list_del(&vbi_out_buf->list); + } + spin_unlock(&dev->slock); + + if (!vid_out_buf && !vbi_out_buf) + return; + + if (vid_out_buf) { + vid_out_buf->vb.v4l2_buf.sequence = dev->vid_out_seq_count; + if (dev->field_out == V4L2_FIELD_ALTERNATE) { + /* + * The sequence counter counts frames, not fields. So divide + * by two. + */ + vid_out_buf->vb.v4l2_buf.sequence /= 2; + } + v4l2_get_timestamp(&vid_out_buf->vb.v4l2_buf.timestamp); + vid_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&vid_out_buf->vb, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "vid_out buffer %d done\n", + vid_out_buf->vb.v4l2_buf.index); + } + + if (vbi_out_buf) { + if (dev->stream_sliced_vbi_out) + vivid_sliced_vbi_out_process(dev, vbi_out_buf); + + vbi_out_buf->vb.v4l2_buf.sequence = dev->vbi_out_seq_count; + v4l2_get_timestamp(&vbi_out_buf->vb.v4l2_buf.timestamp); + vbi_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&vbi_out_buf->vb, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "vbi_out buffer %d done\n", + vbi_out_buf->vb.v4l2_buf.index); + } + dev->dqbuf_error = false; +} + +static int vivid_thread_vid_out(void *data) +{ + struct vivid_dev *dev = data; + u64 numerators_since_start; + u64 buffers_since_start; + u64 next_jiffies_since_start; + unsigned long jiffies_since_start; + unsigned long cur_jiffies; + unsigned wait_jiffies; + unsigned numerator; + unsigned denominator; + + dprintk(dev, 1, "Video Output Thread Start\n"); + + set_freezable(); + + /* Resets frame counters */ + dev->out_seq_offset = 0; + if (dev->seq_wrap) + dev->out_seq_count = 0xffffff80U; + dev->jiffies_vid_out = jiffies; + dev->vid_out_seq_start = dev->vbi_out_seq_start = 0; + dev->out_seq_resync = false; + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + mutex_lock(&dev->mutex); + cur_jiffies = jiffies; + if (dev->out_seq_resync) { + dev->jiffies_vid_out = cur_jiffies; + dev->out_seq_offset = dev->out_seq_count + 1; + dev->out_seq_count = 0; + dev->out_seq_resync = false; + } + numerator = dev->timeperframe_vid_out.numerator; + denominator = dev->timeperframe_vid_out.denominator; + + if (dev->field_out == V4L2_FIELD_ALTERNATE) + denominator *= 2; + + /* Calculate the number of jiffies since we started streaming */ + jiffies_since_start = cur_jiffies - dev->jiffies_vid_out; + /* Get the number of buffers streamed since the start */ + buffers_since_start = (u64)jiffies_since_start * denominator + + (HZ * numerator) / 2; + do_div(buffers_since_start, HZ * numerator); + + /* + * After more than 0xf0000000 (rounded down to a multiple of + * 'jiffies-per-day' to ease jiffies_to_msecs calculation) + * jiffies have passed since we started streaming reset the + * counters and keep track of the sequence offset. + */ + if (jiffies_since_start > JIFFIES_RESYNC) { + dev->jiffies_vid_out = cur_jiffies; + dev->out_seq_offset = buffers_since_start; + buffers_since_start = 0; + } + dev->out_seq_count = buffers_since_start + dev->out_seq_offset; + dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start; + dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start; + + vivid_thread_vid_out_tick(dev); + mutex_unlock(&dev->mutex); + + /* + * Calculate the number of 'numerators' streamed since we started, + * not including the current buffer. + */ + numerators_since_start = buffers_since_start * numerator; + + /* And the number of jiffies since we started */ + jiffies_since_start = jiffies - dev->jiffies_vid_out; + + /* Increase by the 'numerator' of one buffer */ + numerators_since_start += numerator; + /* + * Calculate when that next buffer is supposed to start + * in jiffies since we started streaming. + */ + next_jiffies_since_start = numerators_since_start * HZ + + denominator / 2; + do_div(next_jiffies_since_start, denominator); + /* If it is in the past, then just schedule asap */ + if (next_jiffies_since_start < jiffies_since_start) + next_jiffies_since_start = jiffies_since_start; + + wait_jiffies = next_jiffies_since_start - jiffies_since_start; + schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + } + dprintk(dev, 1, "Video Output Thread End\n"); + return 0; +} + +static void vivid_grab_controls(struct vivid_dev *dev, bool grab) +{ + v4l2_ctrl_grab(dev->ctrl_has_crop_out, grab); + v4l2_ctrl_grab(dev->ctrl_has_compose_out, grab); + v4l2_ctrl_grab(dev->ctrl_has_scaler_out, grab); + v4l2_ctrl_grab(dev->ctrl_tx_mode, grab); + v4l2_ctrl_grab(dev->ctrl_tx_rgb_range, grab); +} + +int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) +{ + dprintk(dev, 1, "%s\n", __func__); + + if (dev->kthread_vid_out) { + u32 seq_count = dev->out_seq_count + dev->seq_wrap * 128; + + if (pstreaming == &dev->vid_out_streaming) + dev->vid_out_seq_start = seq_count; + else + dev->vbi_out_seq_start = seq_count; + *pstreaming = true; + return 0; + } + + /* Resets frame counters */ + dev->jiffies_vid_out = jiffies; + dev->vid_out_seq_start = dev->seq_wrap * 128; + dev->vbi_out_seq_start = dev->seq_wrap * 128; + + dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev, + "%s-vid-out", dev->v4l2_dev.name); + + if (IS_ERR(dev->kthread_vid_out)) { + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + return PTR_ERR(dev->kthread_vid_out); + } + *pstreaming = true; + vivid_grab_controls(dev, true); + + dprintk(dev, 1, "returning from %s\n", __func__); + return 0; +} + +void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) +{ + dprintk(dev, 1, "%s\n", __func__); + + if (dev->kthread_vid_out == NULL) + return; + + *pstreaming = false; + if (pstreaming == &dev->vid_out_streaming) { + /* Release all active buffers */ + while (!list_empty(&dev->vid_out_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->vid_out_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "vid_out buffer %d done\n", + buf->vb.v4l2_buf.index); + } + } + + if (pstreaming == &dev->vbi_out_streaming) { + while (!list_empty(&dev->vbi_out_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->vbi_out_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "vbi_out buffer %d done\n", + buf->vb.v4l2_buf.index); + } + } + + if (dev->vid_out_streaming || dev->vbi_out_streaming) + return; + + /* shutdown control thread */ + vivid_grab_controls(dev, false); + mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_vid_out); + dev->kthread_vid_out = NULL; + mutex_lock(&dev->mutex); +} diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h new file mode 100644 index 0000000..2bf04a1 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-kthread-out.h @@ -0,0 +1,26 @@ +/* + * vivid-kthread-out.h - video/vbi output thread support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_KTHREAD_OUT_H_ +#define _VIVID_KTHREAD_OUT_H_ + +int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming); +void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming); + +#endif diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c new file mode 100644 index 0000000..084d346 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -0,0 +1,400 @@ +/* + * vivid-osd.c - osd support for testing overlays. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/font.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/fb.h> +#include <media/videobuf2-vmalloc.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/v4l2-common.h> + +#include "vivid-core.h" +#include "vivid-osd.h" + +#define MAX_OSD_WIDTH 720 +#define MAX_OSD_HEIGHT 576 + +/* + * Order: white, yellow, cyan, green, magenta, red, blue, black, + * and same again with the alpha bit set (if any) + */ +static const u16 rgb555[16] = { + 0x7fff, 0x7fe0, 0x03ff, 0x03e0, 0x7c1f, 0x7c00, 0x001f, 0x0000, + 0xffff, 0xffe0, 0x83ff, 0x83e0, 0xfc1f, 0xfc00, 0x801f, 0x8000 +}; + +static const u16 rgb565[16] = { + 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000, + 0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000 +}; + +void vivid_clear_fb(struct vivid_dev *dev) +{ + void *p = dev->video_vbase; + const u16 *rgb = rgb555; + unsigned x, y; + + if (dev->fb_defined.green.length == 6) + rgb = rgb565; + + for (y = 0; y < dev->display_height; y++) { + u16 *d = p; + + for (x = 0; x < dev->display_width; x++) + d[x] = rgb[(y / 16 + x / 16) % 16]; + p += dev->display_byte_stride; + } +} + +/* --------------------------------------------------------------------- */ + +static int vivid_fb_ioctl(struct fb_info *info, unsigned cmd, unsigned long arg) +{ + struct vivid_dev *dev = (struct vivid_dev *)info->par; + + switch (cmd) { + case FBIOGET_VBLANK: { + struct fb_vblank vblank; + + vblank.flags = FB_VBLANK_HAVE_COUNT | FB_VBLANK_HAVE_VCOUNT | + FB_VBLANK_HAVE_VSYNC; + vblank.count = 0; + vblank.vcount = 0; + vblank.hcount = 0; + if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) + return -EFAULT; + return 0; + } + + default: + dprintk(dev, 1, "Unknown ioctl %08x\n", cmd); + return -EINVAL; + } + return 0; +} + +/* Framebuffer device handling */ + +static int vivid_fb_set_var(struct vivid_dev *dev, struct fb_var_screeninfo *var) +{ + dprintk(dev, 1, "vivid_fb_set_var\n"); + + if (var->bits_per_pixel != 16) { + dprintk(dev, 1, "vivid_fb_set_var - Invalid bpp\n"); + return -EINVAL; + } + dev->display_byte_stride = var->xres * dev->bytes_per_pixel; + + return 0; +} + +static int vivid_fb_get_fix(struct vivid_dev *dev, struct fb_fix_screeninfo *fix) +{ + dprintk(dev, 1, "vivid_fb_get_fix\n"); + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strlcpy(fix->id, "vioverlay fb", sizeof(fix->id)); + fix->smem_start = dev->video_pbase; + fix->smem_len = dev->video_buffer_size; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->line_length = dev->display_byte_stride; + fix->accel = FB_ACCEL_NONE; + return 0; +} + +/* Check the requested display mode, returning -EINVAL if we can't + handle it. */ + +static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev *dev) +{ + dprintk(dev, 1, "vivid_fb_check_var\n"); + + var->bits_per_pixel = 16; + if (var->green.length == 5) { + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + } else { + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } + var->xoffset = var->yoffset = 0; + var->left_margin = var->upper_margin = 0; + var->nonstd = 0; + + var->vmode &= ~FB_VMODE_MASK; + var->vmode = FB_VMODE_NONINTERLACED; + + /* Dummy values */ + var->hsync_len = 24; + var->vsync_len = 2; + var->pixclock = 84316; + var->right_margin = 776; + var->lower_margin = 591; + return 0; +} + +static int vivid_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct vivid_dev *dev = (struct vivid_dev *) info->par; + + dprintk(dev, 1, "vivid_fb_check_var\n"); + return _vivid_fb_check_var(var, dev); +} + +static int vivid_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + return 0; +} + +static int vivid_fb_set_par(struct fb_info *info) +{ + int rc = 0; + struct vivid_dev *dev = (struct vivid_dev *) info->par; + + dprintk(dev, 1, "vivid_fb_set_par\n"); + + rc = vivid_fb_set_var(dev, &info->var); + vivid_fb_get_fix(dev, &info->fix); + return rc; +} + +static int vivid_fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 color, *palette; + + if (regno >= info->cmap.len) + return -EINVAL; + + color = ((transp & 0xFF00) << 16) | ((red & 0xFF00) << 8) | + (green & 0xFF00) | ((blue & 0xFF00) >> 8); + if (regno >= 16) + return -EINVAL; + + palette = info->pseudo_palette; + if (info->var.bits_per_pixel == 16) { + switch (info->var.green.length) { + case 6: + color = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 5: + color = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11) | + (transp ? 0x8000 : 0); + break; + } + } + palette[regno] = color; + return 0; +} + +/* We don't really support blanking. All this does is enable or + disable the OSD. */ +static int vivid_fb_blank(int blank_mode, struct fb_info *info) +{ + struct vivid_dev *dev = (struct vivid_dev *)info->par; + + dprintk(dev, 1, "Set blanking mode : %d\n", blank_mode); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + break; + case FB_BLANK_NORMAL: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + break; + } + return 0; +} + +static struct fb_ops vivid_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = vivid_fb_check_var, + .fb_set_par = vivid_fb_set_par, + .fb_setcolreg = vivid_fb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = NULL, + .fb_ioctl = vivid_fb_ioctl, + .fb_pan_display = vivid_fb_pan_display, + .fb_blank = vivid_fb_blank, +}; + +/* Initialization */ + + +/* Setup our initial video mode */ +static int vivid_fb_init_vidmode(struct vivid_dev *dev) +{ + struct v4l2_rect start_window; + + /* Color mode */ + + dev->bits_per_pixel = 16; + dev->bytes_per_pixel = dev->bits_per_pixel / 8; + + start_window.width = MAX_OSD_WIDTH; + start_window.left = 0; + + dev->display_byte_stride = start_window.width * dev->bytes_per_pixel; + + /* Vertical size & position */ + + start_window.height = MAX_OSD_HEIGHT; + start_window.top = 0; + + dev->display_width = start_window.width; + dev->display_height = start_window.height; + + /* Generate a valid fb_var_screeninfo */ + + dev->fb_defined.xres = dev->display_width; + dev->fb_defined.yres = dev->display_height; + dev->fb_defined.xres_virtual = dev->display_width; + dev->fb_defined.yres_virtual = dev->display_height; + dev->fb_defined.bits_per_pixel = dev->bits_per_pixel; + dev->fb_defined.vmode = FB_VMODE_NONINTERLACED; + dev->fb_defined.left_margin = start_window.left + 1; + dev->fb_defined.upper_margin = start_window.top + 1; + dev->fb_defined.accel_flags = FB_ACCEL_NONE; + dev->fb_defined.nonstd = 0; + /* set default to 1:5:5:5 */ + dev->fb_defined.green.length = 5; + + /* We've filled in the most data, let the usual mode check + routine fill in the rest. */ + _vivid_fb_check_var(&dev->fb_defined, dev); + + /* Generate valid fb_fix_screeninfo */ + + vivid_fb_get_fix(dev, &dev->fb_fix); + + /* Generate valid fb_info */ + + dev->fb_info.node = -1; + dev->fb_info.flags = FBINFO_FLAG_DEFAULT; + dev->fb_info.fbops = &vivid_fb_ops; + dev->fb_info.par = dev; + dev->fb_info.var = dev->fb_defined; + dev->fb_info.fix = dev->fb_fix; + dev->fb_info.screen_base = (u8 __iomem *)dev->video_vbase; + dev->fb_info.fbops = &vivid_fb_ops; + + /* Supply some monitor specs. Bogus values will do for now */ + dev->fb_info.monspecs.hfmin = 8000; + dev->fb_info.monspecs.hfmax = 70000; + dev->fb_info.monspecs.vfmin = 10; + dev->fb_info.monspecs.vfmax = 100; + + /* Allocate color map */ + if (fb_alloc_cmap(&dev->fb_info.cmap, 256, 1)) { + pr_err("abort, unable to alloc cmap\n"); + return -ENOMEM; + } + + /* Allocate the pseudo palette */ + dev->fb_info.pseudo_palette = kmalloc_array(16, sizeof(u32), GFP_KERNEL); + + return dev->fb_info.pseudo_palette ? 0 : -ENOMEM; +} + +/* Release any memory we've grabbed */ +void vivid_fb_release_buffers(struct vivid_dev *dev) +{ + if (dev->video_vbase == NULL) + return; + + /* Release cmap */ + if (dev->fb_info.cmap.len) + fb_dealloc_cmap(&dev->fb_info.cmap); + + /* Release pseudo palette */ + kfree(dev->fb_info.pseudo_palette); + kfree((void *)dev->video_vbase); +} + +/* Initialize the specified card */ + +int vivid_fb_init(struct vivid_dev *dev) +{ + int ret; + + dev->video_buffer_size = MAX_OSD_HEIGHT * MAX_OSD_WIDTH * 2; + dev->video_vbase = kzalloc(dev->video_buffer_size, GFP_KERNEL | GFP_DMA32); + if (dev->video_vbase == NULL) + return -ENOMEM; + dev->video_pbase = virt_to_phys(dev->video_vbase); + + pr_info("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", + dev->video_pbase, dev->video_vbase, + dev->video_buffer_size / 1024); + + /* Set the startup video mode information */ + ret = vivid_fb_init_vidmode(dev); + if (ret) { + vivid_fb_release_buffers(dev); + return ret; + } + + vivid_clear_fb(dev); + + /* Register the framebuffer */ + if (register_framebuffer(&dev->fb_info) < 0) { + vivid_fb_release_buffers(dev); + return -EINVAL; + } + + /* Set the card to the requested mode */ + vivid_fb_set_par(&dev->fb_info); + return 0; + +} diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/platform/vivid/vivid-osd.h new file mode 100644 index 0000000..57c9daa --- /dev/null +++ b/drivers/media/platform/vivid/vivid-osd.h @@ -0,0 +1,27 @@ +/* + * vivid-osd.h - output overlay support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_OSD_H_ +#define _VIVID_OSD_H_ + +int vivid_fb_init(struct vivid_dev *dev); +void vivid_fb_release_buffers(struct vivid_dev *dev); +void vivid_clear_fb(struct vivid_dev *dev); + +#endif diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c new file mode 100644 index 0000000..78c1e92 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-common.c @@ -0,0 +1,189 @@ +/* + * vivid-radio-common.c - common radio rx/tx support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/videodev2.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-radio-common.h" +#include "vivid-rds-gen.h" + +/* + * These functions are shared between the vivid receiver and transmitter + * since both use the same frequency bands. + */ + +const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = { + /* Band FM */ + { + .type = V4L2_TUNER_RADIO, + .index = 0, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = FM_FREQ_RANGE_LOW, + .rangehigh = FM_FREQ_RANGE_HIGH, + .modulation = V4L2_BAND_MODULATION_FM, + }, + /* Band AM */ + { + .type = V4L2_TUNER_RADIO, + .index = 1, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = AM_FREQ_RANGE_LOW, + .rangehigh = AM_FREQ_RANGE_HIGH, + .modulation = V4L2_BAND_MODULATION_AM, + }, + /* Band SW */ + { + .type = V4L2_TUNER_RADIO, + .index = 2, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = SW_FREQ_RANGE_LOW, + .rangehigh = SW_FREQ_RANGE_HIGH, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +/* + * Initialize the RDS generator. If we can loop, then the RDS generator + * is set up with the values from the RDS TX controls, otherwise it + * will fill in standard values using one of two alternates. + */ +void vivid_radio_rds_init(struct vivid_dev *dev) +{ + struct vivid_rds_gen *rds = &dev->rds_gen; + bool alt = dev->radio_rx_rds_use_alternates; + + /* Do nothing, blocks will be filled by the transmitter */ + if (dev->radio_rds_loop && !dev->radio_tx_rds_controls) + return; + + if (dev->radio_rds_loop) { + v4l2_ctrl_lock(dev->radio_tx_rds_pi); + rds->picode = dev->radio_tx_rds_pi->cur.val; + rds->pty = dev->radio_tx_rds_pty->cur.val; + rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val; + rds->art_head = dev->radio_tx_rds_art_head->cur.val; + rds->compressed = dev->radio_tx_rds_compressed->cur.val; + rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val; + rds->ta = dev->radio_tx_rds_ta->cur.val; + rds->tp = dev->radio_tx_rds_tp->cur.val; + rds->ms = dev->radio_tx_rds_ms->cur.val; + strlcpy(rds->psname, + dev->radio_tx_rds_psname->p_cur.p_char, + sizeof(rds->psname)); + strlcpy(rds->radiotext, + dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64, + sizeof(rds->radiotext)); + v4l2_ctrl_unlock(dev->radio_tx_rds_pi); + } else { + vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt); + } + if (dev->radio_rx_rds_controls) { + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty); + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta); + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp); + v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms); + v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname); + v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext); + if (!dev->radio_rds_loop) + dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates; + } + vivid_rds_generate(rds); +} + +/* + * Calculate the emulated signal quality taking into account the frequency + * the transmitter is using. + */ +static void vivid_radio_calc_sig_qual(struct vivid_dev *dev) +{ + int mod = 16000; + int delta = 800; + int sig_qual, sig_qual_tx = mod; + + /* + * For SW and FM there is a channel every 1000 kHz, for AM there is one + * every 100 kHz. + */ + if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) { + mod /= 10; + delta /= 10; + } + sig_qual = (dev->radio_rx_freq + delta) % mod - delta; + if (dev->has_radio_tx) + sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq; + if (abs(sig_qual_tx) <= abs(sig_qual)) { + sig_qual = sig_qual_tx; + /* + * Zero the internal rds buffer if we are going to loop + * rds blocks. + */ + if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls) + memset(dev->rds_gen.data, 0, + sizeof(dev->rds_gen.data)); + dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW; + } else { + dev->radio_rds_loop = false; + } + if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) + sig_qual *= 10; + dev->radio_rx_sig_qual = sig_qual; +} + +int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf) +{ + if (vf->tuner != 0) + return -EINVAL; + vf->frequency = *pfreq; + return 0; +} + +int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + unsigned freq; + unsigned band; + + if (vf->tuner != 0) + return -EINVAL; + + if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2) + band = BAND_FM; + else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2) + band = BAND_AM; + else + band = BAND_SW; + + freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow, + vivid_radio_bands[band].rangehigh); + *pfreq = freq; + + /* + * For both receiver and transmitter recalculate the signal quality + * (since that depends on both frequencies) and re-init the rds + * generator. + */ + vivid_radio_calc_sig_qual(dev); + vivid_radio_rds_init(dev); + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/platform/vivid/vivid-radio-common.h new file mode 100644 index 0000000..92fe589 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-common.h @@ -0,0 +1,40 @@ +/* + * vivid-radio-common.h - common radio rx/tx support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_RADIO_COMMON_H_ +#define _VIVID_RADIO_COMMON_H_ + +/* The supported radio frequency ranges in kHz */ +#define FM_FREQ_RANGE_LOW (64000U * 16U) +#define FM_FREQ_RANGE_HIGH (108000U * 16U) +#define AM_FREQ_RANGE_LOW (520U * 16U) +#define AM_FREQ_RANGE_HIGH (1710U * 16U) +#define SW_FREQ_RANGE_LOW (2300U * 16U) +#define SW_FREQ_RANGE_HIGH (26100U * 16U) + +enum { BAND_FM, BAND_AM, BAND_SW, TOT_BANDS }; + +extern const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS]; + +int vivid_radio_g_frequency(struct file *file, const unsigned *freq, struct v4l2_frequency *vf); +int vivid_radio_s_frequency(struct file *file, unsigned *freq, const struct v4l2_frequency *vf); + +void vivid_radio_rds_init(struct vivid_dev *dev); + +#endif diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c new file mode 100644 index 0000000..c7651a5 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-rx.c @@ -0,0 +1,287 @@ +/* + * vivid-radio-rx.c - radio receiver support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-radio-common.h" +#include "vivid-rds-gen.h" +#include "vivid-radio-rx.h" + +ssize_t vivid_radio_rx_read(struct file *file, char __user *buf, + size_t size, loff_t *offset) +{ + struct vivid_dev *dev = video_drvdata(file); + struct timespec ts; + struct v4l2_rds_data *data = dev->rds_gen.data; + bool use_alternates; + unsigned blk; + int perc; + int i; + + if (dev->radio_rx_rds_controls) + return -EINVAL; + if (size < sizeof(*data)) + return 0; + size = sizeof(*data) * (size / sizeof(*data)); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + if (dev->radio_rx_rds_owner && + file->private_data != dev->radio_rx_rds_owner) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + if (dev->radio_rx_rds_owner == NULL) { + vivid_radio_rds_init(dev); + dev->radio_rx_rds_owner = file->private_data; + } + +retry: + ktime_get_ts(&ts); + use_alternates = ts.tv_sec % 10 >= 5; + if (dev->radio_rx_rds_last_block == 0 || + dev->radio_rx_rds_use_alternates != use_alternates) { + dev->radio_rx_rds_use_alternates = use_alternates; + /* Re-init the RDS generator */ + vivid_radio_rds_init(dev); + } + ts = timespec_sub(ts, dev->radio_rds_init_ts); + blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000; + blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500; + if (blk >= dev->radio_rx_rds_last_block + VIVID_RDS_GEN_BLOCKS) + dev->radio_rx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1; + + /* + * No data is available if there hasn't been time to get new data, + * or if the RDS receiver has been disabled, or if we use the data + * from the RDS transmitter and that RDS transmitter has been disabled, + * or if the signal quality is too weak. + */ + if (blk == dev->radio_rx_rds_last_block || !dev->radio_rx_rds_enabled || + (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) || + abs(dev->radio_rx_sig_qual) > 200) { + mutex_unlock(&dev->mutex); + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + if (msleep_interruptible(20) && signal_pending(current)) + return -EINTR; + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + goto retry; + } + + /* abs(dev->radio_rx_sig_qual) <= 200, map that to a 0-50% range */ + perc = abs(dev->radio_rx_sig_qual) / 4; + + for (i = 0; i < size && blk > dev->radio_rx_rds_last_block; + dev->radio_rx_rds_last_block++) { + unsigned data_blk = dev->radio_rx_rds_last_block % VIVID_RDS_GEN_BLOCKS; + struct v4l2_rds_data rds = data[data_blk]; + + if (data_blk == 0 && dev->radio_rds_loop) + vivid_radio_rds_init(dev); + if (perc && prandom_u32_max(100) < perc) { + switch (prandom_u32_max(4)) { + case 0: + rds.block |= V4L2_RDS_BLOCK_CORRECTED; + break; + case 1: + rds.block |= V4L2_RDS_BLOCK_INVALID; + break; + case 2: + rds.block |= V4L2_RDS_BLOCK_ERROR; + rds.lsb = prandom_u32_max(256); + rds.msb = prandom_u32_max(256); + break; + case 3: /* Skip block altogether */ + if (i) + continue; + /* + * Must make sure at least one block is + * returned, otherwise the application + * might think that end-of-file occurred. + */ + break; + } + } + if (copy_to_user(buf + i, &rds, sizeof(rds))) { + i = -EFAULT; + break; + } + i += sizeof(rds); + } + mutex_unlock(&dev->mutex); + return i; +} + +unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait) +{ + return POLLIN | POLLRDNORM | v4l2_ctrl_poll(file, wait); +} + +int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) +{ + if (band->tuner != 0) + return -EINVAL; + + if (band->index >= TOT_BANDS) + return -EINVAL; + + *band = vivid_radio_bands[band->index]; + return 0; +} + +int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a) +{ + struct vivid_dev *dev = video_drvdata(file); + unsigned low, high; + unsigned freq; + unsigned spacing; + unsigned band; + + if (a->tuner) + return -EINVAL; + if (a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_BOUNDED) + return -EINVAL; + + if (!a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_WRAP) + return -EINVAL; + if (!a->rangelow ^ !a->rangehigh) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (a->rangelow) { + for (band = 0; band < TOT_BANDS; band++) + if (a->rangelow >= vivid_radio_bands[band].rangelow && + a->rangehigh <= vivid_radio_bands[band].rangehigh) + break; + if (band == TOT_BANDS) + return -EINVAL; + if (!dev->radio_rx_hw_seek_prog_lim && + (a->rangelow != vivid_radio_bands[band].rangelow || + a->rangehigh != vivid_radio_bands[band].rangehigh)) + return -EINVAL; + low = a->rangelow; + high = a->rangehigh; + } else { + for (band = 0; band < TOT_BANDS; band++) + if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow && + dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh) + break; + low = vivid_radio_bands[band].rangelow; + high = vivid_radio_bands[band].rangehigh; + } + spacing = band == BAND_AM ? 1600 : 16000; + freq = clamp(dev->radio_rx_freq, low, high); + + if (a->seek_upward) { + freq = spacing * (freq / spacing) + spacing; + if (freq > high) { + if (!a->wrap_around) + return -ENODATA; + freq = spacing * (low / spacing) + spacing; + if (freq >= dev->radio_rx_freq) + return -ENODATA; + } + } else { + freq = spacing * ((freq + spacing - 1) / spacing) - spacing; + if (freq < low) { + if (!a->wrap_around) + return -ENODATA; + freq = spacing * ((high + spacing - 1) / spacing) - spacing; + if (freq <= dev->radio_rx_freq) + return -ENODATA; + } + } + return 0; +} + +int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct vivid_dev *dev = video_drvdata(file); + int delta = 800; + int sig_qual; + + if (vt->index > 0) + return -EINVAL; + + strlcpy(vt->name, "AM/FM/SW Receiver", sizeof(vt->name)); + vt->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS | + (dev->radio_rx_rds_controls ? + V4L2_TUNER_CAP_RDS_CONTROLS : + V4L2_TUNER_CAP_RDS_BLOCK_IO) | + (dev->radio_rx_hw_seek_prog_lim ? + V4L2_TUNER_CAP_HWSEEK_PROG_LIM : 0); + switch (dev->radio_rx_hw_seek_mode) { + case VIVID_HW_SEEK_BOUNDED: + vt->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; + break; + case VIVID_HW_SEEK_WRAP: + vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP; + break; + case VIVID_HW_SEEK_BOTH: + vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP | + V4L2_TUNER_CAP_HWSEEK_BOUNDED; + break; + } + vt->rangelow = AM_FREQ_RANGE_LOW; + vt->rangehigh = FM_FREQ_RANGE_HIGH; + sig_qual = dev->radio_rx_sig_qual; + vt->signal = abs(sig_qual) > delta ? 0 : + 0xffff - (abs(sig_qual) * 0xffff) / delta; + vt->afc = sig_qual > delta ? 0 : sig_qual; + if (abs(sig_qual) > delta) + vt->rxsubchans = 0; + else if (dev->radio_rx_freq < FM_FREQ_RANGE_LOW || vt->signal < 0x8000) + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + else if (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_STEREO)) + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + else + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + if (dev->radio_rx_rds_enabled && + (!dev->radio_rds_loop || (dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) && + dev->radio_rx_freq >= FM_FREQ_RANGE_LOW && vt->signal >= 0xc000) + vt->rxsubchans |= V4L2_TUNER_SUB_RDS; + if (dev->radio_rx_rds_controls) + vivid_radio_rds_init(dev); + vt->audmode = dev->radio_rx_audmode; + return 0; +} + +int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (vt->index) + return -EINVAL; + dev->radio_rx_audmode = vt->audmode >= V4L2_TUNER_MODE_STEREO; + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/platform/vivid/vivid-radio-rx.h new file mode 100644 index 0000000..1077d8f --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-rx.h @@ -0,0 +1,31 @@ +/* + * vivid-radio-rx.h - radio receiver support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_RADIO_RX_H_ +#define _VIVID_RADIO_RX_H_ + +ssize_t vivid_radio_rx_read(struct file *, char __user *, size_t, loff_t *); +unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait); + +int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band); +int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a); +int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt); +int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt); + +#endif diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c new file mode 100644 index 0000000..8c59d4f --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-tx.c @@ -0,0 +1,141 @@ +/* + * vivid-radio-tx.c - radio transmitter support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-radio-common.h" +#include "vivid-radio-tx.h" + +ssize_t vivid_radio_tx_write(struct file *file, const char __user *buf, + size_t size, loff_t *offset) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_rds_data *data = dev->rds_gen.data; + struct timespec ts; + unsigned blk; + int i; + + if (dev->radio_tx_rds_controls) + return -EINVAL; + + if (size < sizeof(*data)) + return -EINVAL; + size = sizeof(*data) * (size / sizeof(*data)); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + if (dev->radio_tx_rds_owner && + file->private_data != dev->radio_tx_rds_owner) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + dev->radio_tx_rds_owner = file->private_data; + +retry: + ktime_get_ts(&ts); + ts = timespec_sub(ts, dev->radio_rds_init_ts); + blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000; + blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500; + if (blk - VIVID_RDS_GEN_BLOCKS >= dev->radio_tx_rds_last_block) + dev->radio_tx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1; + + /* + * No data is available if there hasn't been time to get new data, + * or if the RDS receiver has been disabled, or if we use the data + * from the RDS transmitter and that RDS transmitter has been disabled, + * or if the signal quality is too weak. + */ + if (blk == dev->radio_tx_rds_last_block || + !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) { + mutex_unlock(&dev->mutex); + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + if (msleep_interruptible(20) && signal_pending(current)) + return -EINTR; + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + goto retry; + } + + for (i = 0; i < size && blk > dev->radio_tx_rds_last_block; + dev->radio_tx_rds_last_block++) { + unsigned data_blk = dev->radio_tx_rds_last_block % VIVID_RDS_GEN_BLOCKS; + struct v4l2_rds_data rds; + + if (copy_from_user(&rds, buf + i, sizeof(rds))) { + i = -EFAULT; + break; + } + i += sizeof(rds); + if (!dev->radio_rds_loop) + continue; + if ((rds.block & V4L2_RDS_BLOCK_MSK) == V4L2_RDS_BLOCK_INVALID || + (rds.block & V4L2_RDS_BLOCK_ERROR)) + continue; + rds.block &= V4L2_RDS_BLOCK_MSK; + data[data_blk] = rds; + } + mutex_unlock(&dev->mutex); + return i; +} + +unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait) +{ + return POLLOUT | POLLWRNORM | v4l2_ctrl_poll(file, wait); +} + +int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (a->index > 0) + return -EINVAL; + + strlcpy(a->name, "AM/FM/SW Transmitter", sizeof(a->name)); + a->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS | + (dev->radio_tx_rds_controls ? + V4L2_TUNER_CAP_RDS_CONTROLS : + V4L2_TUNER_CAP_RDS_BLOCK_IO); + a->rangelow = AM_FREQ_RANGE_LOW; + a->rangehigh = FM_FREQ_RANGE_HIGH; + a->txsubchans = dev->radio_tx_subchans; + return 0; +} + +int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (a->index) + return -EINVAL; + if (a->txsubchans & ~0x13) + return -EINVAL; + dev->radio_tx_subchans = a->txsubchans; + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h new file mode 100644 index 0000000..7f8ff75 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-tx.h @@ -0,0 +1,29 @@ +/* + * vivid-radio-tx.h - radio transmitter support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_RADIO_TX_H_ +#define _VIVID_RADIO_TX_H_ + +ssize_t vivid_radio_tx_write(struct file *, const char __user *, size_t, loff_t *); +unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait); + +int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a); +int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a); + +#endif diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c new file mode 100644 index 0000000..c382343 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-rds-gen.c @@ -0,0 +1,166 @@ +/* + * vivid-rds-gen.c - rds (radio data system) generator support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/string.h> +#include <linux/videodev2.h> + +#include "vivid-rds-gen.h" + +static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp) +{ + switch (grp) { + case 0: + return (rds->dyn_pty << 2) | (grp & 3); + case 1: + return (rds->compressed << 2) | (grp & 3); + case 2: + return (rds->art_head << 2) | (grp & 3); + case 3: + return (rds->mono_stereo << 2) | (grp & 3); + } + return 0; +} + +/* + * This RDS generator creates 57 RDS groups (one group == four RDS blocks). + * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a + * standard 0B group containing the PI code and PS name. + * + * Groups 4-19 and 26-41 use group 2A for the radio text. + * + * Group 56 contains the time (group 4A). + * + * All remaining groups use a filler group 15B block that just repeats + * the PI and PTY codes. + */ +void vivid_rds_generate(struct vivid_rds_gen *rds) +{ + struct v4l2_rds_data *data = rds->data; + unsigned grp; + struct tm tm; + unsigned date; + unsigned time; + int l; + + for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) { + data[0].lsb = rds->picode & 0xff; + data[0].msb = rds->picode >> 8; + data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3); + data[1].lsb = rds->pty << 5; + data[1].msb = (rds->pty >> 3) | (rds->tp << 2); + data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3); + data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3); + + switch (grp) { + case 0 ... 3: + case 22 ... 25: + case 44 ... 47: /* Group 0B */ + data[1].lsb |= (rds->ta << 4) | (rds->ms << 3); + data[1].lsb |= vivid_get_di(rds, grp % 22); + data[1].msb |= 1 << 3; + data[2].lsb = rds->picode & 0xff; + data[2].msb = rds->picode >> 8; + data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3); + data[3].lsb = rds->psname[2 * (grp % 22) + 1]; + data[3].msb = rds->psname[2 * (grp % 22)]; + break; + case 4 ... 19: + case 26 ... 41: /* Group 2A */ + data[1].lsb |= (grp - 4) % 22; + data[1].msb |= 4 << 3; + data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)]; + data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1]; + data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3); + data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2]; + data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 3]; + break; + case 56: + /* + * Group 4A + * + * Uses the algorithm from Annex G of the RDS standard + * EN 50067:1998 to convert a UTC date to an RDS Modified + * Julian Day. + */ + time_to_tm(get_seconds(), 0, &tm); + l = tm.tm_mon <= 1; + date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 + + ((tm.tm_mon + 2 + l * 12) * 306001) / 10000; + time = (tm.tm_hour << 12) | + (tm.tm_min << 6) | + (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) | + (abs(sys_tz.tz_minuteswest) / 30); + data[1].lsb &= ~3; + data[1].lsb |= date >> 15; + data[1].msb |= 8 << 3; + data[2].lsb = (date << 1) & 0xfe; + data[2].lsb |= (time >> 16) & 1; + data[2].msb = (date >> 7) & 0xff; + data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3); + data[3].lsb = time & 0xff; + data[3].msb = (time >> 8) & 0xff; + break; + default: /* Group 15B */ + data[1].lsb |= (rds->ta << 4) | (rds->ms << 3); + data[1].lsb |= vivid_get_di(rds, grp % 22); + data[1].msb |= 0x1f << 3; + data[2].lsb = rds->picode & 0xff; + data[2].msb = rds->picode >> 8; + data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3); + data[3].lsb = rds->pty << 5; + data[3].lsb |= (rds->ta << 4) | (rds->ms << 3); + data[3].lsb |= vivid_get_di(rds, grp % 22); + data[3].msb |= rds->pty >> 3; + data[3].msb |= 0x1f << 3; + break; + } + } +} + +void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq, + bool alt) +{ + /* Alternate PTY between Info and Weather */ + if (rds->use_rbds) { + rds->picode = 0x2e75; /* 'KLNX' call sign */ + rds->pty = alt ? 29 : 2; + } else { + rds->picode = 0x8088; + rds->pty = alt ? 16 : 3; + } + rds->mono_stereo = true; + rds->art_head = false; + rds->compressed = false; + rds->dyn_pty = false; + rds->tp = true; + rds->ta = alt; + rds->ms = true; + snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d", + freq / 16, ((freq & 0xf) * 10) / 16); + if (alt) + strlcpy(rds->radiotext, + " The Radio Data System can switch between different Radio Texts ", + sizeof(rds->radiotext)); + else + strlcpy(rds->radiotext, + "An example of Radio Text as transmitted by the Radio Data System", + sizeof(rds->radiotext)); +} diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/platform/vivid/vivid-rds-gen.h new file mode 100644 index 0000000..eff4bf5 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-rds-gen.h @@ -0,0 +1,53 @@ +/* + * vivid-rds-gen.h - rds (radio data system) generator support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_RDS_GEN_H_ +#define _VIVID_RDS_GEN_H_ + +/* + * It takes almost exactly 5 seconds to transmit 57 RDS groups. + * Each group has 4 blocks and each block has a payload of 16 bits + a + * block identification. The driver will generate the contents of these + * 57 groups only when necessary and it will just be played continuously. + */ +#define VIVID_RDS_GEN_GROUPS 57 +#define VIVID_RDS_GEN_BLKS_PER_GRP 4 +#define VIVID_RDS_GEN_BLOCKS (VIVID_RDS_GEN_BLKS_PER_GRP * VIVID_RDS_GEN_GROUPS) + +struct vivid_rds_gen { + struct v4l2_rds_data data[VIVID_RDS_GEN_BLOCKS]; + bool use_rbds; + u16 picode; + u8 pty; + bool mono_stereo; + bool art_head; + bool compressed; + bool dyn_pty; + bool ta; + bool tp; + bool ms; + char psname[8 + 1]; + char radiotext[64 + 1]; +}; + +void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq, + bool use_alternate); +void vivid_rds_generate(struct vivid_rds_gen *rds); + +#endif diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c new file mode 100644 index 0000000..8c5d661 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -0,0 +1,499 @@ +/* + * vivid-sdr-cap.c - software defined radio support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-sdr-cap.h" + +static const struct v4l2_frequency_band bands_adc[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 300000, + .rangehigh = 300000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 900001, + .rangehigh = 2800000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 2, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 3200000, + .rangehigh = 3200000, + }, +}; + +/* ADC band midpoints */ +#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) +#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) + +static const struct v4l2_frequency_band bands_fm[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 50000000, + .rangehigh = 2000000000, + }, +}; + +static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev) +{ + struct vivid_buffer *sdr_cap_buf = NULL; + + dprintk(dev, 1, "SDR Capture Thread Tick\n"); + + /* Drop a certain percentage of buffers. */ + if (dev->perc_dropped_buffers && + prandom_u32_max(100) < dev->perc_dropped_buffers) + return; + + spin_lock(&dev->slock); + if (!list_empty(&dev->sdr_cap_active)) { + sdr_cap_buf = list_entry(dev->sdr_cap_active.next, + struct vivid_buffer, list); + list_del(&sdr_cap_buf->list); + } + spin_unlock(&dev->slock); + + if (sdr_cap_buf) { + sdr_cap_buf->vb.v4l2_buf.sequence = dev->sdr_cap_seq_count; + vivid_sdr_cap_process(dev, sdr_cap_buf); + v4l2_get_timestamp(&sdr_cap_buf->vb.v4l2_buf.timestamp); + sdr_cap_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&sdr_cap_buf->vb, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev->dqbuf_error = false; + } +} + +static int vivid_thread_sdr_cap(void *data) +{ + struct vivid_dev *dev = data; + u64 samples_since_start; + u64 buffers_since_start; + u64 next_jiffies_since_start; + unsigned long jiffies_since_start; + unsigned long cur_jiffies; + unsigned wait_jiffies; + + dprintk(dev, 1, "SDR Capture Thread Start\n"); + + set_freezable(); + + /* Resets frame counters */ + dev->sdr_cap_seq_offset = 0; + if (dev->seq_wrap) + dev->sdr_cap_seq_offset = 0xffffff80U; + dev->jiffies_sdr_cap = jiffies; + dev->sdr_cap_seq_resync = false; + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + mutex_lock(&dev->mutex); + cur_jiffies = jiffies; + if (dev->sdr_cap_seq_resync) { + dev->jiffies_sdr_cap = cur_jiffies; + dev->sdr_cap_seq_offset = dev->sdr_cap_seq_count + 1; + dev->sdr_cap_seq_count = 0; + dev->sdr_cap_seq_resync = false; + } + /* Calculate the number of jiffies since we started streaming */ + jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap; + /* Get the number of buffers streamed since the start */ + buffers_since_start = (u64)jiffies_since_start * dev->sdr_adc_freq + + (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2; + do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF); + + /* + * After more than 0xf0000000 (rounded down to a multiple of + * 'jiffies-per-day' to ease jiffies_to_msecs calculation) + * jiffies have passed since we started streaming reset the + * counters and keep track of the sequence offset. + */ + if (jiffies_since_start > JIFFIES_RESYNC) { + dev->jiffies_sdr_cap = cur_jiffies; + dev->sdr_cap_seq_offset = buffers_since_start; + buffers_since_start = 0; + } + dev->sdr_cap_seq_count = buffers_since_start + dev->sdr_cap_seq_offset; + + vivid_thread_sdr_cap_tick(dev); + mutex_unlock(&dev->mutex); + + /* + * Calculate the number of samples streamed since we started, + * not including the current buffer. + */ + samples_since_start = buffers_since_start * SDR_CAP_SAMPLES_PER_BUF; + + /* And the number of jiffies since we started */ + jiffies_since_start = jiffies - dev->jiffies_sdr_cap; + + /* Increase by the number of samples in one buffer */ + samples_since_start += SDR_CAP_SAMPLES_PER_BUF; + /* + * Calculate when that next buffer is supposed to start + * in jiffies since we started streaming. + */ + next_jiffies_since_start = samples_since_start * HZ + + dev->sdr_adc_freq / 2; + do_div(next_jiffies_since_start, dev->sdr_adc_freq); + /* If it is in the past, then just schedule asap */ + if (next_jiffies_since_start < jiffies_since_start) + next_jiffies_since_start = jiffies_since_start; + + wait_jiffies = next_jiffies_since_start - jiffies_since_start; + schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + } + dprintk(dev, 1, "SDR Capture Thread End\n"); + return 0; +} + +static int sdr_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + /* 2 = max 16-bit sample returned */ + sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; + *nplanes = 1; + return 0; +} + +static int sdr_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned size = SDR_CAP_SAMPLES_PER_BUF * 2; + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void sdr_cap_buf_queue(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->sdr_cap_active); + spin_unlock(&dev->slock); +} + +static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err = 0; + + dprintk(dev, 1, "%s\n", __func__); + dev->sdr_cap_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else if (dev->kthread_sdr_cap == NULL) { + dev->kthread_sdr_cap = kthread_run(vivid_thread_sdr_cap, dev, + "%s-sdr-cap", dev->v4l2_dev.name); + + if (IS_ERR(dev->kthread_sdr_cap)) { + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + err = PTR_ERR(dev->kthread_sdr_cap); + dev->kthread_sdr_cap = NULL; + } + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void sdr_cap_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + if (dev->kthread_sdr_cap == NULL) + return; + + while (!list_empty(&dev->sdr_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->sdr_cap_active.next, struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + /* shutdown control thread */ + mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_sdr_cap); + dev->kthread_sdr_cap = NULL; + mutex_lock(&dev->mutex); +} + +const struct vb2_ops vivid_sdr_cap_qops = { + .queue_setup = sdr_cap_queue_setup, + .buf_prepare = sdr_cap_buf_prepare, + .buf_queue = sdr_cap_buf_queue, + .start_streaming = sdr_cap_start_streaming, + .stop_streaming = sdr_cap_stop_streaming, + .wait_prepare = vivid_unlock, + .wait_finish = vivid_lock, +}; + +int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) +{ + switch (band->tuner) { + case 0: + if (band->index >= ARRAY_SIZE(bands_adc)) + return -EINVAL; + *band = bands_adc[band->index]; + return 0; + case 1: + if (band->index >= ARRAY_SIZE(bands_fm)) + return -EINVAL; + *band = bands_fm[band->index]; + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + + switch (vf->tuner) { + case 0: + vf->frequency = dev->sdr_adc_freq; + vf->type = V4L2_TUNER_ADC; + return 0; + case 1: + vf->frequency = dev->sdr_fm_freq; + vf->type = V4L2_TUNER_RF; + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + unsigned freq = vf->frequency; + unsigned band; + + switch (vf->tuner) { + case 0: + if (vf->type != V4L2_TUNER_ADC) + return -EINVAL; + if (freq < BAND_ADC_0) + band = 0; + else if (freq < BAND_ADC_1) + band = 1; + else + band = 2; + + freq = clamp_t(unsigned, freq, + bands_adc[band].rangelow, + bands_adc[band].rangehigh); + + if (vb2_is_streaming(&dev->vb_sdr_cap_q) && + freq != dev->sdr_adc_freq) { + /* resync the thread's timings */ + dev->sdr_cap_seq_resync = true; + } + dev->sdr_adc_freq = freq; + return 0; + case 1: + if (vf->type != V4L2_TUNER_RF) + return -EINVAL; + dev->sdr_fm_freq = clamp_t(unsigned, freq, + bands_fm[0].rangelow, + bands_fm[0].rangehigh); + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + switch (vt->index) { + case 0: + strlcpy(vt->name, "ADC", sizeof(vt->name)); + vt->type = V4L2_TUNER_ADC; + vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + vt->rangelow = bands_adc[0].rangelow; + vt->rangehigh = bands_adc[2].rangehigh; + return 0; + case 1: + strlcpy(vt->name, "RF", sizeof(vt->name)); + vt->type = V4L2_TUNER_RF; + vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + vt->rangelow = bands_fm[0].rangelow; + vt->rangehigh = bands_fm[0].rangehigh; + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) +{ + if (vt->index > 1) + return -EINVAL; + return 0; +} + +int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_SDR_FMT_CU8; + strlcpy(f->description, "IQ U8", sizeof(f->description)); + return 0; +} + +int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + f->fmt.sdr.pixelformat = V4L2_SDR_FMT_CU8; + f->fmt.sdr.buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + return 0; +} + +#define FIXP_FRAC (1 << 15) +#define FIXP_PI ((int)(FIXP_FRAC * 3.141592653589)) + +/* cos() from cx88 driver: cx88-dsp.c */ +static s32 fixp_cos(unsigned int x) +{ + u32 t2, t4, t6, t8; + u16 period = x / FIXP_PI; + + if (period % 2) + return -fixp_cos(x - FIXP_PI); + x = x % FIXP_PI; + if (x > FIXP_PI/2) + return -fixp_cos(FIXP_PI/2 - (x % (FIXP_PI/2))); + /* Now x is between 0 and FIXP_PI/2. + * To calculate cos(x) we use it's Taylor polinom. */ + t2 = x*x/FIXP_FRAC/2; + t4 = t2*x/FIXP_FRAC*x/FIXP_FRAC/3/4; + t6 = t4*x/FIXP_FRAC*x/FIXP_FRAC/5/6; + t8 = t6*x/FIXP_FRAC*x/FIXP_FRAC/7/8; + return FIXP_FRAC-t2+t4-t6+t8; +} + +static inline s32 fixp_sin(unsigned int x) +{ + return -fixp_cos(x + (FIXP_PI / 2)); +} + +void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0); + unsigned long i; + unsigned long plane_size = vb2_plane_size(&buf->vb, 0); + int fixp_src_phase_step, fixp_i, fixp_q; + + /* + * TODO: Generated beep tone goes very crackly when sample rate is + * increased to ~1Msps or more. That is because of huge rounding error + * of phase angle caused by used cosine implementation. + */ + + /* calculate phase step */ + #define BEEP_FREQ 1000 /* 1kHz beep */ + fixp_src_phase_step = DIV_ROUND_CLOSEST(2 * FIXP_PI * BEEP_FREQ, + dev->sdr_adc_freq); + + for (i = 0; i < plane_size; i += 2) { + dev->sdr_fixp_mod_phase += fixp_cos(dev->sdr_fixp_src_phase); + dev->sdr_fixp_src_phase += fixp_src_phase_step; + + /* + * Transfer phases to [0 / 2xPI] in order to avoid variable + * overflow and make it suitable for cosine implementation + * used, which does not support negative angles. + */ + while (dev->sdr_fixp_mod_phase < (0 * FIXP_PI)) + dev->sdr_fixp_mod_phase += (2 * FIXP_PI); + while (dev->sdr_fixp_mod_phase > (2 * FIXP_PI)) + dev->sdr_fixp_mod_phase -= (2 * FIXP_PI); + + while (dev->sdr_fixp_src_phase > (2 * FIXP_PI)) + dev->sdr_fixp_src_phase -= (2 * FIXP_PI); + + fixp_i = fixp_cos(dev->sdr_fixp_mod_phase); + fixp_q = fixp_sin(dev->sdr_fixp_mod_phase); + + /* convert 'fixp float' to u8 */ + /* u8 = X * 127.5f + 127.5f; where X is float [-1.0 / +1.0] */ + fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275; + fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275; + *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); + *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + } +} diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h new file mode 100644 index 0000000..79c1890 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-cap.h @@ -0,0 +1,34 @@ +/* + * vivid-sdr-cap.h - software defined radio support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_SDR_CAP_H_ +#define _VIVID_SDR_CAP_H_ + +int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band); +int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); +int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt); +int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt); +int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f); +int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); +void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf); + +extern const struct vb2_ops vivid_sdr_cap_qops; + +#endif diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.c b/drivers/media/platform/vivid/vivid-tpg-colors.c new file mode 100644 index 0000000..2adddc0 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-tpg-colors.c @@ -0,0 +1,310 @@ +/* + * vivid-color.c - A table that converts colors to various colorspaces + * + * The test pattern generator uses the tpg_colors for its test patterns. + * For testing colorspaces the first 8 colors of that table need to be + * converted to their equivalent in the target colorspace. + * + * The tpg_csc_colors[] table is the result of that conversion and since + * it is precalculated the colorspace conversion is just a simple table + * lookup. + * + * This source also contains the code used to generate the tpg_csc_colors + * table. Run the following command to compile it: + * + * gcc vivid-colors.c -DCOMPILE_APP -o gen-colors -lm + * + * and run the utility. + * + * Note that the converted colors are in the range 0x000-0xff0 (so times 16) + * in order to preserve precision. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/videodev2.h> + +#include "vivid-tpg-colors.h" + +/* sRGB colors with range [0-255] */ +const struct color tpg_colors[TPG_COLOR_MAX] = { + /* + * Colors to test colorspace conversion: converting these colors + * to other colorspaces will never lead to out-of-gamut colors. + */ + { 191, 191, 191 }, /* TPG_COLOR_CSC_WHITE */ + { 191, 191, 50 }, /* TPG_COLOR_CSC_YELLOW */ + { 50, 191, 191 }, /* TPG_COLOR_CSC_CYAN */ + { 50, 191, 50 }, /* TPG_COLOR_CSC_GREEN */ + { 191, 50, 191 }, /* TPG_COLOR_CSC_MAGENTA */ + { 191, 50, 50 }, /* TPG_COLOR_CSC_RED */ + { 50, 50, 191 }, /* TPG_COLOR_CSC_BLUE */ + { 50, 50, 50 }, /* TPG_COLOR_CSC_BLACK */ + + /* 75% colors */ + { 191, 191, 0 }, /* TPG_COLOR_75_YELLOW */ + { 0, 191, 191 }, /* TPG_COLOR_75_CYAN */ + { 0, 191, 0 }, /* TPG_COLOR_75_GREEN */ + { 191, 0, 191 }, /* TPG_COLOR_75_MAGENTA */ + { 191, 0, 0 }, /* TPG_COLOR_75_RED */ + { 0, 0, 191 }, /* TPG_COLOR_75_BLUE */ + + /* 100% colors */ + { 255, 255, 255 }, /* TPG_COLOR_100_WHITE */ + { 255, 255, 0 }, /* TPG_COLOR_100_YELLOW */ + { 0, 255, 255 }, /* TPG_COLOR_100_CYAN */ + { 0, 255, 0 }, /* TPG_COLOR_100_GREEN */ + { 255, 0, 255 }, /* TPG_COLOR_100_MAGENTA */ + { 255, 0, 0 }, /* TPG_COLOR_100_RED */ + { 0, 0, 255 }, /* TPG_COLOR_100_BLUE */ + { 0, 0, 0 }, /* TPG_COLOR_100_BLACK */ + + { 0, 0, 0 }, /* TPG_COLOR_RANDOM placeholder */ +}; + +#ifndef COMPILE_APP + +/* Generated table */ +const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = { + [V4L2_COLORSPACE_SMPTE170M][0] = { 2953, 2939, 2939 }, + [V4L2_COLORSPACE_SMPTE170M][1] = { 2954, 2963, 585 }, + [V4L2_COLORSPACE_SMPTE170M][2] = { 84, 2967, 2937 }, + [V4L2_COLORSPACE_SMPTE170M][3] = { 93, 2990, 575 }, + [V4L2_COLORSPACE_SMPTE170M][4] = { 3030, 259, 2933 }, + [V4L2_COLORSPACE_SMPTE170M][5] = { 3031, 406, 557 }, + [V4L2_COLORSPACE_SMPTE170M][6] = { 544, 428, 2931 }, + [V4L2_COLORSPACE_SMPTE170M][7] = { 551, 547, 547 }, + [V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_SMPTE240M][1] = { 2926, 2926, 857 }, + [V4L2_COLORSPACE_SMPTE240M][2] = { 1594, 2901, 2901 }, + [V4L2_COLORSPACE_SMPTE240M][3] = { 1594, 2901, 774 }, + [V4L2_COLORSPACE_SMPTE240M][4] = { 2484, 618, 2858 }, + [V4L2_COLORSPACE_SMPTE240M][5] = { 2484, 618, 617 }, + [V4L2_COLORSPACE_SMPTE240M][6] = { 507, 507, 2832 }, + [V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 }, + [V4L2_COLORSPACE_REC709][2] = { 547, 2939, 2939 }, + [V4L2_COLORSPACE_REC709][3] = { 547, 2939, 547 }, + [V4L2_COLORSPACE_REC709][4] = { 2939, 547, 2939 }, + [V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 }, + [V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 }, + [V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2894, 2988, 2808 }, + [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2847, 3070, 843 }, + [V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 }, + [V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 }, + [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2477, 229, 2743 }, + [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2422, 672, 614 }, + [V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 }, + [V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 621 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 621 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2923 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2923 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 }, + [V4L2_COLORSPACE_SRGB][2] = { 800, 3056, 3056 }, + [V4L2_COLORSPACE_SRGB][3] = { 800, 3056, 800 }, + [V4L2_COLORSPACE_SRGB][4] = { 3056, 800, 3056 }, + [V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 }, + [V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 }, + [V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 }, +}; + +#else + +/* This code generates the table above */ + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +static const double rec709_to_ntsc1953[3][3] = { + { 0.6698, 0.2678, 0.0323 }, + { 0.0185, 1.0742, -0.0603 }, + { 0.0162, 0.0432, 0.8551 } +}; + +static const double rec709_to_ebu[3][3] = { + { 0.9578, 0.0422, 0 }, + { 0 , 1 , 0 }, + { 0 , 0.0118, 0.9882 } +}; + +static const double rec709_to_170m[3][3] = { + { 1.0654, -0.0554, -0.0010 }, + { -0.0196, 1.0364, -0.0167 }, + { 0.0016, 0.0044, 0.9940 } +}; + +static const double rec709_to_240m[3][3] = { + { 0.7151, 0.2849, 0 }, + { 0.0179, 0.9821, 0 }, + { 0.0177, 0.0472, 0.9350 } +}; + + +static void mult_matrix(double *r, double *g, double *b, const double m[3][3]) +{ + double ir, ig, ib; + + ir = m[0][0] * (*r) + m[0][1] * (*g) + m[0][2] * (*b); + ig = m[1][0] * (*r) + m[1][1] * (*g) + m[1][2] * (*b); + ib = m[2][0] * (*r) + m[2][1] * (*g) + m[2][2] * (*b); + *r = ir; + *g = ig; + *b = ib; +} + +static double transfer_srgb_to_rgb(double v) +{ + return (v <= 0.03928) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4); +} + +static double transfer_rgb_to_smpte240m(double v) +{ + return (v <= 0.0228) ? v * 4.0 : 1.1115 * pow(v, 0.45) - 0.1115; +} + +static double transfer_rgb_to_rec709(double v) +{ + return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099; +} + +static double transfer_srgb_to_rec709(double v) +{ + return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v)); +} + +static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b) +{ + /* Convert the primaries of Rec. 709 Linear RGB */ + switch (colorspace) { + case V4L2_COLORSPACE_SMPTE240M: + *r = transfer_srgb_to_rgb(*r); + *g = transfer_srgb_to_rgb(*g); + *b = transfer_srgb_to_rgb(*b); + mult_matrix(r, g, b, rec709_to_240m); + break; + case V4L2_COLORSPACE_SMPTE170M: + *r = transfer_srgb_to_rgb(*r); + *g = transfer_srgb_to_rgb(*g); + *b = transfer_srgb_to_rgb(*b); + mult_matrix(r, g, b, rec709_to_170m); + break; + case V4L2_COLORSPACE_470_SYSTEM_BG: + *r = transfer_srgb_to_rgb(*r); + *g = transfer_srgb_to_rgb(*g); + *b = transfer_srgb_to_rgb(*b); + mult_matrix(r, g, b, rec709_to_ebu); + break; + case V4L2_COLORSPACE_470_SYSTEM_M: + *r = transfer_srgb_to_rgb(*r); + *g = transfer_srgb_to_rgb(*g); + *b = transfer_srgb_to_rgb(*b); + mult_matrix(r, g, b, rec709_to_ntsc1953); + break; + case V4L2_COLORSPACE_SRGB: + case V4L2_COLORSPACE_REC709: + default: + break; + } + + *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r)); + *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g)); + *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b)); + + /* Encode to gamma corrected colorspace */ + switch (colorspace) { + case V4L2_COLORSPACE_SMPTE240M: + *r = transfer_rgb_to_smpte240m(*r); + *g = transfer_rgb_to_smpte240m(*g); + *b = transfer_rgb_to_smpte240m(*b); + break; + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + *r = transfer_rgb_to_rec709(*r); + *g = transfer_rgb_to_rec709(*g); + *b = transfer_rgb_to_rec709(*b); + break; + case V4L2_COLORSPACE_SRGB: + break; + case V4L2_COLORSPACE_REC709: + default: + *r = transfer_srgb_to_rec709(*r); + *g = transfer_srgb_to_rec709(*g); + *b = transfer_srgb_to_rec709(*b); + break; + } +} + +int main(int argc, char **argv) +{ + static const unsigned colorspaces[] = { + 0, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + V4L2_COLORSPACE_REC709, + 0, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + 0, + V4L2_COLORSPACE_SRGB, + }; + static const char * const colorspace_names[] = { + "", + "V4L2_COLORSPACE_SMPTE170M", + "V4L2_COLORSPACE_SMPTE240M", + "V4L2_COLORSPACE_REC709", + "", + "V4L2_COLORSPACE_470_SYSTEM_M", + "V4L2_COLORSPACE_470_SYSTEM_BG", + "", + "V4L2_COLORSPACE_SRGB", + }; + int i; + int c; + + printf("/* Generated table */\n"); + printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {\n"); + for (c = 0; c <= V4L2_COLORSPACE_SRGB; c++) { + for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) { + double r, g, b; + + if (colorspaces[c] == 0) + continue; + + r = tpg_colors[i].r / 255.0; + g = tpg_colors[i].g / 255.0; + b = tpg_colors[i].b / 255.0; + + csc(c, &r, &g, &b); + + printf("\t[%s][%d] = { %d, %d, %d },\n", colorspace_names[c], i, + (int)(r * 4080), (int)(g * 4080), (int)(b * 4080)); + } + } + printf("};\n\n"); + return 0; +} + +#endif diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.h b/drivers/media/platform/vivid/vivid-tpg-colors.h new file mode 100644 index 0000000..a2678fb --- /dev/null +++ b/drivers/media/platform/vivid/vivid-tpg-colors.h @@ -0,0 +1,64 @@ +/* + * vivid-color.h - Color definitions for the test pattern generator + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_COLORS_H_ +#define _VIVID_COLORS_H_ + +struct color { + unsigned char r, g, b; +}; + +struct color16 { + int r, g, b; +}; + +enum tpg_color { + TPG_COLOR_CSC_WHITE, + TPG_COLOR_CSC_YELLOW, + TPG_COLOR_CSC_CYAN, + TPG_COLOR_CSC_GREEN, + TPG_COLOR_CSC_MAGENTA, + TPG_COLOR_CSC_RED, + TPG_COLOR_CSC_BLUE, + TPG_COLOR_CSC_BLACK, + TPG_COLOR_75_YELLOW, + TPG_COLOR_75_CYAN, + TPG_COLOR_75_GREEN, + TPG_COLOR_75_MAGENTA, + TPG_COLOR_75_RED, + TPG_COLOR_75_BLUE, + TPG_COLOR_100_WHITE, + TPG_COLOR_100_YELLOW, + TPG_COLOR_100_CYAN, + TPG_COLOR_100_GREEN, + TPG_COLOR_100_MAGENTA, + TPG_COLOR_100_RED, + TPG_COLOR_100_BLUE, + TPG_COLOR_100_BLACK, + TPG_COLOR_TEXTFG, + TPG_COLOR_TEXTBG, + TPG_COLOR_RANDOM, + TPG_COLOR_RAMP, + TPG_COLOR_MAX = TPG_COLOR_RAMP + 256 +}; + +extern const struct color tpg_colors[TPG_COLOR_MAX]; +extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1]; + +#endif diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c new file mode 100644 index 0000000..0c6fa53 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-tpg.c @@ -0,0 +1,1439 @@ +/* + * vivid-tpg.c - Test Pattern Generator + * + * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the + * vivi.c source for the copyright information of those functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "vivid-tpg.h" + +/* Must remain in sync with enum tpg_pattern */ +const char * const tpg_pattern_strings[] = { + "75% Colorbar", + "100% Colorbar", + "CSC Colorbar", + "Horizontal 100% Colorbar", + "100% Color Squares", + "100% Black", + "100% White", + "100% Red", + "100% Green", + "100% Blue", + "16x16 Checkers", + "1x1 Checkers", + "Alternating Hor Lines", + "Alternating Vert Lines", + "One Pixel Wide Cross", + "Two Pixels Wide Cross", + "Ten Pixels Wide Cross", + "Gray Ramp", + "Noise", + NULL +}; + +/* Must remain in sync with enum tpg_aspect */ +const char * const tpg_aspect_strings[] = { + "Source Width x Height", + "4x3", + "14x9", + "16x9", + "16x9 Anamorphic", + NULL +}; + +/* + * Sine table: sin[0] = 127 * sin(-180 degrees) + * sin[128] = 127 * sin(0 degrees) + * sin[256] = 127 * sin(180 degrees) + */ +static const s8 sin[257] = { + 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48, + -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88, + -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117, + -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118, + -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91, + -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50, + -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2, + 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46, + 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87, + 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119, + 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93, + 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52, + 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4, + 0, +}; + +#define cos(idx) sin[((idx) + 64) % sizeof(sin)] + +/* Global font descriptor */ +static const u8 *font8x16; + +void tpg_set_font(const u8 *f) +{ + font8x16 = f; +} + +void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h) +{ + memset(tpg, 0, sizeof(*tpg)); + tpg->scaled_width = tpg->src_width = w; + tpg->src_height = tpg->buf_height = h; + tpg->crop.width = tpg->compose.width = w; + tpg->crop.height = tpg->compose.height = h; + tpg->recalc_colors = true; + tpg->recalc_square_border = true; + tpg->brightness = 128; + tpg->contrast = 128; + tpg->saturation = 128; + tpg->hue = 0; + tpg->mv_hor_mode = TPG_MOVE_NONE; + tpg->mv_vert_mode = TPG_MOVE_NONE; + tpg->field = V4L2_FIELD_NONE; + tpg_s_fourcc(tpg, V4L2_PIX_FMT_RGB24); + tpg->colorspace = V4L2_COLORSPACE_SRGB; + tpg->perc_fill = 100; +} + +int tpg_alloc(struct tpg_data *tpg, unsigned max_w) +{ + unsigned pat; + unsigned plane; + + tpg->max_line_width = max_w; + for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) { + for (plane = 0; plane < TPG_MAX_PLANES; plane++) { + unsigned pixelsz = plane ? 1 : 4; + + tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz); + if (!tpg->lines[pat][plane]) + return -ENOMEM; + } + } + for (plane = 0; plane < TPG_MAX_PLANES; plane++) { + unsigned pixelsz = plane ? 1 : 4; + + tpg->contrast_line[plane] = vzalloc(max_w * pixelsz); + if (!tpg->contrast_line[plane]) + return -ENOMEM; + tpg->black_line[plane] = vzalloc(max_w * pixelsz); + if (!tpg->black_line[plane]) + return -ENOMEM; + tpg->random_line[plane] = vzalloc(max_w * pixelsz); + if (!tpg->random_line[plane]) + return -ENOMEM; + } + return 0; +} + +void tpg_free(struct tpg_data *tpg) +{ + unsigned pat; + unsigned plane; + + for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) + for (plane = 0; plane < TPG_MAX_PLANES; plane++) { + vfree(tpg->lines[pat][plane]); + tpg->lines[pat][plane] = NULL; + } + for (plane = 0; plane < TPG_MAX_PLANES; plane++) { + vfree(tpg->contrast_line[plane]); + vfree(tpg->black_line[plane]); + vfree(tpg->random_line[plane]); + tpg->contrast_line[plane] = NULL; + tpg->black_line[plane] = NULL; + tpg->random_line[plane] = NULL; + } +} + +bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) +{ + tpg->fourcc = fourcc; + tpg->planes = 1; + tpg->recalc_colors = true; + switch (fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_XRGB555: + case V4L2_PIX_FMT_ARGB555: + case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_ABGR32: + tpg->is_yuv = false; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + tpg->planes = 2; + /* fall-through */ + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + tpg->is_yuv = true; + break; + default: + return false; + } + + switch (fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_XRGB555: + case V4L2_PIX_FMT_ARGB555: + case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + tpg->twopixelsize[0] = 2 * 2; + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + tpg->twopixelsize[0] = 2 * 3; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_ABGR32: + tpg->twopixelsize[0] = 2 * 4; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + tpg->twopixelsize[0] = 2; + tpg->twopixelsize[1] = 2; + break; + } + return true; +} + +void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, + const struct v4l2_rect *compose) +{ + tpg->crop = *crop; + tpg->compose = *compose; + tpg->scaled_width = (tpg->src_width * tpg->compose.width + + tpg->crop.width - 1) / tpg->crop.width; + tpg->scaled_width &= ~1; + if (tpg->scaled_width > tpg->max_line_width) + tpg->scaled_width = tpg->max_line_width; + if (tpg->scaled_width < 2) + tpg->scaled_width = 2; + tpg->recalc_lines = true; +} + +void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, + u32 field) +{ + unsigned p; + + tpg->src_width = width; + tpg->src_height = height; + tpg->field = field; + tpg->buf_height = height; + if (V4L2_FIELD_HAS_T_OR_B(field)) + tpg->buf_height /= 2; + tpg->scaled_width = width; + tpg->crop.top = tpg->crop.left = 0; + tpg->crop.width = width; + tpg->crop.height = height; + tpg->compose.top = tpg->compose.left = 0; + tpg->compose.width = width; + tpg->compose.height = tpg->buf_height; + for (p = 0; p < tpg->planes; p++) + tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2; + tpg->recalc_square_border = true; +} + +static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_BLACK: + return TPG_COLOR_100_WHITE; + case TPG_PAT_CSC_COLORBAR: + return TPG_COLOR_CSC_BLACK; + default: + return TPG_COLOR_100_BLACK; + } +} + +static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_75_COLORBAR: + case TPG_PAT_CSC_COLORBAR: + return TPG_COLOR_CSC_WHITE; + case TPG_PAT_BLACK: + return TPG_COLOR_100_BLACK; + default: + return TPG_COLOR_100_WHITE; + } +} + +static u16 color_to_y(struct tpg_data *tpg, int r, int g, int b) +{ + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return ((16829 * r + 33039 * g + 6416 * b + 16 * 32768) >> 16) + (16 << 4); + case V4L2_COLORSPACE_SMPTE240M: + return ((11932 * r + 39455 * g + 4897 * b + 16 * 32768) >> 16) + (16 << 4); + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + return ((11966 * r + 40254 * g + 4064 * b + 16 * 32768) >> 16) + (16 << 4); + } +} + +static u16 color_to_cb(struct tpg_data *tpg, int r, int g, int b) +{ + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return ((-9714 * r - 19070 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4); + case V4L2_COLORSPACE_SMPTE240M: + return ((-6684 * r - 22100 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4); + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + return ((-6596 * r - 22189 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4); + } +} + +static u16 color_to_cr(struct tpg_data *tpg, int r, int g, int b) +{ + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return ((28784 * r - 24103 * g - 4681 * b + 16 * 32768) >> 16) + (128 << 4); + case V4L2_COLORSPACE_SMPTE240M: + return ((28784 * r - 25606 * g - 3178 * b + 16 * 32768) >> 16) + (128 << 4); + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + return ((28784 * r - 26145 * g - 2639 * b + 16 * 32768) >> 16) + (128 << 4); + } +} + +static u16 ycbcr_to_r(struct tpg_data *tpg, int y, int cb, int cr) +{ + int r; + + y -= 16 << 4; + cb -= 128 << 4; + cr -= 128 << 4; + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + r = 4769 * y + 6537 * cr; + break; + case V4L2_COLORSPACE_SMPTE240M: + r = 4769 * y + 7376 * cr; + break; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + r = 4769 * y + 7343 * cr; + break; + } + return clamp(r >> 12, 0, 0xff0); +} + +static u16 ycbcr_to_g(struct tpg_data *tpg, int y, int cb, int cr) +{ + int g; + + y -= 16 << 4; + cb -= 128 << 4; + cr -= 128 << 4; + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + g = 4769 * y - 1605 * cb - 3330 * cr; + break; + case V4L2_COLORSPACE_SMPTE240M: + g = 4769 * y - 1055 * cb - 2341 * cr; + break; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + g = 4769 * y - 873 * cb - 2183 * cr; + break; + } + return clamp(g >> 12, 0, 0xff0); +} + +static u16 ycbcr_to_b(struct tpg_data *tpg, int y, int cb, int cr) +{ + int b; + + y -= 16 << 4; + cb -= 128 << 4; + cr -= 128 << 4; + switch (tpg->colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + b = 4769 * y + 7343 * cb; + break; + case V4L2_COLORSPACE_SMPTE240M: + b = 4769 * y + 8552 * cb; + break; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SRGB: + default: + b = 4769 * y + 8652 * cb; + break; + } + return clamp(b >> 12, 0, 0xff0); +} + +/* precalculate color bar values to speed up rendering */ +static void precalculate_color(struct tpg_data *tpg, int k) +{ + int col = k; + int r = tpg_colors[col].r; + int g = tpg_colors[col].g; + int b = tpg_colors[col].b; + + if (k == TPG_COLOR_TEXTBG) { + col = tpg_get_textbg_color(tpg); + + r = tpg_colors[col].r; + g = tpg_colors[col].g; + b = tpg_colors[col].b; + } else if (k == TPG_COLOR_TEXTFG) { + col = tpg_get_textfg_color(tpg); + + r = tpg_colors[col].r; + g = tpg_colors[col].g; + b = tpg_colors[col].b; + } else if (tpg->pattern == TPG_PAT_NOISE) { + r = g = b = prandom_u32_max(256); + } else if (k == TPG_COLOR_RANDOM) { + r = g = b = tpg->qual_offset + prandom_u32_max(196); + } else if (k >= TPG_COLOR_RAMP) { + r = g = b = k - TPG_COLOR_RAMP; + } + + if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) { + r = tpg_csc_colors[tpg->colorspace][col].r; + g = tpg_csc_colors[tpg->colorspace][col].g; + b = tpg_csc_colors[tpg->colorspace][col].b; + } else { + r <<= 4; + g <<= 4; + b <<= 4; + } + if (tpg->qual == TPG_QUAL_GRAY) + r = g = b = color_to_y(tpg, r, g, b); + + /* + * The assumption is that the RGB output is always full range, + * so only if the rgb_range overrides the 'real' rgb range do + * we need to convert the RGB values. + * + * Currently there is no way of signalling to userspace if you + * are actually giving it limited range RGB (or full range + * YUV for that matter). + * + * Remember that r, g and b are still in the 0 - 0xff0 range. + */ + if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED && + tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) { + /* + * Convert from full range (which is what r, g and b are) + * to limited range (which is the 'real' RGB range), which + * is then interpreted as full range. + */ + r = (r * 219) / 255 + (16 << 4); + g = (g * 219) / 255 + (16 << 4); + b = (b * 219) / 255 + (16 << 4); + } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED && + tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) { + /* + * Clamp r, g and b to the limited range and convert to full + * range since that's what we deliver. + */ + r = clamp(r, 16 << 4, 235 << 4); + g = clamp(g, 16 << 4, 235 << 4); + b = clamp(b, 16 << 4, 235 << 4); + r = (r - (16 << 4)) * 255 / 219; + g = (g - (16 << 4)) * 255 / 219; + b = (b - (16 << 4)) * 255 / 219; + } + + if (tpg->brightness != 128 || tpg->contrast != 128 || + tpg->saturation != 128 || tpg->hue) { + /* Implement these operations */ + + /* First convert to YCbCr */ + int y = color_to_y(tpg, r, g, b); /* Luma */ + int cb = color_to_cb(tpg, r, g, b); /* Cb */ + int cr = color_to_cr(tpg, r, g, b); /* Cr */ + int tmp_cb, tmp_cr; + + y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128; + y += (tpg->brightness << 4) - (128 << 4); + + cb -= 128 << 4; + cr -= 128 << 4; + tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127; + tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127; + + cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128); + cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128); + if (tpg->is_yuv) { + tpg->colors[k][0] = clamp(y >> 4, 1, 254); + tpg->colors[k][1] = clamp(cb >> 4, 1, 254); + tpg->colors[k][2] = clamp(cr >> 4, 1, 254); + return; + } + r = ycbcr_to_r(tpg, y, cb, cr); + g = ycbcr_to_g(tpg, y, cb, cr); + b = ycbcr_to_b(tpg, y, cb, cr); + } + + if (tpg->is_yuv) { + /* Convert to YCbCr */ + u16 y = color_to_y(tpg, r, g, b); /* Luma */ + u16 cb = color_to_cb(tpg, r, g, b); /* Cb */ + u16 cr = color_to_cr(tpg, r, g, b); /* Cr */ + + tpg->colors[k][0] = clamp(y >> 4, 1, 254); + tpg->colors[k][1] = clamp(cb >> 4, 1, 254); + tpg->colors[k][2] = clamp(cr >> 4, 1, 254); + } else { + switch (tpg->fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + r >>= 7; + g >>= 6; + b >>= 7; + break; + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_XRGB555: + case V4L2_PIX_FMT_ARGB555: + case V4L2_PIX_FMT_RGB555X: + r >>= 7; + g >>= 7; + b >>= 7; + break; + default: + r >>= 4; + g >>= 4; + b >>= 4; + break; + } + + tpg->colors[k][0] = r; + tpg->colors[k][1] = g; + tpg->colors[k][2] = b; + } +} + +static void tpg_precalculate_colors(struct tpg_data *tpg) +{ + int k; + + for (k = 0; k < TPG_COLOR_MAX; k++) + precalculate_color(tpg, k); +} + +/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ +static void gen_twopix(struct tpg_data *tpg, + u8 buf[TPG_MAX_PLANES][8], int color, bool odd) +{ + unsigned offset = odd * tpg->twopixelsize[0] / 2; + u8 alpha = tpg->alpha_component; + u8 r_y, g_u, b_v; + + if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED && + color != TPG_COLOR_100_RED && + color != TPG_COLOR_75_RED) + alpha = 0; + if (color == TPG_COLOR_RANDOM) + precalculate_color(tpg, color); + r_y = tpg->colors[color][0]; /* R or precalculated Y */ + g_u = tpg->colors[color][1]; /* G or precalculated U */ + b_v = tpg->colors[color][2]; /* B or precalculated V */ + + switch (tpg->fourcc) { + case V4L2_PIX_FMT_NV16M: + buf[0][offset] = r_y; + buf[1][offset] = odd ? b_v : g_u; + break; + case V4L2_PIX_FMT_NV61M: + buf[0][offset] = r_y; + buf[1][offset] = odd ? g_u : b_v; + break; + + case V4L2_PIX_FMT_YUYV: + buf[0][offset] = r_y; + buf[0][offset + 1] = odd ? b_v : g_u; + break; + case V4L2_PIX_FMT_UYVY: + buf[0][offset] = odd ? b_v : g_u; + buf[0][offset + 1] = r_y; + break; + case V4L2_PIX_FMT_YVYU: + buf[0][offset] = r_y; + buf[0][offset + 1] = odd ? g_u : b_v; + break; + case V4L2_PIX_FMT_VYUY: + buf[0][offset] = odd ? g_u : b_v; + buf[0][offset + 1] = r_y; + break; + case V4L2_PIX_FMT_RGB565: + buf[0][offset] = (g_u << 5) | b_v; + buf[0][offset + 1] = (r_y << 3) | (g_u >> 3); + break; + case V4L2_PIX_FMT_RGB565X: + buf[0][offset] = (r_y << 3) | (g_u >> 3); + buf[0][offset + 1] = (g_u << 5) | b_v; + break; + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_XRGB555: + alpha = 0; + /* fall through */ + case V4L2_PIX_FMT_ARGB555: + buf[0][offset] = (g_u << 5) | b_v; + buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); + break; + case V4L2_PIX_FMT_RGB555X: + buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); + buf[0][offset + 1] = (g_u << 5) | b_v; + break; + case V4L2_PIX_FMT_RGB24: + buf[0][offset] = r_y; + buf[0][offset + 1] = g_u; + buf[0][offset + 2] = b_v; + break; + case V4L2_PIX_FMT_BGR24: + buf[0][offset] = b_v; + buf[0][offset + 1] = g_u; + buf[0][offset + 2] = r_y; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_XRGB32: + alpha = 0; + /* fall through */ + case V4L2_PIX_FMT_ARGB32: + buf[0][offset] = alpha; + buf[0][offset + 1] = r_y; + buf[0][offset + 2] = g_u; + buf[0][offset + 3] = b_v; + break; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_XBGR32: + alpha = 0; + /* fall through */ + case V4L2_PIX_FMT_ABGR32: + buf[0][offset] = b_v; + buf[0][offset + 1] = g_u; + buf[0][offset + 2] = r_y; + buf[0][offset + 3] = alpha; + break; + } +} + +/* Return how many pattern lines are used by the current pattern. */ +static unsigned tpg_get_pat_lines(struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_CHECKERS_16X16: + case TPG_PAT_CHECKERS_1X1: + case TPG_PAT_ALTERNATING_HLINES: + case TPG_PAT_CROSS_1_PIXEL: + case TPG_PAT_CROSS_2_PIXELS: + case TPG_PAT_CROSS_10_PIXELS: + return 2; + case TPG_PAT_100_COLORSQUARES: + case TPG_PAT_100_HCOLORBAR: + return 8; + default: + return 1; + } +} + +/* Which pattern line should be used for the given frame line. */ +static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line) +{ + switch (tpg->pattern) { + case TPG_PAT_CHECKERS_16X16: + return (line >> 4) & 1; + case TPG_PAT_CHECKERS_1X1: + case TPG_PAT_ALTERNATING_HLINES: + return line & 1; + case TPG_PAT_100_COLORSQUARES: + case TPG_PAT_100_HCOLORBAR: + return (line * 8) / tpg->src_height; + case TPG_PAT_CROSS_1_PIXEL: + return line == tpg->src_height / 2; + case TPG_PAT_CROSS_2_PIXELS: + return (line + 1) / 2 == tpg->src_height / 4; + case TPG_PAT_CROSS_10_PIXELS: + return (line + 10) / 20 == tpg->src_height / 40; + default: + return 0; + } +} + +/* + * Which color should be used for the given pattern line and X coordinate. + * Note: x is in the range 0 to 2 * tpg->src_width. + */ +static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x) +{ + /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code + should be modified */ + static const enum tpg_color bars[3][8] = { + /* Standard ITU-R 75% color bar sequence */ + { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW, + TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN, + TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED, + TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, }, + /* Standard ITU-R 100% color bar sequence */ + { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW, + TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN, + TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED, + TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, }, + /* Color bar sequence suitable to test CSC */ + { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW, + TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN, + TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED, + TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, }, + }; + + switch (tpg->pattern) { + case TPG_PAT_75_COLORBAR: + case TPG_PAT_100_COLORBAR: + case TPG_PAT_CSC_COLORBAR: + return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8]; + case TPG_PAT_100_COLORSQUARES: + return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8]; + case TPG_PAT_100_HCOLORBAR: + return bars[1][pat_line]; + case TPG_PAT_BLACK: + return TPG_COLOR_100_BLACK; + case TPG_PAT_WHITE: + return TPG_COLOR_100_WHITE; + case TPG_PAT_RED: + return TPG_COLOR_100_RED; + case TPG_PAT_GREEN: + return TPG_COLOR_100_GREEN; + case TPG_PAT_BLUE: + return TPG_COLOR_100_BLUE; + case TPG_PAT_CHECKERS_16X16: + return (((x >> 4) & 1) ^ (pat_line & 1)) ? + TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE; + case TPG_PAT_CHECKERS_1X1: + return ((x & 1) ^ (pat_line & 1)) ? + TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; + case TPG_PAT_ALTERNATING_HLINES: + return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; + case TPG_PAT_ALTERNATING_VLINES: + return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; + case TPG_PAT_CROSS_1_PIXEL: + if (pat_line || (x % tpg->src_width) == tpg->src_width / 2) + return TPG_COLOR_100_BLACK; + return TPG_COLOR_100_WHITE; + case TPG_PAT_CROSS_2_PIXELS: + if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4) + return TPG_COLOR_100_BLACK; + return TPG_COLOR_100_WHITE; + case TPG_PAT_CROSS_10_PIXELS: + if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40) + return TPG_COLOR_100_BLACK; + return TPG_COLOR_100_WHITE; + case TPG_PAT_GRAY_RAMP: + return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width; + default: + return TPG_COLOR_100_RED; + } +} + +/* + * Given the pixel aspect ratio and video aspect ratio calculate the + * coordinates of a centered square and the coordinates of the border of + * the active video area. The coordinates are relative to the source + * frame rectangle. + */ +static void tpg_calculate_square_border(struct tpg_data *tpg) +{ + unsigned w = tpg->src_width; + unsigned h = tpg->src_height; + unsigned sq_w, sq_h; + + sq_w = (w * 2 / 5) & ~1; + if (((w - sq_w) / 2) & 1) + sq_w += 2; + sq_h = sq_w; + tpg->square.width = sq_w; + if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) { + unsigned ana_sq_w = (sq_w / 4) * 3; + + if (((w - ana_sq_w) / 2) & 1) + ana_sq_w += 2; + tpg->square.width = ana_sq_w; + } + tpg->square.left = (w - tpg->square.width) / 2; + if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC) + sq_h = sq_w * 10 / 11; + else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL) + sq_h = sq_w * 59 / 54; + tpg->square.height = sq_h; + tpg->square.top = (h - sq_h) / 2; + tpg->border.left = 0; + tpg->border.width = w; + tpg->border.top = 0; + tpg->border.height = h; + switch (tpg->vid_aspect) { + case TPG_VIDEO_ASPECT_4X3: + if (tpg->pix_aspect) + return; + if (3 * w >= 4 * h) { + tpg->border.width = ((4 * h) / 3) & ~1; + if (((w - tpg->border.width) / 2) & ~1) + tpg->border.width -= 2; + tpg->border.left = (w - tpg->border.width) / 2; + break; + } + tpg->border.height = ((3 * w) / 4) & ~1; + tpg->border.top = (h - tpg->border.height) / 2; + break; + case TPG_VIDEO_ASPECT_14X9_CENTRE: + if (tpg->pix_aspect) { + tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506; + tpg->border.top = (h - tpg->border.height) / 2; + break; + } + if (9 * w >= 14 * h) { + tpg->border.width = ((14 * h) / 9) & ~1; + if (((w - tpg->border.width) / 2) & ~1) + tpg->border.width -= 2; + tpg->border.left = (w - tpg->border.width) / 2; + break; + } + tpg->border.height = ((9 * w) / 14) & ~1; + tpg->border.top = (h - tpg->border.height) / 2; + break; + case TPG_VIDEO_ASPECT_16X9_CENTRE: + if (tpg->pix_aspect) { + tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442; + tpg->border.top = (h - tpg->border.height) / 2; + break; + } + if (9 * w >= 16 * h) { + tpg->border.width = ((16 * h) / 9) & ~1; + if (((w - tpg->border.width) / 2) & ~1) + tpg->border.width -= 2; + tpg->border.left = (w - tpg->border.width) / 2; + break; + } + tpg->border.height = ((9 * w) / 16) & ~1; + tpg->border.top = (h - tpg->border.height) / 2; + break; + default: + break; + } +} + +static void tpg_precalculate_line(struct tpg_data *tpg) +{ + enum tpg_color contrast; + unsigned pat; + unsigned p; + unsigned x; + + switch (tpg->pattern) { + case TPG_PAT_GREEN: + contrast = TPG_COLOR_100_RED; + break; + case TPG_PAT_CSC_COLORBAR: + contrast = TPG_COLOR_CSC_GREEN; + break; + default: + contrast = TPG_COLOR_100_GREEN; + break; + } + + for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) { + /* Coarse scaling with Bresenham */ + unsigned int_part = tpg->src_width / tpg->scaled_width; + unsigned fract_part = tpg->src_width % tpg->scaled_width; + unsigned src_x = 0; + unsigned error = 0; + + for (x = 0; x < tpg->scaled_width * 2; x += 2) { + unsigned real_x = src_x; + enum tpg_color color1, color2; + u8 pix[TPG_MAX_PLANES][8]; + + real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x; + color1 = tpg_get_color(tpg, pat, real_x); + + src_x += int_part; + error += fract_part; + if (error >= tpg->scaled_width) { + error -= tpg->scaled_width; + src_x++; + } + + real_x = src_x; + real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x; + color2 = tpg_get_color(tpg, pat, real_x); + + src_x += int_part; + error += fract_part; + if (error >= tpg->scaled_width) { + error -= tpg->scaled_width; + src_x++; + } + + gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0); + gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2; + + memcpy(pos, pix[p], twopixsize); + } + } + } + for (x = 0; x < tpg->scaled_width; x += 2) { + u8 pix[TPG_MAX_PLANES][8]; + + gen_twopix(tpg, pix, contrast, 0); + gen_twopix(tpg, pix, contrast, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2; + + memcpy(pos, pix[p], twopixsize); + } + } + for (x = 0; x < tpg->scaled_width; x += 2) { + u8 pix[TPG_MAX_PLANES][8]; + + gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0); + gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->black_line[p] + x * twopixsize / 2; + + memcpy(pos, pix[p], twopixsize); + } + } + for (x = 0; x < tpg->scaled_width * 2; x += 2) { + u8 pix[TPG_MAX_PLANES][8]; + + gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0); + gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->random_line[p] + x * twopixsize / 2; + + memcpy(pos, pix[p], twopixsize); + } + } + gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0); + gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1); + gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0); + gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1); +} + +/* need this to do rgb24 rendering */ +typedef struct { u16 __; u8 _; } __packed x24; + +void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + int y, int x, char *text) +{ + int line; + unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; + unsigned div = step; + unsigned first = 0; + unsigned len = strlen(text); + unsigned p; + + if (font8x16 == NULL || basep == NULL) + return; + + /* Checks if it is possible to show string */ + if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) + return; + + if (len > (tpg->compose.width - x) / 8) + len = (tpg->compose.width - x) / 8; + if (tpg->vflip) + y = tpg->compose.height - y - 16; + if (tpg->hflip) + x = tpg->compose.width - x - 8; + y += tpg->compose.top; + x += tpg->compose.left; + if (tpg->field == V4L2_FIELD_BOTTOM) + first = 1; + else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT) + div = 2; + + for (p = 0; p < tpg->planes; p++) { + /* Print stream time */ +#define PRINTSTR(PIXTYPE) do { \ + PIXTYPE fg; \ + PIXTYPE bg; \ + memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \ + memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \ + \ + for (line = first; line < 16; line += step) { \ + int l = tpg->vflip ? 15 - line : line; \ + PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \ + ((y * step + l) / div) * tpg->bytesperline[p] + \ + x * sizeof(PIXTYPE)); \ + unsigned s; \ + \ + for (s = 0; s < len; s++) { \ + u8 chr = font8x16[text[s] * 16 + line]; \ + \ + if (tpg->hflip) { \ + pos[7] = (chr & (0x01 << 7) ? fg : bg); \ + pos[6] = (chr & (0x01 << 6) ? fg : bg); \ + pos[5] = (chr & (0x01 << 5) ? fg : bg); \ + pos[4] = (chr & (0x01 << 4) ? fg : bg); \ + pos[3] = (chr & (0x01 << 3) ? fg : bg); \ + pos[2] = (chr & (0x01 << 2) ? fg : bg); \ + pos[1] = (chr & (0x01 << 1) ? fg : bg); \ + pos[0] = (chr & (0x01 << 0) ? fg : bg); \ + } else { \ + 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 += tpg->hflip ? -8 : 8; \ + } \ + } \ +} while (0) + + switch (tpg->twopixelsize[p]) { + case 2: + PRINTSTR(u8); break; + case 4: + PRINTSTR(u16); break; + case 6: + PRINTSTR(x24); break; + case 8: + PRINTSTR(u32); break; + } + } +} + +void tpg_update_mv_step(struct tpg_data *tpg) +{ + int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; + + if (tpg->hflip) + factor = -factor; + switch (tpg->mv_hor_mode) { + case TPG_MOVE_NEG_FAST: + case TPG_MOVE_POS_FAST: + tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4; + break; + case TPG_MOVE_NEG: + case TPG_MOVE_POS: + tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4; + break; + case TPG_MOVE_NEG_SLOW: + case TPG_MOVE_POS_SLOW: + tpg->mv_hor_step = 2; + break; + case TPG_MOVE_NONE: + tpg->mv_hor_step = 0; + break; + } + if (factor < 0) + tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step; + + factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1; + switch (tpg->mv_vert_mode) { + case TPG_MOVE_NEG_FAST: + case TPG_MOVE_POS_FAST: + tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4; + break; + case TPG_MOVE_NEG: + case TPG_MOVE_POS: + tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4; + break; + case TPG_MOVE_NEG_SLOW: + case TPG_MOVE_POS_SLOW: + tpg->mv_vert_step = 1; + break; + case TPG_MOVE_NONE: + tpg->mv_vert_step = 0; + break; + } + if (factor < 0) + tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step; +} + +/* Map the line number relative to the crop rectangle to a frame line number */ +static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y, + unsigned field) +{ + switch (field) { + case V4L2_FIELD_TOP: + return tpg->crop.top + src_y * 2; + case V4L2_FIELD_BOTTOM: + return tpg->crop.top + src_y * 2 + 1; + default: + return src_y + tpg->crop.top; + } +} + +/* + * Map the line number relative to the compose rectangle to a destination + * buffer line number. + */ +static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y, + unsigned field) +{ + y += tpg->compose.top; + switch (field) { + case V4L2_FIELD_SEQ_TB: + if (y & 1) + return tpg->buf_height / 2 + y / 2; + return y / 2; + case V4L2_FIELD_SEQ_BT: + if (y & 1) + return y / 2; + return tpg->buf_height / 2 + y / 2; + default: + return y; + } +} + +static void tpg_recalc(struct tpg_data *tpg) +{ + if (tpg->recalc_colors) { + tpg->recalc_colors = false; + tpg->recalc_lines = true; + tpg_precalculate_colors(tpg); + } + if (tpg->recalc_square_border) { + tpg->recalc_square_border = false; + tpg_calculate_square_border(tpg); + } + if (tpg->recalc_lines) { + tpg->recalc_lines = false; + tpg_precalculate_line(tpg); + } +} + +void tpg_calc_text_basep(struct tpg_data *tpg, + u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf) +{ + unsigned stride = tpg->bytesperline[p]; + + tpg_recalc(tpg); + + basep[p][0] = vbuf; + basep[p][1] = vbuf; + if (tpg->field == V4L2_FIELD_SEQ_TB) + basep[p][1] += tpg->buf_height * stride / 2; + else if (tpg->field == V4L2_FIELD_SEQ_BT) + basep[p][0] += tpg->buf_height * stride / 2; +} + +void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) +{ + bool is_tv = std; + bool is_60hz = is_tv && (std & V4L2_STD_525_60); + unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width; + unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width; + unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height; + unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height; + unsigned wss_width; + unsigned f; + int hmax = (tpg->compose.height * tpg->perc_fill) / 100; + int h; + unsigned twopixsize = tpg->twopixelsize[p]; + unsigned img_width = tpg->compose.width * twopixsize / 2; + unsigned line_offset; + unsigned left_pillar_width = 0; + unsigned right_pillar_start = img_width; + unsigned stride = tpg->bytesperline[p]; + unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; + u8 *orig_vbuf = vbuf; + + /* Coarse scaling with Bresenham */ + unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height; + unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height; + unsigned src_y = 0; + unsigned error = 0; + + tpg_recalc(tpg); + + mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1; + mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1; + wss_width = tpg->crop.left < tpg->src_width / 2 ? + tpg->src_width / 2 - tpg->crop.left : 0; + if (wss_width > tpg->crop.width) + wss_width = tpg->crop.width; + wss_width = wss_width * tpg->scaled_width / tpg->src_width; + + vbuf += tpg->compose.left * twopixsize / 2; + line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width; + line_offset = (line_offset & ~1) * twopixsize / 2; + if (tpg->crop.left < tpg->border.left) { + left_pillar_width = tpg->border.left - tpg->crop.left; + if (left_pillar_width > tpg->crop.width) + left_pillar_width = tpg->crop.width; + left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width; + left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2; + } + if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) { + right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left; + right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width; + right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2; + if (right_pillar_start > img_width) + right_pillar_start = img_width; + } + + f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + + for (h = 0; h < tpg->compose.height; h++) { + bool even; + bool fill_blank = false; + unsigned frame_line; + unsigned buf_line; + unsigned pat_line_old; + unsigned pat_line_new; + u8 *linestart_older; + u8 *linestart_newer; + u8 *linestart_top; + u8 *linestart_bottom; + + frame_line = tpg_calc_frameline(tpg, src_y, tpg->field); + even = !(frame_line & 1); + buf_line = tpg_calc_buffer_line(tpg, h, tpg->field); + src_y += int_part; + error += fract_part; + if (error >= tpg->compose.height) { + error -= tpg->compose.height; + src_y++; + } + + if (h >= hmax) { + if (hmax == tpg->compose.height) + continue; + if (!tpg->perc_fill_blank) + continue; + fill_blank = true; + } + + if (tpg->vflip) + frame_line = tpg->src_height - frame_line - 1; + + if (fill_blank) { + linestart_older = tpg->contrast_line[p]; + linestart_newer = tpg->contrast_line[p]; + } else if (tpg->qual != TPG_QUAL_NOISE && + (frame_line < tpg->border.top || + frame_line >= tpg->border.top + tpg->border.height)) { + linestart_older = tpg->black_line[p]; + linestart_newer = tpg->black_line[p]; + } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) { + linestart_older = tpg->random_line[p] + + twopixsize * prandom_u32_max(tpg->src_width / 2); + linestart_newer = tpg->random_line[p] + + twopixsize * prandom_u32_max(tpg->src_width / 2); + } else { + pat_line_old = tpg_get_pat_line(tpg, + (frame_line + mv_vert_old) % tpg->src_height); + pat_line_new = tpg_get_pat_line(tpg, + (frame_line + mv_vert_new) % tpg->src_height); + linestart_older = tpg->lines[pat_line_old][p] + + mv_hor_old * twopixsize / 2; + linestart_newer = tpg->lines[pat_line_new][p] + + mv_hor_new * twopixsize / 2; + linestart_older += line_offset; + linestart_newer += line_offset; + } + if (is_60hz) { + linestart_top = linestart_newer; + linestart_bottom = linestart_older; + } else { + linestart_top = linestart_older; + linestart_bottom = linestart_newer; + } + + switch (tpg->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + if (even) + memcpy(vbuf + buf_line * stride, linestart_top, img_width); + else + memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); + break; + case V4L2_FIELD_INTERLACED_BT: + if (even) + memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); + else + memcpy(vbuf + buf_line * stride, linestart_top, img_width); + break; + case V4L2_FIELD_TOP: + memcpy(vbuf + buf_line * stride, linestart_top, img_width); + break; + case V4L2_FIELD_BOTTOM: + memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); + break; + case V4L2_FIELD_NONE: + default: + memcpy(vbuf + buf_line * stride, linestart_older, img_width); + break; + } + + if (is_tv && !is_60hz && frame_line == 0 && wss_width) { + /* + * Replace the first half of the top line of a 50 Hz frame + * with random data to simulate a WSS signal. + */ + u8 *wss = tpg->random_line[p] + + twopixsize * prandom_u32_max(tpg->src_width / 2); + + memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2); + } + } + + vbuf = orig_vbuf; + vbuf += tpg->compose.left * twopixsize / 2; + src_y = 0; + error = 0; + for (h = 0; h < tpg->compose.height; h++) { + unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field); + unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field); + const struct v4l2_rect *sq = &tpg->square; + const struct v4l2_rect *b = &tpg->border; + const struct v4l2_rect *c = &tpg->crop; + + src_y += int_part; + error += fract_part; + if (error >= tpg->compose.height) { + error -= tpg->compose.height; + src_y++; + } + + if (tpg->show_border && frame_line >= b->top && + frame_line < b->top + b->height) { + unsigned bottom = b->top + b->height - 1; + unsigned left = left_pillar_width; + unsigned right = right_pillar_start; + + if (frame_line == b->top || frame_line == b->top + 1 || + frame_line == bottom || frame_line == bottom - 1) { + memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], + right - left); + } else { + if (b->left >= c->left && + b->left < c->left + c->width) + memcpy(vbuf + buf_line * stride + left, + tpg->contrast_line[p], twopixsize); + if (b->left + b->width > c->left && + b->left + b->width <= c->left + c->width) + memcpy(vbuf + buf_line * stride + right - twopixsize, + tpg->contrast_line[p], twopixsize); + } + } + if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top && + frame_line < b->top + b->height) { + memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width); + memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p], + img_width - right_pillar_start); + } + if (tpg->show_square && frame_line >= sq->top && + frame_line < sq->top + sq->height && + sq->left < c->left + c->width && + sq->left + sq->width >= c->left) { + unsigned left = sq->left; + unsigned width = sq->width; + + if (c->left > left) { + width -= c->left - left; + left = c->left; + } + if (c->left + c->width < left + width) + width -= left + width - c->left - c->width; + left -= c->left; + left = (left * tpg->scaled_width) / tpg->src_width; + left = (left & ~1) * twopixsize / 2; + width = (width * tpg->scaled_width) / tpg->src_width; + width = (width & ~1) * twopixsize / 2; + memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width); + } + if (tpg->insert_sav) { + unsigned offset = (tpg->compose.width / 6) * twopixsize; + u8 *p = vbuf + buf_line * stride + offset; + unsigned vact = 0, hact = 0; + + p[0] = 0xff; + p[1] = 0; + p[2] = 0; + p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) | + ((hact ^ vact) << 3) | + ((hact ^ f) << 2) | + ((f ^ vact) << 1) | + (hact ^ vact ^ f); + } + if (tpg->insert_eav) { + unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize; + u8 *p = vbuf + buf_line * stride + offset; + unsigned vact = 0, hact = 1; + + p[0] = 0xff; + p[1] = 0; + p[2] = 0; + p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) | + ((hact ^ vact) << 3) | + ((hact ^ f) << 2) | + ((f ^ vact) << 1) | + (hact ^ vact ^ f); + } + } +} diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h new file mode 100644 index 0000000..8ef3e52 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-tpg.h @@ -0,0 +1,439 @@ +/* + * vivid-tpg.h - Test Pattern Generator + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_TPG_H_ +#define _VIVID_TPG_H_ + +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/videodev2.h> + +#include "vivid-tpg-colors.h" + +enum tpg_pattern { + TPG_PAT_75_COLORBAR, + TPG_PAT_100_COLORBAR, + TPG_PAT_CSC_COLORBAR, + TPG_PAT_100_HCOLORBAR, + TPG_PAT_100_COLORSQUARES, + TPG_PAT_BLACK, + TPG_PAT_WHITE, + TPG_PAT_RED, + TPG_PAT_GREEN, + TPG_PAT_BLUE, + TPG_PAT_CHECKERS_16X16, + TPG_PAT_CHECKERS_1X1, + TPG_PAT_ALTERNATING_HLINES, + TPG_PAT_ALTERNATING_VLINES, + TPG_PAT_CROSS_1_PIXEL, + TPG_PAT_CROSS_2_PIXELS, + TPG_PAT_CROSS_10_PIXELS, + TPG_PAT_GRAY_RAMP, + + /* Must be the last pattern */ + TPG_PAT_NOISE, +}; + +extern const char * const tpg_pattern_strings[]; + +enum tpg_quality { + TPG_QUAL_COLOR, + TPG_QUAL_GRAY, + TPG_QUAL_NOISE +}; + +enum tpg_video_aspect { + TPG_VIDEO_ASPECT_IMAGE, + TPG_VIDEO_ASPECT_4X3, + TPG_VIDEO_ASPECT_14X9_CENTRE, + TPG_VIDEO_ASPECT_16X9_CENTRE, + TPG_VIDEO_ASPECT_16X9_ANAMORPHIC, +}; + +enum tpg_pixel_aspect { + TPG_PIXEL_ASPECT_SQUARE, + TPG_PIXEL_ASPECT_NTSC, + TPG_PIXEL_ASPECT_PAL, +}; + +enum tpg_move_mode { + TPG_MOVE_NEG_FAST, + TPG_MOVE_NEG, + TPG_MOVE_NEG_SLOW, + TPG_MOVE_NONE, + TPG_MOVE_POS_SLOW, + TPG_MOVE_POS, + TPG_MOVE_POS_FAST, +}; + +extern const char * const tpg_aspect_strings[]; + +#define TPG_MAX_PLANES 2 +#define TPG_MAX_PAT_LINES 8 + +struct tpg_data { + /* Source frame size */ + unsigned src_width, src_height; + /* Buffer height */ + unsigned buf_height; + /* Scaled output frame size */ + unsigned scaled_width; + u32 field; + /* crop coordinates are frame-based */ + struct v4l2_rect crop; + /* compose coordinates are format-based */ + struct v4l2_rect compose; + /* border and square coordinates are frame-based */ + struct v4l2_rect border; + struct v4l2_rect square; + + /* Color-related fields */ + enum tpg_quality qual; + unsigned qual_offset; + u8 alpha_component; + bool alpha_red_only; + u8 brightness; + u8 contrast; + u8 saturation; + s16 hue; + u32 fourcc; + bool is_yuv; + u32 colorspace; + enum tpg_video_aspect vid_aspect; + enum tpg_pixel_aspect pix_aspect; + unsigned rgb_range; + unsigned real_rgb_range; + unsigned planes; + /* Used to store the colors in native format, either RGB or YUV */ + u8 colors[TPG_COLOR_MAX][3]; + u8 textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8]; + /* size in bytes for two pixels in each plane */ + unsigned twopixelsize[TPG_MAX_PLANES]; + unsigned bytesperline[TPG_MAX_PLANES]; + + /* Configuration */ + enum tpg_pattern pattern; + bool hflip; + bool vflip; + unsigned perc_fill; + bool perc_fill_blank; + bool show_border; + bool show_square; + bool insert_sav; + bool insert_eav; + + /* Test pattern movement */ + enum tpg_move_mode mv_hor_mode; + int mv_hor_count; + int mv_hor_step; + enum tpg_move_mode mv_vert_mode; + int mv_vert_count; + int mv_vert_step; + + bool recalc_colors; + bool recalc_lines; + bool recalc_square_border; + + /* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */ + unsigned max_line_width; + u8 *lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES]; + u8 *random_line[TPG_MAX_PLANES]; + u8 *contrast_line[TPG_MAX_PLANES]; + u8 *black_line[TPG_MAX_PLANES]; +}; + +void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h); +int tpg_alloc(struct tpg_data *tpg, unsigned max_w); +void tpg_free(struct tpg_data *tpg); +void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, + u32 field); + +void tpg_set_font(const u8 *f); +void tpg_gen_text(struct tpg_data *tpg, + u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text); +void tpg_calc_text_basep(struct tpg_data *tpg, + u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf); +void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf); +bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc); +void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, + const struct v4l2_rect *compose); + +static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern) +{ + if (tpg->pattern == pattern) + return; + tpg->pattern = pattern; + tpg->recalc_colors = true; +} + +static inline void tpg_s_quality(struct tpg_data *tpg, + enum tpg_quality qual, unsigned qual_offset) +{ + if (tpg->qual == qual && tpg->qual_offset == qual_offset) + return; + tpg->qual = qual; + tpg->qual_offset = qual_offset; + tpg->recalc_colors = true; +} + +static inline enum tpg_quality tpg_g_quality(const struct tpg_data *tpg) +{ + return tpg->qual; +} + +static inline void tpg_s_alpha_component(struct tpg_data *tpg, + u8 alpha_component) +{ + if (tpg->alpha_component == alpha_component) + return; + tpg->alpha_component = alpha_component; + tpg->recalc_colors = true; +} + +static inline void tpg_s_alpha_mode(struct tpg_data *tpg, + bool red_only) +{ + if (tpg->alpha_red_only == red_only) + return; + tpg->alpha_red_only = red_only; + tpg->recalc_colors = true; +} + +static inline void tpg_s_brightness(struct tpg_data *tpg, + u8 brightness) +{ + if (tpg->brightness == brightness) + return; + tpg->brightness = brightness; + tpg->recalc_colors = true; +} + +static inline void tpg_s_contrast(struct tpg_data *tpg, + u8 contrast) +{ + if (tpg->contrast == contrast) + return; + tpg->contrast = contrast; + tpg->recalc_colors = true; +} + +static inline void tpg_s_saturation(struct tpg_data *tpg, + u8 saturation) +{ + if (tpg->saturation == saturation) + return; + tpg->saturation = saturation; + tpg->recalc_colors = true; +} + +static inline void tpg_s_hue(struct tpg_data *tpg, + s16 hue) +{ + if (tpg->hue == hue) + return; + tpg->hue = hue; + tpg->recalc_colors = true; +} + +static inline void tpg_s_rgb_range(struct tpg_data *tpg, + unsigned rgb_range) +{ + if (tpg->rgb_range == rgb_range) + return; + tpg->rgb_range = rgb_range; + tpg->recalc_colors = true; +} + +static inline void tpg_s_real_rgb_range(struct tpg_data *tpg, + unsigned rgb_range) +{ + if (tpg->real_rgb_range == rgb_range) + return; + tpg->real_rgb_range = rgb_range; + tpg->recalc_colors = true; +} + +static inline void tpg_s_colorspace(struct tpg_data *tpg, u32 colorspace) +{ + if (tpg->colorspace == colorspace) + return; + tpg->colorspace = colorspace; + tpg->recalc_colors = true; +} + +static inline u32 tpg_g_colorspace(const struct tpg_data *tpg) +{ + return tpg->colorspace; +} + +static inline unsigned tpg_g_planes(const struct tpg_data *tpg) +{ + return tpg->planes; +} + +static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane) +{ + return tpg->twopixelsize[plane]; +} + +static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane) +{ + return tpg->bytesperline[plane]; +} + +static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl) +{ + tpg->bytesperline[plane] = bpl; +} + +static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h) +{ + tpg->buf_height = h; +} + +static inline void tpg_s_field(struct tpg_data *tpg, unsigned field) +{ + tpg->field = field; +} + +static inline void tpg_s_perc_fill(struct tpg_data *tpg, + unsigned perc_fill) +{ + tpg->perc_fill = perc_fill; +} + +static inline unsigned tpg_g_perc_fill(const struct tpg_data *tpg) +{ + return tpg->perc_fill; +} + +static inline void tpg_s_perc_fill_blank(struct tpg_data *tpg, + bool perc_fill_blank) +{ + tpg->perc_fill_blank = perc_fill_blank; +} + +static inline void tpg_s_video_aspect(struct tpg_data *tpg, + enum tpg_video_aspect vid_aspect) +{ + if (tpg->vid_aspect == vid_aspect) + return; + tpg->vid_aspect = vid_aspect; + tpg->recalc_square_border = true; +} + +static inline enum tpg_video_aspect tpg_g_video_aspect(const struct tpg_data *tpg) +{ + return tpg->vid_aspect; +} + +static inline void tpg_s_pixel_aspect(struct tpg_data *tpg, + enum tpg_pixel_aspect pix_aspect) +{ + if (tpg->pix_aspect == pix_aspect) + return; + tpg->pix_aspect = pix_aspect; + tpg->recalc_square_border = true; +} + +static inline void tpg_s_show_border(struct tpg_data *tpg, + bool show_border) +{ + tpg->show_border = show_border; +} + +static inline void tpg_s_show_square(struct tpg_data *tpg, + bool show_square) +{ + tpg->show_square = show_square; +} + +static inline void tpg_s_insert_sav(struct tpg_data *tpg, bool insert_sav) +{ + tpg->insert_sav = insert_sav; +} + +static inline void tpg_s_insert_eav(struct tpg_data *tpg, bool insert_eav) +{ + tpg->insert_eav = insert_eav; +} + +void tpg_update_mv_step(struct tpg_data *tpg); + +static inline void tpg_s_mv_hor_mode(struct tpg_data *tpg, + enum tpg_move_mode mv_hor_mode) +{ + tpg->mv_hor_mode = mv_hor_mode; + tpg_update_mv_step(tpg); +} + +static inline void tpg_s_mv_vert_mode(struct tpg_data *tpg, + enum tpg_move_mode mv_vert_mode) +{ + tpg->mv_vert_mode = mv_vert_mode; + tpg_update_mv_step(tpg); +} + +static inline void tpg_init_mv_count(struct tpg_data *tpg) +{ + tpg->mv_hor_count = tpg->mv_vert_count = 0; +} + +static inline void tpg_update_mv_count(struct tpg_data *tpg, bool frame_is_field) +{ + tpg->mv_hor_count += tpg->mv_hor_step * (frame_is_field ? 1 : 2); + tpg->mv_vert_count += tpg->mv_vert_step * (frame_is_field ? 1 : 2); +} + +static inline void tpg_s_hflip(struct tpg_data *tpg, bool hflip) +{ + if (tpg->hflip == hflip) + return; + tpg->hflip = hflip; + tpg_update_mv_step(tpg); + tpg->recalc_lines = true; +} + +static inline bool tpg_g_hflip(const struct tpg_data *tpg) +{ + return tpg->hflip; +} + +static inline void tpg_s_vflip(struct tpg_data *tpg, bool vflip) +{ + tpg->vflip = vflip; +} + +static inline bool tpg_g_vflip(const struct tpg_data *tpg) +{ + return tpg->vflip; +} + +static inline bool tpg_pattern_is_static(const struct tpg_data *tpg) +{ + return tpg->pattern != TPG_PAT_NOISE && + tpg->mv_hor_mode == TPG_MOVE_NONE && + tpg->mv_vert_mode == TPG_MOVE_NONE; +} + +#endif diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c new file mode 100644 index 0000000..2166d0b --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-cap.c @@ -0,0 +1,371 @@ +/* + * vivid-vbi-cap.c - vbi capture support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> + +#include "vivid-core.h" +#include "vivid-kthread-cap.h" +#include "vivid-vbi-cap.h" +#include "vivid-vbi-gen.h" + +static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) +{ + struct vivid_vbi_gen_data *vbi_gen = &dev->vbi_gen; + bool is_60hz = dev->std_cap & V4L2_STD_525_60; + + vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr); + + if (!is_60hz) { + if (dev->loop_video) { + if (dev->vbi_out_have_wss) { + vbi_gen->data[12].data[0] = dev->vbi_out_wss[0]; + vbi_gen->data[12].data[1] = dev->vbi_out_wss[1]; + } else { + vbi_gen->data[12].id = 0; + } + } else { + switch (tpg_g_video_aspect(&dev->tpg)) { + case TPG_VIDEO_ASPECT_14X9_CENTRE: + vbi_gen->data[12].data[0] = 0x01; + break; + case TPG_VIDEO_ASPECT_16X9_CENTRE: + vbi_gen->data[12].data[0] = 0x0b; + break; + case TPG_VIDEO_ASPECT_16X9_ANAMORPHIC: + vbi_gen->data[12].data[0] = 0x07; + break; + case TPG_VIDEO_ASPECT_4X3: + default: + vbi_gen->data[12].data[0] = 0x08; + break; + } + } + } else if (dev->loop_video && is_60hz) { + if (dev->vbi_out_have_cc[0]) { + vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0]; + vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1]; + } else { + vbi_gen->data[0].id = 0; + } + if (dev->vbi_out_have_cc[1]) { + vbi_gen->data[1].data[0] = dev->vbi_out_cc[1][0]; + vbi_gen->data[1].data[1] = dev->vbi_out_cc[1][1]; + } else { + vbi_gen->data[1].id = 0; + } + } +} + +static void vivid_g_fmt_vbi_cap(struct vivid_dev *dev, struct v4l2_vbi_format *vbi) +{ + bool is_60hz = dev->std_cap & V4L2_STD_525_60; + + vbi->sampling_rate = 27000000; + vbi->offset = 24; + vbi->samples_per_line = 1440; + vbi->sample_format = V4L2_PIX_FMT_GREY; + vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5; + vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5; + vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18; + vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0; + vbi->reserved[0] = 0; + vbi->reserved[1] = 0; +} + +void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + struct v4l2_vbi_format vbi; + u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0); + + vivid_g_fmt_vbi_cap(dev, &vbi); + buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count; + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + buf->vb.v4l2_buf.sequence /= 2; + + vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence); + + memset(vbuf, 0x10, vb2_plane_size(&buf->vb, 0)); + + if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) + vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf); + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; +} + + +void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + struct v4l2_sliced_vbi_data *vbuf = vb2_plane_vaddr(&buf->vb, 0); + + buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count; + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + buf->vb.v4l2_buf.sequence /= 2; + + vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence); + + memset(vbuf, 0, vb2_plane_size(&buf->vb, 0)); + if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) { + unsigned i; + + for (i = 0; i < 25; i++) + vbuf[i] = dev->vbi_gen.data[i]; + } + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; +} + +static int vbi_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + bool is_60hz = dev->std_cap & V4L2_STD_525_60; + unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ? + 36 * sizeof(struct v4l2_sliced_vbi_data) : + 1440 * 2 * (is_60hz ? 12 : 18); + + if (!vivid_is_sdtv_cap(dev)) + return -EINVAL; + + sizes[0] = size; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int vbi_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + bool is_60hz = dev->std_cap & V4L2_STD_525_60; + unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ? + 36 * sizeof(struct v4l2_sliced_vbi_data) : + 1440 * 2 * (is_60hz ? 12 : 18); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void vbi_cap_buf_queue(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->vbi_cap_active); + spin_unlock(&dev->slock); +} + +static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->vbi_cap_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_cap(dev, &dev->vbi_cap_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void vbi_cap_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_cap(dev, &dev->vbi_cap_streaming); +} + +const struct vb2_ops vivid_vbi_cap_qops = { + .queue_setup = vbi_cap_queue_setup, + .buf_prepare = vbi_cap_buf_prepare, + .buf_queue = vbi_cap_buf_queue, + .start_streaming = vbi_cap_start_streaming, + .stop_streaming = vbi_cap_stop_streaming, + .wait_prepare = vivid_unlock, + .wait_finish = vivid_lock, +}; + +int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_vbi_format *vbi = &f->fmt.vbi; + + if (!vivid_is_sdtv_cap(dev) || !dev->has_raw_vbi_cap) + return -EINVAL; + + vivid_g_fmt_vbi_cap(dev, vbi); + return 0; +} + +int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + int ret = vidioc_g_fmt_vbi_cap(file, priv, f); + + if (ret) + return ret; + if (dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q)) + return -EBUSY; + dev->stream_sliced_vbi_cap = false; + dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_VBI_CAPTURE; + return 0; +} + +void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set) +{ + vbi->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + vbi->service_set = service_set; + memset(vbi->service_lines, 0, sizeof(vbi->service_lines)); + memset(vbi->reserved, 0, sizeof(vbi->reserved)); + + if (vbi->service_set == 0) + return; + + if (vbi->service_set & V4L2_SLICED_CAPTION_525) { + vbi->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + vbi->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } + if (vbi->service_set & V4L2_SLICED_WSS_625) { + unsigned i; + + for (i = 7; i <= 18; i++) + vbi->service_lines[0][i] = + vbi->service_lines[1][i] = V4L2_SLICED_TELETEXT_B; + vbi->service_lines[0][23] = V4L2_SLICED_WSS_625; + } +} + +int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + + if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap) + return -EINVAL; + + vivid_fill_service_lines(vbi, dev->service_set_cap); + return 0; +} + +int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + bool is_60hz = dev->std_cap & V4L2_STD_525_60; + u32 service_set = vbi->service_set; + + if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap) + return -EINVAL; + + service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 : + V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; + vivid_fill_service_lines(vbi, service_set); + return 0; +} + +int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + int ret = vidioc_try_fmt_sliced_vbi_cap(file, fh, fmt); + + if (ret) + return ret; + if (!dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q)) + return -EBUSY; + dev->service_set_cap = vbi->service_set; + dev->stream_sliced_vbi_cap = true; + dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + return 0; +} + +int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + bool is_60hz; + + if (vdev->vfl_dir == VFL_DIR_RX) { + is_60hz = dev->std_cap & V4L2_STD_525_60; + if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap || + cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + } else { + is_60hz = dev->std_out & V4L2_STD_525_60; + if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out || + cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return -EINVAL; + } + + cap->service_set = is_60hz ? V4L2_SLICED_CAPTION_525 : + V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; + if (is_60hz) { + cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + unsigned i; + + for (i = 7; i <= 18; i++) + cap->service_lines[0][i] = + cap->service_lines[1][i] = V4L2_SLICED_TELETEXT_B; + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + } + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/platform/vivid/vivid-vbi-cap.h new file mode 100644 index 0000000..2d8ea0b --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-cap.h @@ -0,0 +1,40 @@ +/* + * vivid-vbi-cap.h - vbi capture support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VBI_CAP_H_ +#define _VIVID_VBI_CAP_H_ + +void vivid_fill_time_of_day_packet(u8 *packet); +void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf); +void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf); +void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); +int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap); + +void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set); + +extern const struct vb2_ops vivid_vbi_cap_qops; + +#endif diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/platform/vivid/vivid-vbi-gen.c new file mode 100644 index 0000000..a2159de --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-gen.c @@ -0,0 +1,323 @@ +/* + * vivid-vbi-gen.c - vbi generator support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/string.h> +#include <linux/videodev2.h> + +#include "vivid-vbi-gen.h" + +static void wss_insert(u8 *wss, u32 val, unsigned size) +{ + while (size--) + *wss++ = (val & (1 << size)) ? 0xc0 : 0x10; +} + +static void vivid_vbi_gen_wss_raw(const struct v4l2_sliced_vbi_data *data, + u8 *buf, unsigned sampling_rate) +{ + const unsigned rate = 5000000; /* WSS has a 5 MHz transmission rate */ + u8 wss[29 + 24 + 24 + 24 + 18 + 18] = { 0 }; + const unsigned zero = 0x07; + const unsigned one = 0x38; + unsigned bit = 0; + u16 wss_data; + int i; + + wss_insert(wss + bit, 0x1f1c71c7, 29); bit += 29; + wss_insert(wss + bit, 0x1e3c1f, 24); bit += 24; + + wss_data = (data->data[1] << 8) | data->data[0]; + for (i = 0; i <= 13; i++, bit += 6) + wss_insert(wss + bit, (wss_data & (1 << i)) ? one : zero, 6); + + for (i = 0, bit = 0; bit < sizeof(wss); bit++) { + unsigned n = ((bit + 1) * sampling_rate) / rate; + + while (i < n) + buf[i++] = wss[bit]; + } +} + +static void vivid_vbi_gen_teletext_raw(const struct v4l2_sliced_vbi_data *data, + u8 *buf, unsigned sampling_rate) +{ + const unsigned rate = 6937500 / 10; /* Teletext has a 6.9375 MHz transmission rate */ + u8 teletext[45] = { 0x55, 0x55, 0x27 }; + unsigned bit = 0; + int i; + + memcpy(teletext + 3, data->data, sizeof(teletext) - 3); + /* prevents 32 bit overflow */ + sampling_rate /= 10; + + for (i = 0, bit = 0; bit < sizeof(teletext) * 8; bit++) { + unsigned n = ((bit + 1) * sampling_rate) / rate; + u8 val = (teletext[bit / 8] & (1 << (bit & 7))) ? 0xc0 : 0x10; + + while (i < n) + buf[i++] = val; + } +} + +static void cc_insert(u8 *cc, u8 ch) +{ + unsigned tot = 0; + unsigned i; + + for (i = 0; i < 7; i++) { + cc[2 * i] = cc[2 * i + 1] = (ch & (1 << i)) ? 1 : 0; + tot += cc[2 * i]; + } + cc[14] = cc[15] = !(tot & 1); +} + +#define CC_PREAMBLE_BITS (14 + 4 + 2) + +static void vivid_vbi_gen_cc_raw(const struct v4l2_sliced_vbi_data *data, + u8 *buf, unsigned sampling_rate) +{ + const unsigned rate = 1000000; /* CC has a 1 MHz transmission rate */ + + u8 cc[CC_PREAMBLE_BITS + 2 * 16] = { + /* Clock run-in: 7 cycles */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + /* 2 cycles of 0 */ + 0, 0, 0, 0, + /* Start bit of 1 (each bit is two cycles) */ + 1, 1 + }; + unsigned bit, i; + + cc_insert(cc + CC_PREAMBLE_BITS, data->data[0]); + cc_insert(cc + CC_PREAMBLE_BITS + 16, data->data[1]); + + for (i = 0, bit = 0; bit < sizeof(cc); bit++) { + unsigned n = ((bit + 1) * sampling_rate) / rate; + + while (i < n) + buf[i++] = cc[bit] ? 0xc0 : 0x10; + } +} + +void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi, + const struct v4l2_vbi_format *vbi_fmt, u8 *buf) +{ + unsigned idx; + + for (idx = 0; idx < 25; idx++) { + const struct v4l2_sliced_vbi_data *data = vbi->data + idx; + unsigned start_2nd_field; + unsigned line = data->line; + u8 *linebuf = buf; + + start_2nd_field = (data->id & V4L2_SLICED_VBI_525) ? 263 : 313; + if (data->field) + line += start_2nd_field; + line -= vbi_fmt->start[data->field]; + + if (vbi_fmt->flags & V4L2_VBI_INTERLACED) + linebuf += (line * 2 + data->field) * + vbi_fmt->samples_per_line; + else + linebuf += (line + data->field * vbi_fmt->count[0]) * + vbi_fmt->samples_per_line; + if (data->id == V4L2_SLICED_CAPTION_525) + vivid_vbi_gen_cc_raw(data, linebuf, vbi_fmt->sampling_rate); + else if (data->id == V4L2_SLICED_WSS_625) + vivid_vbi_gen_wss_raw(data, linebuf, vbi_fmt->sampling_rate); + else if (data->id == V4L2_SLICED_TELETEXT_B) + vivid_vbi_gen_teletext_raw(data, linebuf, vbi_fmt->sampling_rate); + } +} + +static const u8 vivid_cc_sequence1[30] = { + 0x14, 0x20, /* Resume Caption Loading */ + 'H', 'e', + 'l', 'l', + 'o', ' ', + 'w', 'o', + 'r', 'l', + 'd', '!', + 0x14, 0x2f, /* End of Caption */ +}; + +static const u8 vivid_cc_sequence2[30] = { + 0x14, 0x20, /* Resume Caption Loading */ + 'C', 'l', + 'o', 's', + 'e', 'd', + ' ', 'c', + 'a', 'p', + 't', 'i', + 'o', 'n', + 's', ' ', + 't', 'e', + 's', 't', + 0x14, 0x2f, /* End of Caption */ +}; + +static u8 calc_parity(u8 val) +{ + unsigned i; + unsigned tot = 0; + + for (i = 0; i < 7; i++) + tot += (val & (1 << i)) ? 1 : 0; + return val | ((tot & 1) ? 0 : 0x80); +} + +static void vivid_vbi_gen_set_time_of_day(u8 *packet) +{ + struct tm tm; + u8 checksum, i; + + time_to_tm(get_seconds(), 0, &tm); + packet[0] = calc_parity(0x07); + packet[1] = calc_parity(0x01); + packet[2] = calc_parity(0x40 | tm.tm_min); + packet[3] = calc_parity(0x40 | tm.tm_hour); + packet[4] = calc_parity(0x40 | tm.tm_mday); + if (tm.tm_mday == 1 && tm.tm_mon == 2 && + sys_tz.tz_minuteswest > tm.tm_min + tm.tm_hour * 60) + packet[4] = calc_parity(0x60 | tm.tm_mday); + packet[5] = calc_parity(0x40 | (1 + tm.tm_mon)); + packet[6] = calc_parity(0x40 | (1 + tm.tm_wday)); + packet[7] = calc_parity(0x40 | ((tm.tm_year - 90) & 0x3f)); + packet[8] = calc_parity(0x0f); + for (checksum = i = 0; i <= 8; i++) + checksum += packet[i] & 0x7f; + packet[9] = calc_parity(0x100 - checksum); + checksum = 0; + packet[10] = calc_parity(0x07); + packet[11] = calc_parity(0x04); + if (sys_tz.tz_minuteswest >= 0) + packet[12] = calc_parity(0x40 | ((sys_tz.tz_minuteswest / 60) & 0x1f)); + else + packet[12] = calc_parity(0x40 | ((24 + sys_tz.tz_minuteswest / 60) & 0x1f)); + packet[13] = calc_parity(0); + packet[14] = calc_parity(0x0f); + for (checksum = 0, i = 10; i <= 14; i++) + checksum += packet[i] & 0x7f; + packet[15] = calc_parity(0x100 - checksum); +} + +static const u8 hamming[16] = { + 0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f, + 0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea +}; + +static void vivid_vbi_gen_teletext(u8 *packet, unsigned line, unsigned frame) +{ + unsigned offset = 2; + unsigned i; + + packet[0] = hamming[1 + ((line & 1) << 3)]; + packet[1] = hamming[line >> 1]; + memset(packet + 2, 0x20, 40); + if (line == 0) { + /* subcode */ + packet[2] = hamming[frame % 10]; + packet[3] = hamming[frame / 10]; + packet[4] = hamming[0]; + packet[5] = hamming[0]; + packet[6] = hamming[0]; + packet[7] = hamming[0]; + packet[8] = hamming[0]; + packet[9] = hamming[1]; + offset = 10; + } + packet += offset; + memcpy(packet, "Page: 100 Row: 10", 17); + packet[7] = '0' + frame / 10; + packet[8] = '0' + frame % 10; + packet[15] = '0' + line / 10; + packet[16] = '0' + line % 10; + for (i = 0; i < 42 - offset; i++) + packet[i] = calc_parity(packet[i]); +} + +void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi, + bool is_60hz, unsigned seqnr) +{ + struct v4l2_sliced_vbi_data *data0 = vbi->data; + struct v4l2_sliced_vbi_data *data1 = vbi->data + 1; + unsigned frame = seqnr % 60; + + memset(vbi->data, 0, sizeof(vbi->data)); + + if (!is_60hz) { + unsigned i; + + for (i = 0; i <= 11; i++) { + data0->id = V4L2_SLICED_TELETEXT_B; + data0->line = 7 + i; + vivid_vbi_gen_teletext(data0->data, i, frame); + data0++; + } + data0->id = V4L2_SLICED_WSS_625; + data0->line = 23; + /* 4x3 video aspect ratio */ + data0->data[0] = 0x08; + data0++; + for (i = 0; i <= 11; i++) { + data0->id = V4L2_SLICED_TELETEXT_B; + data0->field = 1; + data0->line = 7 + i; + vivid_vbi_gen_teletext(data0->data, 12 + i, frame); + data0++; + } + return; + } + + data0->id = V4L2_SLICED_CAPTION_525; + data0->line = 21; + data1->id = V4L2_SLICED_CAPTION_525; + data1->field = 1; + data1->line = 21; + + if (frame < 15) { + data0->data[0] = calc_parity(vivid_cc_sequence1[2 * frame]); + data0->data[1] = calc_parity(vivid_cc_sequence1[2 * frame + 1]); + } else if (frame >= 30 && frame < 45) { + frame -= 30; + data0->data[0] = calc_parity(vivid_cc_sequence2[2 * frame]); + data0->data[1] = calc_parity(vivid_cc_sequence2[2 * frame + 1]); + } else { + data0->data[0] = calc_parity(0); + data0->data[1] = calc_parity(0); + } + + frame = seqnr % (30 * 60); + switch (frame) { + case 0: + vivid_vbi_gen_set_time_of_day(vbi->time_of_day_packet); + /* fall through */ + case 1 ... 7: + data1->data[0] = vbi->time_of_day_packet[frame * 2]; + data1->data[1] = vbi->time_of_day_packet[frame * 2 + 1]; + break; + default: + data1->data[0] = calc_parity(0); + data1->data[1] = calc_parity(0); + break; + } +} diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/platform/vivid/vivid-vbi-gen.h new file mode 100644 index 0000000..8444abe --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-gen.h @@ -0,0 +1,33 @@ +/* + * vivid-vbi-gen.h - vbi generator support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VBI_GEN_H_ +#define _VIVID_VBI_GEN_H_ + +struct vivid_vbi_gen_data { + struct v4l2_sliced_vbi_data data[25]; + u8 time_of_day_packet[16]; +}; + +void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi, + bool is_60hz, unsigned seqnr); +void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi, + const struct v4l2_vbi_format *vbi_fmt, u8 *buf); + +#endif diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c new file mode 100644 index 0000000..9d00a07 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-out.c @@ -0,0 +1,248 @@ +/* + * vivid-vbi-out.c - vbi output support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> + +#include "vivid-core.h" +#include "vivid-kthread-out.h" +#include "vivid-vbi-out.h" +#include "vivid-vbi-cap.h" + +static int vbi_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + bool is_60hz = dev->std_out & V4L2_STD_525_60; + unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ? + 36 * sizeof(struct v4l2_sliced_vbi_data) : + 1440 * 2 * (is_60hz ? 12 : 18); + + if (!vivid_is_svid_out(dev)) + return -EINVAL; + + sizes[0] = size; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int vbi_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + bool is_60hz = dev->std_out & V4L2_STD_525_60; + unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ? + 36 * sizeof(struct v4l2_sliced_vbi_data) : + 1440 * 2 * (is_60hz ? 12 : 18); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void vbi_out_buf_queue(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->vbi_out_active); + spin_unlock(&dev->slock); +} + +static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->vbi_out_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_out(dev, &dev->vbi_out_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void vbi_out_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_out(dev, &dev->vbi_out_streaming); + dev->vbi_out_have_wss = false; + dev->vbi_out_have_cc[0] = false; + dev->vbi_out_have_cc[1] = false; +} + +const struct vb2_ops vivid_vbi_out_qops = { + .queue_setup = vbi_out_queue_setup, + .buf_prepare = vbi_out_buf_prepare, + .buf_queue = vbi_out_buf_queue, + .start_streaming = vbi_out_start_streaming, + .stop_streaming = vbi_out_stop_streaming, + .wait_prepare = vivid_unlock, + .wait_finish = vivid_lock, +}; + +int vidioc_g_fmt_vbi_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_vbi_format *vbi = &f->fmt.vbi; + bool is_60hz = dev->std_out & V4L2_STD_525_60; + + if (!vivid_is_svid_out(dev) || !dev->has_raw_vbi_out) + return -EINVAL; + + vbi->sampling_rate = 25000000; + vbi->offset = 24; + vbi->samples_per_line = 1440; + vbi->sample_format = V4L2_PIX_FMT_GREY; + vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5; + vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5; + vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18; + vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0; + vbi->reserved[0] = 0; + vbi->reserved[1] = 0; + return 0; +} + +int vidioc_s_fmt_vbi_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + int ret = vidioc_g_fmt_vbi_out(file, priv, f); + + if (ret) + return ret; + if (vb2_is_busy(&dev->vb_vbi_out_q)) + return -EBUSY; + dev->stream_sliced_vbi_out = false; + dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_VBI_OUTPUT; + return 0; +} + +int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + + if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out) + return -EINVAL; + + vivid_fill_service_lines(vbi, dev->service_set_out); + return 0; +} + +int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + bool is_60hz = dev->std_out & V4L2_STD_525_60; + u32 service_set = vbi->service_set; + + if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out) + return -EINVAL; + + service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 : + V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; + vivid_fill_service_lines(vbi, service_set); + return 0; +} + +int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; + int ret = vidioc_try_fmt_sliced_vbi_out(file, fh, fmt); + + if (ret) + return ret; + if (vb2_is_busy(&dev->vb_vbi_out_q)) + return -EBUSY; + dev->service_set_out = vbi->service_set; + dev->stream_sliced_vbi_out = true; + dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + return 0; +} + +void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + struct v4l2_sliced_vbi_data *vbi = vb2_plane_vaddr(&buf->vb, 0); + unsigned elems = vb2_get_plane_payload(&buf->vb, 0) / sizeof(*vbi); + + dev->vbi_out_have_cc[0] = false; + dev->vbi_out_have_cc[1] = false; + dev->vbi_out_have_wss = false; + while (elems--) { + switch (vbi->id) { + case V4L2_SLICED_CAPTION_525: + if ((dev->std_out & V4L2_STD_525_60) && vbi->line == 21) { + dev->vbi_out_have_cc[!!vbi->field] = true; + dev->vbi_out_cc[!!vbi->field][0] = vbi->data[0]; + dev->vbi_out_cc[!!vbi->field][1] = vbi->data[1]; + } + break; + case V4L2_SLICED_WSS_625: + if ((dev->std_out & V4L2_STD_625_50) && + vbi->field == 0 && vbi->line == 23) { + dev->vbi_out_have_wss = true; + dev->vbi_out_wss[0] = vbi->data[0]; + dev->vbi_out_wss[1] = vbi->data[1]; + } + break; + } + vbi++; + } +} diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/platform/vivid/vivid-vbi-out.h new file mode 100644 index 0000000..6555ba9 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vbi-out.h @@ -0,0 +1,34 @@ +/* + * vivid-vbi-out.h - vbi output support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VBI_OUT_H_ +#define _VIVID_VBI_OUT_H_ + +void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); +int vidioc_g_fmt_vbi_out(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_s_fmt_vbi_out(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt); +int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt); +int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt); + +extern const struct vb2_ops vivid_vbi_out_qops; + +#endif diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c new file mode 100644 index 0000000..331c544 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -0,0 +1,1730 @@ +/* + * vivid-vid-cap.c - video capture support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-kthread-cap.h" +#include "vivid-vid-cap.h" + +/* 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 = 1, .denominator = 30}; + +static const struct vivid_fmt formats_ovl[] = { + { + .name = "RGB565 (LE)", + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .depth = 16, + .planes = 1, + }, + { + .name = "XRGB555 (LE)", + .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ + .depth = 16, + .planes = 1, + }, + { + .name = "ARGB555 (LE)", + .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ + .depth = 16, + .planes = 1, + }, +}; + +/* The number of discrete webcam framesizes */ +#define VIVID_WEBCAM_SIZES 3 +/* The number of discrete webcam frameintervals */ +#define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2) + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = { + { 320, 180 }, + { 640, 360 }, + { 1280, 720 }, +}; + +/* + * Intervals must be in increasing order and there must be twice as many + * elements in this array as there are in webcam_sizes. + */ +static const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = { + { 1, 10 }, + { 1, 15 }, + { 1, 25 }, + { 1, 30 }, + { 1, 50 }, + { 1, 60 }, +}; + +static const struct v4l2_discrete_probe webcam_probe = { + webcam_sizes, + VIVID_WEBCAM_SIZES +}; + +static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned planes = tpg_g_planes(&dev->tpg); + unsigned h = dev->fmt_cap_rect.height; + unsigned p; + + if (dev->field_cap == V4L2_FIELD_ALTERNATE) { + /* + * You cannot use read() with FIELD_ALTERNATE since the field + * information (TOP/BOTTOM) cannot be passed back to the user. + */ + if (vb2_fileio_is_active(vq)) + return -EINVAL; + } + + if (dev->queue_setup_error) { + /* + * Error injection: test what happens if queue_setup() returns + * an error. + */ + dev->queue_setup_error = false; + return -EINVAL; + } + if (fmt) { + const struct v4l2_pix_format_mplane *mp; + struct v4l2_format mp_fmt; + const struct vivid_fmt *vfmt; + + if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { + fmt_sp2mp(fmt, &mp_fmt); + fmt = &mp_fmt; + } + mp = &fmt->fmt.pix_mp; + /* + * Check if the number of planes in the specified format match + * the number of planes in the current format. You can't mix that. + */ + if (mp->num_planes != planes) + return -EINVAL; + vfmt = vivid_get_format(dev, mp->pixelformat); + for (p = 0; p < planes; p++) { + sizes[p] = mp->plane_fmt[p].sizeimage; + if (sizes[0] < tpg_g_bytesperline(&dev->tpg, 0) * h + + vfmt->data_offset[p]) + return -EINVAL; + } + } else { + for (p = 0; p < planes; p++) + sizes[p] = tpg_g_bytesperline(&dev->tpg, p) * h + + dev->fmt_cap->data_offset[p]; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = planes; + + /* + * videobuf2-vmalloc allocator is context-less so no need to set + * alloc_ctxs array. + */ + + if (planes == 2) + dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__, + *nbuffers, sizes[0], sizes[1]); + else + dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__, + *nbuffers, sizes[0]); + + return 0; +} + +static int vid_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size; + unsigned planes = tpg_g_planes(&dev->tpg); + unsigned p; + + dprintk(dev, 1, "%s\n", __func__); + + if (WARN_ON(NULL == dev->fmt_cap)) + return -EINVAL; + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + for (p = 0; p < planes; p++) { + size = tpg_g_bytesperline(&dev->tpg, p) * dev->fmt_cap_rect.height + + dev->fmt_cap->data_offset[p]; + + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n", + __func__, p, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, p, size); + vb->v4l2_planes[p].data_offset = dev->fmt_cap->data_offset[p]; + } + + return 0; +} + +static void vid_cap_buf_finish(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_timecode *tc = &vb->v4l2_buf.timecode; + unsigned fps = 25; + unsigned seq = vb->v4l2_buf.sequence; + + if (!vivid_is_sdtv_cap(dev)) + return; + + /* + * Set the timecode. Rarely used, so it is interesting to + * test this. + */ + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMECODE; + if (dev->std_cap & V4L2_STD_525_60) + fps = 30; + tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS; + tc->flags = 0; + tc->frames = seq % fps; + tc->seconds = (seq / fps) % 60; + tc->minutes = (seq / (60 * fps)) % 60; + tc->hours = (seq / (60 * 60 * fps)) % 24; +} + +static void vid_cap_buf_queue(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->vid_cap_active); + spin_unlock(&dev->slock); +} + +static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned i; + int err; + + if (vb2_is_streaming(&dev->vb_vid_out_q)) + dev->can_loop_video = vivid_vid_can_loop(dev); + + if (dev->kthread_vid_cap) + return 0; + + dev->vid_cap_seq_count = 0; + dprintk(dev, 1, "%s\n", __func__); + for (i = 0; i < VIDEO_MAX_FRAME; i++) + dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_cap(dev, &dev->vid_cap_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void vid_cap_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); + dev->can_loop_video = false; +} + +const struct vb2_ops vivid_vid_cap_qops = { + .queue_setup = vid_cap_queue_setup, + .buf_prepare = vid_cap_buf_prepare, + .buf_finish = vid_cap_buf_finish, + .buf_queue = vid_cap_buf_queue, + .start_streaming = vid_cap_start_streaming, + .stop_streaming = vid_cap_stop_streaming, + .wait_prepare = vivid_unlock, + .wait_finish = vivid_lock, +}; + +/* + * Determine the 'picture' quality based on the current TV frequency: either + * COLOR for a good 'signal', GRAY (grayscale picture) for a slightly off + * signal or NOISE for no signal. + */ +void vivid_update_quality(struct vivid_dev *dev) +{ + unsigned freq_modulus; + + if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { + /* + * The 'noise' will only be replaced by the actual video + * if the output video matches the input video settings. + */ + tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); + return; + } + if (vivid_is_hdmi_cap(dev) && VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)) { + tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); + return; + } + if (vivid_is_sdtv_cap(dev) && VIVID_INVALID_SIGNAL(dev->std_signal_mode)) { + tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); + return; + } + if (!vivid_is_tv_cap(dev)) { + tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); + return; + } + + /* + * There is a fake channel every 6 MHz at 49.25, 55.25, etc. + * From +/- 0.25 MHz around the channel there is color, and from + * +/- 1 MHz there is grayscale (chroma is lost). + * Everywhere else it is just noise. + */ + freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); + if (freq_modulus > 2 * 16) { + tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, + next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f); + return; + } + if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/) + tpg_s_quality(&dev->tpg, TPG_QUAL_GRAY, 0); + else + tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); +} + +/* + * Get the current picture quality and the associated afc value. + */ +static enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc) +{ + unsigned freq_modulus; + + if (afc) + *afc = 0; + if (tpg_g_quality(&dev->tpg) == TPG_QUAL_COLOR || + tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) + return tpg_g_quality(&dev->tpg); + + /* + * There is a fake channel every 6 MHz at 49.25, 55.25, etc. + * From +/- 0.25 MHz around the channel there is color, and from + * +/- 1 MHz there is grayscale (chroma is lost). + * Everywhere else it is just gray. + */ + freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); + if (afc) + *afc = freq_modulus - 1 * 16; + return TPG_QUAL_GRAY; +} + +enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev) +{ + if (vivid_is_sdtv_cap(dev)) + return dev->std_aspect_ratio; + + if (vivid_is_hdmi_cap(dev)) + return dev->dv_timings_aspect_ratio; + + return TPG_VIDEO_ASPECT_IMAGE; +} + +static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) +{ + if (vivid_is_sdtv_cap(dev)) + return (dev->std_cap & V4L2_STD_525_60) ? + TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; + + if (vivid_is_hdmi_cap(dev) && + dev->src_rect.width == 720 && dev->src_rect.height <= 576) + return dev->src_rect.height == 480 ? + TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; + + return TPG_PIXEL_ASPECT_SQUARE; +} + +/* + * Called whenever the format has to be reset which can occur when + * changing inputs, standard, timings, etc. + */ +void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt; + unsigned size; + + switch (dev->input_type[dev->input]) { + case WEBCAM: + default: + dev->src_rect.width = webcam_sizes[dev->webcam_size_idx].width; + dev->src_rect.height = webcam_sizes[dev->webcam_size_idx].height; + dev->timeperframe_vid_cap = webcam_intervals[dev->webcam_ival_idx]; + dev->field_cap = V4L2_FIELD_NONE; + tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); + break; + case TV: + case SVID: + dev->field_cap = dev->tv_field_cap; + dev->src_rect.width = 720; + if (dev->std_cap & V4L2_STD_525_60) { + dev->src_rect.height = 480; + dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 }; + dev->service_set_cap = V4L2_SLICED_CAPTION_525; + } else { + dev->src_rect.height = 576; + dev->timeperframe_vid_cap = (struct v4l2_fract) { 1000, 25000 }; + dev->service_set_cap = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; + } + tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); + break; + case HDMI: + dev->src_rect.width = bt->width; + dev->src_rect.height = bt->height; + size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + dev->timeperframe_vid_cap = (struct v4l2_fract) { + size / 100, (u32)bt->pixelclock / 100 + }; + if (bt->interlaced) + dev->field_cap = V4L2_FIELD_ALTERNATE; + else + dev->field_cap = V4L2_FIELD_NONE; + + /* + * We can be called from within s_ctrl, in that case we can't + * set/get controls. Luckily we don't need to in that case. + */ + if (keep_controls || !dev->colorspace) + break; + if (bt->standards & V4L2_DV_BT_STD_CEA861) { + if (bt->width == 720 && bt->height <= 576) + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M); + else + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709); + v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1); + } else { + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB); + v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0); + } + tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap)); + break; + } + vivid_update_quality(dev); + tpg_reset_source(&dev->tpg, dev->src_rect.width, dev->src_rect.height, dev->field_cap); + dev->crop_cap = dev->src_rect; + dev->crop_bounds_cap = dev->src_rect; + dev->compose_cap = dev->crop_cap; + if (V4L2_FIELD_HAS_T_OR_B(dev->field_cap)) + dev->compose_cap.height /= 2; + dev->fmt_cap_rect = dev->compose_cap; + tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); + tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev)); + tpg_update_mv_step(&dev->tpg); +} + +/* Map the field to something that is valid for the current input */ +static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field field) +{ + if (vivid_is_sdtv_cap(dev)) { + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + return field; + case V4L2_FIELD_INTERLACED: + default: + return V4L2_FIELD_INTERLACED; + } + } + if (vivid_is_hdmi_cap(dev)) + return dev->dv_timings_cap.bt.interlaced ? V4L2_FIELD_ALTERNATE : + V4L2_FIELD_NONE; + return V4L2_FIELD_NONE; +} + +static unsigned vivid_colorspace_cap(struct vivid_dev *dev) +{ + if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + return tpg_g_colorspace(&dev->tpg); + return dev->colorspace_out; +} + +int vivid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + unsigned p; + + mp->width = dev->fmt_cap_rect.width; + mp->height = dev->fmt_cap_rect.height; + mp->field = dev->field_cap; + mp->pixelformat = dev->fmt_cap->fourcc; + mp->colorspace = vivid_colorspace_cap(dev); + mp->num_planes = dev->fmt_cap->planes; + for (p = 0; p < mp->num_planes; p++) { + mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); + mp->plane_fmt[p].sizeimage = + mp->plane_fmt[p].bytesperline * mp->height + + dev->fmt_cap->data_offset[p]; + } + return 0; +} + +int vivid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_fmt *fmt; + unsigned bytesperline, max_bpl; + unsigned factor = 1; + unsigned w, h; + unsigned p; + + fmt = vivid_get_format(dev, mp->pixelformat); + if (!fmt) { + dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", + mp->pixelformat); + mp->pixelformat = V4L2_PIX_FMT_YUYV; + fmt = vivid_get_format(dev, mp->pixelformat); + } + + mp->field = vivid_field_cap(dev, mp->field); + if (vivid_is_webcam(dev)) { + const struct v4l2_frmsize_discrete *sz = + v4l2_find_nearest_format(&webcam_probe, mp->width, mp->height); + + w = sz->width; + h = sz->height; + } else if (vivid_is_sdtv_cap(dev)) { + w = 720; + h = (dev->std_cap & V4L2_STD_525_60) ? 480 : 576; + } else { + w = dev->src_rect.width; + h = dev->src_rect.height; + } + if (V4L2_FIELD_HAS_T_OR_B(mp->field)) + factor = 2; + if (vivid_is_webcam(dev) || + (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) { + mp->width = w; + mp->height = h / factor; + } else { + struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; + + rect_set_min_size(&r, &vivid_min_rect); + rect_set_max_size(&r, &vivid_max_rect); + if (dev->has_scaler_cap && !dev->has_compose_cap) { + struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; + + rect_set_max_size(&r, &max_r); + } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) { + rect_set_max_size(&r, &dev->src_rect); + } else if (!dev->has_scaler_cap && !dev->has_crop_cap) { + rect_set_min_size(&r, &dev->src_rect); + } + mp->width = r.width; + mp->height = r.height / factor; + } + + /* This driver supports custom bytesperline values */ + + /* Calculate the minimum supported bytesperline value */ + bytesperline = (mp->width * fmt->depth) >> 3; + /* Calculate the maximum supported bytesperline value */ + max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3; + mp->num_planes = fmt->planes; + for (p = 0; p < mp->num_planes; p++) { + if (pfmt[p].bytesperline > max_bpl) + pfmt[p].bytesperline = max_bpl; + if (pfmt[p].bytesperline < bytesperline) + pfmt[p].bytesperline = bytesperline; + pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height + + fmt->data_offset[p]; + memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); + } + mp->colorspace = vivid_colorspace_cap(dev); + memset(mp->reserved, 0, sizeof(mp->reserved)); + return 0; +} + +int vivid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_rect *crop = &dev->crop_cap; + struct v4l2_rect *compose = &dev->compose_cap; + struct vb2_queue *q = &dev->vb_vid_cap_q; + int ret = vivid_try_fmt_vid_cap(file, priv, f); + unsigned factor = 1; + unsigned i; + + if (ret < 0) + return ret; + + if (vb2_is_busy(q)) { + dprintk(dev, 1, "%s device busy\n", __func__); + return -EBUSY; + } + + if (dev->overlay_cap_owner && dev->fb_cap.fmt.pixelformat != mp->pixelformat) { + dprintk(dev, 1, "overlay is active, can't change pixelformat\n"); + return -EBUSY; + } + + dev->fmt_cap = vivid_get_format(dev, mp->pixelformat); + if (V4L2_FIELD_HAS_T_OR_B(mp->field)) + factor = 2; + + /* Note: the webcam input doesn't support scaling, cropping or composing */ + + if (!vivid_is_webcam(dev) && + (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) { + struct v4l2_rect r = { 0, 0, mp->width, mp->height }; + + if (dev->has_scaler_cap) { + if (dev->has_compose_cap) + rect_map_inside(compose, &r); + else + *compose = r; + if (dev->has_crop_cap && !dev->has_compose_cap) { + struct v4l2_rect min_r = { + 0, 0, + r.width / MAX_ZOOM, + factor * r.height / MAX_ZOOM + }; + struct v4l2_rect max_r = { + 0, 0, + r.width * MAX_ZOOM, + factor * r.height * MAX_ZOOM + }; + + rect_set_min_size(crop, &min_r); + rect_set_max_size(crop, &max_r); + rect_map_inside(crop, &dev->crop_bounds_cap); + } else if (dev->has_crop_cap) { + struct v4l2_rect min_r = { + 0, 0, + compose->width / MAX_ZOOM, + factor * compose->height / MAX_ZOOM + }; + struct v4l2_rect max_r = { + 0, 0, + compose->width * MAX_ZOOM, + factor * compose->height * MAX_ZOOM + }; + + rect_set_min_size(crop, &min_r); + rect_set_max_size(crop, &max_r); + rect_map_inside(crop, &dev->crop_bounds_cap); + } + } else if (dev->has_crop_cap && !dev->has_compose_cap) { + r.height *= factor; + rect_set_size_to(crop, &r); + rect_map_inside(crop, &dev->crop_bounds_cap); + r = *crop; + r.height /= factor; + rect_set_size_to(compose, &r); + } else if (!dev->has_crop_cap) { + rect_map_inside(compose, &r); + } else { + r.height *= factor; + rect_set_max_size(crop, &r); + rect_map_inside(crop, &dev->crop_bounds_cap); + compose->top *= factor; + compose->height *= factor; + rect_set_size_to(compose, crop); + rect_map_inside(compose, &r); + compose->top /= factor; + compose->height /= factor; + } + } else if (vivid_is_webcam(dev)) { + /* Guaranteed to be a match */ + for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) + if (webcam_sizes[i].width == mp->width && + webcam_sizes[i].height == mp->height) + break; + dev->webcam_size_idx = i; + if (dev->webcam_ival_idx >= 2 * (3 - i)) + dev->webcam_ival_idx = 2 * (3 - i) - 1; + vivid_update_format_cap(dev, false); + } else { + struct v4l2_rect r = { 0, 0, mp->width, mp->height }; + + rect_set_size_to(compose, &r); + r.height *= factor; + rect_set_size_to(crop, &r); + } + + dev->fmt_cap_rect.width = mp->width; + dev->fmt_cap_rect.height = mp->height; + tpg_s_buf_height(&dev->tpg, mp->height); + tpg_s_bytesperline(&dev->tpg, 0, mp->plane_fmt[0].bytesperline); + if (tpg_g_planes(&dev->tpg) > 1) + tpg_s_bytesperline(&dev->tpg, 1, mp->plane_fmt[1].bytesperline); + dev->field_cap = mp->field; + tpg_s_field(&dev->tpg, dev->field_cap); + tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap); + tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); + if (vivid_is_sdtv_cap(dev)) + dev->tv_field_cap = mp->field; + tpg_update_mv_step(&dev->tpg); + return 0; +} + +int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_g_fmt_vid_cap(file, priv, f); +} + +int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_try_fmt_vid_cap(file, priv, f); +} + +int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_s_fmt_vid_cap(file, priv, f); +} + +int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap); +} + +int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap); +} + +int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap); +} + +int vivid_vid_cap_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->has_crop_cap && !dev->has_compose_cap) + return -ENOTTY; + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (vivid_is_webcam(dev)) + return -EINVAL; + + sel->r.left = sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (!dev->has_crop_cap) + return -EINVAL; + sel->r = dev->crop_cap; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (!dev->has_crop_cap) + return -EINVAL; + sel->r = dev->src_rect; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (!dev->has_compose_cap) + return -EINVAL; + sel->r = vivid_max_rect; + break; + case V4L2_SEL_TGT_COMPOSE: + if (!dev->has_compose_cap) + return -EINVAL; + sel->r = dev->compose_cap; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (!dev->has_compose_cap) + return -EINVAL; + sel->r = dev->fmt_cap_rect; + break; + default: + return -EINVAL; + } + return 0; +} + +int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_rect *crop = &dev->crop_cap; + struct v4l2_rect *compose = &dev->compose_cap; + unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; + int ret; + + if (!dev->has_crop_cap && !dev->has_compose_cap) + return -ENOTTY; + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (vivid_is_webcam(dev)) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (!dev->has_crop_cap) + return -EINVAL; + ret = vivid_vid_adjust_sel(s->flags, &s->r); + if (ret) + return ret; + rect_set_min_size(&s->r, &vivid_min_rect); + rect_set_max_size(&s->r, &dev->src_rect); + rect_map_inside(&s->r, &dev->crop_bounds_cap); + s->r.top /= factor; + s->r.height /= factor; + if (dev->has_scaler_cap) { + struct v4l2_rect fmt = dev->fmt_cap_rect; + struct v4l2_rect max_rect = { + 0, 0, + s->r.width * MAX_ZOOM, + s->r.height * MAX_ZOOM + }; + struct v4l2_rect min_rect = { + 0, 0, + s->r.width / MAX_ZOOM, + s->r.height / MAX_ZOOM + }; + + rect_set_min_size(&fmt, &min_rect); + if (!dev->has_compose_cap) + rect_set_max_size(&fmt, &max_rect); + if (!rect_same_size(&dev->fmt_cap_rect, &fmt) && + vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; + if (dev->has_compose_cap) { + rect_set_min_size(compose, &min_rect); + rect_set_max_size(compose, &max_rect); + } + dev->fmt_cap_rect = fmt; + tpg_s_buf_height(&dev->tpg, fmt.height); + } else if (dev->has_compose_cap) { + struct v4l2_rect fmt = dev->fmt_cap_rect; + + rect_set_min_size(&fmt, &s->r); + if (!rect_same_size(&dev->fmt_cap_rect, &fmt) && + vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; + dev->fmt_cap_rect = fmt; + tpg_s_buf_height(&dev->tpg, fmt.height); + rect_set_size_to(compose, &s->r); + rect_map_inside(compose, &dev->fmt_cap_rect); + } else { + if (!rect_same_size(&s->r, &dev->fmt_cap_rect) && + vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; + rect_set_size_to(&dev->fmt_cap_rect, &s->r); + rect_set_size_to(compose, &s->r); + rect_map_inside(compose, &dev->fmt_cap_rect); + tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height); + } + s->r.top *= factor; + s->r.height *= factor; + *crop = s->r; + break; + case V4L2_SEL_TGT_COMPOSE: + if (!dev->has_compose_cap) + return -EINVAL; + ret = vivid_vid_adjust_sel(s->flags, &s->r); + if (ret) + return ret; + rect_set_min_size(&s->r, &vivid_min_rect); + rect_set_max_size(&s->r, &dev->fmt_cap_rect); + if (dev->has_scaler_cap) { + struct v4l2_rect max_rect = { + 0, 0, + dev->src_rect.width * MAX_ZOOM, + (dev->src_rect.height / factor) * MAX_ZOOM + }; + + rect_set_max_size(&s->r, &max_rect); + if (dev->has_crop_cap) { + struct v4l2_rect min_rect = { + 0, 0, + s->r.width / MAX_ZOOM, + (s->r.height * factor) / MAX_ZOOM + }; + struct v4l2_rect max_rect = { + 0, 0, + s->r.width * MAX_ZOOM, + (s->r.height * factor) * MAX_ZOOM + }; + + rect_set_min_size(crop, &min_rect); + rect_set_max_size(crop, &max_rect); + rect_map_inside(crop, &dev->crop_bounds_cap); + } + } else if (dev->has_crop_cap) { + s->r.top *= factor; + s->r.height *= factor; + rect_set_max_size(&s->r, &dev->src_rect); + rect_set_size_to(crop, &s->r); + rect_map_inside(crop, &dev->crop_bounds_cap); + s->r.top /= factor; + s->r.height /= factor; + } else { + rect_set_size_to(&s->r, &dev->src_rect); + s->r.height /= factor; + } + rect_map_inside(&s->r, &dev->fmt_cap_rect); + if (dev->bitmap_cap && (compose->width != s->r.width || + compose->height != s->r.height)) { + kfree(dev->bitmap_cap); + dev->bitmap_cap = NULL; + } + *compose = s->r; + break; + default: + return -EINVAL; + } + + tpg_s_crop_compose(&dev->tpg, crop, compose); + return 0; +} + +int vivid_vid_cap_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (vivid_get_pixel_aspect(dev)) { + case TPG_PIXEL_ASPECT_NTSC: + cap->pixelaspect.numerator = 11; + cap->pixelaspect.denominator = 10; + break; + case TPG_PIXEL_ASPECT_PAL: + cap->pixelaspect.numerator = 54; + cap->pixelaspect.denominator = 59; + break; + case TPG_PIXEL_ASPECT_SQUARE: + cap->pixelaspect.numerator = 1; + cap->pixelaspect.denominator = 1; + break; + } + return 0; +} + +int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct vivid_fmt *fmt; + + if (f->index >= ARRAY_SIZE(formats_ovl)) + return -EINVAL; + + fmt = &formats_ovl[f->index]; + + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + return 0; +} + +int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_cap; + struct v4l2_window *win = &f->fmt.win; + unsigned clipcount = win->clipcount; + + win->w.top = dev->overlay_cap_top; + win->w.left = dev->overlay_cap_left; + win->w.width = compose->width; + win->w.height = compose->height; + win->field = dev->overlay_cap_field; + win->clipcount = dev->clipcount_cap; + if (clipcount > dev->clipcount_cap) + clipcount = dev->clipcount_cap; + if (dev->bitmap_cap == NULL) + win->bitmap = NULL; + else if (win->bitmap) { + if (copy_to_user(win->bitmap, dev->bitmap_cap, + ((compose->width + 7) / 8) * compose->height)) + return -EFAULT; + } + if (clipcount && win->clips) { + if (copy_to_user(win->clips, dev->clips_cap, + clipcount * sizeof(dev->clips_cap[0]))) + return -EFAULT; + } + return 0; +} + +int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_cap; + struct v4l2_window *win = &f->fmt.win; + int i, j; + + win->w.left = clamp_t(int, win->w.left, + -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width); + win->w.top = clamp_t(int, win->w.top, + -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height); + win->w.width = compose->width; + win->w.height = compose->height; + if (win->field != V4L2_FIELD_BOTTOM && win->field != V4L2_FIELD_TOP) + win->field = V4L2_FIELD_ANY; + win->chromakey = 0; + win->global_alpha = 0; + if (win->clipcount && !win->clips) + win->clipcount = 0; + if (win->clipcount > MAX_CLIPS) + win->clipcount = MAX_CLIPS; + if (win->clipcount) { + if (copy_from_user(dev->try_clips_cap, win->clips, + win->clipcount * sizeof(dev->clips_cap[0]))) + return -EFAULT; + for (i = 0; i < win->clipcount; i++) { + struct v4l2_rect *r = &dev->try_clips_cap[i].c; + + r->top = clamp_t(s32, r->top, 0, dev->fb_cap.fmt.height - 1); + r->height = clamp_t(s32, r->height, 1, dev->fb_cap.fmt.height - r->top); + r->left = clamp_t(u32, r->left, 0, dev->fb_cap.fmt.width - 1); + r->width = clamp_t(u32, r->width, 1, dev->fb_cap.fmt.width - r->left); + } + /* + * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small + * number and it's typically a one-time deal. + */ + for (i = 0; i < win->clipcount - 1; i++) { + struct v4l2_rect *r1 = &dev->try_clips_cap[i].c; + + for (j = i + 1; j < win->clipcount; j++) { + struct v4l2_rect *r2 = &dev->try_clips_cap[j].c; + + if (rect_overlap(r1, r2)) + return -EINVAL; + } + } + if (copy_to_user(win->clips, dev->try_clips_cap, + win->clipcount * sizeof(dev->clips_cap[0]))) + return -EFAULT; + } + return 0; +} + +int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_cap; + struct v4l2_window *win = &f->fmt.win; + int ret = vidioc_try_fmt_vid_overlay(file, priv, f); + unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height; + unsigned clips_size = win->clipcount * sizeof(dev->clips_cap[0]); + void *new_bitmap = NULL; + + if (ret) + return ret; + + if (win->bitmap) { + new_bitmap = vzalloc(bitmap_size); + + if (new_bitmap == NULL) + return -ENOMEM; + if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) { + vfree(new_bitmap); + return -EFAULT; + } + } + + dev->overlay_cap_top = win->w.top; + dev->overlay_cap_left = win->w.left; + dev->overlay_cap_field = win->field; + vfree(dev->bitmap_cap); + dev->bitmap_cap = new_bitmap; + dev->clipcount_cap = win->clipcount; + if (dev->clipcount_cap) + memcpy(dev->clips_cap, dev->try_clips_cap, clips_size); + return 0; +} + +int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (i && dev->fb_vbase_cap == NULL) + return -EINVAL; + + if (i && dev->fb_cap.fmt.pixelformat != dev->fmt_cap->fourcc) { + dprintk(dev, 1, "mismatch between overlay and video capture pixelformats\n"); + return -EINVAL; + } + + if (dev->overlay_cap_owner && dev->overlay_cap_owner != fh) + return -EBUSY; + dev->overlay_cap_owner = i ? fh : NULL; + return 0; +} + +int vivid_vid_cap_g_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct vivid_dev *dev = video_drvdata(file); + + *a = dev->fb_cap; + a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING | + V4L2_FBUF_CAP_LIST_CLIPPING; + a->flags = V4L2_FBUF_FLAG_PRIMARY; + a->fmt.field = V4L2_FIELD_NONE; + a->fmt.colorspace = V4L2_COLORSPACE_SRGB; + a->fmt.priv = 0; + return 0; +} + +int vivid_vid_cap_s_fbuf(struct file *file, void *fh, + const struct v4l2_framebuffer *a) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_fmt *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (dev->overlay_cap_owner) + return -EBUSY; + + if (a->base == NULL) { + dev->fb_cap.base = NULL; + dev->fb_vbase_cap = NULL; + return 0; + } + + if (a->fmt.width < 48 || a->fmt.height < 32) + return -EINVAL; + fmt = vivid_get_format(dev, a->fmt.pixelformat); + if (!fmt || !fmt->can_do_overlay) + return -EINVAL; + if (a->fmt.bytesperline < (a->fmt.width * fmt->depth) / 8) + return -EINVAL; + if (a->fmt.height * a->fmt.bytesperline < a->fmt.sizeimage) + return -EINVAL; + + dev->fb_vbase_cap = phys_to_virt((unsigned long)a->base); + dev->fb_cap = *a; + dev->overlay_cap_left = clamp_t(int, dev->overlay_cap_left, + -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width); + dev->overlay_cap_top = clamp_t(int, dev->overlay_cap_top, + -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height); + return 0; +} + +static const struct v4l2_audio vivid_audio_inputs[] = { + { 0, "TV", V4L2_AUDCAP_STEREO }, + { 1, "Line-In", V4L2_AUDCAP_STEREO }, +}; + +int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (inp->index >= dev->num_inputs) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + switch (dev->input_type[inp->index]) { + case WEBCAM: + snprintf(inp->name, sizeof(inp->name), "Webcam %u", + dev->input_name_counter[inp->index]); + inp->capabilities = 0; + break; + case TV: + snprintf(inp->name, sizeof(inp->name), "TV %u", + dev->input_name_counter[inp->index]); + inp->type = V4L2_INPUT_TYPE_TUNER; + inp->std = V4L2_STD_ALL; + if (dev->has_audio_inputs) + inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; + inp->capabilities = V4L2_IN_CAP_STD; + break; + case SVID: + snprintf(inp->name, sizeof(inp->name), "S-Video %u", + dev->input_name_counter[inp->index]); + inp->std = V4L2_STD_ALL; + if (dev->has_audio_inputs) + inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; + inp->capabilities = V4L2_IN_CAP_STD; + break; + case HDMI: + snprintf(inp->name, sizeof(inp->name), "HDMI %u", + dev->input_name_counter[inp->index]); + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; + if (dev->edid_blocks == 0 || + dev->dv_timings_signal_mode == NO_SIGNAL) + inp->status |= V4L2_IN_ST_NO_SIGNAL; + else if (dev->dv_timings_signal_mode == NO_LOCK || + dev->dv_timings_signal_mode == OUT_OF_RANGE) + inp->status |= V4L2_IN_ST_NO_H_LOCK; + break; + } + if (dev->sensor_hflip) + inp->status |= V4L2_IN_ST_HFLIP; + if (dev->sensor_vflip) + inp->status |= V4L2_IN_ST_VFLIP; + if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) { + if (dev->std_signal_mode == NO_SIGNAL) { + inp->status |= V4L2_IN_ST_NO_SIGNAL; + } else if (dev->std_signal_mode == NO_LOCK) { + inp->status |= V4L2_IN_ST_NO_H_LOCK; + } else if (vivid_is_tv_cap(dev)) { + switch (tpg_g_quality(&dev->tpg)) { + case TPG_QUAL_GRAY: + inp->status |= V4L2_IN_ST_COLOR_KILL; + break; + case TPG_QUAL_NOISE: + inp->status |= V4L2_IN_ST_NO_H_LOCK; + break; + default: + break; + } + } + } + return 0; +} + +int vidioc_g_input(struct file *file, void *priv, unsigned *i) +{ + struct vivid_dev *dev = video_drvdata(file); + + *i = dev->input; + return 0; +} + +int vidioc_s_input(struct file *file, void *priv, unsigned i) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt; + unsigned brightness; + + if (i >= dev->num_inputs) + return -EINVAL; + + if (i == dev->input) + return 0; + + if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) + return -EBUSY; + + dev->input = i; + dev->vid_cap_dev.tvnorms = 0; + if (dev->input_type[i] == TV || dev->input_type[i] == SVID) { + dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1; + dev->vid_cap_dev.tvnorms = V4L2_STD_ALL; + } + dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; + vivid_update_format_cap(dev, false); + + if (dev->colorspace) { + switch (dev->input_type[i]) { + case WEBCAM: + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB); + break; + case TV: + case SVID: + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M); + break; + case HDMI: + if (bt->standards & V4L2_DV_BT_STD_CEA861) { + if (dev->src_rect.width == 720 && dev->src_rect.height <= 576) + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M); + else + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709); + } else { + v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB); + } + break; + } + } + + /* + * Modify the brightness range depending on the input. + * This makes it easy to use vivid to test if applications can + * handle control range modifications and is also how this is + * typically used in practice as different inputs may be hooked + * up to different receivers with different control ranges. + */ + brightness = 128 * i + dev->input_brightness[i]; + v4l2_ctrl_modify_range(dev->brightness, + 128 * i, 255 + 128 * i, 1, 128 + 128 * i); + v4l2_ctrl_s_ctrl(dev->brightness, brightness); + return 0; +} + +int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) + return -EINVAL; + *vin = vivid_audio_inputs[vin->index]; + return 0; +} + +int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_sdtv_cap(dev)) + return -EINVAL; + *vin = vivid_audio_inputs[dev->tv_audio_input]; + return 0; +} + +int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_sdtv_cap(dev)) + return -EINVAL; + if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) + return -EINVAL; + dev->tv_audio_input = vin->index; + return 0; +} + +int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (vf->tuner != 0) + return -EINVAL; + vf->frequency = dev->tv_freq; + return 0; +} + +int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (vf->tuner != 0) + return -EINVAL; + dev->tv_freq = clamp_t(unsigned, vf->frequency, MIN_TV_FREQ, MAX_TV_FREQ); + if (vivid_is_tv_cap(dev)) + vivid_update_quality(dev); + return 0; +} + +int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (vt->index != 0) + return -EINVAL; + if (vt->audmode > V4L2_TUNER_MODE_LANG1_LANG2) + return -EINVAL; + dev->tv_audmode = vt->audmode; + return 0; +} + +int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct vivid_dev *dev = video_drvdata(file); + enum tpg_quality qual; + + if (vt->index != 0) + return -EINVAL; + + vt->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->audmode = dev->tv_audmode; + vt->rangelow = MIN_TV_FREQ; + vt->rangehigh = MAX_TV_FREQ; + qual = vivid_get_quality(dev, &vt->afc); + if (qual == TPG_QUAL_COLOR) + vt->signal = 0xffff; + else if (qual == TPG_QUAL_GRAY) + vt->signal = 0x8000; + else + vt->signal = 0; + if (qual == TPG_QUAL_NOISE) { + vt->rxsubchans = 0; + } else if (qual == TPG_QUAL_GRAY) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + } else { + unsigned channel_nr = dev->tv_freq / (6 * 16); + unsigned options = (dev->std_cap & V4L2_STD_NTSC_M) ? 4 : 3; + + switch (channel_nr % options) { + case 0: + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 1: + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + break; + case 2: + if (dev->std_cap & V4L2_STD_NTSC_M) + vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; + else + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 3: + vt->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP; + break; + } + } + strlcpy(vt->name, "TV Tuner", sizeof(vt->name)); + return 0; +} + +/* Must remain in sync with the vivid_ctrl_standard_strings array */ +const v4l2_std_id vivid_standard[] = { + V4L2_STD_NTSC_M, + V4L2_STD_NTSC_M_JP, + V4L2_STD_NTSC_M_KR, + V4L2_STD_NTSC_443, + V4L2_STD_PAL_BG | V4L2_STD_PAL_H, + V4L2_STD_PAL_I, + V4L2_STD_PAL_DK, + V4L2_STD_PAL_M, + V4L2_STD_PAL_N, + V4L2_STD_PAL_Nc, + V4L2_STD_PAL_60, + V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, + V4L2_STD_SECAM_DK, + V4L2_STD_SECAM_L, + V4L2_STD_SECAM_LC, + V4L2_STD_UNKNOWN +}; + +/* Must remain in sync with the vivid_standard array */ +const char * const vivid_ctrl_standard_strings[] = { + "NTSC-M", + "NTSC-M-JP", + "NTSC-M-KR", + "NTSC-443", + "PAL-BGH", + "PAL-I", + "PAL-DK", + "PAL-M", + "PAL-N", + "PAL-Nc", + "PAL-60", + "SECAM-BGH", + "SECAM-DK", + "SECAM-L", + "SECAM-Lc", + NULL, +}; + +int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_sdtv_cap(dev)) + return -ENODATA; + if (dev->std_signal_mode == NO_SIGNAL || + dev->std_signal_mode == NO_LOCK) { + *id = V4L2_STD_UNKNOWN; + return 0; + } + if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) { + *id = V4L2_STD_UNKNOWN; + } else if (dev->std_signal_mode == CURRENT_STD) { + *id = dev->std_cap; + } else if (dev->std_signal_mode == SELECTED_STD) { + *id = dev->query_std; + } else { + *id = vivid_standard[dev->query_std_last]; + dev->query_std_last = (dev->query_std_last + 1) % ARRAY_SIZE(vivid_standard); + } + + return 0; +} + +int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_sdtv_cap(dev)) + return -ENODATA; + if (dev->std_cap == id) + return 0; + if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) + return -EBUSY; + dev->std_cap = id; + vivid_update_format_cap(dev, false); + return 0; +} + +int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_hdmi_cap(dev)) + return -ENODATA; + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; + if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, + 0, NULL, NULL)) + return -EINVAL; + if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0)) + return 0; + dev->dv_timings_cap = *timings; + vivid_update_format_cap(dev, false); + return 0; +} + +int vidioc_query_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_hdmi_cap(dev)) + return -ENODATA; + if (dev->dv_timings_signal_mode == NO_SIGNAL || + dev->edid_blocks == 0) + return -ENOLINK; + if (dev->dv_timings_signal_mode == NO_LOCK) + return -ENOLCK; + if (dev->dv_timings_signal_mode == OUT_OF_RANGE) { + timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2; + return -ERANGE; + } + if (dev->dv_timings_signal_mode == CURRENT_DV_TIMINGS) { + *timings = dev->dv_timings_cap; + } else if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) { + *timings = v4l2_dv_timings_presets[dev->query_dv_timings]; + } else { + *timings = v4l2_dv_timings_presets[dev->query_dv_timings_last]; + dev->query_dv_timings_last = (dev->query_dv_timings_last + 1) % + dev->query_dv_timings_size; + } + return 0; +} + +int vidioc_s_edid(struct file *file, void *_fh, + struct v4l2_edid *edid) +{ + struct vivid_dev *dev = video_drvdata(file); + + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (edid->pad >= dev->num_inputs) + return -EINVAL; + if (dev->input_type[edid->pad] != HDMI || edid->start_block) + return -EINVAL; + if (edid->blocks == 0) { + dev->edid_blocks = 0; + return 0; + } + if (edid->blocks > dev->edid_max_blocks) { + edid->blocks = dev->edid_max_blocks; + return -E2BIG; + } + dev->edid_blocks = edid->blocks; + memcpy(dev->edid, edid->edid, edid->blocks * 128); + return 0; +} + +int vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_webcam(dev) && !dev->has_scaler_cap) + return -EINVAL; + if (vivid_get_format(dev, fsize->pixel_format) == NULL) + return -EINVAL; + if (vivid_is_webcam(dev)) { + if (fsize->index >= ARRAY_SIZE(webcam_sizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = webcam_sizes[fsize->index]; + return 0; + } + if (fsize->index) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH * MAX_ZOOM; + fsize->stepwise.step_width = 2; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT * MAX_ZOOM; + fsize->stepwise.step_height = 2; + return 0; +} + +/* timeperframe is arbitrary and continuous */ +int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_fmt *fmt; + int i; + + fmt = vivid_get_format(dev, fival->pixel_format); + if (!fmt) + return -EINVAL; + + if (!vivid_is_webcam(dev)) { + static const struct v4l2_fract step = { 1, 1 }; + + if (fival->index) + return -EINVAL; + if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM) + return -EINVAL; + if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.min = tpf_min; + fival->stepwise.max = tpf_max; + fival->stepwise.step = step; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) + if (fival->width == webcam_sizes[i].width && + fival->height == webcam_sizes[i].height) + break; + if (i == ARRAY_SIZE(webcam_sizes)) + return -EINVAL; + if (fival->index >= 2 * (3 - i)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = webcam_intervals[fival->index]; + return 0; +} + +int vivid_vid_cap_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (parm->type != (dev->multiplanar ? + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = dev->timeperframe_vid_cap; + parm->parm.capture.readbuffers = 1; + return 0; +} + +#define FRACT_CMP(a, OP, b) \ + ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator) + +int vivid_vid_cap_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivid_dev *dev = video_drvdata(file); + unsigned ival_sz = 2 * (3 - dev->webcam_size_idx); + struct v4l2_fract tpf; + unsigned i; + + if (parm->type != (dev->multiplanar ? + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + if (!vivid_is_webcam(dev)) + return vivid_vid_cap_g_parm(file, priv, parm); + + tpf = parm->parm.capture.timeperframe; + + if (tpf.denominator == 0) + tpf = webcam_intervals[ival_sz - 1]; + for (i = 0; i < ival_sz; i++) + if (FRACT_CMP(tpf, >=, webcam_intervals[i])) + break; + if (i == ival_sz) + i = ival_sz - 1; + dev->webcam_ival_idx = i; + tpf = webcam_intervals[dev->webcam_ival_idx]; + tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + + /* resync the thread's timings */ + dev->cap_seq_resync = true; + dev->timeperframe_vid_cap = tpf; + parm->parm.capture.timeperframe = tpf; + parm->parm.capture.readbuffers = 1; + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/platform/vivid/vivid-vid-cap.h new file mode 100644 index 0000000..9407981 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-cap.h @@ -0,0 +1,71 @@ +/* + * vivid-vid-cap.h - video capture support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VID_CAP_H_ +#define _VIVID_VID_CAP_H_ + +void vivid_update_quality(struct vivid_dev *dev); +void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); +enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev); + +extern const v4l2_std_id vivid_standard[]; +extern const char * const vivid_ctrl_standard_strings[]; + +extern const struct vb2_ops vivid_vid_cap_qops; + +int vivid_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vivid_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vivid_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); +int vivid_vid_cap_g_selection(struct file *file, void *priv, struct v4l2_selection *sel); +int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s); +int vivid_vid_cap_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap); +int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i); +int vivid_vid_cap_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a); +int vivid_vid_cap_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a); +int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp); +int vidioc_g_input(struct file *file, void *priv, unsigned *i); +int vidioc_s_input(struct file *file, void *priv, unsigned i); +int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin); +int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin); +int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin); +int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); +int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt); +int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt); +int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id); +int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id); +int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); +int vidioc_query_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); +int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid); +int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize); +int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival); +int vivid_vid_cap_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm); +int vivid_vid_cap_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm); + +#endif diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c new file mode 100644 index 0000000..16cd6d2 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -0,0 +1,571 @@ +/* + * vivid-vid-common.c - common video support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" + +const struct v4l2_dv_timings_cap vivid_dv_timings_cap = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with GCC < 4.4.6 */ + .reserved = { 0 }, + V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 25000000, 600000000, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT, + V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED) +}; + +/* ------------------------------------------------------------------ + Basic structures + ------------------------------------------------------------------*/ + +struct vivid_fmt vivid_formats[] = { + { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .is_yuv = true, + .planes = 1, + .data_offset = { PLANE0_DATA_OFFSET, 0 }, + }, + { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .is_yuv = true, + .planes = 1, + }, + { + .name = "4:2:2, packed, YVYU", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .is_yuv = true, + .planes = 1, + }, + { + .name = "4:2:2, packed, VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .is_yuv = true, + .planes = 1, + }, + { + .name = "RGB565 (LE)", + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + }, + { + .name = "RGB565 (BE)", + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + }, + { + .name = "RGB555 (LE)", + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + }, + { + .name = "XRGB555 (LE)", + .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + }, + { + .name = "ARGB555 (LE)", + .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + .alpha_mask = 0x8000, + }, + { + .name = "RGB555 (BE)", + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .depth = 16, + .planes = 1, + .can_do_overlay = true, + }, + { + .name = "RGB24 (LE)", + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .depth = 24, + .planes = 1, + }, + { + .name = "RGB24 (BE)", + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .depth = 24, + .planes = 1, + }, + { + .name = "RGB32 (LE)", + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .depth = 32, + .planes = 1, + }, + { + .name = "RGB32 (BE)", + .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ + .depth = 32, + .planes = 1, + }, + { + .name = "XRGB32 (LE)", + .fourcc = V4L2_PIX_FMT_XRGB32, /* argb */ + .depth = 32, + .planes = 1, + }, + { + .name = "XRGB32 (BE)", + .fourcc = V4L2_PIX_FMT_XBGR32, /* bgra */ + .depth = 32, + .planes = 1, + }, + { + .name = "ARGB32 (LE)", + .fourcc = V4L2_PIX_FMT_ARGB32, /* argb */ + .depth = 32, + .planes = 1, + .alpha_mask = 0x000000ff, + }, + { + .name = "ARGB32 (BE)", + .fourcc = V4L2_PIX_FMT_ABGR32, /* bgra */ + .depth = 32, + .planes = 1, + .alpha_mask = 0xff000000, + }, + { + .name = "4:2:2, planar, YUV", + .fourcc = V4L2_PIX_FMT_NV16M, + .depth = 8, + .is_yuv = true, + .planes = 2, + .data_offset = { PLANE0_DATA_OFFSET, 0 }, + }, + { + .name = "4:2:2, planar, YVU", + .fourcc = V4L2_PIX_FMT_NV61M, + .depth = 8, + .is_yuv = true, + .planes = 2, + .data_offset = { 0, PLANE0_DATA_OFFSET }, + }, +}; + +/* There are 2 multiplanar formats in the list */ +#define VIVID_MPLANAR_FORMATS 2 + +const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) +{ + const struct vivid_fmt *fmt; + unsigned k; + + for (k = 0; k < ARRAY_SIZE(vivid_formats); k++) { + fmt = &vivid_formats[k]; + if (fmt->fourcc == pixelformat) + if (fmt->planes == 1 || dev->multiplanar) + return fmt; + } + + return NULL; +} + +bool vivid_vid_can_loop(struct vivid_dev *dev) +{ + if (dev->src_rect.width != dev->sink_rect.width || + dev->src_rect.height != dev->sink_rect.height) + return false; + if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc) + return false; + if (dev->field_cap != dev->field_out) + return false; + if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { + if (!(dev->std_cap & V4L2_STD_525_60) != + !(dev->std_out & V4L2_STD_525_60)) + return false; + return true; + } + if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev)) + return true; + return false; +} + +void vivid_send_source_change(struct vivid_dev *dev, unsigned type) +{ + struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + unsigned i; + + for (i = 0; i < dev->num_inputs; i++) { + ev.id = i; + if (dev->input_type[i] == type) { + if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) + v4l2_event_queue(&dev->vid_cap_dev, &ev); + if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) + v4l2_event_queue(&dev->vbi_cap_dev, &ev); + } + } +} + +/* + * Conversion function that converts a single-planar format to a + * single-plane multiplanar format. + */ +void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt) +{ + struct v4l2_pix_format_mplane *mp = &mp_fmt->fmt.pix_mp; + struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0]; + const struct v4l2_pix_format *pix = &sp_fmt->fmt.pix; + bool is_out = sp_fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT; + + memset(mp->reserved, 0, sizeof(mp->reserved)); + mp_fmt->type = is_out ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_CAP_VIDEO_CAPTURE_MPLANE; + mp->width = pix->width; + mp->height = pix->height; + mp->pixelformat = pix->pixelformat; + mp->field = pix->field; + mp->colorspace = pix->colorspace; + mp->num_planes = 1; + mp->flags = pix->flags; + ppix->sizeimage = pix->sizeimage; + ppix->bytesperline = pix->bytesperline; + memset(ppix->reserved, 0, sizeof(ppix->reserved)); +} + +int fmt_sp2mp_func(struct file *file, void *priv, + struct v4l2_format *f, fmtfunc func) +{ + struct v4l2_format fmt; + struct v4l2_pix_format_mplane *mp = &fmt.fmt.pix_mp; + struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0]; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + /* Converts to a mplane format */ + fmt_sp2mp(f, &fmt); + /* Passes it to the generic mplane format function */ + ret = func(file, priv, &fmt); + /* Copies back the mplane data to the single plane format */ + pix->width = mp->width; + pix->height = mp->height; + pix->pixelformat = mp->pixelformat; + pix->field = mp->field; + pix->colorspace = mp->colorspace; + pix->sizeimage = ppix->sizeimage; + pix->bytesperline = ppix->bytesperline; + pix->flags = mp->flags; + return ret; +} + +/* v4l2_rect helper function: copy the width/height values */ +void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size) +{ + r->width = size->width; + r->height = size->height; +} + +/* v4l2_rect helper function: width and height of r should be >= min_size */ +void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size) +{ + if (r->width < min_size->width) + r->width = min_size->width; + if (r->height < min_size->height) + r->height = min_size->height; +} + +/* v4l2_rect helper function: width and height of r should be <= max_size */ +void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size) +{ + if (r->width > max_size->width) + r->width = max_size->width; + if (r->height > max_size->height) + r->height = max_size->height; +} + +/* v4l2_rect helper function: r should be inside boundary */ +void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary) +{ + rect_set_max_size(r, boundary); + if (r->left < boundary->left) + r->left = boundary->left; + if (r->top < boundary->top) + r->top = boundary->top; + if (r->left + r->width > boundary->width) + r->left = boundary->width - r->width; + if (r->top + r->height > boundary->height) + r->top = boundary->height - r->height; +} + +/* v4l2_rect helper function: return true if r1 has the same size as r2 */ +bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2) +{ + return r1->width == r2->width && r1->height == r2->height; +} + +/* v4l2_rect helper function: calculate the intersection of two rects */ +struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b) +{ + struct v4l2_rect r; + int right, bottom; + + r.top = max(a->top, b->top); + r.left = max(a->left, b->left); + bottom = min(a->top + a->height, b->top + b->height); + right = min(a->left + a->width, b->left + b->width); + r.height = max(0, bottom - r.top); + r.width = max(0, right - r.left); + return r; +} + +/* + * v4l2_rect helper function: scale rect r by to->width / from->width and + * to->height / from->height. + */ +void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from, + const struct v4l2_rect *to) +{ + if (from->width == 0 || from->height == 0) { + r->left = r->top = r->width = r->height = 0; + return; + } + r->left = (((r->left - from->left) * to->width) / from->width) & ~1; + r->width = ((r->width * to->width) / from->width) & ~1; + r->top = ((r->top - from->top) * to->height) / from->height; + r->height = (r->height * to->height) / from->height; +} + +bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2) +{ + /* + * IF the left side of r1 is to the right of the right side of r2 OR + * the left side of r2 is to the right of the right side of r1 THEN + * they do not overlap. + */ + if (r1->left >= r2->left + r2->width || + r2->left >= r1->left + r1->width) + return false; + /* + * IF the top side of r1 is below the bottom of r2 OR + * the top side of r2 is below the bottom of r1 THEN + * they do not overlap. + */ + if (r1->top >= r2->top + r2->height || + r2->top >= r1->top + r1->height) + return false; + return true; +} +int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r) +{ + unsigned w = r->width; + unsigned h = r->height; + + if (!(flags & V4L2_SEL_FLAG_LE)) { + w++; + h++; + if (w < 2) + w = 2; + if (h < 2) + h = 2; + } + if (!(flags & V4L2_SEL_FLAG_GE)) { + if (w > MAX_WIDTH) + w = MAX_WIDTH; + if (h > MAX_HEIGHT) + h = MAX_HEIGHT; + } + w = w & ~1; + h = h & ~1; + if (w < 2 || h < 2) + return -ERANGE; + if (w > MAX_WIDTH || h > MAX_HEIGHT) + return -ERANGE; + if (r->top < 0) + r->top = 0; + if (r->left < 0) + r->left = 0; + r->left &= ~1; + r->top &= ~1; + if (r->left + w > MAX_WIDTH) + r->left = MAX_WIDTH - w; + if (r->top + h > MAX_HEIGHT) + r->top = MAX_HEIGHT - h; + if ((flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE)) == + (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE) && + (r->width != w || r->height != h)) + return -ERANGE; + r->width = w; + r->height = h; + return 0; +} + +int vivid_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_fmt *fmt; + + if (f->index >= ARRAY_SIZE(vivid_formats) - + (dev->multiplanar ? 0 : VIVID_MPLANAR_FORMATS)) + return -EINVAL; + + fmt = &vivid_formats[f->index]; + + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + return 0; +} + +int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_enum_fmt_vid(file, priv, f); +} + +int vidioc_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vivid_enum_fmt_vid(file, priv, f); +} + +int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) { + if (!vivid_is_sdtv_cap(dev)) + return -ENODATA; + *id = dev->std_cap; + } else { + if (!vivid_is_svid_out(dev)) + return -ENODATA; + *id = dev->std_out; + } + return 0; +} + +int vidioc_g_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) { + if (!vivid_is_hdmi_cap(dev)) + return -ENODATA; + *timings = dev->dv_timings_cap; + } else { + if (!vivid_is_hdmi_out(dev)) + return -ENODATA; + *timings = dev->dv_timings_out; + } + return 0; +} + +int vidioc_enum_dv_timings(struct file *file, void *_fh, + struct v4l2_enum_dv_timings *timings) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) { + if (!vivid_is_hdmi_cap(dev)) + return -ENODATA; + } else { + if (!vivid_is_hdmi_out(dev)) + return -ENODATA; + } + return v4l2_enum_dv_timings_cap(timings, &vivid_dv_timings_cap, + NULL, NULL); +} + +int vidioc_dv_timings_cap(struct file *file, void *_fh, + struct v4l2_dv_timings_cap *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_dir == VFL_DIR_RX) { + if (!vivid_is_hdmi_cap(dev)) + return -ENODATA; + } else { + if (!vivid_is_hdmi_out(dev)) + return -ENODATA; + } + *cap = vivid_dv_timings_cap; + return 0; +} + +int vidioc_g_edid(struct file *file, void *_fh, + struct v4l2_edid *edid) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (vdev->vfl_dir == VFL_DIR_RX) { + if (edid->pad >= dev->num_inputs) + return -EINVAL; + if (dev->input_type[edid->pad] != HDMI) + return -EINVAL; + } else { + if (edid->pad >= dev->num_outputs) + return -EINVAL; + if (dev->output_type[edid->pad] != HDMI) + return -EINVAL; + } + if (edid->start_block == 0 && edid->blocks == 0) { + edid->blocks = dev->edid_blocks; + return 0; + } + if (dev->edid_blocks == 0) + return -ENODATA; + if (edid->start_block >= dev->edid_blocks) + return -EINVAL; + if (edid->start_block + edid->blocks > dev->edid_blocks) + edid->blocks = dev->edid_blocks - edid->start_block; + memcpy(edid->edid, dev->edid, edid->blocks * 128); + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h new file mode 100644 index 0000000..3ec4fa8 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-common.h @@ -0,0 +1,61 @@ +/* + * vivid-vid-common.h - common video support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VID_COMMON_H_ +#define _VIVID_VID_COMMON_H_ + +typedef int (*fmtfunc)(struct file *file, void *priv, struct v4l2_format *f); + +/* + * Conversion function that converts a single-planar format to a + * single-plane multiplanar format. + */ +void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt); +int fmt_sp2mp_func(struct file *file, void *priv, + struct v4l2_format *f, fmtfunc func); + +extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap; + +const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat); + +bool vivid_vid_can_loop(struct vivid_dev *dev); +void vivid_send_source_change(struct vivid_dev *dev, unsigned type); + +bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2); +void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size); +void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size); +void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size); +void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary); +bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2); +struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b); +void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from, + const struct v4l2_rect *to); +int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); + +int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vidioc_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id); +int vidioc_g_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); +int vidioc_enum_dv_timings(struct file *file, void *_fh, struct v4l2_enum_dv_timings *timings); +int vidioc_dv_timings_cap(struct file *file, void *_fh, struct v4l2_dv_timings_cap *cap); +int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid); +int vidioc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub); + +#endif diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c new file mode 100644 index 0000000..69c2dbd --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -0,0 +1,1146 @@ +/* + * vivid-vid-out.c - video output support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> + +#include "vivid-core.h" +#include "vivid-vid-common.h" +#include "vivid-kthread-out.h" +#include "vivid-vid-out.h" + +static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned planes = dev->fmt_out->planes; + unsigned h = dev->fmt_out_rect.height; + unsigned size = dev->bytesperline_out[0] * h; + + if (dev->field_out == V4L2_FIELD_ALTERNATE) { + /* + * You cannot use write() with FIELD_ALTERNATE since the field + * information (TOP/BOTTOM) cannot be passed to the kernel. + */ + if (vb2_fileio_is_active(vq)) + return -EINVAL; + } + + if (dev->queue_setup_error) { + /* + * Error injection: test what happens if queue_setup() returns + * an error. + */ + dev->queue_setup_error = false; + return -EINVAL; + } + + if (fmt) { + const struct v4l2_pix_format_mplane *mp; + struct v4l2_format mp_fmt; + + if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { + fmt_sp2mp(fmt, &mp_fmt); + fmt = &mp_fmt; + } + mp = &fmt->fmt.pix_mp; + /* + * Check if the number of planes in the specified format match + * the number of planes in the current format. You can't mix that. + */ + if (mp->num_planes != planes) + return -EINVAL; + sizes[0] = mp->plane_fmt[0].sizeimage; + if (planes == 2) { + sizes[1] = mp->plane_fmt[1].sizeimage; + if (sizes[0] < dev->bytesperline_out[0] * h || + sizes[1] < dev->bytesperline_out[1] * h) + return -EINVAL; + } else if (sizes[0] < size) { + return -EINVAL; + } + } else { + if (planes == 2) { + sizes[0] = dev->bytesperline_out[0] * h; + sizes[1] = dev->bytesperline_out[1] * h; + } else { + sizes[0] = size; + } + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = planes; + + /* + * videobuf2-vmalloc allocator is context-less so no need to set + * alloc_ctxs array. + */ + + if (planes == 2) + dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__, + *nbuffers, sizes[0], sizes[1]); + else + dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__, + *nbuffers, sizes[0]); + return 0; +} + +static int vid_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size; + unsigned planes = dev->fmt_out->planes; + unsigned p; + + dprintk(dev, 1, "%s\n", __func__); + + if (WARN_ON(NULL == dev->fmt_out)) + return -EINVAL; + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + + if (dev->field_out != V4L2_FIELD_ALTERNATE) + vb->v4l2_buf.field = dev->field_out; + else if (vb->v4l2_buf.field != V4L2_FIELD_TOP && + vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) + return -EINVAL; + + for (p = 0; p < planes; p++) { + size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + + vb->v4l2_planes[p].data_offset; + + if (vb2_get_plane_payload(vb, p) < size) { + dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n", + __func__, p, vb2_get_plane_payload(vb, p), size); + return -EINVAL; + } + } + + return 0; +} + +static void vid_out_buf_queue(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->vid_out_active); + spin_unlock(&dev->slock); +} + +static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + if (vb2_is_streaming(&dev->vb_vid_cap_q)) + dev->can_loop_video = vivid_vid_can_loop(dev); + + if (dev->kthread_vid_out) + return 0; + + dev->vid_out_seq_count = 0; + dprintk(dev, 1, "%s\n", __func__); + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void vid_out_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); + dev->can_loop_video = false; +} + +const struct vb2_ops vivid_vid_out_qops = { + .queue_setup = vid_out_queue_setup, + .buf_prepare = vid_out_buf_prepare, + .buf_queue = vid_out_buf_queue, + .start_streaming = vid_out_start_streaming, + .stop_streaming = vid_out_stop_streaming, + .wait_prepare = vivid_unlock, + .wait_finish = vivid_lock, +}; + +/* + * Called whenever the format has to be reset which can occur when + * changing outputs, standard, timings, etc. + */ +void vivid_update_format_out(struct vivid_dev *dev) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; + unsigned size; + + switch (dev->output_type[dev->output]) { + case SVID: + default: + dev->field_out = dev->tv_field_out; + dev->sink_rect.width = 720; + if (dev->std_out & V4L2_STD_525_60) { + dev->sink_rect.height = 480; + dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 }; + dev->service_set_out = V4L2_SLICED_CAPTION_525; + } else { + dev->sink_rect.height = 576; + dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 }; + dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; + } + dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; + break; + case HDMI: + dev->sink_rect.width = bt->width; + dev->sink_rect.height = bt->height; + size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + dev->timeperframe_vid_out = (struct v4l2_fract) { + size / 100, (u32)bt->pixelclock / 100 + }; + if (bt->interlaced) + dev->field_out = V4L2_FIELD_ALTERNATE; + else + dev->field_out = V4L2_FIELD_NONE; + if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) { + if (bt->width == 720 && bt->height <= 576) + dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; + else + dev->colorspace_out = V4L2_COLORSPACE_REC709; + } else { + dev->colorspace_out = V4L2_COLORSPACE_SRGB; + } + break; + } + dev->compose_out = dev->sink_rect; + dev->compose_bounds_out = dev->sink_rect; + dev->crop_out = dev->compose_out; + if (V4L2_FIELD_HAS_T_OR_B(dev->field_out)) + dev->crop_out.height /= 2; + dev->fmt_out_rect = dev->crop_out; + dev->bytesperline_out[0] = (dev->sink_rect.width * dev->fmt_out->depth) / 8; + if (dev->fmt_out->planes == 2) + dev->bytesperline_out[1] = (dev->sink_rect.width * dev->fmt_out->depth) / 8; +} + +/* Map the field to something that is valid for the current output */ +static enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field) +{ + if (vivid_is_svid_out(dev)) { + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_ALTERNATE: + return field; + case V4L2_FIELD_INTERLACED: + default: + return V4L2_FIELD_INTERLACED; + } + } + if (vivid_is_hdmi_out(dev)) + return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE : + V4L2_FIELD_NONE; + return V4L2_FIELD_NONE; +} + +static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) +{ + if (vivid_is_svid_out(dev)) + return (dev->std_out & V4L2_STD_525_60) ? + TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; + + if (vivid_is_hdmi_out(dev) && + dev->sink_rect.width == 720 && dev->sink_rect.height <= 576) + return dev->sink_rect.height == 480 ? + TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; + + return TPG_PIXEL_ASPECT_SQUARE; +} + +int vivid_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + unsigned p; + + mp->width = dev->fmt_out_rect.width; + mp->height = dev->fmt_out_rect.height; + mp->field = dev->field_out; + mp->pixelformat = dev->fmt_out->fourcc; + mp->colorspace = dev->colorspace_out; + mp->num_planes = dev->fmt_out->planes; + for (p = 0; p < mp->num_planes; p++) { + mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; + mp->plane_fmt[p].sizeimage = + mp->plane_fmt[p].bytesperline * mp->height; + } + return 0; +} + +int vivid_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; + const struct vivid_fmt *fmt; + unsigned bytesperline, max_bpl; + unsigned factor = 1; + unsigned w, h; + unsigned p; + + fmt = vivid_get_format(dev, mp->pixelformat); + if (!fmt) { + dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", + mp->pixelformat); + mp->pixelformat = V4L2_PIX_FMT_YUYV; + fmt = vivid_get_format(dev, mp->pixelformat); + } + + mp->field = vivid_field_out(dev, mp->field); + if (vivid_is_svid_out(dev)) { + w = 720; + h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576; + } else { + w = dev->sink_rect.width; + h = dev->sink_rect.height; + } + if (V4L2_FIELD_HAS_T_OR_B(mp->field)) + factor = 2; + if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) { + mp->width = w; + mp->height = h / factor; + } else { + struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; + + rect_set_min_size(&r, &vivid_min_rect); + rect_set_max_size(&r, &vivid_max_rect); + if (dev->has_scaler_out && !dev->has_crop_out) { + struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; + + rect_set_max_size(&r, &max_r); + } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) { + rect_set_max_size(&r, &dev->sink_rect); + } else if (!dev->has_scaler_out && !dev->has_compose_out) { + rect_set_min_size(&r, &dev->sink_rect); + } + mp->width = r.width; + mp->height = r.height / factor; + } + + /* This driver supports custom bytesperline values */ + + /* Calculate the minimum supported bytesperline value */ + bytesperline = (mp->width * fmt->depth) >> 3; + /* Calculate the maximum supported bytesperline value */ + max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3; + mp->num_planes = fmt->planes; + for (p = 0; p < mp->num_planes; p++) { + if (pfmt[p].bytesperline > max_bpl) + pfmt[p].bytesperline = max_bpl; + if (pfmt[p].bytesperline < bytesperline) + pfmt[p].bytesperline = bytesperline; + pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height; + memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); + } + if (vivid_is_svid_out(dev)) + mp->colorspace = V4L2_COLORSPACE_SMPTE170M; + else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861)) + mp->colorspace = V4L2_COLORSPACE_SRGB; + else if (bt->width == 720 && bt->height <= 576) + mp->colorspace = V4L2_COLORSPACE_SMPTE170M; + else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M && + mp->colorspace != V4L2_COLORSPACE_REC709 && + mp->colorspace != V4L2_COLORSPACE_SRGB) + mp->colorspace = V4L2_COLORSPACE_REC709; + memset(mp->reserved, 0, sizeof(mp->reserved)); + return 0; +} + +int vivid_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_rect *crop = &dev->crop_out; + struct v4l2_rect *compose = &dev->compose_out; + struct vb2_queue *q = &dev->vb_vid_out_q; + int ret = vivid_try_fmt_vid_out(file, priv, f); + unsigned factor = 1; + + if (ret < 0) + return ret; + + if (vb2_is_busy(q) && + (vivid_is_svid_out(dev) || + mp->width != dev->fmt_out_rect.width || + mp->height != dev->fmt_out_rect.height || + mp->pixelformat != dev->fmt_out->fourcc || + mp->field != dev->field_out)) { + dprintk(dev, 1, "%s device busy\n", __func__); + return -EBUSY; + } + + /* + * Allow for changing the colorspace on the fly. Useful for testing + * purposes, and it is something that HDMI transmitters are able + * to do. + */ + if (vb2_is_busy(q)) + goto set_colorspace; + + dev->fmt_out = vivid_get_format(dev, mp->pixelformat); + if (V4L2_FIELD_HAS_T_OR_B(mp->field)) + factor = 2; + + if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) { + struct v4l2_rect r = { 0, 0, mp->width, mp->height }; + + if (dev->has_scaler_out) { + if (dev->has_crop_out) + rect_map_inside(crop, &r); + else + *crop = r; + if (dev->has_compose_out && !dev->has_crop_out) { + struct v4l2_rect min_r = { + 0, 0, + r.width / MAX_ZOOM, + factor * r.height / MAX_ZOOM + }; + struct v4l2_rect max_r = { + 0, 0, + r.width * MAX_ZOOM, + factor * r.height * MAX_ZOOM + }; + + rect_set_min_size(compose, &min_r); + rect_set_max_size(compose, &max_r); + rect_map_inside(compose, &dev->compose_bounds_out); + } else if (dev->has_compose_out) { + struct v4l2_rect min_r = { + 0, 0, + crop->width / MAX_ZOOM, + factor * crop->height / MAX_ZOOM + }; + struct v4l2_rect max_r = { + 0, 0, + crop->width * MAX_ZOOM, + factor * crop->height * MAX_ZOOM + }; + + rect_set_min_size(compose, &min_r); + rect_set_max_size(compose, &max_r); + rect_map_inside(compose, &dev->compose_bounds_out); + } + } else if (dev->has_compose_out && !dev->has_crop_out) { + rect_set_size_to(crop, &r); + r.height *= factor; + rect_set_size_to(compose, &r); + rect_map_inside(compose, &dev->compose_bounds_out); + } else if (!dev->has_compose_out) { + rect_map_inside(crop, &r); + r.height /= factor; + rect_set_size_to(compose, &r); + } else { + r.height *= factor; + rect_set_max_size(compose, &r); + rect_map_inside(compose, &dev->compose_bounds_out); + crop->top *= factor; + crop->height *= factor; + rect_set_size_to(crop, compose); + rect_map_inside(crop, &r); + crop->top /= factor; + crop->height /= factor; + } + } else { + struct v4l2_rect r = { 0, 0, mp->width, mp->height }; + + rect_set_size_to(crop, &r); + r.height /= factor; + rect_set_size_to(compose, &r); + } + + dev->fmt_out_rect.width = mp->width; + dev->fmt_out_rect.height = mp->height; + dev->bytesperline_out[0] = mp->plane_fmt[0].bytesperline; + if (mp->num_planes > 1) + dev->bytesperline_out[1] = mp->plane_fmt[1].bytesperline; + dev->field_out = mp->field; + if (vivid_is_svid_out(dev)) + dev->tv_field_out = mp->field; + +set_colorspace: + dev->colorspace_out = mp->colorspace; + if (dev->loop_video) { + vivid_send_source_change(dev, SVID); + vivid_send_source_change(dev, HDMI); + } + return 0; +} + +int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_g_fmt_vid_out(file, priv, f); +} + +int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_try_fmt_vid_out(file, priv, f); +} + +int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + return vivid_s_fmt_vid_out(file, priv, f); +} + +int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out); +} + +int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out); +} + +int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out); +} + +int vivid_vid_out_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!dev->has_crop_out && !dev->has_compose_out) + return -ENOTTY; + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + sel->r.left = sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (!dev->has_crop_out) + return -EINVAL; + sel->r = dev->crop_out; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + if (!dev->has_crop_out) + return -EINVAL; + sel->r = dev->fmt_out_rect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + if (!dev->has_compose_out) + return -EINVAL; + sel->r = vivid_max_rect; + break; + case V4L2_SEL_TGT_COMPOSE: + if (!dev->has_compose_out) + return -EINVAL; + sel->r = dev->compose_out; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (!dev->has_compose_out) + return -EINVAL; + sel->r = dev->sink_rect; + break; + default: + return -EINVAL; + } + return 0; +} + +int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_rect *crop = &dev->crop_out; + struct v4l2_rect *compose = &dev->compose_out; + unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1; + int ret; + + if (!dev->has_crop_out && !dev->has_compose_out) + return -ENOTTY; + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (!dev->has_crop_out) + return -EINVAL; + ret = vivid_vid_adjust_sel(s->flags, &s->r); + if (ret) + return ret; + rect_set_min_size(&s->r, &vivid_min_rect); + rect_set_max_size(&s->r, &dev->fmt_out_rect); + if (dev->has_scaler_out) { + struct v4l2_rect max_rect = { + 0, 0, + dev->sink_rect.width * MAX_ZOOM, + (dev->sink_rect.height / factor) * MAX_ZOOM + }; + + rect_set_max_size(&s->r, &max_rect); + if (dev->has_compose_out) { + struct v4l2_rect min_rect = { + 0, 0, + s->r.width / MAX_ZOOM, + (s->r.height * factor) / MAX_ZOOM + }; + struct v4l2_rect max_rect = { + 0, 0, + s->r.width * MAX_ZOOM, + (s->r.height * factor) * MAX_ZOOM + }; + + rect_set_min_size(compose, &min_rect); + rect_set_max_size(compose, &max_rect); + rect_map_inside(compose, &dev->compose_bounds_out); + } + } else if (dev->has_compose_out) { + s->r.top *= factor; + s->r.height *= factor; + rect_set_max_size(&s->r, &dev->sink_rect); + rect_set_size_to(compose, &s->r); + rect_map_inside(compose, &dev->compose_bounds_out); + s->r.top /= factor; + s->r.height /= factor; + } else { + rect_set_size_to(&s->r, &dev->sink_rect); + s->r.height /= factor; + } + rect_map_inside(&s->r, &dev->fmt_out_rect); + *crop = s->r; + break; + case V4L2_SEL_TGT_COMPOSE: + if (!dev->has_compose_out) + return -EINVAL; + ret = vivid_vid_adjust_sel(s->flags, &s->r); + if (ret) + return ret; + rect_set_min_size(&s->r, &vivid_min_rect); + rect_set_max_size(&s->r, &dev->sink_rect); + rect_map_inside(&s->r, &dev->compose_bounds_out); + s->r.top /= factor; + s->r.height /= factor; + if (dev->has_scaler_out) { + struct v4l2_rect fmt = dev->fmt_out_rect; + struct v4l2_rect max_rect = { + 0, 0, + s->r.width * MAX_ZOOM, + s->r.height * MAX_ZOOM + }; + struct v4l2_rect min_rect = { + 0, 0, + s->r.width / MAX_ZOOM, + s->r.height / MAX_ZOOM + }; + + rect_set_min_size(&fmt, &min_rect); + if (!dev->has_crop_out) + rect_set_max_size(&fmt, &max_rect); + if (!rect_same_size(&dev->fmt_out_rect, &fmt) && + vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; + if (dev->has_crop_out) { + rect_set_min_size(crop, &min_rect); + rect_set_max_size(crop, &max_rect); + } + dev->fmt_out_rect = fmt; + } else if (dev->has_crop_out) { + struct v4l2_rect fmt = dev->fmt_out_rect; + + rect_set_min_size(&fmt, &s->r); + if (!rect_same_size(&dev->fmt_out_rect, &fmt) && + vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; + dev->fmt_out_rect = fmt; + rect_set_size_to(crop, &s->r); + rect_map_inside(crop, &dev->fmt_out_rect); + } else { + if (!rect_same_size(&s->r, &dev->fmt_out_rect) && + vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; + rect_set_size_to(&dev->fmt_out_rect, &s->r); + rect_set_size_to(crop, &s->r); + crop->height /= factor; + rect_map_inside(crop, &dev->fmt_out_rect); + } + s->r.top *= factor; + s->r.height *= factor; + if (dev->bitmap_out && (compose->width != s->r.width || + compose->height != s->r.height)) { + kfree(dev->bitmap_out); + dev->bitmap_out = NULL; + } + *compose = s->r; + break; + default: + return -EINVAL; + } + + return 0; +} + +int vivid_vid_out_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cap) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (vivid_get_pixel_aspect(dev)) { + case TPG_PIXEL_ASPECT_NTSC: + cap->pixelaspect.numerator = 11; + cap->pixelaspect.denominator = 10; + break; + case TPG_PIXEL_ASPECT_PAL: + cap->pixelaspect.numerator = 54; + cap->pixelaspect.denominator = 59; + break; + case TPG_PIXEL_ASPECT_SQUARE: + cap->pixelaspect.numerator = 1; + cap->pixelaspect.denominator = 1; + break; + } + return 0; +} + +int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_out; + struct v4l2_window *win = &f->fmt.win; + unsigned clipcount = win->clipcount; + + if (!dev->has_fb) + return -EINVAL; + win->w.top = dev->overlay_out_top; + win->w.left = dev->overlay_out_left; + win->w.width = compose->width; + win->w.height = compose->height; + win->clipcount = dev->clipcount_out; + win->field = V4L2_FIELD_ANY; + win->chromakey = dev->chromakey_out; + win->global_alpha = dev->global_alpha_out; + if (clipcount > dev->clipcount_out) + clipcount = dev->clipcount_out; + if (dev->bitmap_out == NULL) + win->bitmap = NULL; + else if (win->bitmap) { + if (copy_to_user(win->bitmap, dev->bitmap_out, + ((dev->compose_out.width + 7) / 8) * dev->compose_out.height)) + return -EFAULT; + } + if (clipcount && win->clips) { + if (copy_to_user(win->clips, dev->clips_out, + clipcount * sizeof(dev->clips_out[0]))) + return -EFAULT; + } + return 0; +} + +int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_out; + struct v4l2_window *win = &f->fmt.win; + int i, j; + + if (!dev->has_fb) + return -EINVAL; + win->w.left = clamp_t(int, win->w.left, + -dev->display_width, dev->display_width); + win->w.top = clamp_t(int, win->w.top, + -dev->display_height, dev->display_height); + win->w.width = compose->width; + win->w.height = compose->height; + /* + * It makes no sense for an OSD to overlay only top or bottom fields, + * so always set this to ANY. + */ + win->field = V4L2_FIELD_ANY; + if (win->clipcount && !win->clips) + win->clipcount = 0; + if (win->clipcount > MAX_CLIPS) + win->clipcount = MAX_CLIPS; + if (win->clipcount) { + if (copy_from_user(dev->try_clips_out, win->clips, + win->clipcount * sizeof(dev->clips_out[0]))) + return -EFAULT; + for (i = 0; i < win->clipcount; i++) { + struct v4l2_rect *r = &dev->try_clips_out[i].c; + + r->top = clamp_t(s32, r->top, 0, dev->display_height - 1); + r->height = clamp_t(s32, r->height, 1, dev->display_height - r->top); + r->left = clamp_t(u32, r->left, 0, dev->display_width - 1); + r->width = clamp_t(u32, r->width, 1, dev->display_width - r->left); + } + /* + * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small + * number and it's typically a one-time deal. + */ + for (i = 0; i < win->clipcount - 1; i++) { + struct v4l2_rect *r1 = &dev->try_clips_out[i].c; + + for (j = i + 1; j < win->clipcount; j++) { + struct v4l2_rect *r2 = &dev->try_clips_out[j].c; + + if (rect_overlap(r1, r2)) + return -EINVAL; + } + } + if (copy_to_user(win->clips, dev->try_clips_out, + win->clipcount * sizeof(dev->clips_out[0]))) + return -EFAULT; + } + return 0; +} + +int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct v4l2_rect *compose = &dev->compose_out; + struct v4l2_window *win = &f->fmt.win; + int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f); + unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height; + unsigned clips_size = win->clipcount * sizeof(dev->clips_out[0]); + void *new_bitmap = NULL; + + if (ret) + return ret; + + if (win->bitmap) { + new_bitmap = memdup_user(win->bitmap, bitmap_size); + + if (IS_ERR(new_bitmap)) + return PTR_ERR(new_bitmap); + } + + dev->overlay_out_top = win->w.top; + dev->overlay_out_left = win->w.left; + kfree(dev->bitmap_out); + dev->bitmap_out = new_bitmap; + dev->clipcount_out = win->clipcount; + if (dev->clipcount_out) + memcpy(dev->clips_out, dev->try_clips_out, clips_size); + dev->chromakey_out = win->chromakey; + dev->global_alpha_out = win->global_alpha; + return ret; +} + +int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (i && !dev->fmt_out->can_do_overlay) { + dprintk(dev, 1, "unsupported output format for output overlay\n"); + return -EINVAL; + } + + dev->overlay_out_enabled = i; + return 0; +} + +int vivid_vid_out_g_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct vivid_dev *dev = video_drvdata(file); + + a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | + V4L2_FBUF_CAP_BITMAP_CLIPPING | + V4L2_FBUF_CAP_LIST_CLIPPING | + V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_SRC_CHROMAKEY | + V4L2_FBUF_CAP_GLOBAL_ALPHA | + V4L2_FBUF_CAP_LOCAL_ALPHA | + V4L2_FBUF_CAP_LOCAL_INV_ALPHA; + a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags; + a->base = (void *)dev->video_pbase; + a->fmt.width = dev->display_width; + a->fmt.height = dev->display_height; + if (dev->fb_defined.green.length == 5) + a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555; + else + a->fmt.pixelformat = V4L2_PIX_FMT_RGB565; + a->fmt.bytesperline = dev->display_byte_stride; + a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline; + a->fmt.field = V4L2_FIELD_NONE; + a->fmt.colorspace = V4L2_COLORSPACE_SRGB; + a->fmt.priv = 0; + return 0; +} + +int vivid_vid_out_s_fbuf(struct file *file, void *fh, + const struct v4l2_framebuffer *a) +{ + struct vivid_dev *dev = video_drvdata(file); + const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY | + V4L2_FBUF_FLAG_SRC_CHROMAKEY; + const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA | + V4L2_FBUF_FLAG_LOCAL_ALPHA | + V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; + + + if ((a->flags & chroma_flags) == chroma_flags) + return -EINVAL; + switch (a->flags & alpha_flags) { + case 0: + case V4L2_FBUF_FLAG_GLOBAL_ALPHA: + case V4L2_FBUF_FLAG_LOCAL_ALPHA: + case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA: + break; + default: + return -EINVAL; + } + dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags); + dev->fbuf_out_flags = a->flags & (chroma_flags | alpha_flags); + return 0; +} + +static const struct v4l2_audioout vivid_audio_outputs[] = { + { 0, "Line-Out 1" }, + { 1, "Line-Out 2" }, +}; + +int vidioc_enum_output(struct file *file, void *priv, + struct v4l2_output *out) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (out->index >= dev->num_outputs) + return -EINVAL; + + out->type = V4L2_OUTPUT_TYPE_ANALOG; + switch (dev->output_type[out->index]) { + case SVID: + snprintf(out->name, sizeof(out->name), "S-Video %u", + dev->output_name_counter[out->index]); + out->std = V4L2_STD_ALL; + if (dev->has_audio_outputs) + out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; + out->capabilities = V4L2_OUT_CAP_STD; + break; + case HDMI: + snprintf(out->name, sizeof(out->name), "HDMI %u", + dev->output_name_counter[out->index]); + out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; + break; + } + return 0; +} + +int vidioc_g_output(struct file *file, void *priv, unsigned *o) +{ + struct vivid_dev *dev = video_drvdata(file); + + *o = dev->output; + return 0; +} + +int vidioc_s_output(struct file *file, void *priv, unsigned o) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (o >= dev->num_outputs) + return -EINVAL; + + if (o == dev->output) + return 0; + + if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q)) + return -EBUSY; + + dev->output = o; + dev->tv_audio_output = 0; + if (dev->output_type[o] == SVID) + dev->vid_out_dev.tvnorms = V4L2_STD_ALL; + else + dev->vid_out_dev.tvnorms = 0; + + dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms; + vivid_update_format_out(dev); + return 0; +} + +int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout) +{ + if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) + return -EINVAL; + *vout = vivid_audio_outputs[vout->index]; + return 0; +} + +int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_svid_out(dev)) + return -EINVAL; + *vout = vivid_audio_outputs[dev->tv_audio_output]; + return 0; +} + +int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_svid_out(dev)) + return -EINVAL; + if (vout->index >= ARRAY_SIZE(vivid_audio_outputs)) + return -EINVAL; + dev->tv_audio_output = vout->index; + return 0; +} + +int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_svid_out(dev)) + return -ENODATA; + if (dev->std_out == id) + return 0; + if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q)) + return -EBUSY; + dev->std_out = id; + vivid_update_format_out(dev); + return 0; +} + +int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_hdmi_out(dev)) + return -ENODATA; + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; + if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, + 0, NULL, NULL)) + return -EINVAL; + if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0)) + return 0; + dev->dv_timings_out = *timings; + vivid_update_format_out(dev); + return 0; +} + +int vivid_vid_out_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (parm->type != (dev->multiplanar ? + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.output.timeperframe = dev->timeperframe_vid_out; + parm->parm.output.writebuffers = 1; +return 0; +} + +int vidioc_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_SOURCE_CHANGE: + if (fh->vdev->vfl_dir == VFL_DIR_RX) + return v4l2_src_change_event_subscribe(fh, sub); + break; + default: + break; + } + return -EINVAL; +} diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h new file mode 100644 index 0000000..dfa84db --- /dev/null +++ b/drivers/media/platform/vivid/vivid-vid-out.h @@ -0,0 +1,56 @@ +/* + * vivid-vid-out.h - video output support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_VID_OUT_H_ +#define _VIVID_VID_OUT_H_ + +extern const struct vb2_ops vivid_vid_out_qops; + +void vivid_update_format_out(struct vivid_dev *dev); + +int vivid_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vivid_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vivid_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f); +int vivid_vid_out_g_selection(struct file *file, void *priv, struct v4l2_selection *sel); +int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s); +int vivid_vid_out_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cap); +int vidioc_enum_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f); +int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f); +int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i); +int vivid_vid_out_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a); +int vivid_vid_out_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a); +int vidioc_enum_output(struct file *file, void *priv, struct v4l2_output *out); +int vidioc_g_output(struct file *file, void *priv, unsigned *i); +int vidioc_s_output(struct file *file, void *priv, unsigned i); +int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout); +int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout); +int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout); +int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id); +int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); +int vivid_vid_out_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm); + +#endif diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 235c0e3..cff1eb1 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -332,7 +332,7 @@ static int __init gemtek_init(void) static void __exit gemtek_exit(void) { - hardmute = 1; /* Turn off PLL */ + hardmute = true; /* Turn off PLL */ #ifdef CONFIG_PNP pnp_unregister_driver(&gemtek_driver.pnp_driver); #endif diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index d7ce8fe..28a8946 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -56,7 +56,7 @@ struct fmi static struct fmi fmi_card; static struct pnp_dev *dev; -bool pnp_attached; +static bool pnp_attached; #define RSF16_MINFREQ (87U * 16000) #define RSF16_MAXFREQ (108U * 16000) @@ -285,7 +285,7 @@ static int __init fmi_init(void) io = isapnp_fmi_probe(); if (io < 0) continue; - pnp_attached = 1; + pnp_attached = true; } if (!request_region(io, 2, "radio-sf16fmi")) { if (pnp_attached) @@ -349,7 +349,7 @@ static int __init fmi_init(void) mutex_init(&fmi->lock); /* mute card and set default frequency */ - fmi->mute = 1; + fmi->mute = true; fmi->curfreq = RSF16_MINFREQ; fmi_set_freq(fmi); diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 93d864e..b8d61cb 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -305,7 +305,7 @@ static void fmr2_pnp_remove(struct pnp_dev *pdev) pnp_set_drvdata(pdev, NULL); } -struct isa_driver fmr2_isa_driver = { +static struct isa_driver fmr2_isa_driver = { .match = fmr2_isa_match, .remove = fmr2_isa_remove, .driver = { @@ -313,7 +313,7 @@ struct isa_driver fmr2_isa_driver = { }, }; -struct pnp_driver fmr2_pnp_driver = { +static struct pnp_driver fmr2_pnp_driver = { .name = "radio-sf16fmr2", .id_table = fmr2_pnp_ids, .probe = fmr2_pnp_probe, diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 9250496..cc39901 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -124,11 +124,11 @@ struct tea5764_regs { struct tea5764_write_regs { u8 intreg; /* INTMSK */ - u16 frqset; /* FRQSETMSB & FRQSETLSB */ - u16 tnctrl; /* TNCTRL1 & TNCTRL2 */ - u16 testreg; /* TESTBITS & TESTMODE */ - u16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */ - u16 rdsbbl; /* PAUSEDET & RDSBBL */ + __be16 frqset; /* FRQSETMSB & FRQSETLSB */ + __be16 tnctrl; /* TNCTRL1 & TNCTRL2 */ + __be16 testreg; /* TESTBITS & TESTMODE */ + __be16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */ + __be16 rdsbbl; /* PAUSEDET & RDSBBL */ } __attribute__ ((packed)); #ifdef CONFIG_RADIO_TEA5764_XTAL @@ -165,7 +165,7 @@ static int tea5764_i2c_read(struct tea5764_device *radio) if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1) return -EIO; for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++) - p[i] = __be16_to_cpu(p[i]); + p[i] = __be16_to_cpu((__force __be16)p[i]); return 0; } diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 0e750ae..909c3f9 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -208,7 +208,7 @@ static int si470x_set_band(struct si470x_device *radio, int band) static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) { int retval; - bool timed_out = 0; + bool timed_out = false; /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; @@ -300,7 +300,7 @@ static int si470x_set_seek(struct si470x_device *radio, { int band, retval; unsigned int freq; - bool timed_out = 0; + bool timed_out = false; /* set band */ if (seek->rangelow || seek->rangehigh) { diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 494fac0..57f0bc3 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -607,9 +607,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* Set up interrupt endpoint information. */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == - USB_DIR_IN) && ((endpoint->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) + if (usb_endpoint_is_int_in(endpoint)) radio->int_in_endpoint = endpoint; } if (!radio->int_in_endpoint) { diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 4b2e9e8..6f28f6e 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -440,7 +440,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, * command with u16 payload - convert to be16 */ if (payload != NULL) - *(u16 *)payload = cpu_to_be16(*(u16 *)payload); + *(__be16 *)payload = cpu_to_be16(*(u16 *)payload); } else if (payload != NULL) { fm_cb(skb)->fm_op = *((u8 *)payload + 2); @@ -595,7 +595,7 @@ static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev) skb_pull(skb, sizeof(struct fm_event_msg_hdr)); memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen); - fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag); + fmdev->irq_info.flag = be16_to_cpu((__force __be16)fmdev->irq_info.flag); fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag); /* Continue next function in interrupt handler table */ @@ -764,7 +764,7 @@ static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) * Extract PI code and store in local cache. * We need this during AF switch processing. */ - cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata); + cur_picode = be16_to_cpu((__force __be16)rds_fmt.data.groupgeneral.pidata); if (fmdev->rx.stat_info.picode != cur_picode) fmdev->rx.stat_info.picode = cur_picode; @@ -989,7 +989,7 @@ static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev) /* Skip header info and copy only response data */ skb_pull(skb, sizeof(struct fm_event_msg_hdr)); memcpy(&read_freq, skb->data, sizeof(read_freq)); - read_freq = be16_to_cpu(read_freq); + read_freq = be16_to_cpu((__force __be16)read_freq); curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL); jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]; @@ -1317,7 +1317,8 @@ static int load_default_rx_configuration(struct fmdev *fmdev) /* Does FM power on sequence */ static int fm_power_up(struct fmdev *fmdev, u8 mode) { - u16 payload, asic_id, asic_ver; + u16 payload; + __be16 asic_id, asic_ver; int resp_len, ret; u8 fw_name[50]; diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c index ebf09a3..09632cb 100644 --- a/drivers/media/radio/wl128x/fmdrv_rx.c +++ b/drivers/media/radio/wl128x/fmdrv_rx.c @@ -116,7 +116,7 @@ int fm_rx_set_freq(struct fmdev *fmdev, u32 freq) if (ret < 0) goto exit; - curr_frq = be16_to_cpu(curr_frq); + curr_frq = be16_to_cpu((__force __be16)curr_frq); curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL)); if (curr_frq_in_khz != freq) { @@ -189,7 +189,7 @@ int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward, if (ret < 0) return ret; - curr_frq = be16_to_cpu(curr_frq); + curr_frq = be16_to_cpu((__force __be16)curr_frq); last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; /* Check the offset in order to be aligned to the channel spacing*/ @@ -285,7 +285,7 @@ again: if (ret < 0) return ret; - curr_frq = be16_to_cpu(curr_frq); + curr_frq = be16_to_cpu((__force __be16)curr_frq); fmdev->rx.freq = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL)); @@ -517,7 +517,7 @@ int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute) /* Returns the signal strength level of current channel */ int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl) { - u16 curr_rssi_lel; + __be16 curr_rssi_lel; u32 resp_len; int ret; @@ -608,7 +608,7 @@ int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode) /* Gets current RX stereo/mono mode */ int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode) { - u16 curr_mode; + __be16 curr_mode; u32 resp_len; int ret; diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c index 6ea33e0..839970b 100644 --- a/drivers/media/radio/wl128x/fmdrv_tx.c +++ b/drivers/media/radio/wl128x/fmdrv_tx.c @@ -374,7 +374,7 @@ int fm_tx_get_tune_cap_val(struct fmdev *fmdev) if (ret < 0) return ret; - curr_val = be16_to_cpu(curr_val); + curr_val = be16_to_cpu((__force __be16)curr_val); return curr_val; } diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 5e626af..8ce0810 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -164,6 +164,16 @@ config IR_ENE To compile this driver as a module, choose M here: the module will be called ene_ir. +config IR_HIX5HD2 + tristate "Hisilicon hix5hd2 IR remote control" + depends on RC_CORE + help + Say Y here if you want to use hisilicon hix5hd2 remote control. + To compile this driver as a module, choose M here: the module will be + called ir-hix5hd2. + + If you're not sure, select N here + config IR_IMON tristate "SoundGraph iMON Receiver and Display" depends on USB_ARCH_HAS_HCD @@ -333,7 +343,8 @@ config IR_GPIO_CIR config RC_ST tristate "ST remote control receiver" - depends on ARCH_STI && RC_CORE + depends on RC_CORE + depends on ARCH_STI || COMPILE_TEST help Say Y here if you want support for ST remote control driver which allows both IR and UHF RX. @@ -344,7 +355,7 @@ config RC_ST config IR_SUNXI tristate "SUNXI IR remote control" depends on RC_CORE - depends on ARCH_SUNXI + depends on ARCH_SUNXI || COMPILE_TEST ---help--- Say Y if you want to use sunXi internal IR Controller diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 9f9843a1..0989f94 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o +obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o obj-$(CONFIG_IR_IMON) += imon.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index d16d9b4..e80f2c6 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -979,7 +979,7 @@ static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n) dev->tx_reg = 0; dev->tx_done = 0; dev->tx_sample = 0; - dev->tx_sample_pulse = 0; + dev->tx_sample_pulse = false; dbg("TX: %d samples", dev->tx_len); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index f0a1f7d..b516757 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -148,7 +148,6 @@ static int fintek_hw_detect(struct fintek_dev *fintek) u8 vendor_major, vendor_minor; u8 portsel, ir_class; u16 vendor, chip; - int ret = 0; fintek_config_mode_enable(fintek); @@ -208,7 +207,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek) spin_unlock_irqrestore(&fintek->fintek_lock, flags); - return ret; + return 0; } static void fintek_cir_ldev_init(struct fintek_dev *fintek) @@ -644,7 +643,6 @@ static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) static int fintek_resume(struct pnp_dev *pdev) { - int ret = 0; struct fintek_dev *fintek = pnp_get_drvdata(pdev); fit_dbg("%s called", __func__); @@ -661,7 +659,7 @@ static int fintek_resume(struct pnp_dev *pdev) fintek_cir_regs_init(fintek); - return ret; + return 0; } static void fintek_shutdown(struct pnp_dev *pdev) diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index bfb282a..ec49f94 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -25,12 +25,6 @@ /* Decoders lock (only modified to preprocess them) */ static DEFINE_SPINLOCK(img_ir_decoders_lock); -extern struct img_ir_decoder img_ir_nec; -extern struct img_ir_decoder img_ir_jvc; -extern struct img_ir_decoder img_ir_sony; -extern struct img_ir_decoder img_ir_sharp; -extern struct img_ir_decoder img_ir_sanyo; - static bool img_ir_decoders_preprocessed; static struct img_ir_decoder *img_ir_decoders[] = { #ifdef CONFIG_IR_IMG_NEC diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 3e40ce8..8fcc16c 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -168,6 +168,12 @@ struct img_ir_decoder { struct img_ir_filter *out, u64 protocols); }; +extern struct img_ir_decoder img_ir_nec; +extern struct img_ir_decoder img_ir_jvc; +extern struct img_ir_decoder img_ir_sony; +extern struct img_ir_decoder img_ir_sharp; +extern struct img_ir_decoder img_ir_sanyo; + /** * struct img_ir_reg_timings - Reg values for decoder timings at clock rate. * @ctrl: Processed control register value. diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 7115e68..b8837dd 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -87,6 +87,18 @@ static ssize_t lcd_write(struct file *file, const char __user *buf, /*** G L O B A L S ***/ +struct imon_panel_key_table { + u64 hw_code; + u32 keycode; +}; + +struct imon_usb_dev_descr { + __u16 flags; +#define IMON_NO_FLAGS 0 +#define IMON_NEED_20MS_PKT_DELAY 1 + struct imon_panel_key_table key_table[]; +}; + struct imon_context { struct device *dev; /* Newer devices have two interfaces */ @@ -150,6 +162,8 @@ struct imon_context { struct timer_list ttimer; /* touch screen timer */ int touch_x; /* x coordinate on touchscreen */ int touch_y; /* y coordinate on touchscreen */ + struct imon_usb_dev_descr *dev_descr; /* device description with key + table for front panels */ }; #define TOUCH_TIMEOUT (HZ/30) @@ -186,8 +200,132 @@ enum { IMON_KEY_PANEL = 2, }; -enum { - IMON_NEED_20MS_PKT_DELAY = 1 +static struct usb_class_driver imon_vfd_class = { + .name = DEVICE_NAME, + .fops = &vfd_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +static struct usb_class_driver imon_lcd_class = { + .name = DEVICE_NAME, + .fops = &lcd_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +/* imon receiver front panel/knob key table */ +static const struct imon_usb_dev_descr imon_default_table = { + .flags = IMON_NO_FLAGS, + .key_table = { + { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */ + { 0x000000001200ffeell, KEY_UP }, + { 0x000000001300ffeell, KEY_DOWN }, + { 0x000000001400ffeell, KEY_LEFT }, + { 0x000000001500ffeell, KEY_RIGHT }, + { 0x000000001600ffeell, KEY_ENTER }, + { 0x000000001700ffeell, KEY_ESC }, + { 0x000000001f00ffeell, KEY_AUDIO }, + { 0x000000002000ffeell, KEY_VIDEO }, + { 0x000000002100ffeell, KEY_CAMERA }, + { 0x000000002700ffeell, KEY_DVD }, + { 0x000000002300ffeell, KEY_TV }, + { 0x000000002b00ffeell, KEY_EXIT }, + { 0x000000002c00ffeell, KEY_SELECT }, + { 0x000000002d00ffeell, KEY_MENU }, + { 0x000000000500ffeell, KEY_PREVIOUS }, + { 0x000000000700ffeell, KEY_REWIND }, + { 0x000000000400ffeell, KEY_STOP }, + { 0x000000003c00ffeell, KEY_PLAYPAUSE }, + { 0x000000000800ffeell, KEY_FASTFORWARD }, + { 0x000000000600ffeell, KEY_NEXT }, + { 0x000000010000ffeell, KEY_RIGHT }, + { 0x000001000000ffeell, KEY_LEFT }, + { 0x000000003d00ffeell, KEY_SELECT }, + { 0x000100000000ffeell, KEY_VOLUMEUP }, + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, + { 0x000000000100ffeell, KEY_MUTE }, + /* 0xffdc iMON MCE VFD */ + { 0x00010000ffffffeell, KEY_VOLUMEUP }, + { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, + { 0x00000001ffffffeell, KEY_MUTE }, + { 0x0000000fffffffeell, KEY_MEDIA }, + { 0x00000012ffffffeell, KEY_UP }, + { 0x00000013ffffffeell, KEY_DOWN }, + { 0x00000014ffffffeell, KEY_LEFT }, + { 0x00000015ffffffeell, KEY_RIGHT }, + { 0x00000016ffffffeell, KEY_ENTER }, + { 0x00000017ffffffeell, KEY_ESC }, + /* iMON Knob values */ + { 0x000100ffffffffeell, KEY_VOLUMEUP }, + { 0x010000ffffffffeell, KEY_VOLUMEDOWN }, + { 0x000008ffffffffeell, KEY_MUTE }, + { 0, KEY_RESERVED }, + } +}; + +static const struct imon_usb_dev_descr imon_OEM_VFD = { + .flags = IMON_NEED_20MS_PKT_DELAY, + .key_table = { + { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */ + { 0x000000001200ffeell, KEY_UP }, + { 0x000000001300ffeell, KEY_DOWN }, + { 0x000000001400ffeell, KEY_LEFT }, + { 0x000000001500ffeell, KEY_RIGHT }, + { 0x000000001600ffeell, KEY_ENTER }, + { 0x000000001700ffeell, KEY_ESC }, + { 0x000000001f00ffeell, KEY_AUDIO }, + { 0x000000002b00ffeell, KEY_EXIT }, + { 0x000000002c00ffeell, KEY_SELECT }, + { 0x000000002d00ffeell, KEY_MENU }, + { 0x000000000500ffeell, KEY_PREVIOUS }, + { 0x000000000700ffeell, KEY_REWIND }, + { 0x000000000400ffeell, KEY_STOP }, + { 0x000000003c00ffeell, KEY_PLAYPAUSE }, + { 0x000000000800ffeell, KEY_FASTFORWARD }, + { 0x000000000600ffeell, KEY_NEXT }, + { 0x000000010000ffeell, KEY_RIGHT }, + { 0x000001000000ffeell, KEY_LEFT }, + { 0x000000003d00ffeell, KEY_SELECT }, + { 0x000100000000ffeell, KEY_VOLUMEUP }, + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, + { 0x000000000100ffeell, KEY_MUTE }, + /* 0xffdc iMON MCE VFD */ + { 0x00010000ffffffeell, KEY_VOLUMEUP }, + { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, + { 0x00000001ffffffeell, KEY_MUTE }, + { 0x0000000fffffffeell, KEY_MEDIA }, + { 0x00000012ffffffeell, KEY_UP }, + { 0x00000013ffffffeell, KEY_DOWN }, + { 0x00000014ffffffeell, KEY_LEFT }, + { 0x00000015ffffffeell, KEY_RIGHT }, + { 0x00000016ffffffeell, KEY_ENTER }, + { 0x00000017ffffffeell, KEY_ESC }, + /* iMON Knob values */ + { 0x000100ffffffffeell, KEY_VOLUMEUP }, + { 0x010000ffffffffeell, KEY_VOLUMEDOWN }, + { 0x000008ffffffffeell, KEY_MUTE }, + { 0, KEY_RESERVED }, + } +}; + +/* imon receiver front panel/knob key table for DH102*/ +static const struct imon_usb_dev_descr imon_DH102 = { + .flags = IMON_NO_FLAGS, + .key_table = { + { 0x000100000000ffeell, KEY_VOLUMEUP }, + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, + { 0x000000010000ffeell, KEY_MUTE }, + { 0x0000000f0000ffeell, KEY_MEDIA }, + { 0x000000120000ffeell, KEY_UP }, + { 0x000000130000ffeell, KEY_DOWN }, + { 0x000000140000ffeell, KEY_LEFT }, + { 0x000000150000ffeell, KEY_RIGHT }, + { 0x000000160000ffeell, KEY_ENTER }, + { 0x000000170000ffeell, KEY_ESC }, + { 0x0000002b0000ffeell, KEY_EXIT }, + { 0x0000002c0000ffeell, KEY_SELECT }, + { 0x0000002d0000ffeell, KEY_MENU }, + { 0, KEY_RESERVED } + } }; /* @@ -208,7 +346,8 @@ static struct usb_device_id imon_usb_id_table[] = { * SoundGraph iMON PAD (IR & LCD) * SoundGraph iMON Knob (IR only) */ - { USB_DEVICE(0x15c2, 0xffdc) }, + { USB_DEVICE(0x15c2, 0xffdc), + .driver_info = (unsigned long)&imon_default_table }, /* * Newer devices, all driven by the latest iMON Windows driver, full @@ -216,43 +355,62 @@ static struct usb_device_id imon_usb_id_table[] = { * Need user input to fill in details on unknown devices. */ /* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */ - { USB_DEVICE(0x15c2, 0x0034) }, + { USB_DEVICE(0x15c2, 0x0034), + .driver_info = (unsigned long)&imon_DH102 }, /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */ - { USB_DEVICE(0x15c2, 0x0035) }, + { USB_DEVICE(0x15c2, 0x0035), + .driver_info = (unsigned long)&imon_default_table}, /* SoundGraph iMON OEM VFD (IR & VFD) */ - { USB_DEVICE(0x15c2, 0x0036), .driver_info = IMON_NEED_20MS_PKT_DELAY }, + { USB_DEVICE(0x15c2, 0x0036), + .driver_info = (unsigned long)&imon_OEM_VFD }, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x0037) }, + { USB_DEVICE(0x15c2, 0x0037), + .driver_info = (unsigned long)&imon_default_table}, /* SoundGraph iMON OEM LCD (IR & LCD) */ - { USB_DEVICE(0x15c2, 0x0038) }, + { USB_DEVICE(0x15c2, 0x0038), + .driver_info = (unsigned long)&imon_default_table}, /* SoundGraph iMON UltraBay (IR & LCD) */ - { USB_DEVICE(0x15c2, 0x0039) }, + { USB_DEVICE(0x15c2, 0x0039), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x003a) }, + { USB_DEVICE(0x15c2, 0x003a), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x003b) }, + { USB_DEVICE(0x15c2, 0x003b), + .driver_info = (unsigned long)&imon_default_table}, /* SoundGraph iMON OEM Inside (IR only) */ - { USB_DEVICE(0x15c2, 0x003c) }, + { USB_DEVICE(0x15c2, 0x003c), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x003d) }, + { USB_DEVICE(0x15c2, 0x003d), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x003e) }, + { USB_DEVICE(0x15c2, 0x003e), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x003f) }, + { USB_DEVICE(0x15c2, 0x003f), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x0040) }, + { USB_DEVICE(0x15c2, 0x0040), + .driver_info = (unsigned long)&imon_default_table}, /* SoundGraph iMON MINI (IR only) */ - { USB_DEVICE(0x15c2, 0x0041) }, + { USB_DEVICE(0x15c2, 0x0041), + .driver_info = (unsigned long)&imon_default_table}, /* Antec Veris Multimedia Station EZ External (IR only) */ - { USB_DEVICE(0x15c2, 0x0042) }, + { USB_DEVICE(0x15c2, 0x0042), + .driver_info = (unsigned long)&imon_default_table}, /* Antec Veris Multimedia Station Basic Internal (IR only) */ - { USB_DEVICE(0x15c2, 0x0043) }, + { USB_DEVICE(0x15c2, 0x0043), + .driver_info = (unsigned long)&imon_default_table}, /* Antec Veris Multimedia Station Elite (IR & VFD) */ - { USB_DEVICE(0x15c2, 0x0044) }, + { USB_DEVICE(0x15c2, 0x0044), + .driver_info = (unsigned long)&imon_default_table}, /* Antec Veris Multimedia Station Premiere (IR & LCD) */ - { USB_DEVICE(0x15c2, 0x0045) }, + { USB_DEVICE(0x15c2, 0x0045), + .driver_info = (unsigned long)&imon_default_table}, /* device specifics unknown */ - { USB_DEVICE(0x15c2, 0x0046) }, + { USB_DEVICE(0x15c2, 0x0046), + .driver_info = (unsigned long)&imon_default_table}, {} }; @@ -266,67 +424,6 @@ static struct usb_driver imon_driver = { .id_table = imon_usb_id_table, }; -static struct usb_class_driver imon_vfd_class = { - .name = DEVICE_NAME, - .fops = &vfd_fops, - .minor_base = DISPLAY_MINOR_BASE, -}; - -static struct usb_class_driver imon_lcd_class = { - .name = DEVICE_NAME, - .fops = &lcd_fops, - .minor_base = DISPLAY_MINOR_BASE, -}; - -/* imon receiver front panel/knob key table */ -static const struct { - u64 hw_code; - u32 keycode; -} imon_panel_key_table[] = { - { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */ - { 0x000000001200ffeell, KEY_UP }, - { 0x000000001300ffeell, KEY_DOWN }, - { 0x000000001400ffeell, KEY_LEFT }, - { 0x000000001500ffeell, KEY_RIGHT }, - { 0x000000001600ffeell, KEY_ENTER }, - { 0x000000001700ffeell, KEY_ESC }, - { 0x000000001f00ffeell, KEY_AUDIO }, - { 0x000000002000ffeell, KEY_VIDEO }, - { 0x000000002100ffeell, KEY_CAMERA }, - { 0x000000002700ffeell, KEY_DVD }, - { 0x000000002300ffeell, KEY_TV }, - { 0x000000002b00ffeell, KEY_EXIT }, - { 0x000000002c00ffeell, KEY_SELECT }, - { 0x000000002d00ffeell, KEY_MENU }, - { 0x000000000500ffeell, KEY_PREVIOUS }, - { 0x000000000700ffeell, KEY_REWIND }, - { 0x000000000400ffeell, KEY_STOP }, - { 0x000000003c00ffeell, KEY_PLAYPAUSE }, - { 0x000000000800ffeell, KEY_FASTFORWARD }, - { 0x000000000600ffeell, KEY_NEXT }, - { 0x000000010000ffeell, KEY_RIGHT }, - { 0x000001000000ffeell, KEY_LEFT }, - { 0x000000003d00ffeell, KEY_SELECT }, - { 0x000100000000ffeell, KEY_VOLUMEUP }, - { 0x010000000000ffeell, KEY_VOLUMEDOWN }, - { 0x000000000100ffeell, KEY_MUTE }, - /* 0xffdc iMON MCE VFD */ - { 0x00010000ffffffeell, KEY_VOLUMEUP }, - { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, - { 0x00000001ffffffeell, KEY_MUTE }, - { 0x0000000fffffffeell, KEY_MEDIA }, - { 0x00000012ffffffeell, KEY_UP }, - { 0x00000013ffffffeell, KEY_DOWN }, - { 0x00000014ffffffeell, KEY_LEFT }, - { 0x00000015ffffffeell, KEY_RIGHT }, - { 0x00000016ffffffeell, KEY_ENTER }, - { 0x00000017ffffffeell, KEY_ESC }, - /* iMON Knob values */ - { 0x000100ffffffffeell, KEY_VOLUMEUP }, - { 0x010000ffffffffeell, KEY_VOLUMEDOWN }, - { 0x000008ffffffffeell, KEY_MUTE }, -}; - /* to prevent races between open() and disconnect(), probing, etc */ static DEFINE_MUTEX(driver_lock); @@ -1210,18 +1307,19 @@ static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode) return keycode; } -static u32 imon_panel_key_lookup(u64 code) +static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code) { int i; u32 keycode = KEY_RESERVED; + struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; - for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) { - if (imon_panel_key_table[i].hw_code == (code | 0xffee)) { - keycode = imon_panel_key_table[i].keycode; + for (i = 0; key_table[i].hw_code != 0; i++) { + if (key_table[i].hw_code == (code | 0xffee)) { + keycode = key_table[i].keycode; break; } } - + ictx->release_code = false; return keycode; } @@ -1340,7 +1438,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) } buf[2] = dir & 0xFF; buf[3] = (dir >> 8) & 0xFF; - scancode = be32_to_cpu(*((u32 *)buf)); + scancode = be32_to_cpu(*((__be32 *)buf)); } } else { /* @@ -1404,7 +1502,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) } buf[2] = dir & 0xFF; buf[3] = (dir >> 8) & 0xFF; - scancode = be32_to_cpu(*((u32 *)buf)); + scancode = be32_to_cpu(*((__be32 *)buf)); } else { /* * Hack alert: instead of using keycodes, we have @@ -1509,11 +1607,12 @@ static void imon_incoming_packet(struct imon_context *ictx, /* Figure out what key was pressed */ if (len == 8 && buf[7] == 0xee) { - scancode = be64_to_cpu(*((u64 *)buf)); + scancode = be64_to_cpu(*((__be64 *)buf)); ktype = IMON_KEY_PANEL; - kc = imon_panel_key_lookup(scancode); + kc = imon_panel_key_lookup(ictx, scancode); + ictx->release_code = false; } else { - scancode = be32_to_cpu(*((u32 *)buf)); + scancode = be32_to_cpu(*((__be32 *)buf)); if (ictx->rc_type == RC_BIT_RC6_MCE) { ktype = IMON_KEY_IMON; if (buf[0] == 0x80) @@ -1908,6 +2007,7 @@ out: static struct input_dev *imon_init_idev(struct imon_context *ictx) { + struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; struct input_dev *idev; int ret, i; @@ -1933,8 +2033,8 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx) BIT_MASK(REL_WHEEL); /* panel and/or knob code support */ - for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) { - u32 kc = imon_panel_key_table[i].keycode; + for (i = 0; key_table[i].hw_code != 0; i++) { + u32 kc = key_table[i].keycode; __set_bit(kc, idev->keybit); } @@ -2023,7 +2123,7 @@ static bool imon_find_endpoints(struct imon_context *ictx, for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) { ep = &iface_desc->endpoint[i].desc; ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; - ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + ep_type = usb_endpoint_type(ep); if (!ir_ep_found && ep_dir == USB_DIR_IN && ep_type == USB_ENDPOINT_XFER_INT) { @@ -2135,9 +2235,11 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf, ictx->vendor = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor); ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct); + /* save drive info for later accessing the panel/knob key table */ + ictx->dev_descr = (struct imon_usb_dev_descr *)id->driver_info; /* default send_packet delay is 5ms but some devices need more */ - ictx->send_packet_delay = id->driver_info & IMON_NEED_20MS_PKT_DELAY ? - 20 : 5; + ictx->send_packet_delay = ictx->dev_descr->flags & + IMON_NEED_20MS_PKT_DELAY ? 20 : 5; ret = -ENODEV; iface_desc = intf->cur_altsetting; @@ -2181,6 +2283,7 @@ idev_setup_failed: usb_kill_urb(ictx->rx_urb_intf0); urb_submit_failed: find_endpoint_failed: + usb_put_dev(ictx->usbdev_intf0); mutex_unlock(&ictx->lock); usb_free_urb(tx_urb); tx_urb_alloc_failed: @@ -2253,6 +2356,7 @@ urb_submit_failed: input_unregister_device(ictx->touch); touch_setup_failed: find_endpoint_failed: + usb_put_dev(ictx->usbdev_intf1); mutex_unlock(&ictx->lock); usb_free_urb(rx_urb); rx_urb_alloc_failed: @@ -2366,11 +2470,13 @@ static int imon_probe(struct usb_interface *interface, usbdev->bus->busnum, usbdev->devnum); mutex_unlock(&driver_lock); + usb_put_dev(usbdev); return 0; fail: mutex_unlock(&driver_lock); + usb_put_dev(usbdev); dev_err(dev, "unable to register, err %d\n", ret); return ret; @@ -2410,6 +2516,7 @@ static void imon_disconnect(struct usb_interface *interface) if (ifnum == 0) { ictx->dev_present_intf0 = false; usb_kill_urb(ictx->rx_urb_intf0); + usb_put_dev(ictx->usbdev_intf0); input_unregister_device(ictx->idev); rc_unregister_device(ictx->rdev); if (ictx->display_supported) { @@ -2421,6 +2528,7 @@ static void imon_disconnect(struct usb_interface *interface) } else { ictx->dev_present_intf1 = false; usb_kill_urb(ictx->rx_urb_intf1); + usb_put_dev(ictx->usbdev_intf1); if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) { input_unregister_device(ictx->touch); del_timer_sync(&ictx->ttimer); diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c new file mode 100644 index 0000000..08bbd4f --- /dev/null +++ b/drivers/media/rc/ir-hix5hd2.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <media/rc-core.h> + +/* Allow the driver to compile on all architectures */ +#ifndef writel_relaxed +# define writel_relaxed writel +#endif +#ifndef readl_relaxed +# define readl_relaxed readl +#endif + +#define IR_ENABLE 0x00 +#define IR_CONFIG 0x04 +#define CNT_LEADS 0x08 +#define CNT_LEADE 0x0c +#define CNT_SLEADE 0x10 +#define CNT0_B 0x14 +#define CNT1_B 0x18 +#define IR_BUSY 0x1c +#define IR_DATAH 0x20 +#define IR_DATAL 0x24 +#define IR_INTM 0x28 +#define IR_INTS 0x2c +#define IR_INTC 0x30 +#define IR_START 0x34 + +/* interrupt mask */ +#define INTMS_SYMBRCV (BIT(24) | BIT(8)) +#define INTMS_TIMEOUT (BIT(25) | BIT(9)) +#define INTMS_OVERFLOW (BIT(26) | BIT(10)) +#define INT_CLR_OVERFLOW BIT(18) +#define INT_CLR_TIMEOUT BIT(17) +#define INT_CLR_RCV BIT(16) +#define INT_CLR_RCVTIMEOUT (BIT(16) | BIT(17)) + +#define IR_CLK 0x48 +#define IR_CLK_ENABLE BIT(4) +#define IR_CLK_RESET BIT(5) + +#define IR_CFG_WIDTH_MASK 0xffff +#define IR_CFG_WIDTH_SHIFT 16 +#define IR_CFG_FORMAT_MASK 0x3 +#define IR_CFG_FORMAT_SHIFT 14 +#define IR_CFG_INT_LEVEL_MASK 0x3f +#define IR_CFG_INT_LEVEL_SHIFT 8 +/* only support raw mode */ +#define IR_CFG_MODE_RAW BIT(7) +#define IR_CFG_FREQ_MASK 0x7f +#define IR_CFG_FREQ_SHIFT 0 +#define IR_CFG_INT_THRESHOLD 1 +/* symbol start from low to high, symbol stream end at high*/ +#define IR_CFG_SYMBOL_FMT 0 +#define IR_CFG_SYMBOL_MAXWIDTH 0x3e80 + +#define IR_HIX5HD2_NAME "hix5hd2-ir" + +struct hix5hd2_ir_priv { + int irq; + void volatile __iomem *base; + struct device *dev; + struct rc_dev *rdev; + struct regmap *regmap; + struct clk *clock; + unsigned long rate; +}; + +static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) +{ + u32 val; + + regmap_read(dev->regmap, IR_CLK, &val); + if (on) { + val &= ~IR_CLK_RESET; + val |= IR_CLK_ENABLE; + } else { + val &= ~IR_CLK_ENABLE; + val |= IR_CLK_RESET; + } + regmap_write(dev->regmap, IR_CLK, val); +} + +static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv) +{ + int timeout = 10000; + u32 val, rate; + + writel_relaxed(0x01, priv->base + IR_ENABLE); + while (readl_relaxed(priv->base + IR_BUSY)) { + if (timeout--) { + udelay(1); + } else { + dev_err(priv->dev, "IR_BUSY timeout\n"); + return -ETIMEDOUT; + } + } + + /* Now only support raw mode, with symbol start from low to high */ + rate = DIV_ROUND_CLOSEST(priv->rate, 1000000); + val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT; + val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT; + val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK + << IR_CFG_INT_LEVEL_SHIFT; + val |= IR_CFG_MODE_RAW; + val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT; + writel_relaxed(val, priv->base + IR_CONFIG); + + writel_relaxed(0x00, priv->base + IR_INTM); + /* write arbitrary value to start */ + writel_relaxed(0x01, priv->base + IR_START); + return 0; +} + +static int hix5hd2_ir_open(struct rc_dev *rdev) +{ + struct hix5hd2_ir_priv *priv = rdev->priv; + + hix5hd2_ir_enable(priv, true); + return hix5hd2_ir_config(priv); +} + +static void hix5hd2_ir_close(struct rc_dev *rdev) +{ + struct hix5hd2_ir_priv *priv = rdev->priv; + + hix5hd2_ir_enable(priv, false); +} + +static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) +{ + u32 symb_num, symb_val, symb_time; + u32 data_l, data_h; + u32 irq_sr, i; + struct hix5hd2_ir_priv *priv = data; + + irq_sr = readl_relaxed(priv->base + IR_INTS); + if (irq_sr & INTMS_OVERFLOW) { + /* + * we must read IR_DATAL first, then we can clean up + * IR_INTS availably since logic would not clear + * fifo when overflow, drv do the job + */ + ir_raw_event_reset(priv->rdev); + symb_num = readl_relaxed(priv->base + IR_DATAH); + for (i = 0; i < symb_num; i++) + readl_relaxed(priv->base + IR_DATAL); + + writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC); + dev_info(priv->dev, "overflow, level=%d\n", + IR_CFG_INT_THRESHOLD); + } + + if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) { + DEFINE_IR_RAW_EVENT(ev); + + symb_num = readl_relaxed(priv->base + IR_DATAH); + for (i = 0; i < symb_num; i++) { + symb_val = readl_relaxed(priv->base + IR_DATAL); + data_l = ((symb_val & 0xffff) * 10); + data_h = ((symb_val >> 16) & 0xffff) * 10; + symb_time = (data_l + data_h) / 10; + + ev.duration = US_TO_NS(data_l); + ev.pulse = true; + ir_raw_event_store(priv->rdev, &ev); + + if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) { + ev.duration = US_TO_NS(data_h); + ev.pulse = false; + ir_raw_event_store(priv->rdev, &ev); + } else { + ir_raw_event_set_idle(priv->rdev, true); + } + } + + if (irq_sr & INTMS_SYMBRCV) + writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC); + if (irq_sr & INTMS_TIMEOUT) + writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC); + } + + /* Empty software fifo */ + ir_raw_event_handle(priv->rdev); + return IRQ_HANDLED; +} + +static int hix5hd2_ir_probe(struct platform_device *pdev) +{ + struct rc_dev *rdev; + struct device *dev = &pdev->dev; + struct resource *res; + struct hix5hd2_ir_priv *priv; + struct device_node *node = pdev->dev.of_node; + const char *map_name; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = syscon_regmap_lookup_by_phandle(node, + "hisilicon,power-syscon"); + if (IS_ERR(priv->regmap)) { + dev_err(dev, "no power-reg\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)priv->base)) + return PTR_ERR((__force void *)priv->base); + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + dev_err(dev, "irq can not get\n"); + return priv->irq; + } + + rdev = rc_allocate_device(); + if (!rdev) + return -ENOMEM; + + priv->clock = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clock)) { + dev_err(dev, "clock not found\n"); + ret = PTR_ERR(priv->clock); + goto err; + } + clk_prepare_enable(priv->clock); + priv->rate = clk_get_rate(priv->clock); + + rdev->driver_type = RC_DRIVER_IR_RAW; + rdev->allowed_protocols = RC_BIT_ALL; + rdev->priv = priv; + rdev->open = hix5hd2_ir_open; + rdev->close = hix5hd2_ir_close; + rdev->driver_name = IR_HIX5HD2_NAME; + map_name = of_get_property(node, "linux,rc-map-name", NULL); + rdev->map_name = map_name ?: RC_MAP_EMPTY; + rdev->input_name = IR_HIX5HD2_NAME; + rdev->input_phys = IR_HIX5HD2_NAME "/input0"; + rdev->input_id.bustype = BUS_HOST; + rdev->input_id.vendor = 0x0001; + rdev->input_id.product = 0x0001; + rdev->input_id.version = 0x0100; + rdev->rx_resolution = US_TO_NS(10); + rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10); + + ret = rc_register_device(rdev); + if (ret < 0) + goto clkerr; + + if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt, + IRQF_NO_SUSPEND, pdev->name, priv) < 0) { + dev_err(dev, "IRQ %d register failed\n", priv->irq); + ret = -EINVAL; + goto regerr; + } + + priv->rdev = rdev; + priv->dev = dev; + platform_set_drvdata(pdev, priv); + + return ret; + +regerr: + rc_unregister_device(rdev); + rdev = NULL; +clkerr: + clk_disable_unprepare(priv->clock); +err: + rc_free_device(rdev); + dev_err(dev, "Unable to register device (%d)\n", ret); + return ret; +} + +static int hix5hd2_ir_remove(struct platform_device *pdev) +{ + struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev); + + clk_disable_unprepare(priv->clock); + rc_unregister_device(priv->rdev); + return 0; +} + +#ifdef CONFIG_PM +static int hix5hd2_ir_suspend(struct device *dev) +{ + struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clock); + hix5hd2_ir_enable(priv, false); + + return 0; +} + +static int hix5hd2_ir_resume(struct device *dev) +{ + struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); + + hix5hd2_ir_enable(priv, true); + clk_prepare_enable(priv->clock); + + writel_relaxed(0x01, priv->base + IR_ENABLE); + writel_relaxed(0x00, priv->base + IR_INTM); + writel_relaxed(0xff, priv->base + IR_INTC); + writel_relaxed(0x01, priv->base + IR_START); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend, + hix5hd2_ir_resume); + +static struct of_device_id hix5hd2_ir_table[] = { + { .compatible = "hisilicon,hix5hd2-ir", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_ir_table); + +static struct platform_driver hix5hd2_ir_driver = { + .driver = { + .name = IR_HIX5HD2_NAME, + .of_match_table = hix5hd2_ir_table, + .pm = &hix5hd2_ir_pm_ops, + }, + .probe = hix5hd2_ir_probe, + .remove = hix5hd2_ir_remove, +}; + +module_platform_driver(hix5hd2_ir_driver); + +MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms"); +MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:hix5hd2-ir"); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 447fe35..56abf91 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1666,7 +1666,6 @@ static int ite_suspend(struct pnp_dev *pdev, pm_message_t state) static int ite_resume(struct pnp_dev *pdev) { - int ret = 0; struct ite_dev *dev = pnp_get_drvdata(pdev); unsigned long flags; @@ -1681,7 +1680,7 @@ static int ite_resume(struct pnp_dev *pdev) spin_unlock_irqrestore(&dev->lock, flags); - return ret; + return 0; } static void ite_shutdown(struct pnp_dev *pdev) diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 0b8c549..abf6079 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-dm1105-nec.o \ rc-dntv-live-dvb-t.o \ rc-dntv-live-dvbt-pro.o \ + rc-dvbsky.o \ rc-em-terratec.o \ rc-encore-enltv2.o \ rc-encore-enltv.o \ diff --git a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c new file mode 100644 index 0000000..c5115a1 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-dvbsky.c @@ -0,0 +1,78 @@ +/* rc-dvbsky.c - Keytable for DVBSky Remote Controllers + * + * keymap imported from ir-keymaps.c + * + * + * Copyright (c) 2010-2012 by Nibble Max <nibble.max@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. + */ + +#include <media/rc-map.h> +#include <linux/module.h> +/* + * This table contains the complete RC5 code, instead of just the data part + */ + +static struct rc_map_table rc5_dvbsky[] = { + { 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 }, + { 0x000d, KEY_OK }, + { 0x000b, KEY_STOP }, + { 0x000c, KEY_EXIT }, + { 0x000e, KEY_CAMERA }, /*Snap shot*/ + { 0x000f, KEY_SUBTITLE }, /*PIP*/ + { 0x0010, KEY_VOLUMEUP }, + { 0x0011, KEY_VOLUMEDOWN }, + { 0x0012, KEY_FAVORITES }, + { 0x0013, KEY_LIST }, /*Info*/ + { 0x0016, KEY_PAUSE }, + { 0x0017, KEY_PLAY }, + { 0x001f, KEY_RECORD }, + { 0x0020, KEY_CHANNELDOWN }, + { 0x0021, KEY_CHANNELUP }, + { 0x0025, KEY_POWER2 }, + { 0x0026, KEY_REWIND }, + { 0x0027, KEY_FASTFORWARD }, + { 0x0029, KEY_LAST }, + { 0x002b, KEY_MENU }, + { 0x002c, KEY_EPG }, + { 0x002d, KEY_ZOOM }, +}; + +static struct rc_map_list rc5_dvbsky_map = { + .map = { + .scan = rc5_dvbsky, + .size = ARRAY_SIZE(rc5_dvbsky), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_DVBSKY, + } +}; + +static int __init init_rc_map_rc5_dvbsky(void) +{ + return rc_map_register(&rc5_dvbsky_map); +} + +static void __exit exit_rc_map_rc5_dvbsky(void) +{ + rc_map_unregister(&rc5_dvbsky_map); +} + +module_init(init_rc_map_rc5_dvbsky) +module_exit(exit_rc_map_rc5_dvbsky) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nibble Max <nibble.max@gmail.com>"); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index dc5cbff..249d2fb 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -595,7 +595,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LIRC_GET_FEATURES: - result = put_user(ir->d.features, (__u32 *)arg); + result = put_user(ir->d.features, (__u32 __user *)arg); break; case LIRC_GET_REC_MODE: if (!(ir->d.features & LIRC_CAN_REC_MASK)) { @@ -605,7 +605,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) result = put_user(LIRC_REC2MODE (ir->d.features & LIRC_CAN_REC_MASK), - (__u32 *)arg); + (__u32 __user *)arg); break; case LIRC_SET_REC_MODE: if (!(ir->d.features & LIRC_CAN_REC_MASK)) { @@ -613,7 +613,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = get_user(mode, (__u32 *)arg); + result = get_user(mode, (__u32 __user *)arg); if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) result = -EINVAL; /* @@ -622,7 +622,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ break; case LIRC_GET_LENGTH: - result = put_user(ir->d.code_length, (__u32 *)arg); + result = put_user(ir->d.code_length, (__u32 __user *)arg); break; case LIRC_GET_MIN_TIMEOUT: if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || @@ -631,7 +631,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = put_user(ir->d.min_timeout, (__u32 *)arg); + result = put_user(ir->d.min_timeout, (__u32 __user *)arg); break; case LIRC_GET_MAX_TIMEOUT: if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || @@ -640,7 +640,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = put_user(ir->d.max_timeout, (__u32 *)arg); + result = put_user(ir->d.max_timeout, (__u32 __user *)arg); break; default: result = -EINVAL; @@ -736,7 +736,7 @@ ssize_t lirc_dev_fop_read(struct file *file, } } else { lirc_buffer_read(ir->buf, buf); - ret = copy_to_user((void *)buffer+written, buf, + ret = copy_to_user((void __user *)buffer+written, buf, ir->buf->chunk_size); if (!ret) written += ir->buf->chunk_size; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 45b0894..2cdb740 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -397,6 +397,10 @@ static struct usb_device_id mceusb_dev_table[] = { .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb131), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb138), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb139), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_PCTV, 0x0259), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_PCTV, 0x025e), @@ -1198,10 +1202,9 @@ static void mceusb_flash_led(struct mceusb_dev *ir) mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED)); } -static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir, - struct usb_interface *intf) +static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) { - struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); + struct usb_device *udev = ir->usbdev; struct device *dev = ir->dev; struct rc_dev *rc; int ret; @@ -1341,7 +1344,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, if (!ir->urb_in) goto urb_in_alloc_fail; - ir->usbdev = dev; + ir->usbdev = usb_get_dev(dev); ir->dev = &intf->dev; ir->len_in = maxp; ir->flags.microsoft_gen1 = is_microsoft_gen1; @@ -1362,7 +1365,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, snprintf(name + strlen(name), sizeof(name) - strlen(name), " %s", buf); - ir->rc = mceusb_init_rc_dev(ir, intf); + ir->rc = mceusb_init_rc_dev(ir); if (!ir->rc) goto rc_dev_fail; @@ -1408,6 +1411,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, /* Error-handling path */ rc_dev_fail: + usb_put_dev(ir->usbdev); usb_free_urb(ir->urb_in); urb_in_alloc_fail: usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in); @@ -1435,6 +1439,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); + usb_put_dev(dev); kfree(ir); } diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 7f4fd85..9c2c863 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -229,7 +229,6 @@ static int nvt_hw_detect(struct nvt_dev *nvt) { unsigned long flags; u8 chip_major, chip_minor; - int ret = 0; char chip_id[12]; bool chip_unknown = false; @@ -285,7 +284,7 @@ static int nvt_hw_detect(struct nvt_dev *nvt) nvt->chip_minor = chip_minor; spin_unlock_irqrestore(&nvt->nvt_lock, flags); - return ret; + return 0; } static void nvt_cir_ldev_init(struct nvt_dev *nvt) @@ -1177,7 +1176,6 @@ static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state) static int nvt_resume(struct pnp_dev *pdev) { - int ret = 0; struct nvt_dev *nvt = pnp_get_drvdata(pdev); nvt_dbg("%s called", __func__); @@ -1195,7 +1193,7 @@ static int nvt_resume(struct pnp_dev *pdev) nvt_cir_regs_init(nvt); nvt_cir_wake_regs_init(nvt); - return ret; + return 0; } static void nvt_shutdown(struct pnp_dev *pdev) diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 5c15135..0e758ae 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -22,8 +22,8 @@ struct st_rc_device { int irq; int irq_wake; struct clk *sys_clock; - void *base; /* Register base address */ - void *rx_base;/* RX Register base address */ + volatile void __iomem *base; /* Register base address */ + volatile void __iomem *rx_base;/* RX Register base address */ struct rc_dev *rdev; bool overclocking; int sample_mult; @@ -267,8 +267,8 @@ static int st_rc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rc_dev->base = devm_ioremap_resource(dev, res); - if (IS_ERR(rc_dev->base)) { - ret = PTR_ERR(rc_dev->base); + if (IS_ERR((__force void *)rc_dev->base)) { + ret = PTR_ERR((__force void *)rc_dev->base); goto err; } @@ -278,7 +278,7 @@ static int st_rc_probe(struct platform_device *pdev) rc_dev->rx_base = rc_dev->base; - rc_dev->rstc = reset_control_get(dev, NULL); + rc_dev->rstc = reset_control_get_optional(dev, NULL); if (IS_ERR(rc_dev->rstc)) rc_dev->rstc = NULL; @@ -376,9 +376,10 @@ static int st_rc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume); #endif +static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume); + #ifdef CONFIG_OF static struct of_device_id st_rc_match[] = { { .compatible = "st,comms-irb", }, @@ -391,11 +392,8 @@ MODULE_DEVICE_TABLE(of, st_rc_match); static struct platform_driver st_rc_driver = { .driver = { .name = IR_ST_NAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(st_rc_match), -#ifdef CONFIG_PM .pm = &st_rc_pm_ops, -#endif }, .probe = st_rc_probe, .remove = st_rc_remove, diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 80c4fee..bf4a442 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -362,16 +362,14 @@ static int streamzap_probe(struct usb_interface *intf, } sz->endpoint = &(iface_host->endpoint[0].desc); - if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - != USB_DIR_IN) { + if (!usb_endpoint_dir_in(sz->endpoint)) { dev_err(&intf->dev, "%s: endpoint doesn't match input device " "02%02x\n", __func__, sz->endpoint->bEndpointAddress); retval = -ENODEV; goto free_sz; } - if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - != USB_ENDPOINT_XFER_INT) { + if (!usb_endpoint_xfer_int(sz->endpoint)) { dev_err(&intf->dev, "%s: endpoint attributes don't match xfer " "02%02x\n", __func__, sz->endpoint->bmAttributes); retval = -ENODEV; diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index d79fd1ce..f039dc2 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -204,6 +204,7 @@ config MEDIA_TUNER_FC0013 config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help NXP TDA18212 silicon tuner driver. @@ -226,6 +227,7 @@ config MEDIA_TUNER_FC2580 config MEDIA_TUNER_M88TS2022 tristate "Montage M88TS2022 silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help Montage M88TS2022 silicon tuner driver. @@ -247,6 +249,7 @@ config MEDIA_TUNER_SI2157 config MEDIA_TUNER_IT913X tristate "ITE Tech IT913x silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help ITE Tech IT913x silicon tuner driver. @@ -257,4 +260,18 @@ config MEDIA_TUNER_R820T default m if !MEDIA_SUBDRV_AUTOSELECT help Rafael Micro R820T silicon tuner driver. + +config MEDIA_TUNER_MXL301RF + tristate "MaxLinear MxL301RF tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL301RF OFDM tuner driver. + +config MEDIA_TUNER_QM1D1C0042 + tristate "Sharp QM1D1C0042 tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Sharp QM1D1C0042 trellis coded 8PSK tuner driver. endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 5591699..49fcf80 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -37,8 +37,10 @@ obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o -obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o +obj-$(CONFIG_MEDIA_TUNER_IT913X) += it913x.o obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o +obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o +obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 90d9334..510239f 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -26,7 +26,7 @@ static int e4000_init(struct dvb_frontend *fe) struct e4000 *s = fe->tuner_priv; int ret; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); /* dummy I2C to ensure I2C wakes up */ ret = regmap_write(s->regmap, 0x02, 0x40); @@ -87,7 +87,7 @@ static int e4000_init(struct dvb_frontend *fe) s->active = true; err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -97,7 +97,7 @@ static int e4000_sleep(struct dvb_frontend *fe) struct e4000 *s = fe->tuner_priv; int ret; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); s->active = false; @@ -106,7 +106,7 @@ static int e4000_sleep(struct dvb_frontend *fe) goto err; err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -121,9 +121,8 @@ static int e4000_set_params(struct dvb_frontend *fe) u8 buf[5], i_data[4], q_data[4]; dev_dbg(&s->client->dev, - "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", - __func__, c->delivery_system, c->frequency, - c->bandwidth_hz); + "delivery_system=%d frequency=%u bandwidth_hz=%u\n", + c->delivery_system, c->frequency, c->bandwidth_hz); /* gain control manual */ ret = regmap_write(s->regmap, 0x1a, 0x00); @@ -150,9 +149,8 @@ static int e4000_set_params(struct dvb_frontend *fe) buf[3] = 0x00; buf[4] = e4000_pll_lut[i].div; - dev_dbg(&s->client->dev, - "%s: f_vco=%llu pll div=%d sigma_delta=%04x\n", - __func__, f_vco, buf[0], sigma_delta); + dev_dbg(&s->client->dev, "f_vco=%llu pll div=%d sigma_delta=%04x\n", + f_vco, buf[0], sigma_delta); ret = regmap_bulk_write(s->regmap, 0x09, buf, 5); if (ret) @@ -253,7 +251,7 @@ static int e4000_set_params(struct dvb_frontend *fe) goto err; err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -262,7 +260,7 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { struct e4000 *s = fe->tuner_priv; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); *frequency = 0; /* Zero-IF */ @@ -276,10 +274,9 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe) int ret; u8 u8tmp; - dev_dbg(&s->client->dev, "%s: lna auto=%d->%d val=%d->%d\n", - __func__, s->lna_gain_auto->cur.val, - s->lna_gain_auto->val, s->lna_gain->cur.val, - s->lna_gain->val); + dev_dbg(&s->client->dev, "lna auto=%d->%d val=%d->%d\n", + s->lna_gain_auto->cur.val, s->lna_gain_auto->val, + s->lna_gain->cur.val, s->lna_gain->val); if (s->lna_gain_auto->val && s->if_gain_auto->cur.val) u8tmp = 0x17; @@ -301,7 +298,7 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe) } err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -312,10 +309,9 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe) int ret; u8 u8tmp; - dev_dbg(&s->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n", - __func__, s->mixer_gain_auto->cur.val, - s->mixer_gain_auto->val, s->mixer_gain->cur.val, - s->mixer_gain->val); + dev_dbg(&s->client->dev, "mixer auto=%d->%d val=%d->%d\n", + s->mixer_gain_auto->cur.val, s->mixer_gain_auto->val, + s->mixer_gain->cur.val, s->mixer_gain->val); if (s->mixer_gain_auto->val) u8tmp = 0x15; @@ -333,7 +329,7 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe) } err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -345,10 +341,9 @@ static int e4000_set_if_gain(struct dvb_frontend *fe) u8 buf[2]; u8 u8tmp; - dev_dbg(&s->client->dev, "%s: if auto=%d->%d val=%d->%d\n", - __func__, s->if_gain_auto->cur.val, - s->if_gain_auto->val, s->if_gain->cur.val, - s->if_gain->val); + dev_dbg(&s->client->dev, "if auto=%d->%d val=%d->%d\n", + s->if_gain_auto->cur.val, s->if_gain_auto->val, + s->if_gain->cur.val, s->if_gain->val); if (s->if_gain_auto->val && s->lna_gain_auto->cur.val) u8tmp = 0x17; @@ -372,7 +367,7 @@ static int e4000_set_if_gain(struct dvb_frontend *fe) } err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -390,7 +385,7 @@ static int e4000_pll_lock(struct dvb_frontend *fe) s->pll_lock->val = (utmp & 0x01); err: if (ret) - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -400,7 +395,7 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl) struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl); int ret; - if (s->active == false) + if (!s->active) return 0; switch (ctrl->id) { @@ -408,8 +403,8 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ret = e4000_pll_lock(s->fe); break; default: - dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n", - __func__, ctrl->id, ctrl->name); + dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; } @@ -423,7 +418,7 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - if (s->active == false) + if (!s->active) return 0; switch (ctrl->id) { @@ -445,8 +440,8 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) ret = e4000_set_if_gain(s->fe); break; default: - dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n", - __func__, ctrl->id, ctrl->name); + dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; } @@ -494,7 +489,7 @@ static int e4000_probe(struct i2c_client *client, s = kzalloc(sizeof(struct e4000), GFP_KERNEL); if (!s) { ret = -ENOMEM; - dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + dev_err(&client->dev, "kzalloc() failed\n"); goto err; } @@ -512,7 +507,7 @@ static int e4000_probe(struct i2c_client *client, if (ret) goto err; - dev_dbg(&s->client->dev, "%s: chip id=%02x\n", __func__, utmp); + dev_dbg(&s->client->dev, "chip id=%02x\n", utmp); if (utmp != 0x40) { ret = -ENODEV; @@ -559,9 +554,7 @@ static int e4000_probe(struct i2c_client *client, s->sd.ctrl_handler = &s->hdl; #endif - dev_info(&s->client->dev, - "%s: Elonics E4000 successfully identified\n", - KBUILD_MODNAME); + dev_info(&s->client->dev, "Elonics E4000 successfully identified\n"); fe->tuner_priv = s; memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops, @@ -573,7 +566,7 @@ static int e4000_probe(struct i2c_client *client, return 0; err: if (ret) { - dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); kfree(s); } @@ -586,7 +579,7 @@ static int e4000_remove(struct i2c_client *client) struct e4000 *s = container_of(sd, struct e4000, sd); struct dvb_frontend *fe = s->fe; - dev_dbg(&client->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); #if IS_ENABLED(CONFIG_VIDEO_V4L2) v4l2_ctrl_handler_free(&s->hdl); diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c new file mode 100644 index 0000000..a076c87 --- /dev/null +++ b/drivers/media/tuners/it913x.c @@ -0,0 +1,478 @@ +/* + * ITE IT913X silicon tuner driver + * + * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) + * IT9137 Copyright (C) ITE Tech 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; 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 "it913x.h" +#include <linux/regmap.h> + +struct it913x_dev { + struct i2c_client *client; + struct regmap *regmap; + struct dvb_frontend *fe; + u8 chip_ver:2; + u8 role:2; + u16 xtal; + u8 fdiv; + u8 clk_mode; + u32 fn_min; + bool active; +}; + +static int it913x_init(struct dvb_frontend *fe) +{ + struct it913x_dev *dev = fe->tuner_priv; + int ret; + unsigned int utmp; + u8 iqik_m_cal, nv_val, buf[2]; + static const u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; + unsigned long timeout; + + dev_dbg(&dev->client->dev, "role %u\n", dev->role); + + ret = regmap_write(dev->regmap, 0x80ec4c, 0x68); + if (ret) + goto err; + + usleep_range(10000, 100000); + + ret = regmap_read(dev->regmap, 0x80ec86, &utmp); + if (ret) + goto err; + + switch (utmp) { + case 0: + /* 12.000 MHz */ + dev->clk_mode = utmp; + dev->xtal = 2000; + dev->fdiv = 3; + iqik_m_cal = 16; + break; + case 1: + /* 20.480 MHz */ + dev->clk_mode = utmp; + dev->xtal = 640; + dev->fdiv = 1; + iqik_m_cal = 6; + break; + default: + dev_err(&dev->client->dev, "unknown clock identifier %d\n", utmp); + goto err; + } + + ret = regmap_read(dev->regmap, 0x80ed03, &utmp); + if (ret) + goto err; + + else if (utmp < ARRAY_SIZE(nv)) + nv_val = nv[utmp]; + else + nv_val = 2; + + #define TIMEOUT 50 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = regmap_bulk_read(dev->regmap, 0x80ed23, buf, 2); + if (ret) + goto err; + + utmp = (buf[1] << 8) | (buf[0] << 0); + if (utmp) + break; + } + + dev_dbg(&dev->client->dev, "r_fbc_m_bdry took %u ms, val %u\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT), utmp); + + dev->fn_min = dev->xtal * utmp; + dev->fn_min /= (dev->fdiv * nv_val); + dev->fn_min *= 1000; + dev_dbg(&dev->client->dev, "fn_min %u\n", dev->fn_min); + + /* + * Chip version BX never sets that flag so we just wait 50ms in that + * case. It is possible poll BX similarly than AX and then timeout in + * order to get 50ms delay, but that causes about 120 extra I2C + * messages. As for now, we just wait and reduce IO. + */ + if (dev->chip_ver == 1) { + #define TIMEOUT 50 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = regmap_read(dev->regmap, 0x80ec82, &utmp); + if (ret) + goto err; + + if (utmp) + break; + } + + dev_dbg(&dev->client->dev, "p_tsm_init_mode took %u ms, val %u\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT), utmp); + } else { + msleep(50); + } + + ret = regmap_write(dev->regmap, 0x80ed81, iqik_m_cal); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80ec57, 0x00); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80ec58, 0x00); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80ec40, 0x01); + if (ret) + goto err; + + dev->active = true; + + return 0; +err: + dev_dbg(&dev->client->dev, "failed %d\n", ret); + return ret; +} + +static int it913x_sleep(struct dvb_frontend *fe) +{ + struct it913x_dev *dev = fe->tuner_priv; + int ret, len; + + dev_dbg(&dev->client->dev, "role %u\n", dev->role); + + dev->active = false; + + ret = regmap_bulk_write(dev->regmap, 0x80ec40, "\x00", 1); + if (ret) + goto err; + + /* + * Writing '0x00' to master tuner register '0x80ec08' causes slave tuner + * communication lost. Due to that, we cannot put master full sleep. + */ + if (dev->role == IT913X_ROLE_DUAL_MASTER) + len = 4; + else + len = 15; + + dev_dbg(&dev->client->dev, "role %u, len %d\n", dev->role, len); + + ret = regmap_bulk_write(dev->regmap, 0x80ec02, + "\x3f\x1f\x3f\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + len); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x80ec12, "\x00\x00\x00\x00", 4); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x80ec17, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00", 9); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x80ec22, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x80ec20, "\x00", 1); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x80ec3f, "\x01", 1); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&dev->client->dev, "failed %d\n", ret); + return ret; +} + +static int it913x_set_params(struct dvb_frontend *fe) +{ + struct it913x_dev *dev = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + u32 pre_lo_freq, t_cal_freq; + u16 iqik_m_cal, n_div; + u8 u8tmp, n, l_band, lna_band; + + dev_dbg(&dev->client->dev, "role=%u, frequency %u, bandwidth_hz %u\n", + dev->role, c->frequency, c->bandwidth_hz); + + if (!dev->active) { + ret = -EINVAL; + goto err; + } + + if (c->frequency <= 74000000) { + n_div = 48; + n = 0; + } else if (c->frequency <= 111000000) { + n_div = 32; + n = 1; + } else if (c->frequency <= 148000000) { + n_div = 24; + n = 2; + } else if (c->frequency <= 222000000) { + n_div = 16; + n = 3; + } else if (c->frequency <= 296000000) { + n_div = 12; + n = 4; + } else if (c->frequency <= 445000000) { + n_div = 8; + n = 5; + } else if (c->frequency <= dev->fn_min) { + n_div = 6; + n = 6; + } else if (c->frequency <= 950000000) { + n_div = 4; + n = 7; + } else { + n_div = 2; + n = 0; + } + + ret = regmap_read(dev->regmap, 0x80ed81, &utmp); + if (ret) + goto err; + + iqik_m_cal = utmp * n_div; + + if (utmp < 0x20) { + if (dev->clk_mode == 0) + iqik_m_cal = (iqik_m_cal * 9) >> 5; + else + iqik_m_cal >>= 1; + } else { + iqik_m_cal = 0x40 - iqik_m_cal; + if (dev->clk_mode == 0) + iqik_m_cal = ~((iqik_m_cal * 9) >> 5); + else + iqik_m_cal = ~(iqik_m_cal >> 1); + } + + t_cal_freq = (c->frequency / 1000) * n_div * dev->fdiv; + pre_lo_freq = t_cal_freq / dev->xtal; + utmp = pre_lo_freq * dev->xtal; + + if ((t_cal_freq - utmp) >= (dev->xtal >> 1)) + pre_lo_freq++; + + pre_lo_freq += (u32) n << 13; + /* Frequency OMEGA_IQIK_M_CAL_MID*/ + t_cal_freq = pre_lo_freq + (u32)iqik_m_cal; + dev_dbg(&dev->client->dev, "t_cal_freq %u, pre_lo_freq %u\n", + t_cal_freq, pre_lo_freq); + + if (c->frequency <= 440000000) { + l_band = 0; + lna_band = 0; + } else if (c->frequency <= 484000000) { + l_band = 1; + lna_band = 1; + } else if (c->frequency <= 533000000) { + l_band = 1; + lna_band = 2; + } else if (c->frequency <= 587000000) { + l_band = 1; + lna_band = 3; + } else if (c->frequency <= 645000000) { + l_band = 1; + lna_band = 4; + } else if (c->frequency <= 710000000) { + l_band = 1; + lna_band = 5; + } else if (c->frequency <= 782000000) { + l_band = 1; + lna_band = 6; + } else if (c->frequency <= 860000000) { + l_band = 1; + lna_band = 7; + } else if (c->frequency <= 1492000000) { + l_band = 1; + lna_band = 0; + } else if (c->frequency <= 1685000000) { + l_band = 1; + lna_band = 1; + } else { + ret = -EINVAL; + goto err; + } + + /* XXX: latest windows driver does not set that at all */ + ret = regmap_write(dev->regmap, 0x80ee06, lna_band); + if (ret) + goto err; + + if (c->bandwidth_hz <= 5000000) + u8tmp = 0; + else if (c->bandwidth_hz <= 6000000) + u8tmp = 2; + else if (c->bandwidth_hz <= 7000000) + u8tmp = 4; + else + u8tmp = 6; /* 8000000 */ + + ret = regmap_write(dev->regmap, 0x80ec56, u8tmp); + if (ret) + goto err; + + /* XXX: latest windows driver sets different value (a8 != 68) */ + ret = regmap_write(dev->regmap, 0x80ec4c, 0xa0 | (l_band << 3)); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80ec4d, (t_cal_freq >> 0) & 0xff); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80ec4e, (t_cal_freq >> 8) & 0xff); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80011e, (pre_lo_freq >> 0) & 0xff); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x80011f, (pre_lo_freq >> 8) & 0xff); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&dev->client->dev, "failed %d\n", ret); + return ret; +} + +static const struct dvb_tuner_ops it913x_tuner_ops = { + .info = { + .name = "ITE IT913X", + .frequency_min = 174000000, + .frequency_max = 862000000, + }, + + .init = it913x_init, + .sleep = it913x_sleep, + .set_params = it913x_set_params, +}; + +static int it913x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct it913x_config *cfg = client->dev.platform_data; + struct dvb_frontend *fe = cfg->fe; + struct it913x_dev *dev; + int ret; + char *chip_ver_str; + static const struct regmap_config regmap_config = { + .reg_bits = 24, + .val_bits = 8, + }; + + dev = kzalloc(sizeof(struct it913x_dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "kzalloc() failed\n"); + goto err; + } + + dev->client = client; + dev->fe = cfg->fe; + dev->chip_ver = cfg->chip_ver; + dev->role = cfg->role; + dev->regmap = regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + fe->tuner_priv = dev; + memcpy(&fe->ops.tuner_ops, &it913x_tuner_ops, + sizeof(struct dvb_tuner_ops)); + i2c_set_clientdata(client, dev); + + if (dev->chip_ver == 1) + chip_ver_str = "AX"; + else if (dev->chip_ver == 2) + chip_ver_str = "BX"; + else + chip_ver_str = "??"; + + dev_info(&dev->client->dev, "ITE IT913X %s successfully attached\n", + chip_ver_str); + dev_dbg(&dev->client->dev, "chip_ver %u, role %u\n", + dev->chip_ver, dev->role); + return 0; + +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int it913x_remove(struct i2c_client *client) +{ + struct it913x_dev *dev = i2c_get_clientdata(client); + struct dvb_frontend *fe = dev->fe; + + dev_dbg(&client->dev, "\n"); + + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = NULL; + regmap_exit(dev->regmap); + kfree(dev); + + return 0; +} + +static const struct i2c_device_id it913x_id_table[] = { + {"it913x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, it913x_id_table); + +static struct i2c_driver it913x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "it913x", + }, + .probe = it913x_probe, + .remove = it913x_remove, + .id_table = it913x_id_table, +}; + +module_i2c_driver(it913x_driver); + +MODULE_DESCRIPTION("ITE IT913X silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tuner_it913x.h b/drivers/media/tuners/it913x.h index 12dd36b..33de53d 100644 --- a/drivers/media/tuners/tuner_it913x.h +++ b/drivers/media/tuners/it913x.h @@ -25,21 +25,30 @@ #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_IT913X) || \ - (defined(CONFIG_MEDIA_TUNER_IT913X_MODULE) && defined(MODULE)) -extern struct dvb_frontend *it913x_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, - u8 i2c_addr, - u8 config); -#else -static inline struct dvb_frontend *it913x_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, - u8 i2c_addr, - u8 config) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif +/* + * I2C address + * 0x38, 0x3a, 0x3c, 0x3e + */ +struct it913x_config { + /* + * pointer to DVB frontend + */ + struct dvb_frontend *fe; + + /* + * chip version + * 1 = IT9135 AX + * 2 = IT9135 BX + */ + unsigned int chip_ver:2; + + /* + * tuner role + */ +#define IT913X_ROLE_SINGLE 0 +#define IT913X_ROLE_DUAL_MASTER 1 +#define IT913X_ROLE_DUAL_SLAVE 2 + unsigned int role:2; +}; #endif diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c index 40c42de..caa5423 100644 --- a/drivers/media/tuners/m88ts2022.c +++ b/drivers/media/tuners/m88ts2022.c @@ -18,120 +18,11 @@ #include "m88ts2022_priv.h" -/* write multiple registers */ -static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, - u8 reg, const u8 *val, int len) +static int m88ts2022_cmd(struct m88ts2022_dev *dev, int op, int sleep, u8 reg, + u8 mask, u8 val, u8 *reg_val) { -#define MAX_WR_LEN 3 -#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) - int ret; - u8 buf[MAX_WR_XFER_LEN]; - struct i2c_msg msg[1] = { - { - .addr = priv->client->addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_WR_LEN)) - return -EINVAL; - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->client->adapter, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->client->dev, - "%s: i2c wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* read multiple registers */ -static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, - u8 *val, int len) -{ -#define MAX_RD_LEN 1 -#define MAX_RD_XFER_LEN (MAX_RD_LEN) - int ret; - u8 buf[MAX_RD_XFER_LEN]; - struct i2c_msg msg[2] = { - { - .addr = priv->client->addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_RD_LEN)) - return -EINVAL; - - ret = i2c_transfer(priv->client->adapter, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->client->dev, - "%s: i2c rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write single register */ -static int m88ts2022_wr_reg(struct m88ts2022_priv *priv, u8 reg, u8 val) -{ - return m88ts2022_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -static int m88ts2022_rd_reg(struct m88ts2022_priv *priv, u8 reg, u8 *val) -{ - return m88ts2022_rd_regs(priv, reg, val, 1); -} - -/* write single register with mask */ -static int m88ts2022_wr_reg_mask(struct m88ts2022_priv *priv, - u8 reg, u8 val, u8 mask) -{ - int ret; - u8 u8tmp; - - /* no need for read if whole reg is written */ - if (mask != 0xff) { - ret = m88ts2022_rd_regs(priv, reg, &u8tmp, 1); - if (ret) - return ret; - - val &= mask; - u8tmp &= ~mask; - val |= u8tmp; - } - - return m88ts2022_wr_regs(priv, reg, &val, 1); -} - -static int m88ts2022_cmd(struct dvb_frontend *fe, - int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val) -{ - struct m88ts2022_priv *priv = fe->tuner_priv; int ret, i; - u8 u8tmp; + unsigned int utmp; struct m88ts2022_reg_val reg_vals[] = { {0x51, 0x1f - op}, {0x51, 0x1f}, @@ -140,12 +31,12 @@ static int m88ts2022_cmd(struct dvb_frontend *fe, }; for (i = 0; i < 2; i++) { - dev_dbg(&priv->client->dev, - "%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n", - __func__, i, op, reg, mask, val); + dev_dbg(&dev->client->dev, + "i=%d op=%02x reg=%02x mask=%02x val=%02x\n", + i, op, reg, mask, val); for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { - ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, + ret = regmap_write(dev->regmap, reg_vals[i].reg, reg_vals[i].val); if (ret) goto err; @@ -153,37 +44,38 @@ static int m88ts2022_cmd(struct dvb_frontend *fe, usleep_range(sleep * 1000, sleep * 10000); - ret = m88ts2022_rd_reg(priv, reg, &u8tmp); + ret = regmap_read(dev->regmap, reg, &utmp); if (ret) goto err; - if ((u8tmp & mask) != val) + if ((utmp & mask) != val) break; } if (reg_val) - *reg_val = u8tmp; + *reg_val = utmp; err: return ret; } static int m88ts2022_set_params(struct dvb_frontend *fe) { - struct m88ts2022_priv *priv = fe->tuner_priv; + struct m88ts2022_dev *dev = fe->tuner_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - unsigned int frequency_khz, frequency_offset_khz, f_3db_hz; + unsigned int utmp, frequency_khz, frequency_offset_khz, f_3db_hz; unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28; u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min; u16 u16tmp; - dev_dbg(&priv->client->dev, - "%s: frequency=%d symbol_rate=%d rolloff=%d\n", - __func__, c->frequency, c->symbol_rate, c->rolloff); + + dev_dbg(&dev->client->dev, + "frequency=%d symbol_rate=%d rolloff=%d\n", + c->frequency, c->symbol_rate, c->rolloff); /* * Integer-N PLL synthesizer * kHz is used for all calculations to keep calculations within 32-bit */ - f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000); + f_ref_khz = DIV_ROUND_CLOSEST(dev->cfg.clock, 1000); div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); if (c->symbol_rate < 5000000) @@ -203,14 +95,14 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) buf[0] = u8tmp; buf[1] = 0x40; - ret = m88ts2022_wr_regs(priv, 0x10, buf, 2); + ret = regmap_bulk_write(dev->regmap, 0x10, buf, 2); if (ret) goto err; f_vco_khz = frequency_khz * div_out; pll_n = f_vco_khz * div_ref / f_ref_khz; pll_n += pll_n % 2; - priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; + dev->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; if (pll_n < 4095) u16tmp = pll_n - 1024; @@ -222,88 +114,87 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) buf[0] = (u16tmp >> 8) & 0x3f; buf[1] = (u16tmp >> 0) & 0xff; buf[2] = div_ref - 8; - ret = m88ts2022_wr_regs(priv, 0x01, buf, 3); + ret = regmap_bulk_write(dev->regmap, 0x01, buf, 3); if (ret) goto err; - dev_dbg(&priv->client->dev, - "%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", - __func__, priv->frequency_khz, - priv->frequency_khz - c->frequency, f_vco_khz, pll_n, - div_ref, div_out); + dev_dbg(&dev->client->dev, + "frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", + dev->frequency_khz, dev->frequency_khz - c->frequency, + f_vco_khz, pll_n, div_ref, div_out); - ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); + ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL); if (ret) goto err; - ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); + ret = regmap_read(dev->regmap, 0x14, &utmp); if (ret) goto err; - u8tmp &= 0x7f; - if (u8tmp < 64) { - ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x80, 0x80); + utmp &= 0x7f; + if (utmp < 64) { + ret = regmap_update_bits(dev->regmap, 0x10, 0x80, 0x80); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x11, 0x6f); + ret = regmap_write(dev->regmap, 0x11, 0x6f); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); + ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL); if (ret) goto err; } - ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); + ret = regmap_read(dev->regmap, 0x14, &utmp); if (ret) goto err; - u8tmp &= 0x1f; - if (u8tmp > 19) { - ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x00, 0x02); + utmp &= 0x1f; + if (utmp > 19) { + ret = regmap_update_bits(dev->regmap, 0x10, 0x02, 0x00); if (ret) goto err; } - ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL); + ret = m88ts2022_cmd(dev, 0x08, 5, 0x3c, 0xff, 0x00, NULL); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x25, 0x00); + ret = regmap_write(dev->regmap, 0x25, 0x00); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x27, 0x70); + ret = regmap_write(dev->regmap, 0x27, 0x70); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x41, 0x09); + ret = regmap_write(dev->regmap, 0x41, 0x09); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x08, 0x0b); + ret = regmap_write(dev->regmap, 0x08, 0x0b); if (ret) goto err; /* filters */ gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U); - ret = m88ts2022_wr_reg(priv, 0x04, gdiv28); + ret = regmap_write(dev->regmap, 0x04, gdiv28); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); if (ret) goto err; cap_code = u8tmp & 0x3f; - ret = m88ts2022_wr_reg(priv, 0x41, 0x0d); + ret = regmap_write(dev->regmap, 0x41, 0x0d); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); if (ret) goto err; @@ -314,7 +205,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) div_min = gdiv28 * 78 / 100; div_max = clamp_val(div_max, 0U, 63U); - f_3db_hz = c->symbol_rate * 135UL / 200UL; + f_3db_hz = mult_frac(c->symbol_rate, 135, 200); f_3db_hz += 2000000U + (frequency_offset_khz * 1000U); f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U); @@ -327,25 +218,25 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max); - ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv); + ret = regmap_write(dev->regmap, 0x04, lpf_mxdiv); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x06, lpf_gm); + ret = regmap_write(dev->regmap, 0x06, lpf_gm); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); if (ret) goto err; cap_code = u8tmp & 0x3f; - ret = m88ts2022_wr_reg(priv, 0x41, 0x09); + ret = regmap_write(dev->regmap, 0x41, 0x09); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); if (ret) goto err; @@ -353,31 +244,31 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) cap_code = (cap_code + u8tmp) / 2; u8tmp = cap_code | 0x80; - ret = m88ts2022_wr_reg(priv, 0x25, u8tmp); + ret = regmap_write(dev->regmap, 0x25, u8tmp); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x27, 0x30); + ret = regmap_write(dev->regmap, 0x27, 0x30); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x08, 0x09); + ret = regmap_write(dev->regmap, 0x08, 0x09); if (ret) goto err; - ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL); + ret = m88ts2022_cmd(dev, 0x01, 20, 0x21, 0xff, 0x00, NULL); if (ret) goto err; err: if (ret) - dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int m88ts2022_init(struct dvb_frontend *fe) { - struct m88ts2022_priv *priv = fe->tuner_priv; + struct m88ts2022_dev *dev = fe->tuner_priv; int ret, i; u8 u8tmp; static const struct m88ts2022_reg_val reg_vals[] = { @@ -393,23 +284,24 @@ static int m88ts2022_init(struct dvb_frontend *fe) {0x24, 0x02}, {0x12, 0xa0}, }; - dev_dbg(&priv->client->dev, "%s:\n", __func__); - ret = m88ts2022_wr_reg(priv, 0x00, 0x01); + dev_dbg(&dev->client->dev, "\n"); + + ret = regmap_write(dev->regmap, 0x00, 0x01); if (ret) goto err; - ret = m88ts2022_wr_reg(priv, 0x00, 0x03); + ret = regmap_write(dev->regmap, 0x00, 0x03); if (ret) goto err; - switch (priv->cfg.clock_out) { + switch (dev->cfg.clock_out) { case M88TS2022_CLOCK_OUT_DISABLED: u8tmp = 0x60; break; case M88TS2022_CLOCK_OUT_ENABLED: u8tmp = 0x70; - ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); + ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div); if (ret) goto err; break; @@ -420,58 +312,61 @@ static int m88ts2022_init(struct dvb_frontend *fe) goto err; } - ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); + ret = regmap_write(dev->regmap, 0x42, u8tmp); if (ret) goto err; - if (priv->cfg.loop_through) + if (dev->cfg.loop_through) u8tmp = 0xec; else u8tmp = 0x6c; - ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); + ret = regmap_write(dev->regmap, 0x62, u8tmp); if (ret) goto err; for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { - ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, reg_vals[i].val); + ret = regmap_write(dev->regmap, reg_vals[i].reg, reg_vals[i].val); if (ret) goto err; } err: if (ret) - dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int m88ts2022_sleep(struct dvb_frontend *fe) { - struct m88ts2022_priv *priv = fe->tuner_priv; + struct m88ts2022_dev *dev = fe->tuner_priv; int ret; - dev_dbg(&priv->client->dev, "%s:\n", __func__); - ret = m88ts2022_wr_reg(priv, 0x00, 0x00); + dev_dbg(&dev->client->dev, "\n"); + + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) goto err; err: if (ret) - dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct m88ts2022_priv *priv = fe->tuner_priv; - dev_dbg(&priv->client->dev, "%s:\n", __func__); + struct m88ts2022_dev *dev = fe->tuner_priv; - *frequency = priv->frequency_khz; + dev_dbg(&dev->client->dev, "\n"); + + *frequency = dev->frequency_khz; return 0; } static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct m88ts2022_priv *priv = fe->tuner_priv; - dev_dbg(&priv->client->dev, "%s:\n", __func__); + struct m88ts2022_dev *dev = fe->tuner_priv; + + dev_dbg(&dev->client->dev, "\n"); *frequency = 0; /* Zero-IF */ return 0; @@ -479,31 +374,30 @@ static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) { - struct m88ts2022_priv *priv = fe->tuner_priv; + struct m88ts2022_dev *dev = fe->tuner_priv; int ret; - u8 u8tmp; u16 gain, u16tmp; - unsigned int gain1, gain2, gain3; + unsigned int utmp, gain1, gain2, gain3; - ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp); + ret = regmap_read(dev->regmap, 0x3d, &utmp); if (ret) goto err; - gain1 = (u8tmp >> 0) & 0x1f; + gain1 = (utmp >> 0) & 0x1f; gain1 = clamp(gain1, 0U, 15U); - ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp); + ret = regmap_read(dev->regmap, 0x21, &utmp); if (ret) goto err; - gain2 = (u8tmp >> 0) & 0x1f; + gain2 = (utmp >> 0) & 0x1f; gain2 = clamp(gain2, 2U, 16U); - ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp); + ret = regmap_read(dev->regmap, 0x66, &utmp); if (ret) goto err; - gain3 = (u8tmp >> 3) & 0x07; + gain3 = (utmp >> 3) & 0x07; gain3 = clamp(gain3, 0U, 6U); gain = gain1 * 265 + gain2 * 338 + gain3 * 285; @@ -515,7 +409,7 @@ static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); err: if (ret) - dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); return ret; } @@ -540,46 +434,56 @@ static int m88ts2022_probe(struct i2c_client *client, { struct m88ts2022_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; - struct m88ts2022_priv *priv; + struct m88ts2022_dev *dev; int ret; - u8 chip_id, u8tmp; + u8 u8tmp; + unsigned int utmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; - dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + dev_err(&client->dev, "kzalloc() failed\n"); goto err; } - memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config)); - priv->client = client; + memcpy(&dev->cfg, cfg, sizeof(struct m88ts2022_config)); + dev->client = client; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err; + } /* check if the tuner is there */ - ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp); + ret = regmap_read(dev->regmap, 0x00, &utmp); if (ret) goto err; - if ((u8tmp & 0x03) == 0x00) { - ret = m88ts2022_wr_reg(priv, 0x00, 0x01); - if (ret < 0) + if ((utmp & 0x03) == 0x00) { + ret = regmap_write(dev->regmap, 0x00, 0x01); + if (ret) goto err; usleep_range(2000, 50000); } - ret = m88ts2022_wr_reg(priv, 0x00, 0x03); + ret = regmap_write(dev->regmap, 0x00, 0x03); if (ret) goto err; usleep_range(2000, 50000); - ret = m88ts2022_rd_reg(priv, 0x00, &chip_id); + ret = regmap_read(dev->regmap, 0x00, &utmp); if (ret) goto err; - dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id); + dev_dbg(&dev->client->dev, "chip_id=%02x\n", utmp); - switch (chip_id) { + switch (utmp) { case 0xc3: case 0x83: break; @@ -587,13 +491,13 @@ static int m88ts2022_probe(struct i2c_client *client, goto err; } - switch (priv->cfg.clock_out) { + switch (dev->cfg.clock_out) { case M88TS2022_CLOCK_OUT_DISABLED: u8tmp = 0x60; break; case M88TS2022_CLOCK_OUT_ENABLED: u8tmp = 0x70; - ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); + ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div); if (ret) goto err; break; @@ -604,49 +508,48 @@ static int m88ts2022_probe(struct i2c_client *client, goto err; } - ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); + ret = regmap_write(dev->regmap, 0x42, u8tmp); if (ret) goto err; - if (priv->cfg.loop_through) + if (dev->cfg.loop_through) u8tmp = 0xec; else u8tmp = 0x6c; - ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); + ret = regmap_write(dev->regmap, 0x62, u8tmp); if (ret) goto err; /* sleep */ - ret = m88ts2022_wr_reg(priv, 0x00, 0x00); + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) goto err; - dev_info(&priv->client->dev, - "%s: Montage M88TS2022 successfully identified\n", - KBUILD_MODNAME); + dev_info(&dev->client->dev, "Montage M88TS2022 successfully identified\n"); - fe->tuner_priv = priv; + fe->tuner_priv = dev; memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops, sizeof(struct dvb_tuner_ops)); - i2c_set_clientdata(client, priv); + i2c_set_clientdata(client, dev); return 0; err: - dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); + dev_dbg(&client->dev, "failed=%d\n", ret); + kfree(dev); return ret; } static int m88ts2022_remove(struct i2c_client *client) { - struct m88ts2022_priv *priv = i2c_get_clientdata(client); - struct dvb_frontend *fe = priv->cfg.fe; - dev_dbg(&client->dev, "%s:\n", __func__); + struct m88ts2022_dev *dev = i2c_get_clientdata(client); + struct dvb_frontend *fe = dev->cfg.fe; + + dev_dbg(&client->dev, "\n"); memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = NULL; - kfree(priv); + kfree(dev); return 0; } diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h index 0363dd8..feeb5ad 100644 --- a/drivers/media/tuners/m88ts2022_priv.h +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -18,11 +18,12 @@ #define M88TS2022_PRIV_H #include "m88ts2022.h" +#include <linux/regmap.h> -struct m88ts2022_priv { +struct m88ts2022_dev { struct m88ts2022_config cfg; struct i2c_client *client; - struct dvb_frontend *fe; + struct regmap *regmap; u32 frequency_khz; }; diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c index ee99e37..26019e7 100644 --- a/drivers/media/tuners/msi001.c +++ b/drivers/media/tuners/msi001.c @@ -67,7 +67,8 @@ static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, { int ret; u32 reg; - dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__, + + dev_dbg(&s->spi->dev, "lna=%d mixer=%d if=%d\n", lna_gain, mixer_gain, if_gain); reg = 1 << 0; @@ -83,7 +84,7 @@ static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, return 0; err: - dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + dev_dbg(&s->spi->dev, "failed %d\n", ret); return ret; }; @@ -94,6 +95,7 @@ static int msi001_set_tuner(struct msi001 *s) u32 reg; u64 f_vco, tmp64; u8 mode, filter_mode, lo_div; + static const struct { u32 rf; u8 mode; @@ -145,9 +147,7 @@ static int msi001_set_tuner(struct msi001 *s) #define R_REF 4 #define F_OUT_STEP 1 - dev_dbg(&s->spi->dev, - "%s: f_rf=%d f_if=%d\n", - __func__, f_rf, f_if); + dev_dbg(&s->spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if); for (i = 0; i < ARRAY_SIZE(band_lut); i++) { if (f_rf <= band_lut[i].rf) { @@ -198,8 +198,7 @@ static int msi001_set_tuner(struct msi001 *s) s->bandwidth->val = bandwidth_lut[i].freq; - dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n", - __func__, bandwidth_lut[i].freq); + dev_dbg(&s->spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq); f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; tmp64 = f_vco; @@ -225,9 +224,8 @@ static int msi001_set_tuner(struct msi001 *s) tmp += 1ul * F_REF * R_REF * frac / thresh; tmp /= lo_div; - dev_dbg(&s->spi->dev, - "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", - __func__, f_rf, tmp, n, thresh, frac); + dev_dbg(&s->spi->dev, "rf=%u:%u n=%d thresh=%d frac=%d\n", + f_rf, tmp, n, thresh, frac); ret = msi001_wreg(s, 0x00000e); if (ret) @@ -276,7 +274,7 @@ static int msi001_set_tuner(struct msi001 *s) return 0; err: - dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + dev_dbg(&s->spi->dev, "failed %d\n", ret); return ret; }; @@ -284,7 +282,8 @@ static int msi001_s_power(struct v4l2_subdev *sd, int on) { struct msi001 *s = sd_to_msi001(sd); int ret; - dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on); + + dev_dbg(&s->spi->dev, "on=%d\n", on); if (on) ret = 0; @@ -301,7 +300,8 @@ static const struct v4l2_subdev_core_ops msi001_core_ops = { static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) { struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + + dev_dbg(&s->spi->dev, "index=%d\n", v->index); strlcpy(v->name, "Mirics MSi001", sizeof(v->name)); v->type = V4L2_TUNER_RF; @@ -315,14 +315,16 @@ static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) { struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + + dev_dbg(&s->spi->dev, "index=%d\n", v->index); return 0; } static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) { struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner); + + dev_dbg(&s->spi->dev, "tuner=%d\n", f->tuner); f->frequency = s->f_tuner; return 0; } @@ -332,8 +334,9 @@ static int msi001_s_frequency(struct v4l2_subdev *sd, { struct msi001 *s = sd_to_msi001(sd); unsigned int band; - dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); + + dev_dbg(&s->spi->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2)) band = 0; @@ -349,8 +352,9 @@ static int msi001_enum_freq_bands(struct v4l2_subdev *sd, struct v4l2_frequency_band *band) { struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); + + dev_dbg(&s->spi->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->index >= ARRAY_SIZE(bands)) return -EINVAL; @@ -380,9 +384,10 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl); int ret; + dev_dbg(&s->spi->dev, - "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - __func__, ctrl->id, ctrl->name, ctrl->val, + "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); switch (ctrl->id) { @@ -403,8 +408,7 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) s->mixer_gain->cur.val, s->if_gain->val); break; default: - dev_dbg(&s->spi->dev, "%s: unkown control %d\n", - __func__, ctrl->id); + dev_dbg(&s->spi->dev, "unkown control %d\n", ctrl->id); ret = -EINVAL; } @@ -419,7 +423,8 @@ static int msi001_probe(struct spi_device *spi) { struct msi001 *s; int ret; - dev_dbg(&spi->dev, "%s:\n", __func__); + + dev_dbg(&spi->dev, "\n"); s = kzalloc(sizeof(struct msi001), GFP_KERNEL); if (s == NULL) { @@ -466,7 +471,8 @@ static int msi001_remove(struct spi_device *spi) { struct v4l2_subdev *sd = spi_get_drvdata(spi); struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&spi->dev, "%s:\n", __func__); + + dev_dbg(&spi->dev, "\n"); /* * Registered by v4l2_spi_new_subdev() from master driver, but we must diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 13381de..b87b254 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -157,7 +157,6 @@ static int mt2060_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct mt2060_priv *priv; - int ret=0; int i=0; u32 freq; u8 lnaband; @@ -240,7 +239,7 @@ static int mt2060_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ - return ret; + return 0; } static void mt2060_calibrate(struct mt2060_priv *priv) diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index f640dcf..9e9c5eb 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -1216,7 +1216,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, if (status >= 0) { val = (state-> - reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode] + reg[MT2063_REG_PD1_TGT] & ~0x40) | (RFAGCEN[Mode] ? 0x40 : 0x00); if (state->reg[MT2063_REG_PD1_TGT] != val) @@ -1225,7 +1225,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* LNARin */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) | + u8 val = (state->reg[MT2063_REG_CTRL_2C] & ~0x03) | (LNARIN[Mode] & 0x03); if (state->reg[MT2063_REG_CTRL_2C] != val) status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val); @@ -1235,19 +1235,19 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, if (status >= 0) { val = (state-> - reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) | + reg[MT2063_REG_FIFF_CTRL2] & ~0xF0) | (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4); if (state->reg[MT2063_REG_FIFF_CTRL2] != val) { status |= mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val); /* trigger FIFF calibration, needed after changing FIFFQ */ val = - (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01); + (state->reg[MT2063_REG_FIFF_CTRL] | 0x01); status |= mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); val = (state-> - reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01); + reg[MT2063_REG_FIFF_CTRL] & ~0x01); status |= mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); } @@ -1259,7 +1259,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* acLNAmax */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) | + u8 val = (state->reg[MT2063_REG_LNA_OV] & ~0x1F) | (ACLNAMAX[Mode] & 0x1F); if (state->reg[MT2063_REG_LNA_OV] != val) status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val); @@ -1267,7 +1267,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* LNATGT */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) | + u8 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x3F) | (LNATGT[Mode] & 0x3F); if (state->reg[MT2063_REG_LNA_TGT] != val) status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); @@ -1275,7 +1275,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* ACRF */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) | + u8 val = (state->reg[MT2063_REG_RF_OV] & ~0x1F) | (ACRFMAX[Mode] & 0x1F); if (state->reg[MT2063_REG_RF_OV] != val) status |= mt2063_setreg(state, MT2063_REG_RF_OV, val); @@ -1283,7 +1283,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* PD1TGT */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) | + u8 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x3F) | (PD1TGT[Mode] & 0x3F); if (state->reg[MT2063_REG_PD1_TGT] != val) status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); @@ -1294,7 +1294,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, u8 val = ACFIFMAX[Mode]; if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5) val = 5; - val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) | + val = (state->reg[MT2063_REG_FIF_OV] & ~0x1F) | (val & 0x1F); if (state->reg[MT2063_REG_FIF_OV] != val) status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val); @@ -1302,7 +1302,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* PD2TGT */ if (status >= 0) { - u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) | + u8 val = (state->reg[MT2063_REG_PD2_TGT] & ~0x3F) | (PD2TGT[Mode] & 0x3F); if (state->reg[MT2063_REG_PD2_TGT] != val) status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val); @@ -1310,7 +1310,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* Ignore ATN Overload */ if (status >= 0) { - val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) | + val = (state->reg[MT2063_REG_LNA_TGT] & ~0x80) | (RFOVDIS[Mode] ? 0x80 : 0x00); if (state->reg[MT2063_REG_LNA_TGT] != val) status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); @@ -1318,7 +1318,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state, /* Ignore FIF Overload */ if (status >= 0) { - val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) | + val = (state->reg[MT2063_REG_PD1_TGT] & ~0x80) | (FIFOVDIS[Mode] ? 0x80 : 0x00); if (state->reg[MT2063_REG_PD1_TGT] != val) status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c new file mode 100644 index 0000000..1575a5d --- /dev/null +++ b/drivers/media/tuners/mxl301rf.c @@ -0,0 +1,349 @@ +/* + * MaxLinear MxL301RF OFDM tuner driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +/* + * NOTICE: + * This driver is incomplete and lacks init/config of the chips, + * as the necessary info is not disclosed. + * Other features like get_if_frequency() are missing as well. + * It assumes that users of this driver (such as a PCI bridge of + * DTV receiver cards) properly init and configure the chip + * via I2C *before* calling this driver's init() function. + * + * Currently, PT3 driver is the only one that uses this driver, + * and contains init/config code in its firmware. + * Thus some part of the code might be dependent on PT3 specific config. + */ + +#include <linux/kernel.h> +#include "mxl301rf.h" + +struct mxl301rf_state { + struct mxl301rf_config cfg; + struct i2c_client *i2c; +}; + +static struct mxl301rf_state *cfg_to_state(struct mxl301rf_config *c) +{ + return container_of(c, struct mxl301rf_state, cfg); +} + +static int raw_write(struct mxl301rf_state *state, const u8 *buf, int len) +{ + int ret; + + ret = i2c_master_send(state->i2c, buf, len); + if (ret >= 0 && ret < len) + ret = -EIO; + return (ret == len) ? 0 : ret; +} + +static int reg_write(struct mxl301rf_state *state, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + + return raw_write(state, buf, 2); +} + +static int reg_read(struct mxl301rf_state *state, u8 reg, u8 *val) +{ + u8 wbuf[2] = { 0xfb, reg }; + int ret; + + ret = raw_write(state, wbuf, sizeof(wbuf)); + if (ret == 0) + ret = i2c_master_recv(state->i2c, val, 1); + if (ret >= 0 && ret < 1) + ret = -EIO; + return (ret == 1) ? 0 : ret; +} + +/* tuner_ops */ + +/* get RSSI and update propery cache, set to *out in % */ +static int mxl301rf_get_rf_strength(struct dvb_frontend *fe, u16 *out) +{ + struct mxl301rf_state *state; + int ret; + u8 rf_in1, rf_in2, rf_off1, rf_off2; + u16 rf_in, rf_off; + s64 level; + struct dtv_fe_stats *rssi; + + rssi = &fe->dtv_property_cache.strength; + rssi->len = 1; + rssi->stat[0].scale = FE_SCALE_NOT_AVAILABLE; + *out = 0; + + state = fe->tuner_priv; + ret = reg_write(state, 0x14, 0x01); + if (ret < 0) + return ret; + usleep_range(1000, 2000); + + ret = reg_read(state, 0x18, &rf_in1); + if (ret == 0) + ret = reg_read(state, 0x19, &rf_in2); + if (ret == 0) + ret = reg_read(state, 0xd6, &rf_off1); + if (ret == 0) + ret = reg_read(state, 0xd7, &rf_off2); + if (ret != 0) + return ret; + + rf_in = (rf_in2 & 0x07) << 8 | rf_in1; + rf_off = (rf_off2 & 0x0f) << 5 | (rf_off1 >> 3); + level = rf_in - rf_off - (113 << 3); /* x8 dBm */ + level = level * 1000 / 8; + rssi->stat[0].svalue = level; + rssi->stat[0].scale = FE_SCALE_DECIBEL; + /* *out = (level - min) * 100 / (max - min) */ + *out = (rf_in - rf_off + (1 << 9) - 1) * 100 / ((5 << 9) - 2); + return 0; +} + +/* spur shift parameters */ +struct shf { + u32 freq; /* Channel center frequency */ + u32 ofst_th; /* Offset frequency threshold */ + u8 shf_val; /* Spur shift value */ + u8 shf_dir; /* Spur shift direction */ +}; + +static const struct shf shf_tab[] = { + { 64500, 500, 0x92, 0x07 }, + { 191500, 300, 0xe2, 0x07 }, + { 205500, 500, 0x2c, 0x04 }, + { 212500, 500, 0x1e, 0x04 }, + { 226500, 500, 0xd4, 0x07 }, + { 99143, 500, 0x9c, 0x07 }, + { 173143, 500, 0xd4, 0x07 }, + { 191143, 300, 0xd4, 0x07 }, + { 207143, 500, 0xce, 0x07 }, + { 225143, 500, 0xce, 0x07 }, + { 243143, 500, 0xd4, 0x07 }, + { 261143, 500, 0xd4, 0x07 }, + { 291143, 500, 0xd4, 0x07 }, + { 339143, 500, 0x2c, 0x04 }, + { 117143, 500, 0x7a, 0x07 }, + { 135143, 300, 0x7a, 0x07 }, + { 153143, 500, 0x01, 0x07 } +}; + +struct reg_val { + u8 reg; + u8 val; +} __attribute__ ((__packed__)); + +static const struct reg_val set_idac[] = { + { 0x0d, 0x00 }, + { 0x0c, 0x67 }, + { 0x6f, 0x89 }, + { 0x70, 0x0c }, + { 0x6f, 0x8a }, + { 0x70, 0x0e }, + { 0x6f, 0x8b }, + { 0x70, 0x1c }, +}; + +static int mxl301rf_set_params(struct dvb_frontend *fe) +{ + struct reg_val tune0[] = { + { 0x13, 0x00 }, /* abort tuning */ + { 0x3b, 0xc0 }, + { 0x3b, 0x80 }, + { 0x10, 0x95 }, /* BW */ + { 0x1a, 0x05 }, + { 0x61, 0x00 }, /* spur shift value (placeholder) */ + { 0x62, 0xa0 } /* spur shift direction (placeholder) */ + }; + + struct reg_val tune1[] = { + { 0x11, 0x40 }, /* RF frequency L (placeholder) */ + { 0x12, 0x0e }, /* RF frequency H (placeholder) */ + { 0x13, 0x01 } /* start tune */ + }; + + struct mxl301rf_state *state; + u32 freq; + u16 f; + u32 tmp, div; + int i, ret; + + state = fe->tuner_priv; + freq = fe->dtv_property_cache.frequency; + + /* spur shift function (for analog) */ + for (i = 0; i < ARRAY_SIZE(shf_tab); i++) { + if (freq >= (shf_tab[i].freq - shf_tab[i].ofst_th) * 1000 && + freq <= (shf_tab[i].freq + shf_tab[i].ofst_th) * 1000) { + tune0[5].val = shf_tab[i].shf_val; + tune0[6].val = 0xa0 | shf_tab[i].shf_dir; + break; + } + } + ret = raw_write(state, (u8 *) tune0, sizeof(tune0)); + if (ret < 0) + goto failed; + usleep_range(3000, 4000); + + /* convert freq to 10.6 fixed point float [MHz] */ + f = freq / 1000000; + tmp = freq % 1000000; + div = 1000000; + for (i = 0; i < 6; i++) { + f <<= 1; + div >>= 1; + if (tmp > div) { + tmp -= div; + f |= 1; + } + } + if (tmp > 7812) + f++; + tune1[0].val = f & 0xff; + tune1[1].val = f >> 8; + ret = raw_write(state, (u8 *) tune1, sizeof(tune1)); + if (ret < 0) + goto failed; + msleep(31); + + ret = reg_write(state, 0x1a, 0x0d); + if (ret < 0) + goto failed; + ret = raw_write(state, (u8 *) set_idac, sizeof(set_idac)); + if (ret < 0) + goto failed; + return 0; + +failed: + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static const struct reg_val standby_data[] = { + { 0x01, 0x00 }, + { 0x13, 0x00 } +}; + +static int mxl301rf_sleep(struct dvb_frontend *fe) +{ + struct mxl301rf_state *state; + int ret; + + state = fe->tuner_priv; + ret = raw_write(state, (u8 *)standby_data, sizeof(standby_data)); + if (ret < 0) + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + + +/* init sequence is not public. + * the parent must have init'ed the device. + * just wake up here. + */ +static int mxl301rf_init(struct dvb_frontend *fe) +{ + struct mxl301rf_state *state; + int ret; + + state = fe->tuner_priv; + + ret = reg_write(state, 0x01, 0x01); + if (ret < 0) { + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; + } + return 0; +} + +/* I2C driver functions */ + +static const struct dvb_tuner_ops mxl301rf_ops = { + .info = { + .name = "MaxLinear MxL301RF", + + .frequency_min = 93000000, + .frequency_max = 803142857, + }, + + .init = mxl301rf_init, + .sleep = mxl301rf_sleep, + + .set_params = mxl301rf_set_params, + .get_rf_strength = mxl301rf_get_rf_strength, +}; + + +static int mxl301rf_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxl301rf_state *state; + struct mxl301rf_config *cfg; + struct dvb_frontend *fe; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->i2c = client; + cfg = client->dev.platform_data; + + memcpy(&state->cfg, cfg, sizeof(state->cfg)); + fe = cfg->fe; + fe->tuner_priv = state; + memcpy(&fe->ops.tuner_ops, &mxl301rf_ops, sizeof(mxl301rf_ops)); + + i2c_set_clientdata(client, &state->cfg); + dev_info(&client->dev, "MaxLinear MxL301RF attached.\n"); + return 0; +} + +static int mxl301rf_remove(struct i2c_client *client) +{ + struct mxl301rf_state *state; + + state = cfg_to_state(i2c_get_clientdata(client)); + state->cfg.fe->tuner_priv = NULL; + kfree(state); + return 0; +} + + +static const struct i2c_device_id mxl301rf_id[] = { + {"mxl301rf", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mxl301rf_id); + +static struct i2c_driver mxl301rf_driver = { + .driver = { + .name = "mxl301rf", + }, + .probe = mxl301rf_probe, + .remove = mxl301rf_remove, + .id_table = mxl301rf_id, +}; + +module_i2c_driver(mxl301rf_driver); + +MODULE_DESCRIPTION("MaxLinear MXL301RF tuner"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h new file mode 100644 index 0000000..19e6840 --- /dev/null +++ b/drivers/media/tuners/mxl301rf.h @@ -0,0 +1,26 @@ +/* + * MaxLinear MxL301RF OFDM tuner driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +#ifndef MXL301RF_H +#define MXL301RF_H + +#include "dvb_frontend.h" + +struct mxl301rf_config { + struct dvb_frontend *fe; +}; + +#endif /* MXL301RF_H */ diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index b473b76..92a3be4 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -1692,7 +1692,6 @@ static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, ) { struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0; state->Mode = Mode; state->IF_Mode = IF_mode; @@ -1715,7 +1714,7 @@ static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, /* Synthesizer LO frequency calculation */ MXL_SynthIFLO_Calc(fe); - return status; + return 0; } static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe) diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c new file mode 100644 index 0000000..18bc745 --- /dev/null +++ b/drivers/media/tuners/qm1d1c0042.c @@ -0,0 +1,448 @@ +/* + * Sharp QM1D1C0042 8PSK tuner driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +/* + * NOTICE: + * As the disclosed information on the chip is very limited, + * this driver lacks some features, including chip config like IF freq. + * It assumes that users of this driver (such as a PCI bridge of + * DTV receiver cards) know the relevant info and + * configure the chip via I2C if necessary. + * + * Currently, PT3 driver is the only one that uses this driver, + * and contains init/config code in its firmware. + * Thus some part of the code might be dependent on PT3 specific config. + */ + +#include <linux/kernel.h> +#include <linux/math64.h> +#include "qm1d1c0042.h" + +#define QM1D1C0042_NUM_REGS 0x20 + +static const u8 reg_initval[QM1D1C0042_NUM_REGS] = { + 0x48, 0x1c, 0xa0, 0x10, 0xbc, 0xc5, 0x20, 0x33, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xf3, 0x00, 0x2a, 0x64, 0xa6, 0x86, + 0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00 +}; + +static const struct qm1d1c0042_config default_cfg = { + .xtal_freq = 16000, + .lpf = 1, + .fast_srch = 0, + .lpf_wait = 20, + .fast_srch_wait = 4, + .normal_srch_wait = 15, +}; + +struct qm1d1c0042_state { + struct qm1d1c0042_config cfg; + struct i2c_client *i2c; + u8 regs[QM1D1C0042_NUM_REGS]; +}; + +static struct qm1d1c0042_state *cfg_to_state(struct qm1d1c0042_config *c) +{ + return container_of(c, struct qm1d1c0042_state, cfg); +} + +static int reg_write(struct qm1d1c0042_state *state, u8 reg, u8 val) +{ + u8 wbuf[2] = { reg, val }; + int ret; + + ret = i2c_master_send(state->i2c, wbuf, sizeof(wbuf)); + if (ret >= 0 && ret < sizeof(wbuf)) + ret = -EIO; + return (ret == sizeof(wbuf)) ? 0 : ret; +} + +static int reg_read(struct qm1d1c0042_state *state, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2] = { + { + .addr = state->i2c->addr, + .flags = 0, + .buf = ®, + .len = 1, + }, + { + .addr = state->i2c->addr, + .flags = I2C_M_RD, + .buf = val, + .len = 1, + }, + }; + int ret; + + ret = i2c_transfer(state->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret >= 0 && ret < ARRAY_SIZE(msgs)) + ret = -EIO; + return (ret == ARRAY_SIZE(msgs)) ? 0 : ret; +} + + +static int qm1d1c0042_set_srch_mode(struct qm1d1c0042_state *state, bool fast) +{ + if (fast) + state->regs[0x03] |= 0x01; /* set fast search mode */ + else + state->regs[0x03] &= ~0x01 & 0xff; + + return reg_write(state, 0x03, state->regs[0x03]); +} + +static int qm1d1c0042_wakeup(struct qm1d1c0042_state *state) +{ + int ret; + + state->regs[0x01] |= 1 << 3; /* BB_Reg_enable */ + state->regs[0x01] &= (~(1 << 0)) & 0xff; /* NORMAL (wake-up) */ + state->regs[0x05] &= (~(1 << 3)) & 0xff; /* pfd_rst NORMAL */ + ret = reg_write(state, 0x01, state->regs[0x01]); + if (ret == 0) + ret = reg_write(state, 0x05, state->regs[0x05]); + + if (ret < 0) + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, state->cfg.fe->dvb->num, state->cfg.fe->id); + return ret; +} + +/* tuner_ops */ + +static int qm1d1c0042_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct qm1d1c0042_state *state; + struct qm1d1c0042_config *cfg; + + state = fe->tuner_priv; + cfg = priv_cfg; + + if (cfg->fe) + state->cfg.fe = cfg->fe; + + if (cfg->xtal_freq != QM1D1C0042_CFG_XTAL_DFLT) + dev_warn(&state->i2c->dev, + "(%s) changing xtal_freq not supported. ", __func__); + state->cfg.xtal_freq = default_cfg.xtal_freq; + + state->cfg.lpf = cfg->lpf; + state->cfg.fast_srch = cfg->fast_srch; + + if (cfg->lpf_wait != QM1D1C0042_CFG_WAIT_DFLT) + state->cfg.lpf_wait = cfg->lpf_wait; + else + state->cfg.lpf_wait = default_cfg.lpf_wait; + + if (cfg->fast_srch_wait != QM1D1C0042_CFG_WAIT_DFLT) + state->cfg.fast_srch_wait = cfg->fast_srch_wait; + else + state->cfg.fast_srch_wait = default_cfg.fast_srch_wait; + + if (cfg->normal_srch_wait != QM1D1C0042_CFG_WAIT_DFLT) + state->cfg.normal_srch_wait = cfg->normal_srch_wait; + else + state->cfg.normal_srch_wait = default_cfg.normal_srch_wait; + return 0; +} + +/* divisor, vco_band parameters */ +/* {maxfreq, param1(band?), param2(div?) */ +static const u32 conv_table[9][3] = { + { 2151000, 1, 7 }, + { 1950000, 1, 6 }, + { 1800000, 1, 5 }, + { 1600000, 1, 4 }, + { 1450000, 1, 3 }, + { 1250000, 1, 2 }, + { 1200000, 0, 7 }, + { 975000, 0, 6 }, + { 950000, 0, 0 } +}; + +static int qm1d1c0042_set_params(struct dvb_frontend *fe) +{ + struct qm1d1c0042_state *state; + u32 freq; + int i, ret; + u8 val, mask; + u32 a, sd; + s32 b; + + state = fe->tuner_priv; + freq = fe->dtv_property_cache.frequency; + + state->regs[0x08] &= 0xf0; + state->regs[0x08] |= 0x09; + + state->regs[0x13] &= 0x9f; + state->regs[0x13] |= 0x20; + + /* div2/vco_band */ + val = state->regs[0x02] & 0x0f; + for (i = 0; i < 8; i++) + if (freq < conv_table[i][0] && freq >= conv_table[i + 1][0]) { + val |= conv_table[i][1] << 7; + val |= conv_table[i][2] << 4; + break; + } + ret = reg_write(state, 0x02, val); + if (ret < 0) + return ret; + + a = (freq + state->cfg.xtal_freq / 2) / state->cfg.xtal_freq; + + state->regs[0x06] &= 0x40; + state->regs[0x06] |= (a - 12) / 4; + ret = reg_write(state, 0x06, state->regs[0x06]); + if (ret < 0) + return ret; + + state->regs[0x07] &= 0xf0; + state->regs[0x07] |= (a - 4 * ((a - 12) / 4 + 1) - 5) & 0x0f; + ret = reg_write(state, 0x07, state->regs[0x07]); + if (ret < 0) + return ret; + + /* LPF */ + val = state->regs[0x08]; + if (state->cfg.lpf) { + /* LPF_CLK, LPF_FC */ + val &= 0xf0; + val |= 0x02; + } + ret = reg_write(state, 0x08, val); + if (ret < 0) + return ret; + + /* + * b = (freq / state->cfg.xtal_freq - a) << 20; + * sd = b (b >= 0) + * 1<<22 + b (b < 0) + */ + b = (s32)div64_s64(((s64) freq) << 20, state->cfg.xtal_freq) + - (((s64) a) << 20); + + if (b >= 0) + sd = b; + else + sd = (1 << 22) + b; + + state->regs[0x09] &= 0xc0; + state->regs[0x09] |= (sd >> 16) & 0x3f; + state->regs[0x0a] = (sd >> 8) & 0xff; + state->regs[0x0b] = sd & 0xff; + ret = reg_write(state, 0x09, state->regs[0x09]); + if (ret == 0) + ret = reg_write(state, 0x0a, state->regs[0x0a]); + if (ret == 0) + ret = reg_write(state, 0x0b, state->regs[0x0b]); + if (ret != 0) + return ret; + + if (!state->cfg.lpf) { + /* CSEL_Offset */ + ret = reg_write(state, 0x13, state->regs[0x13]); + if (ret < 0) + return ret; + } + + /* VCO_TM, LPF_TM */ + mask = state->cfg.lpf ? 0x3f : 0x7f; + val = state->regs[0x0c] & mask; + ret = reg_write(state, 0x0c, val); + if (ret < 0) + return ret; + usleep_range(2000, 3000); + val = state->regs[0x0c] | ~mask; + ret = reg_write(state, 0x0c, val); + if (ret < 0) + return ret; + + if (state->cfg.lpf) + msleep(state->cfg.lpf_wait); + else if (state->regs[0x03] & 0x01) + msleep(state->cfg.fast_srch_wait); + else + msleep(state->cfg.normal_srch_wait); + + if (state->cfg.lpf) { + /* LPF_FC */ + ret = reg_write(state, 0x08, 0x09); + if (ret < 0) + return ret; + + /* CSEL_Offset */ + ret = reg_write(state, 0x13, state->regs[0x13]); + if (ret < 0) + return ret; + } + return 0; +} + +static int qm1d1c0042_sleep(struct dvb_frontend *fe) +{ + struct qm1d1c0042_state *state; + int ret; + + state = fe->tuner_priv; + state->regs[0x01] &= (~(1 << 3)) & 0xff; /* BB_Reg_disable */ + state->regs[0x01] |= 1 << 0; /* STDBY */ + state->regs[0x05] |= 1 << 3; /* pfd_rst STANDBY */ + ret = reg_write(state, 0x05, state->regs[0x05]); + if (ret == 0) + ret = reg_write(state, 0x01, state->regs[0x01]); + if (ret < 0) + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static int qm1d1c0042_init(struct dvb_frontend *fe) +{ + struct qm1d1c0042_state *state; + u8 val; + int i, ret; + + state = fe->tuner_priv; + memcpy(state->regs, reg_initval, sizeof(reg_initval)); + + reg_write(state, 0x01, 0x0c); + reg_write(state, 0x01, 0x0c); + + ret = reg_write(state, 0x01, 0x0c); /* soft reset on */ + if (ret < 0) + goto failed; + usleep_range(2000, 3000); + + val = state->regs[0x01] | 0x10; + ret = reg_write(state, 0x01, val); /* soft reset off */ + if (ret < 0) + goto failed; + + /* check ID */ + ret = reg_read(state, 0x00, &val); + if (ret < 0 || val != 0x48) + goto failed; + usleep_range(2000, 3000); + + state->regs[0x0c] |= 0x40; + ret = reg_write(state, 0x0c, state->regs[0x0c]); + if (ret < 0) + goto failed; + msleep(state->cfg.lpf_wait); + + /* set all writable registers */ + for (i = 1; i <= 0x0c ; i++) { + ret = reg_write(state, i, state->regs[i]); + if (ret < 0) + goto failed; + } + for (i = 0x11; i < QM1D1C0042_NUM_REGS; i++) { + ret = reg_write(state, i, state->regs[i]); + if (ret < 0) + goto failed; + } + + ret = qm1d1c0042_wakeup(state); + if (ret < 0) + goto failed; + + ret = qm1d1c0042_set_srch_mode(state, state->cfg.fast_srch); + if (ret < 0) + goto failed; + + return ret; + +failed: + dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +/* I2C driver functions */ + +static const struct dvb_tuner_ops qm1d1c0042_ops = { + .info = { + .name = "Sharp QM1D1C0042", + + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .init = qm1d1c0042_init, + .sleep = qm1d1c0042_sleep, + .set_config = qm1d1c0042_set_config, + .set_params = qm1d1c0042_set_params, +}; + + +static int qm1d1c0042_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qm1d1c0042_state *state; + struct qm1d1c0042_config *cfg; + struct dvb_frontend *fe; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + state->i2c = client; + + cfg = client->dev.platform_data; + fe = cfg->fe; + fe->tuner_priv = state; + qm1d1c0042_set_config(fe, cfg); + memcpy(&fe->ops.tuner_ops, &qm1d1c0042_ops, sizeof(qm1d1c0042_ops)); + + i2c_set_clientdata(client, &state->cfg); + dev_info(&client->dev, "Sharp QM1D1C0042 attached.\n"); + return 0; +} + +static int qm1d1c0042_remove(struct i2c_client *client) +{ + struct qm1d1c0042_state *state; + + state = cfg_to_state(i2c_get_clientdata(client)); + state->cfg.fe->tuner_priv = NULL; + kfree(state); + return 0; +} + + +static const struct i2c_device_id qm1d1c0042_id[] = { + {"qm1d1c0042", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id); + +static struct i2c_driver qm1d1c0042_driver = { + .driver = { + .name = "qm1d1c0042", + }, + .probe = qm1d1c0042_probe, + .remove = qm1d1c0042_remove, + .id_table = qm1d1c0042_id, +}; + +module_i2c_driver(qm1d1c0042_driver); + +MODULE_DESCRIPTION("Sharp QM1D1C0042 tuner"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h new file mode 100644 index 0000000..4f5c188 --- /dev/null +++ b/drivers/media/tuners/qm1d1c0042.h @@ -0,0 +1,37 @@ +/* + * Sharp QM1D1C0042 8PSK tuner driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@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 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. + */ + +#ifndef QM1D1C0042_H +#define QM1D1C0042_H + +#include "dvb_frontend.h" + + +struct qm1d1c0042_config { + struct dvb_frontend *fe; + + u32 xtal_freq; /* [kHz] */ /* currently ignored */ + bool lpf; /* enable LPF */ + bool fast_srch; /* enable fast search mode, no LPF */ + u32 lpf_wait; /* wait in tuning with LPF enabled. [ms] */ + u32 fast_srch_wait; /* with fast-search mode, no LPF. [ms] */ + u32 normal_srch_wait; /* with no LPF/fast-search mode. [ms] */ +}; +/* special values indicating to use the default in qm1d1c0042_config */ +#define QM1D1C0042_CFG_XTAL_DFLT 0 +#define QM1D1C0042_CFG_WAIT_DFLT 0 + +#endif /* QM1D1C0042_H */ diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 6c53edb..cf97142 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157/2158 silicon tuner driver + * Silicon Labs Si2147/2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> * @@ -55,8 +55,7 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) break; } - dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", - __func__, + dev_dbg(&s->client->dev, "cmd execution took %d ms\n", jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - TIMEOUT)); @@ -75,7 +74,7 @@ err_mutex_unlock: return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -88,9 +87,12 @@ static int si2157_init(struct dvb_frontend *fe) u8 *fw_file; unsigned int chip_id; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); - /* configure? */ + if (s->fw_loaded) + goto warm; + + /* power up */ memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); cmd.wlen = 15; cmd.rlen = 1; @@ -111,45 +113,47 @@ static int si2157_init(struct dvb_frontend *fe) #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) + #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) switch (chip_id) { case SI2158_A20: fw_file = SI2158_A20_FIRMWARE; break; case SI2157_A30: + case SI2147_A30: goto skip_fw_download; break; default: dev_err(&s->client->dev, - "%s: unkown chip version Si21%d-%c%c%c\n", - KBUILD_MODNAME, cmd.args[2], cmd.args[1], + "unknown chip version Si21%d-%c%c%c\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); ret = -EINVAL; goto err; } /* cold state - try to download firmware */ - dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", - KBUILD_MODNAME, si2157_ops.info.name); + dev_info(&s->client->dev, "found a '%s' in cold state\n", + si2157_ops.info.name); /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, &s->client->dev); if (ret) { - dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", - KBUILD_MODNAME, fw_file); + dev_err(&s->client->dev, "firmware file '%s' not found\n", + fw_file); goto err; } /* firmware should be n chunks of 17 bytes */ if (fw->size % 17 != 0) { - dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", - KBUILD_MODNAME, fw_file); + dev_err(&s->client->dev, "firmware file '%s' is invalid\n", + fw_file); ret = -EINVAL; goto err; } - dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", - KBUILD_MODNAME, fw_file); + dev_info(&s->client->dev, "downloading firmware from file '%s'\n", + fw_file); for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; @@ -159,8 +163,8 @@ static int si2157_init(struct dvb_frontend *fe) ret = si2157_cmd_execute(s, &cmd); if (ret) { dev_err(&s->client->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); + "firmware download failed=%d\n", + ret); goto err; } } @@ -177,14 +181,17 @@ skip_fw_download: if (ret) goto err; - s->active = true; + s->fw_loaded = true; +warm: + s->active = true; return 0; + err: if (fw) release_firmware(fw); - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -194,20 +201,21 @@ static int si2157_sleep(struct dvb_frontend *fe) int ret; struct si2157_cmd cmd; - dev_dbg(&s->client->dev, "%s:\n", __func__); + dev_dbg(&s->client->dev, "\n"); s->active = false; - memcpy(cmd.args, "\x13", 1); - cmd.wlen = 1; - cmd.rlen = 0; + /* standby */ + memcpy(cmd.args, "\x16\x00", 2); + cmd.wlen = 2; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -220,8 +228,8 @@ static int si2157_set_params(struct dvb_frontend *fe) u8 bandwidth, delivery_system; dev_dbg(&s->client->dev, - "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", - __func__, c->delivery_system, c->frequency, + "delivery_system=%d frequency=%u bandwidth_hz=%u\n", + c->delivery_system, c->frequency, c->bandwidth_hz); if (!s->active) { @@ -239,6 +247,9 @@ static int si2157_set_params(struct dvb_frontend *fe) bandwidth = 0x0f; switch (c->delivery_system) { + case SYS_ATSC: + delivery_system = 0x00; + break; case SYS_DVBT: case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ delivery_system = 0x20; @@ -256,7 +267,14 @@ static int si2157_set_params(struct dvb_frontend *fe) if (s->inversion) cmd.args[5] = 0x01; cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -275,7 +293,7 @@ static int si2157_set_params(struct dvb_frontend *fe) return 0; err: - dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&s->client->dev, "failed=%d\n", ret); return ret; } @@ -310,13 +328,14 @@ static int si2157_probe(struct i2c_client *client, s = kzalloc(sizeof(struct si2157), GFP_KERNEL); if (!s) { ret = -ENOMEM; - dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + dev_err(&client->dev, "kzalloc() failed\n"); goto err; } s->client = client; s->fe = cfg->fe; s->inversion = cfg->inversion; + s->fw_loaded = false; mutex_init(&s->i2c_mutex); /* check if the tuner is there */ @@ -333,11 +352,10 @@ static int si2157_probe(struct i2c_client *client, i2c_set_clientdata(client, s); dev_info(&s->client->dev, - "%s: Silicon Labs Si2157/Si2158 successfully attached\n", - KBUILD_MODNAME); + "Silicon Labs Si2157/Si2158 successfully attached\n"); return 0; err: - dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); kfree(s); return ret; @@ -348,7 +366,7 @@ static int si2157_remove(struct i2c_client *client) struct si2157 *s = i2c_get_clientdata(client); struct dvb_frontend *fe = s->fe; - dev_dbg(&client->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = NULL; diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index 6da4d5d..d3b19ca 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157/2158 silicon tuner driver + * Silicon Labs Si2147/2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> * diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 3ddab5e..e71ffaf 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157/2158 silicon tuner driver + * Silicon Labs Si2147/2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> * @@ -26,6 +26,7 @@ struct si2157 { struct i2c_client *client; struct dvb_frontend *fe; bool active; + bool fw_loaded; bool inversion; }; diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 05a4ac9..d93e066 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -19,125 +19,19 @@ */ #include "tda18212.h" +#include <linux/regmap.h> -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - -struct tda18212_priv { - struct tda18212_config *cfg; - struct i2c_adapter *i2c; +struct tda18212_dev { + struct tda18212_config cfg; + struct i2c_client *client; + struct regmap *regmap; u32 if_frequency; }; -/* write multiple registers */ -static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val, - int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_address, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (1 + len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple registers */ -static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val, - int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg->i2c_address, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg->i2c_address, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c rd reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write single register */ -static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val) -{ - return tda18212_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val) -{ - return tda18212_rd_regs(priv, reg, val, 1); -} - -#if 0 /* keep, useful when developing driver */ -static void tda18212_dump_regs(struct tda18212_priv *priv) -{ - int i; - u8 buf[256]; - - #define TDA18212_RD_LEN 32 - for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN) - tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN); - - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf, - sizeof(buf), true); - - return; -} -#endif - static int tda18212_set_params(struct dvb_frontend *fe) { - struct tda18212_priv *priv = fe->tuner_priv; + struct tda18212_dev *dev = fe->tuner_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u32 if_khz; @@ -166,9 +60,9 @@ static int tda18212_set_params(struct dvb_frontend *fe) [ATSC_QAM] = { 0x7d, 0x20, 0x63 }, }; - dev_dbg(&priv->i2c->dev, - "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", - __func__, c->delivery_system, c->frequency, + dev_dbg(&dev->client->dev, + "delivery_system=%d frequency=%d bandwidth_hz=%d\n", + c->delivery_system, c->frequency, c->bandwidth_hz); if (fe->ops.i2c_gate_ctrl) @@ -176,25 +70,25 @@ static int tda18212_set_params(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_ATSC: - if_khz = priv->cfg->if_atsc_vsb; + if_khz = dev->cfg.if_atsc_vsb; i = ATSC_VSB; break; case SYS_DVBC_ANNEX_B: - if_khz = priv->cfg->if_atsc_qam; + if_khz = dev->cfg.if_atsc_qam; i = ATSC_QAM; break; case SYS_DVBT: switch (c->bandwidth_hz) { case 6000000: - if_khz = priv->cfg->if_dvbt_6; + if_khz = dev->cfg.if_dvbt_6; i = DVBT_6; break; case 7000000: - if_khz = priv->cfg->if_dvbt_7; + if_khz = dev->cfg.if_dvbt_7; i = DVBT_7; break; case 8000000: - if_khz = priv->cfg->if_dvbt_8; + if_khz = dev->cfg.if_dvbt_8; i = DVBT_8; break; default: @@ -205,15 +99,15 @@ static int tda18212_set_params(struct dvb_frontend *fe) case SYS_DVBT2: switch (c->bandwidth_hz) { case 6000000: - if_khz = priv->cfg->if_dvbt2_6; + if_khz = dev->cfg.if_dvbt2_6; i = DVBT2_6; break; case 7000000: - if_khz = priv->cfg->if_dvbt2_7; + if_khz = dev->cfg.if_dvbt2_7; i = DVBT2_7; break; case 8000000: - if_khz = priv->cfg->if_dvbt2_8; + if_khz = dev->cfg.if_dvbt2_8; i = DVBT2_8; break; default: @@ -223,7 +117,7 @@ static int tda18212_set_params(struct dvb_frontend *fe) break; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: - if_khz = priv->cfg->if_dvbc; + if_khz = dev->cfg.if_dvbc; i = DVBC_8; break; default: @@ -231,15 +125,15 @@ static int tda18212_set_params(struct dvb_frontend *fe) goto error; } - ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]); + ret = regmap_write(dev->regmap, 0x23, bw_params[i][2]); if (ret) goto error; - ret = tda18212_wr_reg(priv, 0x06, 0x00); + ret = regmap_write(dev->regmap, 0x06, 0x00); if (ret) goto error; - ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]); + ret = regmap_write(dev->regmap, 0x0f, bw_params[i][0]); if (ret) goto error; @@ -252,12 +146,12 @@ static int tda18212_set_params(struct dvb_frontend *fe) buf[6] = ((c->frequency / 1000) >> 0) & 0xff; buf[7] = 0xc1; buf[8] = 0x01; - ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf)); + ret = regmap_bulk_write(dev->regmap, 0x12, buf, sizeof(buf)); if (ret) goto error; /* actual IF rounded as it is on register */ - priv->if_frequency = buf[3] * 50 * 1000; + dev->if_frequency = buf[3] * 50 * 1000; exit: if (fe->ops.i2c_gate_ctrl) @@ -266,26 +160,19 @@ exit: return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&dev->client->dev, "failed=%d\n", ret); goto exit; } static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct tda18212_priv *priv = fe->tuner_priv; + struct tda18212_dev *dev = fe->tuner_priv; - *frequency = priv->if_frequency; + *frequency = dev->if_frequency; return 0; } -static int tda18212_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - static const struct dvb_tuner_ops tda18212_tuner_ops = { .info = { .name = "NXP TDA18212", @@ -295,53 +182,110 @@ static const struct dvb_tuner_ops tda18212_tuner_ops = { .frequency_step = 1000, }, - .release = tda18212_release, - .set_params = tda18212_set_params, .get_if_frequency = tda18212_get_if_frequency, }; -struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tda18212_config *cfg) +static int tda18212_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct tda18212_priv *priv = NULL; + struct tda18212_config *cfg = client->dev.platform_data; + struct dvb_frontend *fe = cfg->fe; + struct tda18212_dev *dev; int ret; - u8 val; + unsigned int chip_id; + char *version; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; - priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "kzalloc() failed\n"); + goto err; + } - priv->cfg = cfg; - priv->i2c = i2c; - fe->tuner_priv = priv; + memcpy(&dev->cfg, cfg, sizeof(struct tda18212_config)); + dev->client = client; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err; + } + /* check if the tuner is there */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - /* check if the tuner is there */ - ret = tda18212_rd_reg(priv, 0x00, &val); + ret = regmap_read(dev->regmap, 0x00, &chip_id); + dev_dbg(&dev->client->dev, "chip_id=%02x\n", chip_id); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - if (!ret) - dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); - if (ret || val != 0xc7) { - kfree(priv); - return NULL; + if (ret) + goto err; + + switch (chip_id) { + case 0xc7: + version = "M"; /* master */ + break; + case 0x47: + version = "S"; /* slave */ + break; + default: + ret = -ENODEV; + goto err; } - dev_info(&priv->i2c->dev, - "%s: NXP TDA18212HN successfully identified\n", - KBUILD_MODNAME); + dev_info(&dev->client->dev, + "NXP TDA18212HN/%s successfully identified\n", version); + fe->tuner_priv = dev; memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops, - sizeof(struct dvb_tuner_ops)); + sizeof(struct dvb_tuner_ops)); + i2c_set_clientdata(client, dev); - return fe; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + kfree(dev); + return ret; } -EXPORT_SYMBOL(tda18212_attach); + +static int tda18212_remove(struct i2c_client *client) +{ + struct tda18212_dev *dev = i2c_get_clientdata(client); + struct dvb_frontend *fe = dev->cfg.fe; + + dev_dbg(&client->dev, "\n"); + + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = NULL; + kfree(dev); + + return 0; +} + +static const struct i2c_device_id tda18212_id[] = { + {"tda18212", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tda18212_id); + +static struct i2c_driver tda18212_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tda18212", + }, + .probe = tda18212_probe, + .remove = tda18212_remove, + .id_table = tda18212_id, +}; + +module_i2c_driver(tda18212_driver); MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/tuners/tda18212.h b/drivers/media/tuners/tda18212.h index c36b49e..e58c909 100644 --- a/drivers/media/tuners/tda18212.h +++ b/drivers/media/tuners/tda18212.h @@ -25,8 +25,6 @@ #include "dvb_frontend.h" struct tda18212_config { - u8 i2c_address; - u16 if_dvbt_6; u16 if_dvbt_7; u16 if_dvbt_8; @@ -37,18 +35,11 @@ struct tda18212_config { u16 if_dvbc; u16 if_atsc_vsb; u16 if_atsc_qam; -}; -#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18212) -extern struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tda18212_config *cfg); -#else -static inline struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tda18212_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif + /* + * pointer to DVB frontend + */ + struct dvb_frontend *fe; +}; #endif diff --git a/drivers/media/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c index 18c77af..86e5e31 100644 --- a/drivers/media/tuners/tda18271-common.c +++ b/drivers/media/tuners/tda18271-common.c @@ -714,12 +714,11 @@ fail: return ret; } -int _tda_printk(struct tda18271_priv *state, const char *level, - const char *func, const char *fmt, ...) +void _tda_printk(struct tda18271_priv *state, const char *level, + const char *func, const char *fmt, ...) { struct va_format vaf; va_list args; - int rtn; va_start(args, fmt); @@ -727,15 +726,13 @@ int _tda_printk(struct tda18271_priv *state, const char *level, vaf.va = &args; if (state) - rtn = printk("%s%s: [%d-%04x|%c] %pV", - level, func, i2c_adapter_id(state->i2c_props.adap), - state->i2c_props.addr, - (state->role == TDA18271_MASTER) ? 'M' : 'S', - &vaf); + printk("%s%s: [%d-%04x|%c] %pV", + level, func, i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr, + (state->role == TDA18271_MASTER) ? 'M' : 'S', + &vaf); else - rtn = printk("%s%s: %pV", level, func, &vaf); + printk("%s%s: %pV", level, func, &vaf); va_end(args); - - return rtn; } diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h index 454c152..b36a7b7 100644 --- a/drivers/media/tuners/tda18271-priv.h +++ b/drivers/media/tuners/tda18271-priv.h @@ -139,8 +139,8 @@ extern int tda18271_debug; #define DBG_CAL 16 __attribute__((format(printf, 4, 5))) -int _tda_printk(struct tda18271_priv *state, const char *level, - const char *func, const char *fmt, ...); +void _tda_printk(struct tda18271_priv *state, const char *level, + const char *func, const char *fmt, ...); #define tda_printk(st, lvl, fmt, arg...) \ _tda_printk(st, lvl, __func__, fmt, ##arg) diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 565eeeb..d12f5e4 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -178,67 +178,67 @@ static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) #define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) { - if (type & BASE) + if (type & BASE) printk("BASE "); - if (type & INIT1) + if (type & INIT1) printk("INIT1 "); - if (type & F8MHZ) + if (type & F8MHZ) printk("F8MHZ "); - if (type & MTS) + if (type & MTS) printk("MTS "); - if (type & D2620) + if (type & D2620) printk("D2620 "); - if (type & D2633) + if (type & D2633) printk("D2633 "); - if (type & DTV6) + if (type & DTV6) printk("DTV6 "); - if (type & QAM) + if (type & QAM) printk("QAM "); - if (type & DTV7) + if (type & DTV7) printk("DTV7 "); - if (type & DTV78) + if (type & DTV78) printk("DTV78 "); - if (type & DTV8) + if (type & DTV8) printk("DTV8 "); - if (type & FM) + if (type & FM) printk("FM "); - if (type & INPUT1) + if (type & INPUT1) printk("INPUT1 "); - if (type & LCD) + if (type & LCD) printk("LCD "); - if (type & NOGD) + if (type & NOGD) printk("NOGD "); - if (type & MONO) + if (type & MONO) printk("MONO "); - if (type & ATSC) + if (type & ATSC) printk("ATSC "); - if (type & IF) + if (type & IF) printk("IF "); - if (type & LG60) + if (type & LG60) printk("LG60 "); - if (type & ATI638) + if (type & ATI638) printk("ATI638 "); - if (type & OREN538) + if (type & OREN538) printk("OREN538 "); - if (type & OREN36) + if (type & OREN36) printk("OREN36 "); - if (type & TOYOTA388) + if (type & TOYOTA388) printk("TOYOTA388 "); - if (type & TOYOTA794) + if (type & TOYOTA794) printk("TOYOTA794 "); - if (type & DIBCOM52) + if (type & DIBCOM52) printk("DIBCOM52 "); - if (type & ZARLINK456) + if (type & ZARLINK456) printk("ZARLINK456 "); - if (type & CHINA) + if (type & CHINA) printk("CHINA "); - if (type & F6MHZ) + if (type & F6MHZ) printk("F6MHZ "); - if (type & INPUT2) + if (type & INPUT2) printk("INPUT2 "); - if (type & SCODE) + if (type & SCODE) printk("SCODE "); - if (type & HAS_IF) + if (type & HAS_IF) printk("HAS_IF_%d ", int_freq); } diff --git a/drivers/media/tuners/tuner_it913x.c b/drivers/media/tuners/tuner_it913x.c deleted file mode 100644 index 3d83c42..0000000 --- a/drivers/media/tuners/tuner_it913x.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * ITE Tech IT9137 silicon tuner driver - * - * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) - * IT9137 Copyright (C) ITE Tech 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; 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 "tuner_it913x_priv.h" - -struct it913x_state { - struct i2c_adapter *i2c_adap; - u8 i2c_addr; - u8 chip_ver; - u8 tuner_type; - u8 firmware_ver; - u16 tun_xtal; - u8 tun_fdiv; - u8 tun_clk_mode; - u32 tun_fn_min; -}; - -/* read multiple registers */ -static int it913x_rd_regs(struct it913x_state *state, - u32 reg, u8 *data, u8 count) -{ - int ret; - u8 b[3]; - struct i2c_msg msg[2] = { - { .addr = state->i2c_addr, .flags = 0, - .buf = b, .len = sizeof(b) }, - { .addr = state->i2c_addr, .flags = I2C_M_RD, - .buf = data, .len = count } - }; - b[0] = (u8)(reg >> 16) & 0xff; - b[1] = (u8)(reg >> 8) & 0xff; - b[2] = (u8) reg & 0xff; - b[0] |= 0x80; /* All reads from demodulator */ - - ret = i2c_transfer(state->i2c_adap, msg, 2); - - return ret; -} - -/* read single register */ -static int it913x_rd_reg(struct it913x_state *state, u32 reg) -{ - int ret; - u8 b[1]; - ret = it913x_rd_regs(state, reg, &b[0], sizeof(b)); - return (ret < 0) ? -ENODEV : b[0]; -} - -/* write multiple registers */ -static int it913x_wr_regs(struct it913x_state *state, - u8 pro, u32 reg, u8 buf[], u8 count) -{ - u8 b[256]; - struct i2c_msg msg[1] = { - { .addr = state->i2c_addr, .flags = 0, - .buf = b, .len = 3 + count } - }; - int ret; - b[0] = (u8)(reg >> 16) & 0xff; - b[1] = (u8)(reg >> 8) & 0xff; - b[2] = (u8) reg & 0xff; - memcpy(&b[3], buf, count); - - if (pro == PRO_DMOD) - b[0] |= 0x80; - - ret = i2c_transfer(state->i2c_adap, msg, 1); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* write single register */ -static int it913x_wr_reg(struct it913x_state *state, - u8 pro, u32 reg, u32 data) -{ - int ret; - u8 b[4]; - u8 s; - - b[0] = data >> 24; - b[1] = (data >> 16) & 0xff; - b[2] = (data >> 8) & 0xff; - b[3] = data & 0xff; - /* expand write as needed */ - if (data < 0x100) - s = 3; - else if (data < 0x1000) - s = 2; - else if (data < 0x100000) - s = 1; - else - s = 0; - - ret = it913x_wr_regs(state, pro, reg, &b[s], sizeof(b) - s); - - return ret; -} - -static int it913x_script_loader(struct it913x_state *state, - struct it913xset *loadscript) -{ - int ret, i; - if (loadscript == NULL) - return -EINVAL; - - for (i = 0; i < 1000; ++i) { - if (loadscript[i].pro == 0xff) - break; - ret = it913x_wr_regs(state, loadscript[i].pro, - loadscript[i].address, - loadscript[i].reg, loadscript[i].count); - if (ret < 0) - return -ENODEV; - } - return 0; -} - -static int it913x_init(struct dvb_frontend *fe) -{ - struct it913x_state *state = fe->tuner_priv; - int ret, i, reg; - u8 val, nv_val; - u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; - u8 b[2]; - - reg = it913x_rd_reg(state, 0xec86); - switch (reg) { - case 0: - state->tun_clk_mode = reg; - state->tun_xtal = 2000; - state->tun_fdiv = 3; - val = 16; - break; - case -ENODEV: - return -ENODEV; - case 1: - default: - state->tun_clk_mode = reg; - state->tun_xtal = 640; - state->tun_fdiv = 1; - val = 6; - break; - } - - reg = it913x_rd_reg(state, 0xed03); - - if (reg < 0) - return -ENODEV; - else if (reg < ARRAY_SIZE(nv)) - nv_val = nv[reg]; - else - nv_val = 2; - - for (i = 0; i < 50; i++) { - ret = it913x_rd_regs(state, 0xed23, &b[0], sizeof(b)); - reg = (b[1] << 8) + b[0]; - if (reg > 0) - break; - if (ret < 0) - return -ENODEV; - udelay(2000); - } - state->tun_fn_min = state->tun_xtal * reg; - state->tun_fn_min /= (state->tun_fdiv * nv_val); - dev_dbg(&state->i2c_adap->dev, "%s: Tuner fn_min %d\n", __func__, - state->tun_fn_min); - - if (state->chip_ver > 1) - msleep(50); - else { - for (i = 0; i < 50; i++) { - reg = it913x_rd_reg(state, 0xec82); - if (reg > 0) - break; - if (reg < 0) - return -ENODEV; - udelay(2000); - } - } - - /* Power Up Tuner - common all versions */ - ret = it913x_wr_reg(state, PRO_DMOD, 0xec40, 0x1); - ret |= it913x_wr_reg(state, PRO_DMOD, 0xfba8, 0x0); - ret |= it913x_wr_reg(state, PRO_DMOD, 0xec57, 0x0); - ret |= it913x_wr_reg(state, PRO_DMOD, 0xec58, 0x0); - - return it913x_wr_reg(state, PRO_DMOD, 0xed81, val); -} - -static int it9137_set_params(struct dvb_frontend *fe) -{ - struct it913x_state *state = fe->tuner_priv; - struct it913xset *set_tuner = set_it9137_template; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 bandwidth = p->bandwidth_hz; - u32 frequency_m = p->frequency; - int ret, reg; - u32 frequency = frequency_m / 1000; - u32 freq, temp_f, tmp; - u16 iqik_m_cal; - u16 n_div; - u8 n; - u8 l_band; - u8 lna_band; - u8 bw; - - if (state->firmware_ver == 1) - set_tuner = set_it9135_template; - else - set_tuner = set_it9137_template; - - dev_dbg(&state->i2c_adap->dev, "%s: Tuner Frequency %d Bandwidth %d\n", - __func__, frequency, bandwidth); - - if (frequency >= 51000 && frequency <= 440000) { - l_band = 0; - lna_band = 0; - } else if (frequency > 440000 && frequency <= 484000) { - l_band = 1; - lna_band = 1; - } else if (frequency > 484000 && frequency <= 533000) { - l_band = 1; - lna_band = 2; - } else if (frequency > 533000 && frequency <= 587000) { - l_band = 1; - lna_band = 3; - } else if (frequency > 587000 && frequency <= 645000) { - l_band = 1; - lna_band = 4; - } else if (frequency > 645000 && frequency <= 710000) { - l_band = 1; - lna_band = 5; - } else if (frequency > 710000 && frequency <= 782000) { - l_band = 1; - lna_band = 6; - } else if (frequency > 782000 && frequency <= 860000) { - l_band = 1; - lna_band = 7; - } else if (frequency > 1450000 && frequency <= 1492000) { - l_band = 1; - lna_band = 0; - } else if (frequency > 1660000 && frequency <= 1685000) { - l_band = 1; - lna_band = 1; - } else - return -EINVAL; - set_tuner[0].reg[0] = lna_band; - - switch (bandwidth) { - case 5000000: - bw = 0; - break; - case 6000000: - bw = 2; - break; - case 7000000: - bw = 4; - break; - default: - case 8000000: - bw = 6; - break; - } - - set_tuner[1].reg[0] = bw; - set_tuner[2].reg[0] = 0xa0 | (l_band << 3); - - if (frequency > 53000 && frequency <= 74000) { - n_div = 48; - n = 0; - } else if (frequency > 74000 && frequency <= 111000) { - n_div = 32; - n = 1; - } else if (frequency > 111000 && frequency <= 148000) { - n_div = 24; - n = 2; - } else if (frequency > 148000 && frequency <= 222000) { - n_div = 16; - n = 3; - } else if (frequency > 222000 && frequency <= 296000) { - n_div = 12; - n = 4; - } else if (frequency > 296000 && frequency <= 445000) { - n_div = 8; - n = 5; - } else if (frequency > 445000 && frequency <= state->tun_fn_min) { - n_div = 6; - n = 6; - } else if (frequency > state->tun_fn_min && frequency <= 950000) { - n_div = 4; - n = 7; - } else if (frequency > 1450000 && frequency <= 1680000) { - n_div = 2; - n = 0; - } else - return -EINVAL; - - reg = it913x_rd_reg(state, 0xed81); - iqik_m_cal = (u16)reg * n_div; - - if (reg < 0x20) { - if (state->tun_clk_mode == 0) - iqik_m_cal = (iqik_m_cal * 9) >> 5; - else - iqik_m_cal >>= 1; - } else { - iqik_m_cal = 0x40 - iqik_m_cal; - if (state->tun_clk_mode == 0) - iqik_m_cal = ~((iqik_m_cal * 9) >> 5); - else - iqik_m_cal = ~(iqik_m_cal >> 1); - } - - temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv; - freq = temp_f / state->tun_xtal; - tmp = freq * state->tun_xtal; - - if ((temp_f - tmp) >= (state->tun_xtal >> 1)) - freq++; - - freq += (u32) n << 13; - /* Frequency OMEGA_IQIK_M_CAL_MID*/ - temp_f = freq + (u32)iqik_m_cal; - - set_tuner[3].reg[0] = temp_f & 0xff; - set_tuner[4].reg[0] = (temp_f >> 8) & 0xff; - - dev_dbg(&state->i2c_adap->dev, "%s: High Frequency = %04x\n", - __func__, temp_f); - - /* Lower frequency */ - set_tuner[5].reg[0] = freq & 0xff; - set_tuner[6].reg[0] = (freq >> 8) & 0xff; - - dev_dbg(&state->i2c_adap->dev, "%s: low Frequency = %04x\n", - __func__, freq); - - ret = it913x_script_loader(state, set_tuner); - - return (ret < 0) ? -ENODEV : 0; -} - -/* Power sequence */ -/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */ -/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */ - -static int it913x_sleep(struct dvb_frontend *fe) -{ - struct it913x_state *state = fe->tuner_priv; - return it913x_script_loader(state, it9137_tuner_off); -} - -static int it913x_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - return 0; -} - -static const struct dvb_tuner_ops it913x_tuner_ops = { - .info = { - .name = "ITE Tech IT913X", - .frequency_min = 174000000, - .frequency_max = 862000000, - }, - - .release = it913x_release, - - .init = it913x_init, - .sleep = it913x_sleep, - .set_params = it9137_set_params, -}; - -struct dvb_frontend *it913x_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, u8 i2c_addr, u8 config) -{ - struct it913x_state *state = NULL; - int ret; - - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct it913x_state), GFP_KERNEL); - if (state == NULL) - return NULL; - - state->i2c_adap = i2c_adap; - state->i2c_addr = i2c_addr; - - switch (config) { - case AF9033_TUNER_IT9135_38: - case AF9033_TUNER_IT9135_51: - case AF9033_TUNER_IT9135_52: - state->chip_ver = 0x01; - break; - case AF9033_TUNER_IT9135_60: - case AF9033_TUNER_IT9135_61: - case AF9033_TUNER_IT9135_62: - state->chip_ver = 0x02; - break; - default: - dev_dbg(&i2c_adap->dev, - "%s: invalid config=%02x\n", __func__, config); - goto error; - } - - state->tuner_type = config; - state->firmware_ver = 1; - - /* tuner RF initial */ - ret = it913x_wr_reg(state, PRO_DMOD, 0xec4c, 0x68); - if (ret < 0) - goto error; - - fe->tuner_priv = state; - memcpy(&fe->ops.tuner_ops, &it913x_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - dev_info(&i2c_adap->dev, - "%s: ITE Tech IT913X successfully attached\n", - KBUILD_MODNAME); - dev_dbg(&i2c_adap->dev, "%s: config=%02x chip_ver=%02x\n", - __func__, config, state->chip_ver); - - return fe; -error: - kfree(state); - return NULL; -} -EXPORT_SYMBOL(it913x_attach); - -MODULE_DESCRIPTION("ITE Tech IT913X silicon tuner driver"); -MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tuner_it913x_priv.h b/drivers/media/tuners/tuner_it913x_priv.h deleted file mode 100644 index ce65210..0000000 --- a/drivers/media/tuners/tuner_it913x_priv.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * ITE Tech IT9137 silicon tuner driver - * - * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) - * IT9137 Copyright (C) ITE Tech 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; 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 IT913X_PRIV_H -#define IT913X_PRIV_H - -#include "tuner_it913x.h" -#include "af9033.h" - -#define PRO_LINK 0x0 -#define PRO_DMOD 0x1 -#define TRIGGER_OFSM 0x0000 - -struct it913xset { u32 pro; - u32 address; - u8 reg[15]; - u8 count; -}; - -/* Tuner setting scripts (still keeping it9137) */ -static struct it913xset it9137_tuner_off[] = { - {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */ - {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */ - {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04}, - {PRO_DMOD, 0xec06, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00}, 0x0c}, - {PRO_DMOD, 0xec12, {0x00, 0x00, 0x00, 0x00}, 0x04}, - {PRO_DMOD, 0xec17, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00}, 0x09}, - {PRO_DMOD, 0xec22, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00}, 0x0a}, - {PRO_DMOD, 0xec20, {0x00}, 0x01}, - {PRO_DMOD, 0xec3f, {0x01}, 0x01}, - {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ -}; - -static struct it913xset set_it9135_template[] = { - {PRO_DMOD, 0xee06, {0x00}, 0x01}, - {PRO_DMOD, 0xec56, {0x00}, 0x01}, - {PRO_DMOD, 0xec4c, {0x00}, 0x01}, - {PRO_DMOD, 0xec4d, {0x00}, 0x01}, - {PRO_DMOD, 0xec4e, {0x00}, 0x01}, - {PRO_DMOD, 0x011e, {0x00}, 0x01}, /* Older Devices */ - {PRO_DMOD, 0x011f, {0x00}, 0x01}, - {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ -}; - -static struct it913xset set_it9137_template[] = { - {PRO_DMOD, 0xee06, {0x00}, 0x01}, - {PRO_DMOD, 0xec56, {0x00}, 0x01}, - {PRO_DMOD, 0xec4c, {0x00}, 0x01}, - {PRO_DMOD, 0xec4d, {0x00}, 0x01}, - {PRO_DMOD, 0xec4e, {0x00}, 0x01}, - {PRO_DMOD, 0xec4f, {0x00}, 0x01}, - {PRO_DMOD, 0xec50, {0x00}, 0x01}, - {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ -}; - -#endif diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index f9ab79e..219ebaf 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -569,67 +569,67 @@ static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val) #define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) { - if (type & BASE) + if (type & BASE) printk(KERN_CONT "BASE "); - if (type & INIT1) + if (type & INIT1) printk(KERN_CONT "INIT1 "); - if (type & F8MHZ) + if (type & F8MHZ) printk(KERN_CONT "F8MHZ "); - if (type & MTS) + if (type & MTS) printk(KERN_CONT "MTS "); - if (type & D2620) + if (type & D2620) printk(KERN_CONT "D2620 "); - if (type & D2633) + if (type & D2633) printk(KERN_CONT "D2633 "); - if (type & DTV6) + if (type & DTV6) printk(KERN_CONT "DTV6 "); - if (type & QAM) + if (type & QAM) printk(KERN_CONT "QAM "); - if (type & DTV7) + if (type & DTV7) printk(KERN_CONT "DTV7 "); - if (type & DTV78) + if (type & DTV78) printk(KERN_CONT "DTV78 "); - if (type & DTV8) + if (type & DTV8) printk(KERN_CONT "DTV8 "); - if (type & FM) + if (type & FM) printk(KERN_CONT "FM "); - if (type & INPUT1) + if (type & INPUT1) printk(KERN_CONT "INPUT1 "); - if (type & LCD) + if (type & LCD) printk(KERN_CONT "LCD "); - if (type & NOGD) + if (type & NOGD) printk(KERN_CONT "NOGD "); - if (type & MONO) + if (type & MONO) printk(KERN_CONT "MONO "); - if (type & ATSC) + if (type & ATSC) printk(KERN_CONT "ATSC "); - if (type & IF) + if (type & IF) printk(KERN_CONT "IF "); - if (type & LG60) + if (type & LG60) printk(KERN_CONT "LG60 "); - if (type & ATI638) + if (type & ATI638) printk(KERN_CONT "ATI638 "); - if (type & OREN538) + if (type & OREN538) printk(KERN_CONT "OREN538 "); - if (type & OREN36) + if (type & OREN36) printk(KERN_CONT "OREN36 "); - if (type & TOYOTA388) + if (type & TOYOTA388) printk(KERN_CONT "TOYOTA388 "); - if (type & TOYOTA794) + if (type & TOYOTA794) printk(KERN_CONT "TOYOTA794 "); - if (type & DIBCOM52) + if (type & DIBCOM52) printk(KERN_CONT "DIBCOM52 "); - if (type & ZARLINK456) + if (type & ZARLINK456) printk(KERN_CONT "ZARLINK456 "); - if (type & CHINA) + if (type & CHINA) printk(KERN_CONT "CHINA "); - if (type & F6MHZ) + if (type & F6MHZ) printk(KERN_CONT "F6MHZ "); - if (type & INPUT2) + if (type & INPUT2) printk(KERN_CONT "INPUT2 "); - if (type & SCODE) + if (type & SCODE) printk(KERN_CONT "SCODE "); - if (type & HAS_IF) + if (type & HAS_IF) printk(KERN_CONT "HAS_IF_%d ", int_freq); } diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index e135760..e44c8ab 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -59,6 +59,7 @@ struct xc5000_priv { u32 freq_hz, freq_offset; u32 bandwidth; u8 video_standard; + unsigned int mode; u8 rf_mode; u8 radio_input; @@ -69,6 +70,8 @@ struct xc5000_priv { struct dvb_frontend *fe; struct delayed_work timer_sleep; + + const struct firmware *firmware; }; /* Misc Defines */ @@ -712,9 +715,50 @@ static void xc_debug_dump(struct xc5000_priv *priv) } } -static int xc5000_set_params(struct dvb_frontend *fe) +static int xc5000_tune_digital(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + u32 bw = fe->dtv_property_cache.bandwidth_hz; + + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { + printk(KERN_ERR + "xc5000: xc_set_signal_source(%d) failed\n", + priv->rf_mode); + return -EREMOTEIO; + } + + ret = xc_set_tv_standard(priv, + xc5000_standard[priv->video_standard].video_mode, + xc5000_standard[priv->video_standard].audio_mode, 0); + if (ret != 0) { + printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n"); + return -EREMOTEIO; + } + + ret = xc_set_IF_frequency(priv, priv->if_khz); + if (ret != 0) { + printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", + priv->if_khz); + return -EIO; + } + + xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a); + + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL); + + if (debug) + xc_debug_dump(priv); + + priv->bandwidth = bw; + + return 0; +} + +static int xc5000_set_digital_params(struct dvb_frontend *fe) { - int ret, b; + int b; struct xc5000_priv *priv = fe->tuner_priv; u32 bw = fe->dtv_property_cache.bandwidth_hz; u32 freq = fe->dtv_property_cache.frequency; @@ -794,43 +838,12 @@ static int xc5000_set_params(struct dvb_frontend *fe) } priv->freq_hz = freq - priv->freq_offset; + priv->mode = V4L2_TUNER_DIGITAL_TV; dprintk(1, "%s() frequency=%d (compensated to %d)\n", __func__, freq, priv->freq_hz); - ret = xc_set_signal_source(priv, priv->rf_mode); - if (ret != 0) { - printk(KERN_ERR - "xc5000: xc_set_signal_source(%d) failed\n", - priv->rf_mode); - return -EREMOTEIO; - } - - ret = xc_set_tv_standard(priv, - xc5000_standard[priv->video_standard].video_mode, - xc5000_standard[priv->video_standard].audio_mode, 0); - if (ret != 0) { - printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n"); - return -EREMOTEIO; - } - - ret = xc_set_IF_frequency(priv, priv->if_khz); - if (ret != 0) { - printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", - priv->if_khz); - return -EIO; - } - - xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a); - - xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL); - - if (debug) - xc_debug_dump(priv); - - priv->bandwidth = bw; - - return 0; + return xc5000_tune_digital(fe); } static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) @@ -852,12 +865,10 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) return ret; } -static int xc5000_set_tv_freq(struct dvb_frontend *fe, - struct analog_parameters *params) +static void xc5000_config_tv(struct dvb_frontend *fe, + struct analog_parameters *params) { struct xc5000_priv *priv = fe->tuner_priv; - u16 pll_lock_status; - int ret; dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", __func__, params->frequency); @@ -876,42 +887,49 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe, if (params->std & V4L2_STD_MN) { /* default to BTSC audio standard */ priv->video_standard = MN_NTSC_PAL_BTSC; - goto tune_channel; + return; } if (params->std & V4L2_STD_PAL_BG) { /* default to NICAM audio standard */ priv->video_standard = BG_PAL_NICAM; - goto tune_channel; + return; } if (params->std & V4L2_STD_PAL_I) { /* default to NICAM audio standard */ priv->video_standard = I_PAL_NICAM; - goto tune_channel; + return; } if (params->std & V4L2_STD_PAL_DK) { /* default to NICAM audio standard */ priv->video_standard = DK_PAL_NICAM; - goto tune_channel; + return; } if (params->std & V4L2_STD_SECAM_DK) { /* default to A2 DK1 audio standard */ priv->video_standard = DK_SECAM_A2DK1; - goto tune_channel; + return; } if (params->std & V4L2_STD_SECAM_L) { priv->video_standard = L_SECAM_NICAM; - goto tune_channel; + return; } if (params->std & V4L2_STD_SECAM_LC) { priv->video_standard = LC_SECAM_NICAM; - goto tune_channel; + return; } +} + +static int xc5000_set_tv_freq(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + u16 pll_lock_status; + int ret; tune_channel: ret = xc_set_signal_source(priv, priv->rf_mode); @@ -955,12 +973,11 @@ tune_channel: return 0; } -static int xc5000_set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) +static int xc5000_config_radio(struct dvb_frontend *fe, + struct analog_parameters *params) + { struct xc5000_priv *priv = fe->tuner_priv; - int ret = -EINVAL; - u8 radio_input; dprintk(1, "%s() frequency=%d (in units of khz)\n", __func__, params->frequency); @@ -970,6 +987,18 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, return -EINVAL; } + priv->freq_hz = params->frequency * 125 / 2; + priv->rf_mode = XC_RF_MODE_AIR; + + return 0; +} + +static int xc5000_set_radio_freq(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + u8 radio_input; + if (priv->radio_input == XC5000_RADIO_FM1) radio_input = FM_RADIO_INPUT1; else if (priv->radio_input == XC5000_RADIO_FM2) @@ -982,10 +1011,6 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, return -EINVAL; } - priv->freq_hz = params->frequency * 125 / 2; - - priv->rf_mode = XC_RF_MODE_AIR; - ret = xc_set_tv_standard(priv, xc5000_standard[radio_input].video_mode, xc5000_standard[radio_input].audio_mode, radio_input); @@ -1013,34 +1038,53 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, return 0; } -static int xc5000_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) +static int xc5000_set_params(struct dvb_frontend *fe) { struct xc5000_priv *priv = fe->tuner_priv; - int ret = -EINVAL; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; if (xc_load_fw_and_init_tuner(fe, 0) != 0) { dprintk(1, "Unable to load firmware and init tuner\n"); return -EINVAL; } + switch (priv->mode) { + case V4L2_TUNER_RADIO: + return xc5000_set_radio_freq(fe); + case V4L2_TUNER_ANALOG_TV: + return xc5000_set_tv_freq(fe); + case V4L2_TUNER_DIGITAL_TV: + return xc5000_tune_digital(fe); + } + + return 0; +} + +static int xc5000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + switch (params->mode) { case V4L2_TUNER_RADIO: - ret = xc5000_set_radio_freq(fe, params); + ret = xc5000_config_radio(fe, params); + if (ret) + return ret; break; case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - ret = xc5000_set_tv_freq(fe, params); + xc5000_config_tv(fe, params); + break; + default: break; } + priv->mode = params->mode; - return ret; + return xc5000_set_params(fe); } - static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; @@ -1094,20 +1138,23 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (!force && xc5000_is_firmware_loaded(fe) == 0) return 0; - ret = request_firmware(&fw, desired_fw->name, - priv->i2c_props.adap->dev.parent); - if (ret) { - printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); - return ret; - } - - dprintk(1, "firmware read %Zu bytes.\n", fw->size); + if (!priv->firmware) { + ret = request_firmware(&fw, desired_fw->name, + priv->i2c_props.adap->dev.parent); + if (ret) { + pr_err("xc5000: Upload failed. rc %d\n", ret); + return ret; + } + dprintk(1, "firmware read %Zu bytes.\n", fw->size); - if (fw->size != desired_fw->size) { - printk(KERN_ERR "xc5000: Firmware file with incorrect size\n"); - ret = -EINVAL; - goto err; - } + if (fw->size != desired_fw->size) { + pr_err("xc5000: Firmware file with incorrect size\n"); + release_firmware(fw); + return -EINVAL; + } + priv->firmware = fw; + } else + fw = priv->firmware; /* Try up to 5 times to load firmware */ for (i = 0; i < 5; i++) { @@ -1190,7 +1237,6 @@ err: else printk(KERN_CONT " - too many retries. Giving up\n"); - release_firmware(fw); return ret; } @@ -1229,6 +1275,38 @@ static int xc5000_sleep(struct dvb_frontend *fe) return 0; } +static int xc5000_suspend(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + + dprintk(1, "%s()\n", __func__); + + cancel_delayed_work(&priv->timer_sleep); + + ret = xc5000_tuner_reset(fe); + if (ret != 0) + printk(KERN_ERR + "xc5000: %s() unable to shutdown tuner\n", + __func__); + + return 0; +} + +static int xc5000_resume(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + + dprintk(1, "%s()\n", __func__); + + /* suspended before firmware is loaded. + Avoid firmware load in resume path. */ + if (!priv->firmware) + return 0; + + return xc5000_set_params(fe); +} + static int xc5000_init(struct dvb_frontend *fe) { struct xc5000_priv *priv = fe->tuner_priv; @@ -1256,6 +1334,8 @@ static int xc5000_release(struct dvb_frontend *fe) if (priv) { cancel_delayed_work(&priv->timer_sleep); hybrid_tuner_release_state(priv); + if (priv->firmware) + release_firmware(priv->firmware); } mutex_unlock(&xc5000_list_mutex); @@ -1293,9 +1373,11 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { .release = xc5000_release, .init = xc5000_init, .sleep = xc5000_sleep, + .suspend = xc5000_suspend, + .resume = xc5000_resume, .set_config = xc5000_set_config, - .set_params = xc5000_set_params, + .set_params = xc5000_set_digital_params, .set_analog_params = xc5000_set_analog_params, .get_frequency = xc5000_get_frequency, .get_if_frequency = xc5000_get_if_frequency, diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 94d51e0..056181f 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -46,6 +46,7 @@ source "drivers/media/usb/ttusb-budget/Kconfig" source "drivers/media/usb/ttusb-dec/Kconfig" source "drivers/media/usb/siano/Kconfig" source "drivers/media/usb/b2c2/Kconfig" +source "drivers/media/usb/as102/Kconfig" endif if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) @@ -55,8 +56,9 @@ endif if MEDIA_SDR_SUPPORT comment "Software defined radio USB devices" -source "drivers/media/usb/msi2500/Kconfig" source "drivers/media/usb/airspy/Kconfig" +source "drivers/media/usb/hackrf/Kconfig" +source "drivers/media/usb/msi2500/Kconfig" endif endif #MEDIA_USB_SUPPORT diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index f438eff..6f2eb7c 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -9,8 +9,9 @@ obj-y += zr364xx/ stkwebcam/ s2255/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ -obj-$(CONFIG_USB_MSI2500) += msi2500/ obj-$(CONFIG_USB_AIRSPY) += airspy/ +obj-$(CONFIG_USB_HACKRF) += hackrf/ +obj-$(CONFIG_USB_MSI2500) += msi2500/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ @@ -23,3 +24,4 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_DVB_AS102) += as102/ diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index cb0e515..4069234 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -107,6 +107,7 @@ struct airspy { #define USB_STATE_URB_BUF (1 << 3) unsigned long flags; + struct device *dev; struct usb_device *udev; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -154,16 +155,15 @@ struct airspy { unsigned int sample_measured; }; -#define airspy_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ +#define airspy_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \ char *_direction; \ if (_t & USB_DIR_IN) \ _direction = "<<<"; \ else \ _direction = ">>>"; \ - dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ - _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ - _l, _b); \ + dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \ + _t, _r, _v & 0xff, _v >> 8, _i & 0xff, _i >> 8, \ + _l & 0xff, _l >> 8, _direction, _l, _b); \ } /* execute firmware command */ @@ -192,7 +192,7 @@ static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index, requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); break; default: - dev_err(&s->udev->dev, "Unknown command %02x\n", request); + dev_err(s->dev, "Unknown command %02x\n", request); ret = -EINVAL; goto err; } @@ -203,11 +203,10 @@ static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index, ret = usb_control_msg(s->udev, pipe, request, requesttype, value, index, s->buf, size, 1000); - airspy_dbg_usb_control_msg(s->udev, request, requesttype, value, + airspy_dbg_usb_control_msg(s->dev, request, requesttype, value, index, s->buf, size); if (ret < 0) { - dev_err(&s->udev->dev, - "usb_control_msg() failed %d request %02x\n", + dev_err(s->dev, "usb_control_msg() failed %d request %02x\n", ret, request); goto err; } @@ -224,7 +223,7 @@ err: /* Private functions */ static struct airspy_frame_buf *airspy_get_next_fill_buf(struct airspy *s) { - unsigned long flags = 0; + unsigned long flags; struct airspy_frame_buf *buf = NULL; spin_lock_irqsave(&s->queued_bufs_lock, flags); @@ -251,16 +250,18 @@ static unsigned int airspy_convert_stream(struct airspy *s, dst_len = 0; } - /* calculate samping rate and output it in 10 seconds intervals */ + /* calculate sample rate and output it in 10 seconds intervals */ if (unlikely(time_is_before_jiffies(s->jiffies_next))) { #define MSECS 10000UL + unsigned int msecs = jiffies_to_msecs(jiffies - + s->jiffies_next + msecs_to_jiffies(MSECS)); unsigned int samples = s->sample - s->sample_measured; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample_measured = s->sample; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sample rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); + dev_dbg(s->dev, "slen=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); } /* total number of samples */ @@ -278,9 +279,8 @@ static void airspy_urb_complete(struct urb *urb) struct airspy *s = urb->context; struct airspy_frame_buf *fbuf; - dev_dbg_ratelimited(&s->udev->dev, - "%s: status=%d length=%d/%d errors=%d\n", - __func__, urb->status, urb->actual_length, + dev_dbg_ratelimited(s->dev, "status=%d length=%d/%d errors=%d\n", + urb->status, urb->actual_length, urb->transfer_buffer_length, urb->error_count); switch (urb->status) { @@ -292,8 +292,7 @@ static void airspy_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_err_ratelimited(&s->udev->dev, "URB failed %d\n", - urb->status); + dev_err_ratelimited(s->dev, "URB failed %d\n", urb->status); break; } @@ -304,7 +303,7 @@ static void airspy_urb_complete(struct urb *urb) fbuf = airspy_get_next_fill_buf(s); if (unlikely(fbuf == NULL)) { s->vb_full++; - dev_notice_ratelimited(&s->udev->dev, + dev_notice_ratelimited(s->dev, "videobuf is full, %d packets dropped\n", s->vb_full); goto skip; @@ -328,7 +327,7 @@ static int airspy_kill_urbs(struct airspy *s) int i; for (i = s->urbs_submitted - 1; i >= 0; i--) { - dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); + dev_dbg(s->dev, "kill urb=%d\n", i); /* stop the URB */ usb_kill_urb(s->urb_list[i]); } @@ -342,11 +341,10 @@ static int airspy_submit_urbs(struct airspy *s) int i, ret; for (i = 0; i < s->urbs_initialized; i++) { - dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); + dev_dbg(s->dev, "submit urb=%d\n", i); ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); if (ret) { - dev_err(&s->udev->dev, - "Could not submit URB no. %d - get them all back\n", + dev_err(s->dev, "Could not submit URB no. %d - get them all back\n", i); airspy_kill_urbs(s); return ret; @@ -362,8 +360,7 @@ static int airspy_free_stream_bufs(struct airspy *s) if (s->flags & USB_STATE_URB_BUF) { while (s->buf_num) { s->buf_num--; - dev_dbg(&s->udev->dev, "%s: free buf=%d\n", - __func__, s->buf_num); + dev_dbg(s->dev, "free buf=%d\n", s->buf_num); usb_free_coherent(s->udev, s->buf_size, s->buf_list[s->buf_num], s->dma_addr[s->buf_num]); @@ -379,23 +376,20 @@ static int airspy_alloc_stream_bufs(struct airspy *s) s->buf_num = 0; s->buf_size = BULK_BUFFER_SIZE; - dev_dbg(&s->udev->dev, - "%s: all in all I will use %u bytes for streaming\n", - __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); + dev_dbg(s->dev, "all in all I will use %u bytes for streaming\n", + MAX_BULK_BUFS * BULK_BUFFER_SIZE); for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, BULK_BUFFER_SIZE, GFP_ATOMIC, &s->dma_addr[s->buf_num]); if (!s->buf_list[s->buf_num]) { - dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", - __func__, s->buf_num); + dev_dbg(s->dev, "alloc buf=%d failed\n", s->buf_num); airspy_free_stream_bufs(s); return -ENOMEM; } - dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", - __func__, s->buf_num, + dev_dbg(s->dev, "alloc buf=%d %p (dma %llu)\n", s->buf_num, s->buf_list[s->buf_num], (long long)s->dma_addr[s->buf_num]); s->flags |= USB_STATE_URB_BUF; @@ -412,8 +406,7 @@ static int airspy_free_urbs(struct airspy *s) for (i = s->urbs_initialized - 1; i >= 0; i--) { if (s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: free urb=%d\n", - __func__, i); + dev_dbg(s->dev, "free urb=%d\n", i); /* free the URBs */ usb_free_urb(s->urb_list[i]); } @@ -429,10 +422,10 @@ static int airspy_alloc_urbs(struct airspy *s) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { - dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); + dev_dbg(s->dev, "alloc urb=%d\n", i); s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); if (!s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: failed\n", __func__); + dev_dbg(s->dev, "failed\n"); for (j = 0; j < i; j++) usb_free_urb(s->urb_list[j]); return -ENOMEM; @@ -455,13 +448,14 @@ static int airspy_alloc_urbs(struct airspy *s) /* Must be called with vb_queue_lock hold */ static void airspy_cleanup_queued_bufs(struct airspy *s) { - unsigned long flags = 0; + unsigned long flags; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); spin_lock_irqsave(&s->queued_bufs_lock, flags); while (!list_empty(&s->queued_bufs)) { struct airspy_frame_buf *buf; + buf = list_entry(s->queued_bufs.next, struct airspy_frame_buf, list); list_del(&buf->list); @@ -476,7 +470,7 @@ static void airspy_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct airspy *s = container_of(v, struct airspy, v4l2_dev); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); mutex_lock(&s->vb_queue_lock); mutex_lock(&s->v4l2_lock); @@ -497,7 +491,7 @@ static int airspy_queue_setup(struct vb2_queue *vq, { struct airspy *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ if (vq->num_buffers + *nbuffers < 8) @@ -505,8 +499,7 @@ static int airspy_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = PAGE_ALIGN(s->buffersize); - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); + dev_dbg(s->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); return 0; } @@ -515,7 +508,7 @@ static void airspy_buf_queue(struct vb2_buffer *vb) struct airspy *s = vb2_get_drv_priv(vb->vb2_queue); struct airspy_frame_buf *buf = container_of(vb, struct airspy_frame_buf, vb); - unsigned long flags = 0; + unsigned long flags; /* Check the device has not disconnected between prep and queuing */ if (unlikely(!s->udev)) { @@ -533,34 +526,56 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) struct airspy *s = vb2_get_drv_priv(vq); int ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); if (!s->udev) return -ENODEV; mutex_lock(&s->v4l2_lock); - set_bit(POWER_ON, &s->flags); - s->sequence = 0; + set_bit(POWER_ON, &s->flags); + ret = airspy_alloc_stream_bufs(s); if (ret) - goto err; + goto err_clear_bit; ret = airspy_alloc_urbs(s); if (ret) - goto err; + goto err_free_stream_bufs; ret = airspy_submit_urbs(s); if (ret) - goto err; + goto err_free_urbs; /* start hardware streaming */ ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 1, 0, NULL, 0); if (ret) - goto err; -err: + goto err_kill_urbs; + + goto exit_mutex_unlock; + +err_kill_urbs: + airspy_kill_urbs(s); +err_free_urbs: + airspy_free_urbs(s); +err_free_stream_bufs: + airspy_free_stream_bufs(s); +err_clear_bit: + clear_bit(POWER_ON, &s->flags); + + /* return all queued buffers to vb2 */ + { + struct airspy_frame_buf *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &s->queued_bufs, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + +exit_mutex_unlock: mutex_unlock(&s->v4l2_lock); return ret; @@ -570,7 +585,7 @@ static void airspy_stop_streaming(struct vb2_queue *vq) { struct airspy *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); mutex_lock(&s->v4l2_lock); @@ -602,8 +617,6 @@ static int airspy_querycap(struct file *file, void *fh, { struct airspy *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); - strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -617,10 +630,6 @@ static int airspy_querycap(struct file *file, void *fh, static int airspy_enum_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct airspy *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); - if (f->index >= NUM_FORMATS) return -EINVAL; @@ -635,9 +644,6 @@ static int airspy_g_fmt_sdr_cap(struct file *file, void *priv, { struct airspy *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&s->pixelformat); - f->fmt.sdr.pixelformat = s->pixelformat; f->fmt.sdr.buffersize = s->buffersize; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); @@ -652,9 +658,6 @@ static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, struct vb2_queue *q = &s->vb_queue; int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - if (vb2_is_busy(q)) return -EBUSY; @@ -679,12 +682,8 @@ static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct airspy *s = video_drvdata(file); int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < NUM_FORMATS; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { @@ -702,11 +701,8 @@ static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, static int airspy_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *v) { - struct airspy *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - if (v->index == 0) ret = 0; else if (v->index == 1) @@ -719,11 +715,8 @@ static int airspy_s_tuner(struct file *file, void *priv, static int airspy_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct airspy *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - if (v->index == 0) { strlcpy(v->name, "AirSpy ADC", sizeof(v->name)); v->type = V4L2_TUNER_ADC; @@ -749,17 +742,18 @@ static int airspy_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct airspy *s = video_drvdata(file); - int ret = 0; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); + int ret; if (f->tuner == 0) { f->type = V4L2_TUNER_ADC; f->frequency = s->f_adc; + dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc); ret = 0; } else if (f->tuner == 1) { f->type = V4L2_TUNER_RF; f->frequency = s->f_rf; + dev_dbg(s->dev, "RF frequency=%u Hz\n", s->f_rf); + ret = 0; } else { ret = -EINVAL; } @@ -774,22 +768,17 @@ static int airspy_s_frequency(struct file *file, void *priv, int ret; u8 buf[4]; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); - if (f->tuner == 0) { s->f_adc = clamp_t(unsigned int, f->frequency, bands[0].rangelow, bands[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); + dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc); ret = 0; } else if (f->tuner == 1) { s->f_rf = clamp_t(unsigned int, f->frequency, bands_rf[0].rangelow, bands_rf[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", - __func__, s->f_rf); + dev_dbg(s->dev, "RF frequency=%u Hz\n", s->f_rf); buf[0] = (s->f_rf >> 0) & 0xff; buf[1] = (s->f_rf >> 8) & 0xff; buf[2] = (s->f_rf >> 16) & 0xff; @@ -805,10 +794,7 @@ static int airspy_s_frequency(struct file *file, void *priv, static int airspy_enum_freq_bands(struct file *file, void *priv, struct v4l2_frequency_band *band) { - struct airspy *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); if (band->tuner == 0) { if (band->index >= ARRAY_SIZE(bands)) { @@ -892,10 +878,9 @@ static int airspy_set_lna_gain(struct airspy *s) int ret; u8 u8tmp; - dev_dbg(&s->udev->dev, "%s: lna auto=%d->%d val=%d->%d\n", - __func__, s->lna_gain_auto->cur.val, - s->lna_gain_auto->val, s->lna_gain->cur.val, - s->lna_gain->val); + dev_dbg(s->dev, "lna auto=%d->%d val=%d->%d\n", + s->lna_gain_auto->cur.val, s->lna_gain_auto->val, + s->lna_gain->cur.val, s->lna_gain->val); ret = airspy_ctrl_msg(s, CMD_SET_LNA_AGC, 0, s->lna_gain_auto->val, &u8tmp, 1); @@ -910,7 +895,7 @@ static int airspy_set_lna_gain(struct airspy *s) } err: if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(s->dev, "failed=%d\n", ret); return ret; } @@ -920,10 +905,9 @@ static int airspy_set_mixer_gain(struct airspy *s) int ret; u8 u8tmp; - dev_dbg(&s->udev->dev, "%s: mixer auto=%d->%d val=%d->%d\n", - __func__, s->mixer_gain_auto->cur.val, - s->mixer_gain_auto->val, s->mixer_gain->cur.val, - s->mixer_gain->val); + dev_dbg(s->dev, "mixer auto=%d->%d val=%d->%d\n", + s->mixer_gain_auto->cur.val, s->mixer_gain_auto->val, + s->mixer_gain->cur.val, s->mixer_gain->val); ret = airspy_ctrl_msg(s, CMD_SET_MIXER_AGC, 0, s->mixer_gain_auto->val, &u8tmp, 1); @@ -938,7 +922,7 @@ static int airspy_set_mixer_gain(struct airspy *s) } err: if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(s->dev, "failed=%d\n", ret); return ret; } @@ -948,8 +932,7 @@ static int airspy_set_if_gain(struct airspy *s) int ret; u8 u8tmp; - dev_dbg(&s->udev->dev, "%s: val=%d->%d\n", - __func__, s->if_gain->cur.val, s->if_gain->val); + dev_dbg(s->dev, "val=%d->%d\n", s->if_gain->cur.val, s->if_gain->val); ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, &u8tmp, 1); @@ -957,7 +940,7 @@ static int airspy_set_if_gain(struct airspy *s) goto err; err: if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(s->dev, "failed=%d\n", ret); return ret; } @@ -980,8 +963,8 @@ static int airspy_s_ctrl(struct v4l2_ctrl *ctrl) ret = airspy_set_if_gain(s); break; default: - dev_dbg(&s->udev->dev, "%s: unknown ctrl: id=%d name=%s\n", - __func__, ctrl->id, ctrl->name); + dev_dbg(s->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; } @@ -995,15 +978,13 @@ static const struct v4l2_ctrl_ops airspy_ctrl_ops = { static int airspy_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); - struct airspy *s = NULL; + struct airspy *s; int ret; u8 u8tmp, buf[BUF_SIZE]; s = kzalloc(sizeof(struct airspy), GFP_KERNEL); if (s == NULL) { - dev_err(&udev->dev, - "Could not allocate memory for airspy state\n"); + dev_err(&intf->dev, "Could not allocate memory for state\n"); return -ENOMEM; } @@ -1011,7 +992,8 @@ static int airspy_probe(struct usb_interface *intf, mutex_init(&s->vb_queue_lock); spin_lock_init(&s->queued_bufs_lock); INIT_LIST_HEAD(&s->queued_bufs); - s->udev = udev; + s->dev = &intf->dev; + s->udev = interface_to_usbdev(intf); s->f_adc = bands[0].rangelow; s->f_rf = bands_rf[0].rangelow; s->pixelformat = formats[0].pixelformat; @@ -1023,14 +1005,14 @@ static int airspy_probe(struct usb_interface *intf, ret = airspy_ctrl_msg(s, CMD_VERSION_STRING_READ, 0, 0, buf, BUF_SIZE); if (ret) { - dev_err(&s->udev->dev, "Could not detect board\n"); + dev_err(s->dev, "Could not detect board\n"); goto err_free_mem; } buf[BUF_SIZE - 1] = '\0'; - dev_info(&s->udev->dev, "Board ID: %02x\n", u8tmp); - dev_info(&s->udev->dev, "Firmware version: %s\n", buf); + dev_info(s->dev, "Board ID: %02x\n", u8tmp); + dev_info(s->dev, "Firmware version: %s\n", buf); /* Init videobuf2 queue structure */ s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; @@ -1042,7 +1024,7 @@ static int airspy_probe(struct usb_interface *intf, s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(&s->vb_queue); if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + dev_err(s->dev, "Could not initialize vb2 queue\n"); goto err_free_mem; } @@ -1056,8 +1038,7 @@ static int airspy_probe(struct usb_interface *intf, s->v4l2_dev.release = airspy_video_release; ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); + dev_err(s->dev, "Failed to register v4l2-device (%d)\n", ret); goto err_free_mem; } @@ -1077,7 +1058,7 @@ static int airspy_probe(struct usb_interface *intf, V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 0); if (s->hdl.error) { ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); + dev_err(s->dev, "Could not initialize controls\n"); goto err_free_controls; } @@ -1089,16 +1070,13 @@ static int airspy_probe(struct usb_interface *intf, ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", + dev_err(s->dev, "Failed to register as video device (%d)\n", ret); goto err_unregister_v4l2_dev; } - dev_info(&s->udev->dev, "Registered as %s\n", + dev_info(s->dev, "Registered as %s\n", video_device_node_name(&s->vdev)); - dev_notice(&s->udev->dev, - "%s: SDR API is still slightly experimental and functionality changes may follow\n", - KBUILD_MODNAME); + dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); return 0; err_free_controls: diff --git a/drivers/media/usb/as102/Kconfig b/drivers/media/usb/as102/Kconfig new file mode 100644 index 0000000..28aba00 --- /dev/null +++ b/drivers/media/usb/as102/Kconfig @@ -0,0 +1,8 @@ +config DVB_AS102 + tristate "Abilis AS102 DVB receiver" + depends on DVB_CORE && USB && I2C && INPUT + select FW_LOADER + help + Choose Y or M here if you have a device containing an AS102 + + To compile this driver as a module, choose M here diff --git a/drivers/media/usb/as102/Makefile b/drivers/media/usb/as102/Makefile new file mode 100644 index 0000000..22f43ee --- /dev/null +++ b/drivers/media/usb/as102/Makefile @@ -0,0 +1,7 @@ +dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \ + as102_usb_drv.o as10x_cmd_cfg.o + +obj-$(CONFIG_DVB_AS102) += dvb-as102.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/as102/as102_drv.c b/drivers/media/usb/as102/as102_drv.c new file mode 100644 index 0000000..8be1474 --- /dev/null +++ b/drivers/media/usb/as102/as102_drv.c @@ -0,0 +1,401 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/kref.h> +#include <linux/uaccess.h> +#include <linux/usb.h> + +/* header file for usb device driver*/ +#include "as102_drv.h" +#include "as10x_cmd.h" +#include "as102_fe.h" +#include "as102_fw.h" +#include "dvbdev.h" + +int dual_tuner; +module_param_named(dual_tuner, dual_tuner, int, 0644); +MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)"); + +static int fw_upload = 1; +module_param_named(fw_upload, fw_upload, int, 0644); +MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)"); + +static int pid_filtering; +module_param_named(pid_filtering, pid_filtering, int, 0644); +MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)"); + +static int ts_auto_disable; +module_param_named(ts_auto_disable, ts_auto_disable, int, 0644); +MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)"); + +int elna_enable = 1; +module_param_named(elna_enable, elna_enable, int, 0644); +MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void as102_stop_stream(struct as102_dev_t *dev) +{ + struct as10x_bus_adapter_t *bus_adap; + + if (dev != NULL) + bus_adap = &dev->bus_adap; + else + return; + + if (bus_adap->ops->stop_stream != NULL) + bus_adap->ops->stop_stream(dev); + + if (ts_auto_disable) { + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return; + + if (as10x_cmd_stop_streaming(bus_adap) < 0) + dev_dbg(&dev->bus_adap.usb_dev->dev, + "as10x_cmd_stop_streaming failed\n"); + + mutex_unlock(&dev->bus_adap.lock); + } +} + +static int as102_start_stream(struct as102_dev_t *dev) +{ + struct as10x_bus_adapter_t *bus_adap; + int ret = -EFAULT; + + if (dev != NULL) + bus_adap = &dev->bus_adap; + else + return ret; + + if (bus_adap->ops->start_stream != NULL) + ret = bus_adap->ops->start_stream(dev); + + if (ts_auto_disable) { + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EFAULT; + + ret = as10x_cmd_start_streaming(bus_adap); + + mutex_unlock(&dev->bus_adap.lock); + } + + return ret; +} + +static int as10x_pid_filter(struct as102_dev_t *dev, + int index, u16 pid, int onoff) { + + struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap; + int ret = -EFAULT; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) { + dev_dbg(&dev->bus_adap.usb_dev->dev, + "amutex_lock_interruptible(lock) failed !\n"); + return -EBUSY; + } + + switch (onoff) { + case 0: + ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid); + dev_dbg(&dev->bus_adap.usb_dev->dev, + "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n", + index, pid, ret); + break; + case 1: + { + struct as10x_ts_filter filter; + + filter.type = TS_PID_TYPE_TS; + filter.idx = 0xFF; + filter.pid = pid; + + ret = as10x_cmd_add_PID_filter(bus_adap, &filter); + dev_dbg(&dev->bus_adap.usb_dev->dev, + "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", + index, filter.idx, filter.pid, ret); + break; + } + } + + mutex_unlock(&dev->bus_adap.lock); + return ret; +} + +static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + int ret = 0; + struct dvb_demux *demux = dvbdmxfeed->demux; + struct as102_dev_t *as102_dev = demux->priv; + + if (mutex_lock_interruptible(&as102_dev->sem)) + return -ERESTARTSYS; + + if (pid_filtering) + as10x_pid_filter(as102_dev, dvbdmxfeed->index, + dvbdmxfeed->pid, 1); + + if (as102_dev->streaming++ == 0) + ret = as102_start_stream(as102_dev); + + mutex_unlock(&as102_dev->sem); + return ret; +} + +static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct as102_dev_t *as102_dev = demux->priv; + + if (mutex_lock_interruptible(&as102_dev->sem)) + return -ERESTARTSYS; + + if (--as102_dev->streaming == 0) + as102_stop_stream(as102_dev); + + if (pid_filtering) + as10x_pid_filter(as102_dev, dvbdmxfeed->index, + dvbdmxfeed->pid, 0); + + mutex_unlock(&as102_dev->sem); + return 0; +} + +static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args) +{ + struct as10x_bus_adapter_t *bus_adap = priv; + int ret; + + /* Set frontend arguments */ + if (mutex_lock_interruptible(&bus_adap->lock)) + return -EBUSY; + + ret = as10x_cmd_set_tune(bus_adap, tune_args); + if (ret != 0) + dev_dbg(&bus_adap->usb_dev->dev, + "as10x_cmd_set_tune failed. (err = %d)\n", ret); + + mutex_unlock(&bus_adap->lock); + + return ret; +} + +static int as102_get_tps(void *priv, struct as10x_tps *tps) +{ + struct as10x_bus_adapter_t *bus_adap = priv; + int ret; + + if (mutex_lock_interruptible(&bus_adap->lock)) + return -EBUSY; + + /* send abilis command: GET_TPS */ + ret = as10x_cmd_get_tps(bus_adap, tps); + + mutex_unlock(&bus_adap->lock); + + return ret; +} + +static int as102_get_status(void *priv, struct as10x_tune_status *tstate) +{ + struct as10x_bus_adapter_t *bus_adap = priv; + int ret; + + if (mutex_lock_interruptible(&bus_adap->lock)) + return -EBUSY; + + /* send abilis command: GET_TUNE_STATUS */ + ret = as10x_cmd_get_tune_status(bus_adap, tstate); + if (ret < 0) { + dev_dbg(&bus_adap->usb_dev->dev, + "as10x_cmd_get_tune_status failed (err = %d)\n", + ret); + } + + mutex_unlock(&bus_adap->lock); + + return ret; +} + +static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats) +{ + struct as10x_bus_adapter_t *bus_adap = priv; + int ret; + + if (mutex_lock_interruptible(&bus_adap->lock)) + return -EBUSY; + + /* send abilis command: GET_TUNE_STATUS */ + ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats); + if (ret < 0) { + dev_dbg(&bus_adap->usb_dev->dev, + "as10x_cmd_get_demod_stats failed (probably not tuned)\n"); + } else { + dev_dbg(&bus_adap->usb_dev->dev, + "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n", + demod_stats->frame_count, + demod_stats->bad_frame_count, + demod_stats->bytes_fixed_by_rs, + demod_stats->mer); + } + mutex_unlock(&bus_adap->lock); + + return ret; +} + +static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg) +{ + struct as10x_bus_adapter_t *bus_adap = priv; + int ret; + + if (mutex_lock_interruptible(&bus_adap->lock)) + return -EBUSY; + + if (acquire) { + if (elna_enable) + as10x_cmd_set_context(bus_adap, + CONTEXT_LNA, elna_cfg); + + ret = as10x_cmd_turn_on(bus_adap); + } else { + ret = as10x_cmd_turn_off(bus_adap); + } + + mutex_unlock(&bus_adap->lock); + + return ret; +} + +static const struct as102_fe_ops as102_fe_ops = { + .set_tune = as102_set_tune, + .get_tps = as102_get_tps, + .get_status = as102_get_status, + .get_stats = as102_get_stats, + .stream_ctrl = as102_stream_ctrl, +}; + +int as102_dvb_register(struct as102_dev_t *as102_dev) +{ + struct device *dev = &as102_dev->bus_adap.usb_dev->dev; + int ret; + + ret = dvb_register_adapter(&as102_dev->dvb_adap, + as102_dev->name, THIS_MODULE, + dev, adapter_nr); + if (ret < 0) { + dev_err(dev, "%s: dvb_register_adapter() failed: %d\n", + __func__, ret); + return ret; + } + + as102_dev->dvb_dmx.priv = as102_dev; + as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256; + as102_dev->dvb_dmx.feednum = 256; + as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed; + as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed; + + as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING; + + as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum; + as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx; + as102_dev->dvb_dmxdev.capabilities = 0; + + ret = dvb_dmx_init(&as102_dev->dvb_dmx); + if (ret < 0) { + dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret); + goto edmxinit; + } + + ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap); + if (ret < 0) { + dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n", + __func__, ret); + goto edmxdinit; + } + + /* Attach the frontend */ + as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name, + &as102_fe_ops, + &as102_dev->bus_adap, + as102_dev->elna_cfg); + if (!as102_dev->dvb_fe) { + dev_err(dev, "%s: as102_attach() failed: %d", + __func__, ret); + goto efereg; + } + + ret = dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe); + if (ret < 0) { + dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d", + __func__, ret); + goto efereg; + } + + /* init bus mutex for token locking */ + mutex_init(&as102_dev->bus_adap.lock); + + /* init start / stop stream mutex */ + mutex_init(&as102_dev->sem); + + /* + * try to load as102 firmware. If firmware upload failed, we'll be + * able to upload it later. + */ + if (fw_upload) + try_then_request_module(as102_fw_upload(&as102_dev->bus_adap), + "firmware_class"); + + pr_info("Registered device %s", as102_dev->name); + return 0; + +efereg: + dvb_dmxdev_release(&as102_dev->dvb_dmxdev); +edmxdinit: + dvb_dmx_release(&as102_dev->dvb_dmx); +edmxinit: + dvb_unregister_adapter(&as102_dev->dvb_adap); + return ret; +} + +void as102_dvb_unregister(struct as102_dev_t *as102_dev) +{ + /* unregister as102 frontend */ + dvb_unregister_frontend(as102_dev->dvb_fe); + + /* detach frontend */ + dvb_frontend_detach(as102_dev->dvb_fe); + + /* unregister demux device */ + dvb_dmxdev_release(&as102_dev->dvb_dmxdev); + dvb_dmx_release(&as102_dev->dvb_dmx); + + /* unregister dvb adapter */ + dvb_unregister_adapter(&as102_dev->dvb_adap); + + pr_info("Unregistered device %s", as102_dev->name); +} + +module_usb_driver(as102_usb_driver); + +/* modinfo details */ +MODULE_DESCRIPTION(DRIVER_FULL_NAME); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); diff --git a/drivers/media/usb/as102/as102_drv.h b/drivers/media/usb/as102/as102_drv.h new file mode 100644 index 0000000..aee2d76 --- /dev/null +++ b/drivers/media/usb/as102/as102_drv.h @@ -0,0 +1,83 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ + +#ifndef _AS102_DRV_H +#define _AS102_DRV_H +#include <linux/usb.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dmxdev.h> +#include "as10x_handle.h" +#include "as10x_cmd.h" +#include "as102_usb_drv.h" + +#define DRIVER_FULL_NAME "Abilis Systems as10x usb driver" +#define DRIVER_NAME "as10x_usb" + +#define debug as102_debug +extern struct usb_driver as102_usb_driver; +extern int elna_enable; + +#define AS102_DEVICE_MAJOR 192 + +#define AS102_USB_BUF_SIZE 512 +#define MAX_STREAM_URB 32 + +struct as10x_bus_adapter_t { + struct usb_device *usb_dev; + /* bus token lock */ + struct mutex lock; + /* low level interface for bus adapter */ + union as10x_bus_token_t { + /* usb token */ + struct as10x_usb_token_cmd_t usb; + } token; + + /* token cmd xfer id */ + uint16_t cmd_xid; + + /* as10x command and response for dvb interface*/ + struct as10x_cmd_t *cmd, *rsp; + + /* bus adapter private ops callback */ + struct as102_priv_ops_t *ops; +}; + +struct as102_dev_t { + const char *name; + struct as10x_bus_adapter_t bus_adap; + struct list_head device_entry; + struct kref kref; + uint8_t elna_cfg; + + struct dvb_adapter dvb_adap; + struct dvb_frontend *dvb_fe; + struct dvb_demux dvb_dmx; + struct dmxdev dvb_dmxdev; + + /* timer handle to trig ts stream download */ + struct timer_list timer_handle; + + struct mutex sem; + dma_addr_t dma_addr; + void *stream; + int streaming; + struct urb *stream_urb[MAX_STREAM_URB]; +}; + +int as102_dvb_register(struct as102_dev_t *dev); +void as102_dvb_unregister(struct as102_dev_t *dev); + +#endif diff --git a/drivers/media/usb/as102/as102_fw.c b/drivers/media/usb/as102/as102_fw.c new file mode 100644 index 0000000..07d08c4 --- /dev/null +++ b/drivers/media/usb/as102/as102_fw.c @@ -0,0 +1,228 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/firmware.h> + +#include "as102_drv.h" +#include "as102_fw.h" + +static const char as102_st_fw1[] = "as102_data1_st.hex"; +static const char as102_st_fw2[] = "as102_data2_st.hex"; +static const char as102_dt_fw1[] = "as102_data1_dt.hex"; +static const char as102_dt_fw2[] = "as102_data2_dt.hex"; + +static unsigned char atohx(unsigned char *dst, char *src) +{ + unsigned char value = 0; + + char msb = tolower(*src) - '0'; + char lsb = tolower(*(src + 1)) - '0'; + + if (msb > 9) + msb -= 7; + if (lsb > 9) + lsb -= 7; + + *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); + return value; +} + +/* + * Parse INTEL HEX firmware file to extract address and data. + */ +static int parse_hex_line(unsigned char *fw_data, unsigned char *addr, + unsigned char *data, int *dataLength, + unsigned char *addr_has_changed) { + + int count = 0; + unsigned char *src, dst; + + if (*fw_data++ != ':') { + pr_err("invalid firmware file\n"); + return -EFAULT; + } + + /* locate end of line */ + for (src = fw_data; *src != '\n'; src += 2) { + atohx(&dst, src); + /* parse line to split addr / data */ + switch (count) { + case 0: + *dataLength = dst; + break; + case 1: + addr[2] = dst; + break; + case 2: + addr[3] = dst; + break; + case 3: + /* check if data is an address */ + if (dst == 0x04) + *addr_has_changed = 1; + else + *addr_has_changed = 0; + break; + case 4: + case 5: + if (*addr_has_changed) + addr[(count - 4)] = dst; + else + data[(count - 4)] = dst; + break; + default: + data[(count - 4)] = dst; + break; + } + count++; + } + + /* return read value + ':' + '\n' */ + return (count * 2) + 2; +} + +static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, + unsigned char *cmd, + const struct firmware *firmware) { + + struct as10x_fw_pkt_t fw_pkt; + int total_read_bytes = 0, errno = 0; + unsigned char addr_has_changed = 0; + + for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { + int read_bytes = 0, data_len = 0; + + /* parse intel hex line */ + read_bytes = parse_hex_line( + (u8 *) (firmware->data + total_read_bytes), + fw_pkt.raw.address, + fw_pkt.raw.data, + &data_len, + &addr_has_changed); + + if (read_bytes <= 0) + goto error; + + /* detect the end of file */ + total_read_bytes += read_bytes; + if (total_read_bytes == firmware->size) { + fw_pkt.u.request[0] = 0x00; + fw_pkt.u.request[1] = 0x03; + + /* send EOF command */ + errno = bus_adap->ops->upload_fw_pkt(bus_adap, + (uint8_t *) + &fw_pkt, 2, 0); + if (errno < 0) + goto error; + } else { + if (!addr_has_changed) { + /* prepare command to send */ + fw_pkt.u.request[0] = 0x00; + fw_pkt.u.request[1] = 0x01; + + data_len += sizeof(fw_pkt.u.request); + data_len += sizeof(fw_pkt.raw.address); + + /* send cmd to device */ + errno = bus_adap->ops->upload_fw_pkt(bus_adap, + (uint8_t *) + &fw_pkt, + data_len, + 0); + if (errno < 0) + goto error; + } + } + } +error: + return (errno == 0) ? total_read_bytes : errno; +} + +int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) +{ + int errno = -EFAULT; + const struct firmware *firmware = NULL; + unsigned char *cmd_buf = NULL; + const char *fw1, *fw2; + struct usb_device *dev = bus_adap->usb_dev; + + /* select fw file to upload */ + if (dual_tuner) { + fw1 = as102_dt_fw1; + fw2 = as102_dt_fw2; + } else { + fw1 = as102_st_fw1; + fw2 = as102_st_fw2; + } + + /* allocate buffer to store firmware upload command and data */ + cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); + if (cmd_buf == NULL) { + errno = -ENOMEM; + goto error; + } + + /* request kernel to locate firmware file: part1 */ + errno = request_firmware(&firmware, fw1, &dev->dev); + if (errno < 0) { + pr_err("%s: unable to locate firmware file: %s\n", + DRIVER_NAME, fw1); + goto error; + } + + /* initiate firmware upload */ + errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); + if (errno < 0) { + pr_err("%s: error during firmware upload part1\n", + DRIVER_NAME); + goto error; + } + + pr_info("%s: firmware: %s loaded with success\n", + DRIVER_NAME, fw1); + release_firmware(firmware); + + /* wait for boot to complete */ + mdelay(100); + + /* request kernel to locate firmware file: part2 */ + errno = request_firmware(&firmware, fw2, &dev->dev); + if (errno < 0) { + pr_err("%s: unable to locate firmware file: %s\n", + DRIVER_NAME, fw2); + goto error; + } + + /* initiate firmware upload */ + errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); + if (errno < 0) { + pr_err("%s: error during firmware upload part2\n", + DRIVER_NAME); + goto error; + } + + pr_info("%s: firmware: %s loaded with success\n", + DRIVER_NAME, fw2); +error: + kfree(cmd_buf); + release_firmware(firmware); + + return errno; +} diff --git a/drivers/media/usb/as102/as102_fw.h b/drivers/media/usb/as102/as102_fw.h new file mode 100644 index 0000000..2732b7842 --- /dev/null +++ b/drivers/media/usb/as102/as102_fw.h @@ -0,0 +1,34 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ +#define MAX_FW_PKT_SIZE 64 + +extern int dual_tuner; + +struct as10x_raw_fw_pkt { + unsigned char address[4]; + unsigned char data[MAX_FW_PKT_SIZE - 6]; +} __packed; + +struct as10x_fw_pkt_t { + union { + unsigned char request[2]; + unsigned char length[2]; + } __packed u; + struct as10x_raw_fw_pkt raw; +} __packed; + +#ifdef __KERNEL__ +int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap); +#endif diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c new file mode 100644 index 0000000..3f66906 --- /dev/null +++ b/drivers/media/usb/as102/as102_usb_drv.c @@ -0,0 +1,475 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/usb.h> + +#include "as102_drv.h" +#include "as102_usb_drv.h" +#include "as102_fw.h" + +static void as102_usb_disconnect(struct usb_interface *interface); +static int as102_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id); + +static int as102_usb_start_stream(struct as102_dev_t *dev); +static void as102_usb_stop_stream(struct as102_dev_t *dev); + +static int as102_open(struct inode *inode, struct file *file); +static int as102_release(struct inode *inode, struct file *file); + +static struct usb_device_id as102_usb_id_table[] = { + { USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) }, + { USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) }, + { USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) }, + { USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) }, + { USB_DEVICE(SKY_IT_DIGITAL_KEY_USB_VID, SKY_IT_DIGITAL_KEY_USB_PID) }, + { } /* Terminating entry */ +}; + +/* Note that this table must always have the same number of entries as the + as102_usb_id_table struct */ +static const char * const as102_device_names[] = { + AS102_REFERENCE_DESIGN, + AS102_PCTV_74E, + AS102_ELGATO_EYETV_DTT_NAME, + AS102_NBOX_DVBT_DONGLE_NAME, + AS102_SKY_IT_DIGITAL_KEY_NAME, + NULL /* Terminating entry */ +}; + +/* eLNA configuration: devices built on the reference design work best + with 0xA0, while custom designs seem to require 0xC0 */ +static uint8_t const as102_elna_cfg[] = { + 0xA0, + 0xC0, + 0xC0, + 0xA0, + 0xA0, + 0x00 /* Terminating entry */ +}; + +struct usb_driver as102_usb_driver = { + .name = DRIVER_FULL_NAME, + .probe = as102_usb_probe, + .disconnect = as102_usb_disconnect, + .id_table = as102_usb_id_table +}; + +static const struct file_operations as102_dev_fops = { + .owner = THIS_MODULE, + .open = as102_open, + .release = as102_release, +}; + +static struct usb_class_driver as102_usb_class_driver = { + .name = "aton2-%d", + .fops = &as102_dev_fops, + .minor_base = AS102_DEVICE_MAJOR, +}; + +static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap, + unsigned char *send_buf, int send_buf_len, + unsigned char *recv_buf, int recv_buf_len) +{ + int ret = 0; + + if (send_buf != NULL) { + ret = usb_control_msg(bus_adap->usb_dev, + usb_sndctrlpipe(bus_adap->usb_dev, 0), + AS102_USB_DEVICE_TX_CTRL_CMD, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + bus_adap->cmd_xid, /* value */ + 0, /* index */ + send_buf, send_buf_len, + USB_CTRL_SET_TIMEOUT /* 200 */); + if (ret < 0) { + dev_dbg(&bus_adap->usb_dev->dev, + "usb_control_msg(send) failed, err %i\n", ret); + return ret; + } + + if (ret != send_buf_len) { + dev_dbg(&bus_adap->usb_dev->dev, + "only wrote %d of %d bytes\n", ret, send_buf_len); + return -1; + } + } + + if (recv_buf != NULL) { +#ifdef TRACE + dev_dbg(bus_adap->usb_dev->dev, + "want to read: %d bytes\n", recv_buf_len); +#endif + ret = usb_control_msg(bus_adap->usb_dev, + usb_rcvctrlpipe(bus_adap->usb_dev, 0), + AS102_USB_DEVICE_RX_CTRL_CMD, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + bus_adap->cmd_xid, /* value */ + 0, /* index */ + recv_buf, recv_buf_len, + USB_CTRL_GET_TIMEOUT /* 200 */); + if (ret < 0) { + dev_dbg(&bus_adap->usb_dev->dev, + "usb_control_msg(recv) failed, err %i\n", ret); + return ret; + } +#ifdef TRACE + dev_dbg(bus_adap->usb_dev->dev, + "read %d bytes\n", recv_buf_len); +#endif + } + + return ret; +} + +static int as102_send_ep1(struct as10x_bus_adapter_t *bus_adap, + unsigned char *send_buf, + int send_buf_len, + int swap32) +{ + int ret, actual_len; + + ret = usb_bulk_msg(bus_adap->usb_dev, + usb_sndbulkpipe(bus_adap->usb_dev, 1), + send_buf, send_buf_len, &actual_len, 200); + if (ret) { + dev_dbg(&bus_adap->usb_dev->dev, + "usb_bulk_msg(send) failed, err %i\n", ret); + return ret; + } + + if (actual_len != send_buf_len) { + dev_dbg(&bus_adap->usb_dev->dev, "only wrote %d of %d bytes\n", + actual_len, send_buf_len); + return -1; + } + return actual_len; +} + +static int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap, + unsigned char *recv_buf, int recv_buf_len) +{ + int ret, actual_len; + + if (recv_buf == NULL) + return -EINVAL; + + ret = usb_bulk_msg(bus_adap->usb_dev, + usb_rcvbulkpipe(bus_adap->usb_dev, 2), + recv_buf, recv_buf_len, &actual_len, 200); + if (ret) { + dev_dbg(&bus_adap->usb_dev->dev, + "usb_bulk_msg(recv) failed, err %i\n", ret); + return ret; + } + + if (actual_len != recv_buf_len) { + dev_dbg(&bus_adap->usb_dev->dev, "only read %d of %d bytes\n", + actual_len, recv_buf_len); + return -1; + } + return actual_len; +} + +static struct as102_priv_ops_t as102_priv_ops = { + .upload_fw_pkt = as102_send_ep1, + .xfer_cmd = as102_usb_xfer_cmd, + .as102_read_ep2 = as102_read_ep2, + .start_stream = as102_usb_start_stream, + .stop_stream = as102_usb_stop_stream, +}; + +static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb) +{ + int err; + + usb_fill_bulk_urb(urb, + dev->bus_adap.usb_dev, + usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2), + urb->transfer_buffer, + AS102_USB_BUF_SIZE, + as102_urb_stream_irq, + dev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + dev_dbg(&urb->dev->dev, + "%s: usb_submit_urb failed\n", __func__); + + return err; +} + +void as102_urb_stream_irq(struct urb *urb) +{ + struct as102_dev_t *as102_dev = urb->context; + + if (urb->actual_length > 0) { + dvb_dmx_swfilter(&as102_dev->dvb_dmx, + urb->transfer_buffer, + urb->actual_length); + } else { + if (urb->actual_length == 0) + memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE); + } + + /* is not stopped, re-submit urb */ + if (as102_dev->streaming) + as102_submit_urb_stream(as102_dev, urb); +} + +static void as102_free_usb_stream_buffer(struct as102_dev_t *dev) +{ + int i; + + for (i = 0; i < MAX_STREAM_URB; i++) + usb_free_urb(dev->stream_urb[i]); + + usb_free_coherent(dev->bus_adap.usb_dev, + MAX_STREAM_URB * AS102_USB_BUF_SIZE, + dev->stream, + dev->dma_addr); +} + +static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev) +{ + int i; + + dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev, + MAX_STREAM_URB * AS102_USB_BUF_SIZE, + GFP_KERNEL, + &dev->dma_addr); + if (!dev->stream) { + dev_dbg(&dev->bus_adap.usb_dev->dev, + "%s: usb_buffer_alloc failed\n", __func__); + return -ENOMEM; + } + + memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE); + + /* init urb buffers */ + for (i = 0; i < MAX_STREAM_URB; i++) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + dev_dbg(&dev->bus_adap.usb_dev->dev, + "%s: usb_alloc_urb failed\n", __func__); + as102_free_usb_stream_buffer(dev); + return -ENOMEM; + } + + urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE); + urb->transfer_dma = dev->dma_addr + (i * AS102_USB_BUF_SIZE); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer_length = AS102_USB_BUF_SIZE; + + dev->stream_urb[i] = urb; + } + return 0; +} + +static void as102_usb_stop_stream(struct as102_dev_t *dev) +{ + int i; + + for (i = 0; i < MAX_STREAM_URB; i++) + usb_kill_urb(dev->stream_urb[i]); +} + +static int as102_usb_start_stream(struct as102_dev_t *dev) +{ + int i, ret = 0; + + for (i = 0; i < MAX_STREAM_URB; i++) { + ret = as102_submit_urb_stream(dev, dev->stream_urb[i]); + if (ret) { + as102_usb_stop_stream(dev); + return ret; + } + } + + return 0; +} + +static void as102_usb_release(struct kref *kref) +{ + struct as102_dev_t *as102_dev; + + as102_dev = container_of(kref, struct as102_dev_t, kref); + if (as102_dev != NULL) { + usb_put_dev(as102_dev->bus_adap.usb_dev); + kfree(as102_dev); + } +} + +static void as102_usb_disconnect(struct usb_interface *intf) +{ + struct as102_dev_t *as102_dev; + + /* extract as102_dev_t from usb_device private data */ + as102_dev = usb_get_intfdata(intf); + + /* unregister dvb layer */ + as102_dvb_unregister(as102_dev); + + /* free usb buffers */ + as102_free_usb_stream_buffer(as102_dev); + + usb_set_intfdata(intf, NULL); + + /* usb unregister device */ + usb_deregister_dev(intf, &as102_usb_class_driver); + + /* decrement usage counter */ + kref_put(&as102_dev->kref, as102_usb_release); + + pr_info("%s: device has been disconnected\n", DRIVER_NAME); +} + +static int as102_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct as102_dev_t *as102_dev; + int i; + + /* This should never actually happen */ + if (ARRAY_SIZE(as102_usb_id_table) != + (sizeof(as102_device_names) / sizeof(const char *))) { + pr_err("Device names table invalid size"); + return -EINVAL; + } + + as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL); + if (as102_dev == NULL) + return -ENOMEM; + + /* Assign the user-friendly device name */ + for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) { + if (id == &as102_usb_id_table[i]) { + as102_dev->name = as102_device_names[i]; + as102_dev->elna_cfg = as102_elna_cfg[i]; + } + } + + if (as102_dev->name == NULL) + as102_dev->name = "Unknown AS102 device"; + + /* set private callback functions */ + as102_dev->bus_adap.ops = &as102_priv_ops; + + /* init cmd token for usb bus */ + as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c; + as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r; + + /* init kernel device reference */ + kref_init(&as102_dev->kref); + + /* store as102 device to usb_device private data */ + usb_set_intfdata(intf, (void *) as102_dev); + + /* store in as102 device the usb_device pointer */ + as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf)); + + /* we can register the device now, as it is ready */ + ret = usb_register_dev(intf, &as102_usb_class_driver); + if (ret < 0) { + /* something prevented us from registering this driver */ + dev_err(&intf->dev, + "%s: usb_register_dev() failed (errno = %d)\n", + __func__, ret); + goto failed; + } + + pr_info("%s: device has been detected\n", DRIVER_NAME); + + /* request buffer allocation for streaming */ + ret = as102_alloc_usb_stream_buffer(as102_dev); + if (ret != 0) + goto failed_stream; + + /* register dvb layer */ + ret = as102_dvb_register(as102_dev); + if (ret != 0) + goto failed_dvb; + + return ret; + +failed_dvb: + as102_free_usb_stream_buffer(as102_dev); +failed_stream: + usb_deregister_dev(intf, &as102_usb_class_driver); +failed: + usb_put_dev(as102_dev->bus_adap.usb_dev); + usb_set_intfdata(intf, NULL); + kfree(as102_dev); + return ret; +} + +static int as102_open(struct inode *inode, struct file *file) +{ + int ret = 0, minor = 0; + struct usb_interface *intf = NULL; + struct as102_dev_t *dev = NULL; + + /* read minor from inode */ + minor = iminor(inode); + + /* fetch device from usb interface */ + intf = usb_find_interface(&as102_usb_driver, minor); + if (intf == NULL) { + pr_err("%s: can't find device for minor %d\n", + __func__, minor); + ret = -ENODEV; + goto exit; + } + + /* get our device */ + dev = usb_get_intfdata(intf); + if (dev == NULL) { + ret = -EFAULT; + goto exit; + } + + /* save our device object in the file's private structure */ + file->private_data = dev; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + +exit: + return ret; +} + +static int as102_release(struct inode *inode, struct file *file) +{ + struct as102_dev_t *dev = NULL; + + dev = file->private_data; + if (dev != NULL) { + /* decrement the count on our device */ + kref_put(&dev->kref, as102_usb_release); + } + + return 0; +} + +MODULE_DEVICE_TABLE(usb, as102_usb_id_table); diff --git a/drivers/media/usb/as102/as102_usb_drv.h b/drivers/media/usb/as102/as102_usb_drv.h new file mode 100644 index 0000000..4fb1baa --- /dev/null +++ b/drivers/media/usb/as102/as102_usb_drv.h @@ -0,0 +1,57 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ +#ifndef _AS102_USB_DRV_H_ +#define _AS102_USB_DRV_H_ + +#define AS102_USB_DEVICE_TX_CTRL_CMD 0xF1 +#define AS102_USB_DEVICE_RX_CTRL_CMD 0xF2 + +/* define these values to match the supported devices */ + +/* Abilis system: "TITAN" */ +#define AS102_REFERENCE_DESIGN "Abilis Systems DVB-Titan" +#define AS102_USB_DEVICE_VENDOR_ID 0x1BA6 +#define AS102_USB_DEVICE_PID_0001 0x0001 + +/* PCTV Systems: PCTV picoStick (74e) */ +#define AS102_PCTV_74E "PCTV Systems picoStick (74e)" +#define PCTV_74E_USB_VID 0x2013 +#define PCTV_74E_USB_PID 0x0246 + +/* Elgato: EyeTV DTT Deluxe */ +#define AS102_ELGATO_EYETV_DTT_NAME "Elgato EyeTV DTT Deluxe" +#define ELGATO_EYETV_DTT_USB_VID 0x0fd9 +#define ELGATO_EYETV_DTT_USB_PID 0x002c + +/* nBox: nBox DVB-T Dongle */ +#define AS102_NBOX_DVBT_DONGLE_NAME "nBox DVB-T Dongle" +#define NBOX_DVBT_DONGLE_USB_VID 0x0b89 +#define NBOX_DVBT_DONGLE_USB_PID 0x0007 + +/* Sky Italia: Digital Key (green led) */ +#define AS102_SKY_IT_DIGITAL_KEY_NAME "Sky IT Digital Key (green led)" +#define SKY_IT_DIGITAL_KEY_USB_VID 0x2137 +#define SKY_IT_DIGITAL_KEY_USB_PID 0x0001 + +void as102_urb_stream_irq(struct urb *urb); + +struct as10x_usb_token_cmd_t { + /* token cmd */ + struct as10x_cmd_t c; + /* token response */ + struct as10x_cmd_t r; +}; +#endif diff --git a/drivers/media/usb/as102/as10x_cmd.c b/drivers/media/usb/as102/as10x_cmd.c new file mode 100644 index 0000000..8706179 --- /dev/null +++ b/drivers/media/usb/as102/as10x_cmd.c @@ -0,0 +1,413 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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. + */ + +#include <linux/kernel.h> +#include "as102_drv.h" +#include "as10x_cmd.h" + +/** + * as10x_cmd_turn_on - send turn on command to AS10x + * @adap: pointer to AS10x bus adapter + * + * Return 0 when no error, < 0 in case of error. + */ +int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.turn_on.req)); + + /* fill command */ + pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.turn_on.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.turn_on.rsp) + + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP); + +out: + return error; +} + +/** + * as10x_cmd_turn_off - send turn off command to AS10x + * @adap: pointer to AS10x bus adapter + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.turn_off.req)); + + /* fill command */ + pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd( + adap, (uint8_t *) pcmd, + sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP); + +out: + return error; +} + +/** + * as10x_cmd_set_tune - send set tune command to AS10x + * @adap: pointer to AS10x bus adapter + * @ptune: tune parameters + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, + struct as10x_tune_args *ptune) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *preq, *prsp; + + preq = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(preq, (++adap->cmd_xid), + sizeof(preq->body.set_tune.req)); + + /* fill command */ + preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE); + preq->body.set_tune.req.args.freq = (__force __u32)cpu_to_le32(ptune->freq); + preq->body.set_tune.req.args.bandwidth = ptune->bandwidth; + preq->body.set_tune.req.args.hier_select = ptune->hier_select; + preq->body.set_tune.req.args.modulation = ptune->modulation; + preq->body.set_tune.req.args.hierarchy = ptune->hierarchy; + preq->body.set_tune.req.args.interleaving_mode = + ptune->interleaving_mode; + preq->body.set_tune.req.args.code_rate = ptune->code_rate; + preq->body.set_tune.req.args.guard_interval = ptune->guard_interval; + preq->body.set_tune.req.args.transmission_mode = + ptune->transmission_mode; + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) preq, + sizeof(preq->body.set_tune.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.set_tune.rsp) + + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP); + +out: + return error; +} + +/** + * as10x_cmd_get_tune_status - send get tune status command to AS10x + * @adap: pointer to AS10x bus adapter + * @pstatus: pointer to updated status structure of the current tune + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, + struct as10x_tune_status *pstatus) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *preq, *prsp; + + preq = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(preq, (++adap->cmd_xid), + sizeof(preq->body.get_tune_status.req)); + + /* fill command */ + preq->body.get_tune_status.req.proc_id = + cpu_to_le16(CONTROL_PROC_GETTUNESTAT); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd( + adap, + (uint8_t *) preq, + sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP); + if (error < 0) + goto out; + + /* Response OK -> get response data */ + pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state; + pstatus->signal_strength = + le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.signal_strength); + pstatus->PER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.PER); + pstatus->BER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.BER); + +out: + return error; +} + +/** + * as10x_cmd_get_tps - send get TPS command to AS10x + * @adap: pointer to AS10x handle + * @ptps: pointer to TPS parameters structure + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.get_tps.req)); + + /* fill command */ + pcmd->body.get_tune_status.req.proc_id = + cpu_to_le16(CONTROL_PROC_GETTPS); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) pcmd, + sizeof(pcmd->body.get_tps.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.get_tps.rsp) + + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP); + if (error < 0) + goto out; + + /* Response OK -> get response data */ + ptps->modulation = prsp->body.get_tps.rsp.tps.modulation; + ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy; + ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode; + ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP; + ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP; + ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval; + ptps->transmission_mode = prsp->body.get_tps.rsp.tps.transmission_mode; + ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP; + ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP; + ptps->cell_ID = le16_to_cpu((__force __le16)prsp->body.get_tps.rsp.tps.cell_ID); + +out: + return error; +} + +/** + * as10x_cmd_get_demod_stats - send get demod stats command to AS10x + * @adap: pointer to AS10x bus adapter + * @pdemod_stats: pointer to demod stats parameters structure + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, + struct as10x_demod_stats *pdemod_stats) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.get_demod_stats.req)); + + /* fill command */ + pcmd->body.get_demod_stats.req.proc_id = + cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) pcmd, + sizeof(pcmd->body.get_demod_stats.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.get_demod_stats.rsp) + + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_DEMOD_STATS_RSP); + if (error < 0) + goto out; + + /* Response OK -> get response data */ + pdemod_stats->frame_count = + le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.frame_count); + pdemod_stats->bad_frame_count = + le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bad_frame_count); + pdemod_stats->bytes_fixed_by_rs = + le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs); + pdemod_stats->mer = + le16_to_cpu((__force __le16)prsp->body.get_demod_stats.rsp.stats.mer); + pdemod_stats->has_started = + prsp->body.get_demod_stats.rsp.stats.has_started; + +out: + return error; +} + +/** + * as10x_cmd_get_impulse_resp - send get impulse response command to AS10x + * @adap: pointer to AS10x bus adapter + * @is_ready: pointer to value indicating when impulse + * response data is ready + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, + uint8_t *is_ready) +{ + int error = AS10X_CMD_ERROR; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.get_impulse_rsp.req)); + + /* fill command */ + pcmd->body.get_impulse_rsp.req.proc_id = + cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) pcmd, + sizeof(pcmd->body.get_impulse_rsp.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.get_impulse_rsp.rsp) + + HEADER_SIZE); + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_IMPULSE_RESP_RSP); + if (error < 0) + goto out; + + /* Response OK -> get response data */ + *is_ready = prsp->body.get_impulse_rsp.rsp.is_ready; + +out: + return error; +} + +/** + * as10x_cmd_build - build AS10x command header + * @pcmd: pointer to AS10x command buffer + * @xid: sequence id of the command + * @cmd_len: length of the command + */ +void as10x_cmd_build(struct as10x_cmd_t *pcmd, + uint16_t xid, uint16_t cmd_len) +{ + pcmd->header.req_id = cpu_to_le16(xid); + pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID); + pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION); + pcmd->header.data_len = cpu_to_le16(cmd_len); +} + +/** + * as10x_rsp_parse - Parse command response + * @prsp: pointer to AS10x command buffer + * @proc_id: id of the command + * + * Return 0 on success or negative value in case of error. + */ +int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id) +{ + int error; + + /* extract command error code */ + error = prsp->body.common.rsp.error; + + if ((error == 0) && + (le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) { + return 0; + } + + return AS10X_CMD_ERROR; +} diff --git a/drivers/media/usb/as102/as10x_cmd.h b/drivers/media/usb/as102/as10x_cmd.h new file mode 100644 index 0000000..e06b84e --- /dev/null +++ b/drivers/media/usb/as102/as10x_cmd.h @@ -0,0 +1,523 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ +#ifndef _AS10X_CMD_H_ +#define _AS10X_CMD_H_ + +#include <linux/kernel.h> + +#include "as102_fe_types.h" + +/*********************************/ +/* MACRO DEFINITIONS */ +/*********************************/ +#define AS10X_CMD_ERROR -1 + +#define SERVICE_PROG_ID 0x0002 +#define SERVICE_PROG_VERSION 0x0001 + +#define HIER_NONE 0x00 +#define HIER_LOW_PRIORITY 0x01 + +#define HEADER_SIZE (sizeof(struct as10x_cmd_header_t)) + +/* context request types */ +#define GET_CONTEXT_DATA 1 +#define SET_CONTEXT_DATA 2 + +/* ODSP suspend modes */ +#define CFG_MODE_ODSP_RESUME 0 +#define CFG_MODE_ODSP_SUSPEND 1 + +/* Dump memory size */ +#define DUMP_BLOCK_SIZE_MAX 0x20 + +/*********************************/ +/* TYPE DEFINITION */ +/*********************************/ +enum control_proc { + CONTROL_PROC_TURNON = 0x0001, + CONTROL_PROC_TURNON_RSP = 0x0100, + CONTROL_PROC_SET_REGISTER = 0x0002, + CONTROL_PROC_SET_REGISTER_RSP = 0x0200, + CONTROL_PROC_GET_REGISTER = 0x0003, + CONTROL_PROC_GET_REGISTER_RSP = 0x0300, + CONTROL_PROC_SETTUNE = 0x000A, + CONTROL_PROC_SETTUNE_RSP = 0x0A00, + CONTROL_PROC_GETTUNESTAT = 0x000B, + CONTROL_PROC_GETTUNESTAT_RSP = 0x0B00, + CONTROL_PROC_GETTPS = 0x000D, + CONTROL_PROC_GETTPS_RSP = 0x0D00, + CONTROL_PROC_SETFILTER = 0x000E, + CONTROL_PROC_SETFILTER_RSP = 0x0E00, + CONTROL_PROC_REMOVEFILTER = 0x000F, + CONTROL_PROC_REMOVEFILTER_RSP = 0x0F00, + CONTROL_PROC_GET_IMPULSE_RESP = 0x0012, + CONTROL_PROC_GET_IMPULSE_RESP_RSP = 0x1200, + CONTROL_PROC_START_STREAMING = 0x0013, + CONTROL_PROC_START_STREAMING_RSP = 0x1300, + CONTROL_PROC_STOP_STREAMING = 0x0014, + CONTROL_PROC_STOP_STREAMING_RSP = 0x1400, + CONTROL_PROC_GET_DEMOD_STATS = 0x0015, + CONTROL_PROC_GET_DEMOD_STATS_RSP = 0x1500, + CONTROL_PROC_ELNA_CHANGE_MODE = 0x0016, + CONTROL_PROC_ELNA_CHANGE_MODE_RSP = 0x1600, + CONTROL_PROC_ODSP_CHANGE_MODE = 0x0017, + CONTROL_PROC_ODSP_CHANGE_MODE_RSP = 0x1700, + CONTROL_PROC_AGC_CHANGE_MODE = 0x0018, + CONTROL_PROC_AGC_CHANGE_MODE_RSP = 0x1800, + + CONTROL_PROC_CONTEXT = 0x00FC, + CONTROL_PROC_CONTEXT_RSP = 0xFC00, + CONTROL_PROC_DUMP_MEMORY = 0x00FD, + CONTROL_PROC_DUMP_MEMORY_RSP = 0xFD00, + CONTROL_PROC_DUMPLOG_MEMORY = 0x00FE, + CONTROL_PROC_DUMPLOG_MEMORY_RSP = 0xFE00, + CONTROL_PROC_TURNOFF = 0x00FF, + CONTROL_PROC_TURNOFF_RSP = 0xFF00 +}; + +union as10x_turn_on { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_turn_off { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t err; + } __packed rsp; +} __packed; + +union as10x_set_tune { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* tune params */ + struct as10x_tune_args args; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_get_tune_status { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + /* tune status */ + struct as10x_tune_status sts; + } __packed rsp; +} __packed; + +union as10x_get_tps { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + /* tps details */ + struct as10x_tps tps; + } __packed rsp; +} __packed; + +union as10x_common { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_add_pid_filter { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* PID to filter */ + __le16 pid; + /* stream type (MPE, PSI/SI or PES )*/ + uint8_t stream_type; + /* PID index in filter table */ + uint8_t idx; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + /* Filter id */ + uint8_t filter_id; + } __packed rsp; +} __packed; + +union as10x_del_pid_filter { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* PID to remove */ + __le16 pid; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* response error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_start_streaming { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_stop_streaming { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_get_demod_stats { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + /* demod stats */ + struct as10x_demod_stats stats; + } __packed rsp; +} __packed; + +union as10x_get_impulse_resp { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + /* impulse response ready */ + uint8_t is_ready; + } __packed rsp; +} __packed; + +union as10x_fw_context { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* value to write (for set context)*/ + struct as10x_register_value reg_val; + /* context tag */ + __le16 tag; + /* context request type */ + __le16 type; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* value read (for get context) */ + struct as10x_register_value reg_val; + /* context request type */ + __le16 type; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_set_register { + /* request */ + struct { + /* response identifier */ + __le16 proc_id; + /* register description */ + struct as10x_register_addr reg_addr; + /* register content */ + struct as10x_register_value reg_val; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +union as10x_get_register { + /* request */ + struct { + /* response identifier */ + __le16 proc_id; + /* register description */ + struct as10x_register_addr reg_addr; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + /* register content */ + struct as10x_register_value reg_val; + } __packed rsp; +} __packed; + +union as10x_cfg_change_mode { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* mode */ + uint8_t mode; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + } __packed rsp; +} __packed; + +struct as10x_cmd_header_t { + __le16 req_id; + __le16 prog; + __le16 version; + __le16 data_len; +} __packed; + +#define DUMP_BLOCK_SIZE 16 + +union as10x_dump_memory { + /* request */ + struct { + /* request identifier */ + __le16 proc_id; + /* dump memory type request */ + uint8_t dump_req; + /* register description */ + struct as10x_register_addr reg_addr; + /* nb blocks to read */ + __le16 num_blocks; + } __packed req; + /* response */ + struct { + /* response identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + /* dump response */ + uint8_t dump_rsp; + /* data */ + union { + uint8_t data8[DUMP_BLOCK_SIZE]; + __le16 data16[DUMP_BLOCK_SIZE / sizeof(__le16)]; + __le32 data32[DUMP_BLOCK_SIZE / sizeof(__le32)]; + } __packed u; + } __packed rsp; +} __packed; + +union as10x_dumplog_memory { + struct { + /* request identifier */ + __le16 proc_id; + /* dump memory type request */ + uint8_t dump_req; + } __packed req; + struct { + /* request identifier */ + __le16 proc_id; + /* error */ + uint8_t error; + /* dump response */ + uint8_t dump_rsp; + /* dump data */ + uint8_t data[DUMP_BLOCK_SIZE]; + } __packed rsp; +} __packed; + +union as10x_raw_data { + /* request */ + struct { + __le16 proc_id; + uint8_t data[64 - sizeof(struct as10x_cmd_header_t) + - 2 /* proc_id */]; + } __packed req; + /* response */ + struct { + __le16 proc_id; + uint8_t error; + uint8_t data[64 - sizeof(struct as10x_cmd_header_t) + - 2 /* proc_id */ - 1 /* rc */]; + } __packed rsp; +} __packed; + +struct as10x_cmd_t { + struct as10x_cmd_header_t header; + union { + union as10x_turn_on turn_on; + union as10x_turn_off turn_off; + union as10x_set_tune set_tune; + union as10x_get_tune_status get_tune_status; + union as10x_get_tps get_tps; + union as10x_common common; + union as10x_add_pid_filter add_pid_filter; + union as10x_del_pid_filter del_pid_filter; + union as10x_start_streaming start_streaming; + union as10x_stop_streaming stop_streaming; + union as10x_get_demod_stats get_demod_stats; + union as10x_get_impulse_resp get_impulse_rsp; + union as10x_fw_context context; + union as10x_set_register set_register; + union as10x_get_register get_register; + union as10x_cfg_change_mode cfg_change_mode; + union as10x_dump_memory dump_memory; + union as10x_dumplog_memory dumplog_memory; + union as10x_raw_data raw_data; + } __packed body; +} __packed; + +struct as10x_token_cmd_t { + /* token cmd */ + struct as10x_cmd_t c; + /* token response */ + struct as10x_cmd_t r; +} __packed; + + +/**************************/ +/* FUNCTION DECLARATION */ +/**************************/ + +void as10x_cmd_build(struct as10x_cmd_t *pcmd, uint16_t proc_id, + uint16_t cmd_len); +int as10x_rsp_parse(struct as10x_cmd_t *r, uint16_t proc_id); + +/* as10x cmd */ +int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap); +int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap); + +int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, + struct as10x_tune_args *ptune); + +int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, + struct as10x_tune_status *pstatus); + +int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, + struct as10x_tps *ptps); + +int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, + struct as10x_demod_stats *pdemod_stats); + +int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, + uint8_t *is_ready); + +/* as10x cmd stream */ +int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap, + struct as10x_ts_filter *filter); +int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap, + uint16_t pid_value); + +int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap); +int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap); + +/* as10x cmd cfg */ +int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, + uint16_t tag, + uint32_t value); +int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, + uint16_t tag, + uint32_t *pvalue); + +int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode); +int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id); +#endif diff --git a/drivers/media/usb/as102/as10x_cmd_cfg.c b/drivers/media/usb/as102/as10x_cmd_cfg.c new file mode 100644 index 0000000..c87f2ca --- /dev/null +++ b/drivers/media/usb/as102/as10x_cmd_cfg.c @@ -0,0 +1,201 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ + +#include <linux/kernel.h> +#include "as102_drv.h" +#include "as10x_cmd.h" + +/***************************/ +/* FUNCTION DEFINITION */ +/***************************/ + +/** + * as10x_cmd_get_context - Send get context command to AS10x + * @adap: pointer to AS10x bus adapter + * @tag: context tag + * @pvalue: pointer where to store context value read + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, uint16_t tag, + uint32_t *pvalue) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.context.req)); + + /* fill command */ + pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT); + pcmd->body.context.req.tag = cpu_to_le16(tag); + pcmd->body.context.req.type = cpu_to_le16(GET_CONTEXT_DATA); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) pcmd, + sizeof(pcmd->body.context.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.context.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response: context command do not follow the common response */ + /* structure -> specific handling response parse required */ + error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP); + + if (error == 0) { + /* Response OK -> get response data */ + *pvalue = le32_to_cpu((__force __le32)prsp->body.context.rsp.reg_val.u.value32); + /* value returned is always a 32-bit value */ + } + +out: + return error; +} + +/** + * as10x_cmd_set_context - send set context command to AS10x + * @adap: pointer to AS10x bus adapter + * @tag: context tag + * @value: value to set in context + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, uint16_t tag, + uint32_t value) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.context.req)); + + /* fill command */ + pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT); + /* pcmd->body.context.req.reg_val.mode initialization is not required */ + pcmd->body.context.req.reg_val.u.value32 = (__force u32)cpu_to_le32(value); + pcmd->body.context.req.tag = cpu_to_le16(tag); + pcmd->body.context.req.type = cpu_to_le16(SET_CONTEXT_DATA); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, + (uint8_t *) pcmd, + sizeof(pcmd->body.context.req) + + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.context.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response: context command do not follow the common response */ + /* structure -> specific handling response parse required */ + error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP); + +out: + return error; +} + +/** + * as10x_cmd_eLNA_change_mode - send eLNA change mode command to AS10x + * @adap: pointer to AS10x bus adapter + * @mode: mode selected: + * - ON : 0x0 => eLNA always ON + * - OFF : 0x1 => eLNA always OFF + * - AUTO : 0x2 => eLNA follow hysteresis parameters + * to be ON or OFF + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.cfg_change_mode.req)); + + /* fill command */ + pcmd->body.cfg_change_mode.req.proc_id = + cpu_to_le16(CONTROL_PROC_ELNA_CHANGE_MODE); + pcmd->body.cfg_change_mode.req.mode = mode; + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.cfg_change_mode.req) + + HEADER_SIZE, (uint8_t *) prsp, + sizeof(prsp->body.cfg_change_mode.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_ELNA_CHANGE_MODE_RSP); + +out: + return error; +} + +/** + * as10x_context_rsp_parse - Parse context command response + * @prsp: pointer to AS10x command response buffer + * @proc_id: id of the command + * + * 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. + */ +int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id) +{ + int err; + + err = prsp->body.context.rsp.error; + + if ((err == 0) && + (le16_to_cpu(prsp->body.context.rsp.proc_id) == proc_id)) { + return 0; + } + return AS10X_CMD_ERROR; +} diff --git a/drivers/media/usb/as102/as10x_cmd_stream.c b/drivers/media/usb/as102/as10x_cmd_stream.c new file mode 100644 index 0000000..126aea9 --- /dev/null +++ b/drivers/media/usb/as102/as10x_cmd_stream.c @@ -0,0 +1,207 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ + +#include <linux/kernel.h> +#include "as102_drv.h" +#include "as10x_cmd.h" + +/** + * as10x_cmd_add_PID_filter - send add filter command to AS10x + * @adap: pointer to AS10x bus adapter + * @filter: TSFilter filter for DVB-T + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap, + struct as10x_ts_filter *filter) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.add_pid_filter.req)); + + /* fill command */ + pcmd->body.add_pid_filter.req.proc_id = + cpu_to_le16(CONTROL_PROC_SETFILTER); + pcmd->body.add_pid_filter.req.pid = cpu_to_le16(filter->pid); + pcmd->body.add_pid_filter.req.stream_type = filter->type; + + if (filter->idx < 16) + pcmd->body.add_pid_filter.req.idx = filter->idx; + else + pcmd->body.add_pid_filter.req.idx = 0xFF; + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.add_pid_filter.req) + + HEADER_SIZE, (uint8_t *) prsp, + sizeof(prsp->body.add_pid_filter.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_SETFILTER_RSP); + + if (error == 0) { + /* Response OK -> get response data */ + filter->idx = prsp->body.add_pid_filter.rsp.filter_id; + } + +out: + return error; +} + +/** + * as10x_cmd_del_PID_filter - Send delete filter command to AS10x + * @adap: pointer to AS10x bus adapte + * @pid_value: PID to delete + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap, + uint16_t pid_value) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.del_pid_filter.req)); + + /* fill command */ + pcmd->body.del_pid_filter.req.proc_id = + cpu_to_le16(CONTROL_PROC_REMOVEFILTER); + pcmd->body.del_pid_filter.req.pid = cpu_to_le16(pid_value); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.del_pid_filter.req) + + HEADER_SIZE, (uint8_t *) prsp, + sizeof(prsp->body.del_pid_filter.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_REMOVEFILTER_RSP); + +out: + return error; +} + +/** + * as10x_cmd_start_streaming - Send start streaming command to AS10x + * @adap: pointer to AS10x bus adapter + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.start_streaming.req)); + + /* fill command */ + pcmd->body.start_streaming.req.proc_id = + cpu_to_le16(CONTROL_PROC_START_STREAMING); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.start_streaming.req) + + HEADER_SIZE, (uint8_t *) prsp, + sizeof(prsp->body.start_streaming.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_START_STREAMING_RSP); + +out: + return error; +} + +/** + * as10x_cmd_stop_streaming - Send stop streaming command to AS10x + * @adap: pointer to AS10x bus adapter + * + * Return 0 on success or negative value in case of error. + */ +int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap) +{ + int8_t error; + struct as10x_cmd_t *pcmd, *prsp; + + pcmd = adap->cmd; + prsp = adap->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++adap->cmd_xid), + sizeof(pcmd->body.stop_streaming.req)); + + /* fill command */ + pcmd->body.stop_streaming.req.proc_id = + cpu_to_le16(CONTROL_PROC_STOP_STREAMING); + + /* send command */ + if (adap->ops->xfer_cmd) { + error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, + sizeof(pcmd->body.stop_streaming.req) + + HEADER_SIZE, (uint8_t *) prsp, + sizeof(prsp->body.stop_streaming.rsp) + + HEADER_SIZE); + } else { + error = AS10X_CMD_ERROR; + } + + if (error < 0) + goto out; + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_STOP_STREAMING_RSP); + +out: + return error; +} diff --git a/drivers/media/usb/as102/as10x_handle.h b/drivers/media/usb/as102/as10x_handle.h new file mode 100644 index 0000000..d6b58c7 --- /dev/null +++ b/drivers/media/usb/as102/as10x_handle.h @@ -0,0 +1,51 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, 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. + */ +#ifndef _AS10X_HANDLE_H +#define _AS10X_HANDLE_H +struct as10x_bus_adapter_t; +struct as102_dev_t; + +#include "as10x_cmd.h" + +/* values for "mode" field */ +#define REGMODE8 8 +#define REGMODE16 16 +#define REGMODE32 32 + +struct as102_priv_ops_t { + int (*upload_fw_pkt)(struct as10x_bus_adapter_t *bus_adap, + unsigned char *buf, int buflen, int swap32); + + int (*send_cmd)(struct as10x_bus_adapter_t *bus_adap, + unsigned char *buf, int buflen); + + int (*xfer_cmd)(struct as10x_bus_adapter_t *bus_adap, + unsigned char *send_buf, int send_buf_len, + unsigned char *recv_buf, int recv_buf_len); + + int (*start_stream)(struct as102_dev_t *dev); + void (*stop_stream)(struct as102_dev_t *dev); + + int (*reset_target)(struct as10x_bus_adapter_t *bus_adap); + + int (*read_write)(struct as10x_bus_adapter_t *bus_adap, uint8_t mode, + uint32_t rd_addr, uint16_t rd_len, + uint32_t wr_addr, uint16_t wr_len); + + int (*as102_read_ep2)(struct as10x_bus_adapter_t *bus_adap, + unsigned char *recv_buf, + int recv_buf_len); +}; +#endif diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 2c6b7da..9eb77ac 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -46,6 +46,8 @@ struct au0828_board au0828_boards[] = { .name = "Hauppauge HVR850", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, + .has_ir_i2c = 1, + .has_analog = 1, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, .input = { { @@ -72,12 +74,7 @@ struct au0828_board au0828_boards[] = { .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, .has_ir_i2c = 1, - /* The au0828 hardware i2c implementation does not properly - support the xc5000's i2c clock stretching. So we need to - lower the clock frequency enough where the 15us clock - stretch fits inside of a normal clock cycle, or else the - au0828 fails to set the STOP bit. A 30 KHz clock puts the - clock pulse width at 18us */ + .has_analog = 1, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, .input = { { @@ -101,20 +98,20 @@ struct au0828_board au0828_boards[] = { }, [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { .name = "Hauppauge HVR950Q rev xxF8", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, [AU0828_BOARD_DVICO_FUSIONHDTV7] = { .name = "DViCO FusionHDTV USB", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { .name = "Hauppauge Woodbury", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, }; @@ -142,8 +139,7 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg) mdelay(10); return 0; } else { - printk(KERN_ERR - "%s(): Unknown command.\n", __func__); + pr_err("%s(): Unknown command.\n", __func__); return -EINVAL; } break; @@ -177,12 +173,12 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: - printk(KERN_WARNING "%s: warning: " - "unknown hauppauge model #%d\n", __func__, tv.model); + pr_warn("%s: warning: unknown hauppauge model #%d\n", + __func__, tv.model); break; } - printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + pr_info("%s: hauppauge eeprom: model=%d\n", __func__, tv.model); } @@ -228,16 +224,16 @@ void au0828_card_analog_fe_setup(struct au0828_dev *dev) sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "au8522", 0x8e >> 1, NULL); if (sd == NULL) - printk(KERN_ERR "analog subdev registration failed\n"); + pr_err("analog subdev registration failed\n"); } /* Setup tuners */ - if (dev->board.tuner_type != TUNER_ABSENT) { + if (dev->board.tuner_type != TUNER_ABSENT && dev->board.has_analog) { /* Load the tuner module, which does the attach */ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", dev->board.tuner_addr, NULL); if (sd == NULL) - printk(KERN_ERR "tuner subdev registration fail\n"); + pr_err("tuner subdev registration fail\n"); tun_setup.mode_mask = mode_mask; tun_setup.type = dev->board.tuner_type; diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 56025e6..bc06480 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -19,14 +19,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "au0828.h" + #include <linux/module.h> #include <linux/slab.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/mutex.h> -#include "au0828.h" - /* * 1 = General debug messages * 2 = USB handling @@ -90,7 +90,7 @@ static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, status = min(status, 0); if (status < 0) { - printk(KERN_ERR "%s() Failed sending control message, error %d.\n", + pr_err("%s() Failed sending control message, error %d.\n", __func__, status); } @@ -115,7 +115,7 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, status = min(status, 0); if (status < 0) { - printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", + pr_err("%s() Failed receiving control message, error %d.\n", __func__, status); } @@ -153,9 +153,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface) dprintk(1, "%s()\n", __func__); -#ifdef CONFIG_VIDEO_AU0828_RC au0828_rc_unregister(dev); -#endif /* Digital TV */ au0828_dvb_unregister(dev); @@ -199,15 +197,14 @@ static int au0828_usb_probe(struct usb_interface *interface, * not enough even for most Digital TV streams. */ if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { - printk(KERN_ERR "au0828: Device initialization failed.\n"); - printk(KERN_ERR "au0828: Device must be connected to a " - "high-speed USB 2.0 port.\n"); + pr_err("au0828: Device initialization failed.\n"); + pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n"); return -ENODEV; } dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { - printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); + pr_err("%s() Unable to allocate memory\n", __func__); return -ENOMEM; } @@ -266,10 +263,8 @@ static int au0828_usb_probe(struct usb_interface *interface, pr_err("%s() au0282_dev_register failed\n", __func__); -#ifdef CONFIG_VIDEO_AU0828_RC /* Remote controller */ au0828_rc_register(dev); -#endif /* * Store the pointer to the au0828_dev so it can be accessed in @@ -277,7 +272,7 @@ static int au0828_usb_probe(struct usb_interface *interface, */ usb_set_intfdata(interface, dev); - printk(KERN_INFO "Registered device AU0828 [%s]\n", + pr_info("Registered device AU0828 [%s]\n", dev->board.name == NULL ? "Unset" : dev->board.name); mutex_unlock(&dev->lock); @@ -285,13 +280,56 @@ static int au0828_usb_probe(struct usb_interface *interface, return retval; } +static int au0828_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + + if (!dev) + return 0; + + pr_info("Suspend\n"); + + au0828_rc_suspend(dev); + au0828_v4l2_suspend(dev); + au0828_dvb_suspend(dev); + + /* FIXME: should suspend also ATV/DTV */ + + return 0; +} + +static int au0828_resume(struct usb_interface *interface) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + if (!dev) + return 0; + + pr_info("Resume\n"); + + /* Power Up the bridge */ + au0828_write(dev, REG_600, 1 << 4); + + /* Bring up the GPIO's and supporting devices */ + au0828_gpio_setup(dev); + + au0828_rc_resume(dev); + au0828_v4l2_resume(dev); + au0828_dvb_resume(dev); + + /* FIXME: should resume also ATV/DTV */ + + return 0; +} + static struct usb_driver au0828_usb_driver = { - .name = DRIVER_NAME, + .name = KBUILD_MODNAME, .probe = au0828_usb_probe, .disconnect = au0828_usb_disconnect, .id_table = au0828_usb_id_table, - - /* FIXME: Add suspend and resume functions */ + .suspend = au0828_suspend, + .resume = au0828_resume, + .reset_resume = au0828_resume, }; static int __init au0828_init(void) @@ -299,27 +337,27 @@ static int __init au0828_init(void) int ret; if (au0828_debug & 1) - printk(KERN_INFO "%s() Debugging is enabled\n", __func__); + pr_info("%s() Debugging is enabled\n", __func__); if (au0828_debug & 2) - printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); + pr_info("%s() USB Debugging is enabled\n", __func__); if (au0828_debug & 4) - printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); + pr_info("%s() I2C Debugging is enabled\n", __func__); if (au0828_debug & 8) - printk(KERN_INFO "%s() Bridge Debugging is enabled\n", + pr_info("%s() Bridge Debugging is enabled\n", __func__); if (au0828_debug & 16) - printk(KERN_INFO "%s() IR Debugging is enabled\n", + pr_info("%s() IR Debugging is enabled\n", __func__); - printk(KERN_INFO "au0828 driver loaded\n"); + pr_info("au0828 driver loaded\n"); ret = usb_register(&au0828_usb_driver); if (ret) - printk(KERN_ERR "usb_register failed, error = %d\n", ret); + pr_err("usb_register failed, error = %d\n", ret); return ret; } diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index d8b5d948..00ab1563 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -19,15 +19,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "au0828.h" + #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/device.h> -#include <linux/suspend.h> #include <media/v4l2-common.h> #include <media/tuner.h> -#include "au0828.h" #include "au8522.h" #include "xc5000.h" #include "mxl5007t.h" @@ -121,13 +121,13 @@ static void urb_completion(struct urb *purb) return; } - if (dev->urb_streaming == 0) { + if (!dev->urb_streaming) { dprintk(2, "%s: not streaming!\n", __func__); return; } if (ptype != PIPE_BULK) { - printk(KERN_ERR "%s: Unsupported URB type %d\n", + pr_err("%s: Unsupported URB type %d\n", __func__, ptype); return; } @@ -159,7 +159,10 @@ static int stop_urb_transfer(struct au0828_dev *dev) dprintk(2, "%s()\n", __func__); - dev->urb_streaming = 0; + if (!dev->urb_streaming) + return 0; + + dev->urb_streaming = false; for (i = 0; i < URB_COUNT; i++) { if (dev->urbs[i]) { usb_kill_urb(dev->urbs[i]); @@ -202,8 +205,7 @@ static int start_urb_transfer(struct au0828_dev *dev) if (!purb->transfer_buffer) { usb_free_urb(purb); dev->urbs[i] = NULL; - printk(KERN_ERR - "%s: failed big buffer allocation, err = %d\n", + pr_err("%s: failed big buffer allocation, err = %d\n", __func__, ret); goto err; } @@ -224,13 +226,13 @@ static int start_urb_transfer(struct au0828_dev *dev) ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); if (ret != 0) { stop_urb_transfer(dev); - printk(KERN_ERR "%s: failed urb submission, " - "err = %d\n", __func__, ret); + pr_err("%s: failed urb submission, err = %d\n", + __func__, ret); return ret; } } - dev->urb_streaming = 1; + dev->urb_streaming = true; ret = 0; err: @@ -268,7 +270,7 @@ static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (dvb) { + if (dvb->frontend) { mutex_lock(&dvb->lock); dvb->start_count++; dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, @@ -297,7 +299,7 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) dprintk(1, "%s()\n", __func__); - if (dvb) { + if (dvb->frontend) { cancel_work_sync(&dev->restart_streaming); mutex_lock(&dvb->lock); @@ -324,7 +326,7 @@ static void au0828_restart_dvb_streaming(struct work_struct *work) restart_streaming); struct au0828_dvb *dvb = &dev->dvb; - if (dev->urb_streaming == 0) + if (!dev->urb_streaming) return; dprintk(1, "Restarting streaming...!\n"); @@ -393,9 +395,8 @@ static int dvb_register(struct au0828_dev *dev) if (!dev->dig_transfer_buffer[i]) { result = -ENOMEM; - printk(KERN_ERR - "%s: failed buffer allocation (errno = %d)\n", - DRIVER_NAME, result); + pr_err("failed buffer allocation (errno = %d)\n", + result); goto fail_adapter; } } @@ -404,11 +405,12 @@ static int dvb_register(struct au0828_dev *dev) INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming); /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + result = dvb_register_adapter(&dvb->adapter, + KBUILD_MODNAME, THIS_MODULE, &dev->usbdev->dev, adapter_nr); if (result < 0) { - printk(KERN_ERR "%s: dvb_register_adapter failed " - "(errno = %d)\n", DRIVER_NAME, result); + pr_err("dvb_register_adapter failed (errno = %d)\n", + result); goto fail_adapter; } dvb->adapter.priv = dev; @@ -416,8 +418,8 @@ static int dvb_register(struct au0828_dev *dev) /* register frontend */ result = dvb_register_frontend(&dvb->adapter, dvb->frontend); if (result < 0) { - printk(KERN_ERR "%s: dvb_register_frontend failed " - "(errno = %d)\n", DRIVER_NAME, result); + pr_err("dvb_register_frontend failed (errno = %d)\n", + result); goto fail_frontend; } @@ -436,8 +438,7 @@ static int dvb_register(struct au0828_dev *dev) dvb->demux.stop_feed = au0828_dvb_stop_feed; result = dvb_dmx_init(&dvb->demux); if (result < 0) { - printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("dvb_dmx_init failed (errno = %d)\n", result); goto fail_dmx; } @@ -446,31 +447,29 @@ static int dvb_register(struct au0828_dev *dev) dvb->dmxdev.capabilities = 0; result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); if (result < 0) { - printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); goto fail_dmxdev; } dvb->fe_hw.source = DMX_FRONTEND_0; result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + result); goto fail_fe_hw; } dvb->fe_mem.source = DMX_MEMORY_FE; result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + result); goto fail_fe_mem; } result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); if (result < 0) { - printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", - DRIVER_NAME, result); + pr_err("connect_frontend failed (errno = %d)\n", result); goto fail_fe_conn; } @@ -530,8 +529,7 @@ void au0828_dvb_unregister(struct au0828_dev *dev) for (i = 0; i < URB_COUNT; i++) kfree(dev->dig_transfer_buffer[i]); } - - + dvb->frontend = NULL; } /* All the DVB attach calls go here, this function get's modified @@ -596,12 +594,11 @@ int au0828_dvb_register(struct au0828_dev *dev) } break; default: - printk(KERN_WARNING "The frontend of your DVB/ATSC card " - "isn't supported yet\n"); + pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n"); break; } if (NULL == dvb->frontend) { - printk(KERN_ERR "%s() Frontend initialization failed\n", + pr_err("%s() Frontend initialization failed\n", __func__); return -1; } @@ -613,8 +610,49 @@ int au0828_dvb_register(struct au0828_dev *dev) if (ret < 0) { if (dvb->frontend->ops.release) dvb->frontend->ops.release(dvb->frontend); + dvb->frontend = NULL; return ret; } return 0; } + +void au0828_dvb_suspend(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int rc; + + if (dvb->frontend) { + if (dev->urb_streaming) { + cancel_work_sync(&dev->restart_streaming); + /* Stop transport */ + mutex_lock(&dvb->lock); + stop_urb_transfer(dev); + au0828_stop_transport(dev, 1); + mutex_unlock(&dvb->lock); + dev->need_urb_start = true; + } + /* suspend frontend - does tuner and fe to sleep */ + rc = dvb_frontend_suspend(dvb->frontend); + pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc); + } +} + +void au0828_dvb_resume(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int rc; + + if (dvb->frontend) { + /* resume frontend - does fe and tuner init */ + rc = dvb_frontend_resume(dvb->frontend); + pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc); + if (dev->need_urb_start) { + /* Start transport */ + mutex_lock(&dvb->lock); + au0828_start_transport(dev); + start_urb_transfer(dev); + mutex_unlock(&dvb->lock); + } + } +} diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index daaeaf1..ae7ac66 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -19,13 +19,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "au0828.h" + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/io.h> -#include "au0828.h" #include "media/tuner.h" #include <media/v4l2-common.h> @@ -340,7 +341,7 @@ static struct i2c_algorithm au0828_i2c_algo_template = { /* ----------------------------------------------------------------------- */ static struct i2c_adapter au0828_i2c_adap_template = { - .name = DRIVER_NAME, + .name = KBUILD_MODNAME, .owner = THIS_MODULE, .algo = &au0828_i2c_algo_template, }; @@ -365,7 +366,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c) rc = i2c_master_recv(c, &buf, 0); if (rc < 0) continue; - printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", + pr_info("%s: i2c scan: found device @ 0x%x [%s]\n", name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); } } @@ -381,7 +382,7 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_adap.dev.parent = &dev->usbdev->dev; - strlcpy(dev->i2c_adap.name, DRIVER_NAME, + strlcpy(dev->i2c_adap.name, KBUILD_MODNAME, sizeof(dev->i2c_adap.name)); dev->i2c_adap.algo = &dev->i2c_algo; @@ -396,11 +397,11 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_client.adapter = &dev->i2c_adap; if (0 == dev->i2c_rc) { - printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); + pr_info("i2c bus registered\n"); if (i2c_scan) - do_i2c_scan(DRIVER_NAME, &dev->i2c_client); + do_i2c_scan(KBUILD_MODNAME, &dev->i2c_client); } else - printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); + pr_info("i2c bus register FAILED\n"); return dev->i2c_rc; } diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index fd0d3a90..63995f9 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -17,6 +17,8 @@ GNU General Public License for more details. */ +#include "au0828.h" + #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -25,7 +27,9 @@ #include <linux/slab.h> #include <media/rc-core.h> -#include "au0828.h" +static int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); struct au0828_rc { struct au0828_dev *dev; @@ -90,14 +94,19 @@ static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val, static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value) { int rc; - char buf; + char buf, oldbuf; rc = au8522_rc_read(ir, reg, -1, &buf, 1); if (rc < 0) return rc; + oldbuf = buf; buf = (buf & ~mask) | (value & mask); + /* Nothing to do, just return */ + if (buf == oldbuf) + return 0; + return au8522_rc_write(ir, reg, buf); } @@ -122,8 +131,11 @@ static int au0828_get_key_au8522(struct au0828_rc *ir) /* Check IR int */ rc = au8522_rc_read(ir, 0xe1, -1, buf, 1); - if (rc < 0 || !(buf[0] & (1 << 4))) + if (rc < 0 || !(buf[0] & (1 << 4))) { + /* Be sure that IR is enabled */ + au8522_rc_set(ir, 0xe0, 1 << 4); return 0; + } /* Something arrived. Get the data */ rc = au8522_rc_read(ir, 0xe3, 0x11, buf, sizeof(buf)); @@ -135,8 +147,6 @@ static int au0828_get_key_au8522(struct au0828_rc *ir) /* Disable IR */ au8522_rc_clear(ir, 0xe0, 1 << 4); - usleep_range(45000, 46000); - /* Enable IR */ au8522_rc_set(ir, 0xe0, 1 << 4); @@ -243,10 +253,10 @@ static void au0828_rc_stop(struct rc_dev *rc) { struct au0828_rc *ir = rc->priv; + cancel_delayed_work_sync(&ir->work); + /* Disable IR */ au8522_rc_clear(ir, 0xe0, 1 << 4); - - cancel_delayed_work_sync(&ir->work); } static int au0828_probe_i2c_ir(struct au0828_dev *dev) @@ -273,7 +283,7 @@ int au0828_rc_register(struct au0828_dev *dev) int err = -ENOMEM; u16 i2c_rc_dev_addr = 0; - if (!dev->board.has_ir_i2c) + if (!dev->board.has_ir_i2c || disable_ir) return 0; i2c_rc_dev_addr = au0828_probe_i2c_ir(dev); @@ -368,8 +378,13 @@ int au0828_rc_suspend(struct au0828_dev *dev) if (!ir) return 0; + pr_info("Stopping RC\n"); + cancel_delayed_work_sync(&ir->work); + /* Disable IR */ + au8522_rc_clear(ir, 0xe0, 1 << 4); + return 0; } @@ -380,6 +395,11 @@ int au0828_rc_resume(struct au0828_dev *dev) if (!ir) return 0; + pr_info("Restarting RC\n"); + + /* Enable IR */ + au8522_rc_set(ir, 0xe0, 1 << 4); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); return 0; diff --git a/drivers/media/usb/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c index 63f5930..932d24f 100644 --- a/drivers/media/usb/au0828/au0828-vbi.c +++ b/drivers/media/usb/au0828/au0828-vbi.c @@ -21,13 +21,13 @@ 02110-1301, USA. */ +#include "au0828.h" + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include "au0828.h" - static unsigned int vbibufs = 5; module_param(vbibufs, int, 0644); MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 98f7ea1..5f337b1 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -28,16 +28,16 @@ * */ +#include "au0828.h" + #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/device.h> -#include <linux/suspend.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> #include <media/tuner.h> -#include "au0828.h" #include "au0828-reg.h" static DEFINE_MUTEX(au0828_sysfs_lock); @@ -53,7 +53,7 @@ MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); #define au0828_isocdbg(fmt, arg...) \ do {\ if (isoc_debug) { \ - printk(KERN_INFO "au0828 %s :"fmt, \ + pr_info("au0828 %s :"fmt, \ __func__ , ##arg); \ } \ } while (0) @@ -106,12 +106,12 @@ static inline void print_err_status(struct au0828_dev *dev, static int check_dev(struct au0828_dev *dev) { if (dev->dev_state & DEV_DISCONNECTED) { - printk(KERN_INFO "v4l2 ioctl: device not present\n"); + pr_info("v4l2 ioctl: device not present\n"); return -ENODEV; } if (dev->dev_state & DEV_MISCONFIGURED) { - printk(KERN_INFO "v4l2 ioctl: device is misconfigured; " + pr_info("v4l2 ioctl: device is misconfigured; " "close and open it again\n"); return -EIO; } @@ -159,6 +159,7 @@ static void au0828_irq_callback(struct urb *urb) au0828_isocdbg("urb resubmit failed (error=%i)\n", urb->status); } + dev->stream_state = STREAM_ON; } /* @@ -198,6 +199,8 @@ static void au0828_uninit_isoc(struct au0828_dev *dev) dev->isoc_ctl.urb = NULL; dev->isoc_ctl.transfer_buffer = NULL; dev->isoc_ctl.num_bufs = 0; + + dev->stream_state = STREAM_OFF; } /* @@ -717,7 +720,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) { - printk(KERN_INFO "videobuf_iolock failed\n"); + pr_info("videobuf_iolock failed\n"); goto fail; } } @@ -730,7 +733,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, AU0828_MAX_ISO_BUFS, dev->max_pkt_size, au0828_isoc_copy); if (rc < 0) { - printk(KERN_INFO "au0828_init_isoc failed\n"); + pr_info("au0828_init_isoc failed\n"); goto fail; } } @@ -801,7 +804,7 @@ static int au0828_analog_stream_enable(struct au0828_dev *d) /* set au0828 interface0 to AS5 here again */ ret = usb_set_interface(d->usbdev, 0, 5); if (ret < 0) { - printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); + pr_info("Au0828 can't set alt setting to 5!\n"); return -EBUSY; } } @@ -1090,7 +1093,7 @@ static int au0828_v4l2_close(struct file *filp) USB bandwidth */ ret = usb_set_interface(dev->usbdev, 0, 0); if (ret < 0) - printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); + pr_info("Au0828 can't set alternate to 0!\n"); } mutex_unlock(&dev->lock); @@ -1344,7 +1347,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return rc; if (videobuf_queue_is_busy(&fh->vb_vidq)) { - printk(KERN_INFO "%s queue busy\n", __func__); + pr_info("%s queue busy\n", __func__); rc = -EBUSY; goto out; } @@ -1868,6 +1871,69 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) return rc; } +void au0828_v4l2_suspend(struct au0828_dev *dev) +{ + struct urb *urb; + int i; + + pr_info("stopping V4L2\n"); + + if (dev->stream_state == STREAM_ON) { + pr_info("stopping V4L2 active URBs\n"); + au0828_analog_stream_disable(dev); + /* stop urbs */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + } + + if (dev->vid_timeout_running) + del_timer_sync(&dev->vid_timeout); + if (dev->vbi_timeout_running) + del_timer_sync(&dev->vbi_timeout); +} + +void au0828_v4l2_resume(struct au0828_dev *dev) +{ + int i, rc; + + pr_info("restarting V4L2\n"); + + if (dev->stream_state == STREAM_ON) { + au0828_stream_interrupt(dev); + au0828_init_tuner(dev); + } + + if (dev->vid_timeout_running) + mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); + if (dev->vbi_timeout_running) + mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); + + /* If we were doing ac97 instead of i2s, it would go here...*/ + au0828_i2s_init(dev); + + au0828_analog_stream_enable(dev); + + if (!(dev->stream_state == STREAM_ON)) { + au0828_analog_stream_reset(dev); + /* submit urbs */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + au0828_isocdbg("submit of urb %i failed (error=%i)\n", + i, rc); + au0828_uninit_isoc(dev); + } + } + } +} + static struct v4l2_file_operations au0828_v4l_fops = { .owner = THIS_MODULE, .open = au0828_v4l2_open, @@ -1939,7 +2005,7 @@ int au0828_analog_register(struct au0828_dev *dev, retval = usb_set_interface(dev->usbdev, interface->cur_altsetting->desc.bInterfaceNumber, 5); if (retval != 0) { - printk(KERN_INFO "Failure setting usb interface0 to as5\n"); + pr_info("Failure setting usb interface0 to as5\n"); return retval; } @@ -1963,7 +2029,7 @@ int au0828_analog_register(struct au0828_dev *dev, } } if (!(dev->isoc_in_endpointaddr)) { - printk(KERN_INFO "Could not locate isoc endpoint\n"); + pr_info("Could not locate isoc endpoint\n"); kfree(dev); return -ENODEV; } diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 96bec05..36815a3 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/usb.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> @@ -42,7 +44,6 @@ #include "au0828-reg.h" #include "au0828-cards.h" -#define DRIVER_NAME "au0828" #define URB_COUNT 16 #define URB_BUFSIZE (0xe522) @@ -89,6 +90,7 @@ struct au0828_board { unsigned char tuner_addr; unsigned char i2c_clk_divider; unsigned char has_ir_i2c:1; + unsigned char has_analog:1; struct au0828_input input[AU0828_MAX_INPUT]; }; @@ -266,8 +268,8 @@ struct au0828_dev { char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc transfer */ - /* USB / URB Related */ - int urb_streaming; + /* DVB USB / URB Related */ + bool urb_streaming, need_urb_start; struct urb *urbs[URB_COUNT]; /* Preallocated transfer digital transfer buffers */ @@ -311,22 +313,38 @@ int au0828_analog_register(struct au0828_dev *dev, struct usb_interface *interface); int au0828_analog_stream_disable(struct au0828_dev *d); void au0828_analog_unregister(struct au0828_dev *dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 +void au0828_v4l2_suspend(struct au0828_dev *dev); +void au0828_v4l2_resume(struct au0828_dev *dev); +#else +static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { }; +static inline void au0828_v4l2_resume(struct au0828_dev *dev) { }; +#endif /* ----------------------------------------------------------- */ /* au0828-dvb.c */ extern int au0828_dvb_register(struct au0828_dev *dev); extern void au0828_dvb_unregister(struct au0828_dev *dev); +void au0828_dvb_suspend(struct au0828_dev *dev); +void au0828_dvb_resume(struct au0828_dev *dev); /* au0828-vbi.c */ extern struct videobuf_queue_ops au0828_vbi_qops; #define dprintk(level, fmt, arg...)\ do { if (au0828_debug & level)\ - printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ + printk(KERN_DEBUG pr_fmt(fmt), ## arg);\ } while (0) /* au0828-input.c */ -int au0828_rc_register(struct au0828_dev *dev); -void au0828_rc_unregister(struct au0828_dev *dev); -int au0828_rc_suspend(struct au0828_dev *dev); -int au0828_rc_resume(struct au0828_dev *dev); +#ifdef CONFIG_VIDEO_AU0828_RC +extern int au0828_rc_register(struct au0828_dev *dev); +extern void au0828_rc_unregister(struct au0828_dev *dev); +extern int au0828_rc_suspend(struct au0828_dev *dev); +extern int au0828_rc_resume(struct au0828_dev *dev); +#else +static inline int au0828_rc_register(struct au0828_dev *dev) { return 0; } +static inline void au0828_rc_unregister(struct au0828_dev *dev) { } +static inline int au0828_rc_suspend(struct au0828_dev *dev) { return 0; } +static inline int au0828_rc_resume(struct au0828_dev *dev) { return 0; } +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index a428c10..40a6987 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -1595,7 +1595,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, if_freq = 16000000; } - cx231xx_info("Enter IF=%zd\n", + cx231xx_info("Enter IF=%zu\n", ARRAY_SIZE(Dif_set_array)); for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) { if (Dif_set_array[i].if_freq == if_freq) { @@ -2223,7 +2223,7 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) if (status < 0) return status; - tmp = le32_to_cpu(*((u32 *) value)); + tmp = le32_to_cpu(*((__le32 *) value)); switch (mode) { case POLARIS_AVMODE_ENXTERNAL_AV: @@ -2444,7 +2444,7 @@ int cx231xx_power_suspend(struct cx231xx *dev) if (status > 0) return status; - tmp = le32_to_cpu(*((u32 *) value)); + tmp = le32_to_cpu(*((__le32 *) value)); tmp &= (~PWR_MODE_MASK); value[0] = (u8) tmp; @@ -2472,7 +2472,7 @@ int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) if (status < 0) return status; - tmp = le32_to_cpu(*((u32 *) value)); + tmp = le32_to_cpu(*((__le32 *) value)); tmp |= ep_mask; value[0] = (u8) tmp; value[1] = (u8) (tmp >> 8); @@ -2497,7 +2497,7 @@ int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) if (status < 0) return status; - tmp = le32_to_cpu(*((u32 *) value)); + tmp = le32_to_cpu(*((__le32 *) value)); tmp &= (~ep_mask); value[0] = (u8) tmp; value[1] = (u8) (tmp >> 8); @@ -2644,7 +2644,7 @@ static int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 gpio_val) { int status = 0; - gpio_val = cpu_to_le32(gpio_val); + gpio_val = (__force u32)cpu_to_le32(gpio_val); status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&gpio_val, 4, 0, 0); return status; @@ -2652,7 +2652,7 @@ static int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 gpio_val) static int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 *gpio_val) { - u32 tmp; + __le32 tmp; int status = 0; status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&tmp, 4, 0, 1); diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 8039b76..791f00c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -705,7 +705,7 @@ struct cx231xx_board cx231xx_boards[] = { }, }, [CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx] = { - .name = "Hauppauge WinTV 930C-HD (1113xx) / PCTV QuatroStick 521e", + .name = "Hauppauge WinTV 930C-HD (1113xx) / HVR-900H (111xxx) / PCTV QuatroStick 521e", .tuner_type = TUNER_NXP_TDA18271, .tuner_addr = 0x60, .tuner_gpio = RDE250_XCV_TUNER, @@ -744,7 +744,7 @@ struct cx231xx_board cx231xx_boards[] = { } }, }, [CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx] = { - .name = "Hauppauge WinTV 930C-HD (1114xx) / PCTV QuatroStick 522e", + .name = "Hauppauge WinTV 930C-HD (1114xx) / HVR-901H (1114xx) / PCTV QuatroStick 522e", .tuner_type = TUNER_ABSENT, .tuner_addr = 0x60, .tuner_gpio = RDE250_XCV_TUNER, @@ -815,6 +815,12 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, {USB_DEVICE(0x2040, 0xb131), .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx}, + /* Hauppauge WinTV-HVR-900-H */ + {USB_DEVICE(0x2040, 0xb138), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, + /* Hauppauge WinTV-HVR-901-H */ + {USB_DEVICE(0x2040, 0xb139), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx}, {USB_DEVICE(0x2040, 0xb140), .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xc200), diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 513194a..180103e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1491,7 +1491,7 @@ int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) if (status < 0) return status; - tmp = le32_to_cpu(*((u32 *) value)); + tmp = le32_to_cpu(*((__le32 *) value)); tmp |= mode; value[0] = (u8) tmp; diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 1fa7974..6c7b5e2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -403,8 +403,6 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev) int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) { - int status = 0; - if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; @@ -423,7 +421,7 @@ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) } - return status; + return 0; } int cx231xx_reset_analog_tuner(struct cx231xx *dev) @@ -740,7 +738,7 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dev->dvb->frontend->ops.i2c_gate_ctrl = 0; + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ dvb->frontend->callback = cx231xx_tuner_callback; @@ -773,7 +771,7 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dev->dvb->frontend->ops.i2c_gate_ctrl = 0; + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ dvb->frontend->callback = cx231xx_tuner_callback; diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 66645b0..5b34323 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -141,3 +141,10 @@ config DVB_USB_RTL28XXU help Say Y here to support the Realtek RTL28xxU DVB USB receiver. +config DVB_USB_DVBSKY + tristate "DVBSky USB support" + depends on DVB_USB_V2 + select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the USB receivers from DVBSky. diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index bc38f03..f10d4df 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -37,6 +37,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o dvb-usb-rtl28xxu-objs := rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o +dvb-usb-dvbsky-objs := dvbsky.o +obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o + ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ccflags-y += -I$(srctree)/drivers/media/tuners diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 5ca738a..16c0b7d 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -419,7 +419,7 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d) /* calculate checksum */ for (i = 0; i < AF9015_EEPROM_SIZE / sizeof(u32); i++) { state->eeprom_sum *= GOLDEN_RATIO_PRIME_32; - state->eeprom_sum += le32_to_cpu(((u32 *)buf)[i]); + state->eeprom_sum += le32_to_cpu(((__le32 *)buf)[i]); } for (i = 0; i < AF9015_EEPROM_SIZE; i += 16) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index c82beac..00758c8 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -193,6 +193,92 @@ static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, return af9035_wr_regs(d, reg, &val, 1); } +static int af9035_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, + void *platform_data, struct i2c_adapter *adapter) +{ + int ret, num; + struct state *state = d_to_priv(d); + struct i2c_client *client; + struct i2c_board_info board_info = { + .addr = addr, + .platform_data = platform_data, + }; + + strlcpy(board_info.type, type, I2C_NAME_SIZE); + + /* find first free client */ + for (num = 0; num < AF9035_I2C_CLIENT_MAX; num++) { + if (state->i2c_client[num] == NULL) + break; + } + + dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + + if (num == AF9035_I2C_CLIENT_MAX) { + dev_err(&d->udev->dev, "%s: I2C client out of index\n", + KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + request_module(board_info.type); + + /* register I2C device */ + client = i2c_new_device(adapter, &board_info); + if (client == NULL || client->dev.driver == NULL) { + ret = -ENODEV; + goto err; + } + + /* increase I2C driver usage count */ + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + ret = -ENODEV; + goto err; + } + + state->i2c_client[num] = client; + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static void af9035_del_i2c_dev(struct dvb_usb_device *d) +{ + int num; + struct state *state = d_to_priv(d); + struct i2c_client *client; + + /* find last used client */ + num = AF9035_I2C_CLIENT_MAX; + while (num--) { + if (state->i2c_client[num] != NULL) + break; + } + + dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + + if (num == -1) { + dev_err(&d->udev->dev, "%s: I2C client out of index\n", + KBUILD_MODNAME); + goto err; + } + + client = state->i2c_client[num]; + + /* decrease I2C driver usage count */ + module_put(client->dev.driver->owner); + + /* unregister I2C device */ + i2c_unregister_device(client); + + state->i2c_client[num] = NULL; + return; +err: + dev_dbg(&d->udev->dev, "%s: failed\n", __func__); +} + static int af9035_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { @@ -204,7 +290,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, return -EAGAIN; /* - * I2C sub header is 5 bytes long. Meaning of those bytes are: + * AF9035 I2C sub header is 5 bytes long. Meaning of those bytes are: * 0: data len * 1: I2C addr << 1 * 2: reg addr len @@ -218,110 +304,156 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, * NOTE: As a firmware knows tuner type there is very small possibility * there could be some tuner I2C hacks done by firmware and this may * lead problems if firmware expects those bytes are used. + * + * TODO: Here is few hacks. AF9035 chip integrates AF9033 demodulator. + * IT9135 chip integrates AF9033 demodulator and RF tuner. For dual + * tuner devices, there is also external AF9033 demodulator connected + * via external I2C bus. All AF9033 demod I2C traffic, both single and + * dual tuner configuration, is covered by firmware - actual USB IO + * looks just like a memory access. + * In case of IT913x chip, there is own tuner driver. It is implemented + * currently as a I2C driver, even tuner IP block is likely build + * directly into the demodulator memory space and there is no own I2C + * bus. I2C subsystem does not allow register multiple devices to same + * bus, having same slave address. Due to that we reuse demod address, + * shifted by one bit, on that case. + * + * For IT930x we use a different command and the sub header is + * different as well: + * 0: data len + * 1: I2C bus (0x03 seems to be only value used) + * 2: I2C addr << 1 */ - if (num == 2 && !(msg[0].flags & I2C_M_RD) && - (msg[1].flags & I2C_M_RD)) { +#define AF9035_IS_I2C_XFER_WRITE_READ(_msg, _num) \ + (_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD)) +#define AF9035_IS_I2C_XFER_WRITE(_msg, _num) \ + (_num == 1 && !(_msg[0].flags & I2C_M_RD)) +#define AF9035_IS_I2C_XFER_READ(_msg, _num) \ + (_num == 1 && (_msg[0].flags & I2C_M_RD)) + + if (AF9035_IS_I2C_XFER_WRITE_READ(msg, num)) { 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) || - (msg[0].addr == state->af9033_config[1].i2c_addr)) { + } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || + (msg[0].addr == state->af9033_i2c_addr[1]) || + (state->chip_type == 0x9135)) { /* 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) + if (msg[0].addr == state->af9033_i2c_addr[1] || + msg[0].addr == (state->af9033_i2c_addr[1] >> 1)) reg |= 0x100000; ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { - /* I2C */ + /* I2C write + read */ u8 buf[MAX_XFER_SIZE]; struct usb_req req = { CMD_I2C_RD, 0, 5 + msg[0].len, buf, msg[1].len, msg[1].buf }; - if (5 + msg[0].len > sizeof(buf)) { - dev_warn(&d->udev->dev, - "%s: i2c xfer: len=%d is too big!\n", - KBUILD_MODNAME, msg[0].len); - ret = -EOPNOTSUPP; - goto unlock; + if (state->chip_type == 0x9306) { + req.cmd = CMD_GENERIC_I2C_RD; + req.wlen = 3 + msg[0].len; } req.mbox |= ((msg[0].addr & 0x80) >> 3); + buf[0] = msg[1].len; - buf[1] = msg[0].addr << 1; - buf[2] = 0x00; /* reg addr len */ - buf[3] = 0x00; /* reg addr MSB */ - buf[4] = 0x00; /* reg addr LSB */ - memcpy(&buf[5], msg[0].buf, msg[0].len); + if (state->chip_type == 0x9306) { + buf[1] = 0x03; /* I2C bus */ + buf[2] = msg[0].addr << 1; + memcpy(&buf[3], msg[0].buf, msg[0].len); + } else { + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + } ret = af9035_ctrl_msg(d, &req); } - } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + } else if (AF9035_IS_I2C_XFER_WRITE(msg, num)) { if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || - (msg[0].addr == state->af9033_config[1].i2c_addr)) { + } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || + (msg[0].addr == state->af9033_i2c_addr[1]) || + (state->chip_type == 0x9135)) { /* 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) + if (msg[0].addr == state->af9033_i2c_addr[1] || + msg[0].addr == (state->af9033_i2c_addr[1] >> 1)) reg |= 0x100000; ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { - /* I2C */ + /* I2C write */ u8 buf[MAX_XFER_SIZE]; struct usb_req req = { CMD_I2C_WR, 0, 5 + msg[0].len, buf, 0, NULL }; - if (5 + msg[0].len > sizeof(buf)) { - dev_warn(&d->udev->dev, - "%s: i2c xfer: len=%d is too big!\n", - KBUILD_MODNAME, msg[0].len); - ret = -EOPNOTSUPP; - goto unlock; + if (state->chip_type == 0x9306) { + req.cmd = CMD_GENERIC_I2C_WR; + req.wlen = 3 + msg[0].len; } + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = msg[0].addr << 1; - buf[2] = 0x00; /* reg addr len */ - buf[3] = 0x00; /* reg addr MSB */ - buf[4] = 0x00; /* reg addr LSB */ - memcpy(&buf[5], msg[0].buf, msg[0].len); + if (state->chip_type == 0x9306) { + buf[1] = 0x03; /* I2C bus */ + buf[2] = msg[0].addr << 1; + memcpy(&buf[3], msg[0].buf, msg[0].len); + } else { + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + } ret = af9035_ctrl_msg(d, &req); } - } else if (num == 1 && (msg[0].flags & I2C_M_RD)) { + } else if (AF9035_IS_I2C_XFER_READ(msg, num)) { if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; } else { - /* I2C */ + /* I2C read */ u8 buf[5]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), - buf, msg[0].len, msg[0].buf }; + buf, msg[0].len, msg[0].buf }; + + if (state->chip_type == 0x9306) { + req.cmd = CMD_GENERIC_I2C_RD; + req.wlen = 3; + } req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = msg[0].addr << 1; - buf[2] = 0x00; /* reg addr len */ - buf[3] = 0x00; /* reg addr MSB */ - buf[4] = 0x00; /* reg addr LSB */ + if (state->chip_type == 0x9306) { + buf[1] = 0x03; /* I2C bus */ + buf[2] = msg[0].addr << 1; + } else { + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + } ret = af9035_ctrl_msg(d, &req); } } else { /* * We support only three kind of I2C transactions: - * 1) 1 x read + 1 x write (repeated start) + * 1) 1 x write + 1 x read (repeated start) * 2) 1 x write * 3) 1 x read */ ret = -EOPNOTSUPP; } -unlock: mutex_unlock(&d->i2c_mutex); if (ret < 0) @@ -371,6 +503,9 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) else *name = AF9035_FIRMWARE_IT9135_V1; state->eeprom_addr = EEPROM_BASE_IT9135; + } else if (state->chip_type == 0x9306) { + *name = AF9035_FIRMWARE_IT9303; + state->eeprom_addr = EEPROM_BASE_IT9135; } else { *name = AF9035_FIRMWARE_AF9035; state->eeprom_addr = EEPROM_BASE_AF9035; @@ -536,6 +671,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d, u8 tmp; struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf }; + dev_dbg(&d->udev->dev, "%s:\n", __func__); /* @@ -579,7 +715,8 @@ static int af9035_download_firmware(struct dvb_usb_device *d, if (!tmp) tmp = 0x3a; - if (state->chip_type == 0x9135) { + if ((state->chip_type == 0x9135) || + (state->chip_type == 0x9306)) { ret = af9035_wr_reg(d, 0x004bfb, tmp); if (ret < 0) goto err; @@ -640,23 +777,26 @@ static int af9035_read_config(struct dvb_usb_device *d) u16 tmp16, addr; /* demod I2C "address" */ - state->af9033_config[0].i2c_addr = 0x38; - state->af9033_config[1].i2c_addr = 0x3a; + state->af9033_i2c_addr[0] = 0x38; + state->af9033_i2c_addr[1] = 0x3a; state->af9033_config[0].adc_multiplier = AF9033_ADC_MULTIPLIER_2X; state->af9033_config[1].adc_multiplier = AF9033_ADC_MULTIPLIER_2X; state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB; state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL; - /* eeprom memory mapped location */ if (state->chip_type == 0x9135) { + /* feed clock for integrated RF tuner */ + state->af9033_config[0].dyn0_clk = true; + state->af9033_config[1].dyn0_clk = true; + if (state->chip_version == 0x02) { state->af9033_config[0].tuner = AF9033_TUNER_IT9135_60; state->af9033_config[1].tuner = AF9033_TUNER_IT9135_60; - tmp16 = 0x00461d; + tmp16 = 0x00461d; /* eeprom memory mapped location */ } else { state->af9033_config[0].tuner = AF9033_TUNER_IT9135_38; state->af9033_config[1].tuner = AF9033_TUNER_IT9135_38; - tmp16 = 0x00461b; + tmp16 = 0x00461b; /* eeprom memory mapped location */ } /* check if eeprom exists */ @@ -668,8 +808,16 @@ static int af9035_read_config(struct dvb_usb_device *d) dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__); goto skip_eeprom; } + } else if (state->chip_type == 0x9306) { + /* + * IT930x is an USB bridge, only single demod-single tuner + * configurations seen so far. + */ + return 0; } + + /* check if there is dual tuners */ ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp); if (ret < 0) @@ -690,7 +838,7 @@ static int af9035_read_config(struct dvb_usb_device *d) goto err; if (tmp) - state->af9033_config[1].i2c_addr = tmp; + state->af9033_i2c_addr[1] = tmp; dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", __func__, tmp); @@ -799,25 +947,6 @@ static int af9035_read_config(struct dvb_usb_device *d) addr += 0x10; /* shift for the 2nd tuner params */ } - /* - * These AVerMedia devices has a bad EEPROM content :-( - * Override some wrong values here. - */ - if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) { - switch (le16_to_cpu(d->udev->descriptor.idProduct)) { - case USB_PID_AVERMEDIA_A835B_1835: - case USB_PID_AVERMEDIA_A835B_2835: - case USB_PID_AVERMEDIA_A835B_3835: - dev_info(&d->udev->dev, - "%s: overriding tuner from %02x to %02x\n", - KBUILD_MODNAME, state->af9033_config[0].tuner, - AF9033_TUNER_IT9135_60); - - state->af9033_config[0].tuner = AF9033_TUNER_IT9135_60; - break; - } - } - skip_eeprom: /* get demod clock */ ret = af9035_rd_reg(d, 0x00d800, &tmp); @@ -990,6 +1119,7 @@ static int af9035_frontend_callback(void *adapter_priv, int component, static int af9035_get_adapter_count(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); + return state->dual_mode + 1; } @@ -998,7 +1128,8 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); int ret; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); if (!state->af9033_config[adap->id].tuner) { /* unsupported tuner */ @@ -1006,9 +1137,13 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) goto err; } - /* attach demodulator */ - adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id], - &d->i2c_adap, &state->ops); + state->af9033_config[adap->id].fe = &adap->fe[0]; + state->af9033_config[adap->id].ops = &state->ops; + ret = af9035_add_i2c_dev(d, "af9033", state->af9033_i2c_addr[adap->id], + &state->af9033_config[adap->id], &d->i2c_adap); + if (ret) + goto err; + if (adap->fe[0] == NULL) { ret = -ENODEV; goto err; @@ -1026,6 +1161,78 @@ err: return ret; } +static int it930x_frontend_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 si2168_config si2168_config; + struct i2c_adapter *adapter; + + dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &adap->fe[0]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + + state->af9033_config[adap->id].fe = &adap->fe[0]; + state->af9033_config[adap->id].ops = &state->ops; + ret = af9035_add_i2c_dev(d, "si2168", 0x67, &si2168_config, + &d->i2c_adap); + if (ret) + goto err; + + if (adap->fe[0] == NULL) { + ret = -ENODEV; + goto err; + } + state->i2c_adapter_demod = adapter; + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_frontend_detach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int demod2; + + dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + + /* + * For dual tuner devices we have to resolve 2nd demod client, as there + * is two different kind of tuner drivers; one is using I2C binding + * and the other is using DVB attach/detach binding. + */ + switch (state->af9033_config[adap->id].tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + demod2 = 2; + break; + default: + demod2 = 1; + } + + if (adap->id == 1) { + if (state->i2c_client[demod2]) + af9035_del_i2c_dev(d); + } else if (adap->id == 0) { + if (state->i2c_client[0]) + af9035_del_i2c_dev(d); + } + + return 0; +} + static struct tua9001_config af9035_tua9001_config = { .i2c_addr = 0x60, }; @@ -1084,7 +1291,8 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_frontend *fe; struct i2c_msg msg[1]; u8 tuner_addr; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] @@ -1243,14 +1451,53 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) case AF9033_TUNER_IT9135_38: case AF9033_TUNER_IT9135_51: case AF9033_TUNER_IT9135_52: + { + struct it913x_config it913x_config = { + .fe = adap->fe[0], + .chip_ver = 1, + }; + + if (state->dual_mode) { + if (adap->id == 0) + it913x_config.role = IT913X_ROLE_DUAL_MASTER; + else + it913x_config.role = IT913X_ROLE_DUAL_SLAVE; + } + + ret = af9035_add_i2c_dev(d, "it913x", + state->af9033_i2c_addr[adap->id] >> 1, + &it913x_config, &d->i2c_adap); + if (ret) + goto err; + + fe = adap->fe[0]; + break; + } case AF9033_TUNER_IT9135_60: case AF9033_TUNER_IT9135_61: case AF9033_TUNER_IT9135_62: - /* attach tuner */ - fe = dvb_attach(it913x_attach, adap->fe[0], &d->i2c_adap, - state->af9033_config[adap->id].i2c_addr, - state->af9033_config[0].tuner); + { + struct it913x_config it913x_config = { + .fe = adap->fe[0], + .chip_ver = 2, + }; + + if (state->dual_mode) { + if (adap->id == 0) + it913x_config.role = IT913X_ROLE_DUAL_MASTER; + else + it913x_config.role = IT913X_ROLE_DUAL_SLAVE; + } + + ret = af9035_add_i2c_dev(d, "it913x", + state->af9033_i2c_addr[adap->id] >> 1, + &it913x_config, &d->i2c_adap); + if (ret) + goto err; + + fe = adap->fe[0]; break; + } default: fe = NULL; } @@ -1268,6 +1515,119 @@ err: return ret; } +static int it930x_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 si2157_config si2157_config; + + dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + + /* I2C master bus 2 clock speed 300k */ + ret = af9035_wr_reg(d, 0x00f6a7, 0x07); + if (ret < 0) + goto err; + + /* I2C master bus 1,3 clock speed 300k */ + ret = af9035_wr_reg(d, 0x00f103, 0x07); + if (ret < 0) + goto err; + + /* set gpio11 low */ + ret = af9035_wr_reg_mask(d, 0xd8d4, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d5, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d3, 0x01, 0x01); + if (ret < 0) + goto err; + + /* Tuner enable using gpiot2_en, gpiot2_on and gpiot2_o (reset) */ + ret = af9035_wr_reg_mask(d, 0xd8b8, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8b9, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8b7, 0x00, 0x01); + if (ret < 0) + goto err; + + msleep(200); + + ret = af9035_wr_reg_mask(d, 0xd8b7, 0x01, 0x01); + if (ret < 0) + goto err; + + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = adap->fe[0]; + ret = af9035_add_i2c_dev(d, "si2157", 0x63, + &si2157_config, state->i2c_adapter_demod); + + if (ret) + goto err; + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + + +static int it930x_tuner_detach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + + dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + + if (adap->id == 1) { + if (state->i2c_client[3]) + af9035_del_i2c_dev(d); + } else if (adap->id == 0) { + if (state->i2c_client[1]) + af9035_del_i2c_dev(d); + } + + return 0; +} + + +static int af9035_tuner_detach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + + dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + + switch (state->af9033_config[adap->id].tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + if (adap->id == 1) { + if (state->i2c_client[3]) + af9035_del_i2c_dev(d); + } else if (adap->id == 0) { + if (state->i2c_client[1]) + af9035_del_i2c_dev(d); + } + } + + return 0; +} + static int af9035_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); @@ -1315,6 +1675,89 @@ err: return ret; } +static int it930x_init(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + int ret, i; + u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 816) * 188 / 4; + u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; + struct reg_val_mask tab[] = { + { 0x00da1a, 0x00, 0x01 }, /* ignore_sync_byte */ + { 0x00f41f, 0x04, 0x04 }, /* dvbt_inten */ + { 0x00da10, 0x00, 0x01 }, /* mpeg_full_speed */ + { 0x00f41a, 0x01, 0x01 }, /* dvbt_en */ + { 0x00da1d, 0x01, 0x01 }, /* mp2_sw_rst, reset EP4 */ + { 0x00dd11, 0x00, 0x20 }, /* ep4_tx_en, disable EP4 */ + { 0x00dd13, 0x00, 0x20 }, /* ep4_tx_nak, disable EP4 NAK */ + { 0x00dd11, 0x20, 0x20 }, /* ep4_tx_en, enable EP4 */ + { 0x00dd11, 0x00, 0x40 }, /* ep5_tx_en, disable EP5 */ + { 0x00dd13, 0x00, 0x40 }, /* ep5_tx_nak, disable EP5 NAK */ + { 0x00dd11, state->dual_mode << 6, 0x40 }, /* enable EP5 */ + { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0c, packet_size, 0xff}, + { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0d, packet_size, 0xff }, + { 0x00da1d, 0x00, 0x01 }, /* mp2_sw_rst, disable */ + { 0x00d833, 0x01, 0xff }, /* slew rate ctrl: slew rate boosts */ + { 0x00d830, 0x00, 0xff }, /* Bit 0 of output driving control */ + { 0x00d831, 0x01, 0xff }, /* Bit 1 of output driving control */ + { 0x00d832, 0x00, 0xff }, /* Bit 2 of output driving control */ + + /* suspend gpio1 for TS-C */ + { 0x00d8b0, 0x01, 0xff }, /* gpio1 */ + { 0x00d8b1, 0x01, 0xff }, /* gpio1 */ + { 0x00d8af, 0x00, 0xff }, /* gpio1 */ + + /* suspend gpio7 for TS-D */ + { 0x00d8c4, 0x01, 0xff }, /* gpio7 */ + { 0x00d8c5, 0x01, 0xff }, /* gpio7 */ + { 0x00d8c3, 0x00, 0xff }, /* gpio7 */ + + /* suspend gpio13 for TS-B */ + { 0x00d8dc, 0x01, 0xff }, /* gpio13 */ + { 0x00d8dd, 0x01, 0xff }, /* gpio13 */ + { 0x00d8db, 0x00, 0xff }, /* gpio13 */ + + /* suspend gpio14 for TS-E */ + { 0x00d8e4, 0x01, 0xff }, /* gpio14 */ + { 0x00d8e5, 0x01, 0xff }, /* gpio14 */ + { 0x00d8e3, 0x00, 0xff }, /* gpio14 */ + + /* suspend gpio15 for TS-A */ + { 0x00d8e8, 0x01, 0xff }, /* gpio15 */ + { 0x00d8e9, 0x01, 0xff }, /* gpio15 */ + { 0x00d8e7, 0x00, 0xff }, /* gpio15 */ + + { 0x00da58, 0x00, 0x01 }, /* ts_in_src, serial */ + { 0x00da73, 0x01, 0xff }, /* ts0_aggre_mode */ + { 0x00da78, 0x47, 0xff }, /* ts0_sync_byte */ + { 0x00da4c, 0x01, 0xff }, /* ts0_en */ + { 0x00da5a, 0x1f, 0xff }, /* ts_fail_ignore */ + }; + + dev_dbg(&d->udev->dev, + "%s: USB speed=%d frame_size=%04x packet_size=%02x\n", + __func__, d->udev->speed, frame_size, packet_size); + + /* init endpoints */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9035_wr_reg_mask(d, tab[i].reg, + tab[i].val, tab[i].mask); + + if (ret < 0) + goto err; + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + + #if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { @@ -1409,6 +1852,7 @@ static int af9035_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, struct usb_data_stream_properties *stream) { struct dvb_usb_device *d = fe_to_d(fe); + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id); if (d->udev->speed == USB_SPEED_FULL) @@ -1486,7 +1930,9 @@ static const struct dvb_usb_device_properties af9035_props = { .i2c_algo = &af9035_i2c_algo, .read_config = af9035_read_config, .frontend_attach = af9035_frontend_attach, + .frontend_detach = af9035_frontend_detach, .tuner_attach = af9035_tuner_attach, + .tuner_detach = af9035_tuner_detach, .init = af9035_init, .get_rc_config = af9035_get_rc_config, .get_stream_config = af9035_get_stream_config, @@ -1515,6 +1961,37 @@ static const struct dvb_usb_device_properties af9035_props = { }, }; +static const struct dvb_usb_device_properties it930x_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .identify_state = af9035_identify_state, + .download_firmware = af9035_download_firmware, + + .i2c_algo = &af9035_i2c_algo, + .read_config = af9035_read_config, + .frontend_attach = it930x_frontend_attach, + .frontend_detach = af9035_frontend_detach, + .tuner_attach = it930x_tuner_attach, + .tuner_detach = it930x_tuner_detach, + .init = it930x_init, + .get_stream_config = af9035_get_stream_config, + + .get_adapter_count = af9035_get_adapter_count, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x84, 4, 816 * 188), + }, { + .stream = DVB_USB_STREAM_BULK(0x85, 4, 816 * 188), + }, + }, +}; + static const struct usb_device_id af9035_id_table[] = { /* AF9035 devices */ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035, @@ -1568,17 +2045,21 @@ static const struct usb_device_id af9035_id_table[] = { { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2, &af9035_props, "Digital Dual TV Receiver CTVDIGDUAL_V2", RC_MAP_IT913X_V1) }, + /* IT930x devices */ + { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9303, + &it930x_props, "ITE 9303 Generic", NULL) }, /* XXX: that same ID [0ccd:0099] is used by af9015 driver too */ { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099, - &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) }, + &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", + NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a05, &af9035_props, "Leadtek WinFast DTV Dongle Dual", NULL) }, { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xf900, &af9035_props, "Hauppauge WinTV-MiniStick 2", NULL) }, { DVB_USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_78E, - &af9035_props, "PCTV 78e", RC_MAP_IT913X_V1) }, + &af9035_props, "PCTV AndroiDTV (78e)", RC_MAP_IT913X_V1) }, { DVB_USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_79E, - &af9035_props, "PCTV 79e", RC_MAP_IT913X_V2) }, + &af9035_props, "PCTV microStick (79e)", RC_MAP_IT913X_V2) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); @@ -1603,3 +2084,4 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(AF9035_FIRMWARE_AF9035); MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V1); MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V2); +MODULE_FIRMWARE(AF9035_FIRMWARE_IT9303); diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index c21902f..416a97f 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -30,7 +30,9 @@ #include "mxl5007t.h" #include "tda18218.h" #include "fc2580.h" -#include "tuner_it913x.h" +#include "it913x.h" +#include "si2168.h" +#include "si2157.h" struct reg_val { u32 reg; @@ -61,9 +63,12 @@ struct state { u16 chip_type; u8 dual_mode:1; u16 eeprom_addr; + u8 af9033_i2c_addr[2]; struct af9033_config af9033_config[2]; - struct af9033_ops ops; + #define AF9035_I2C_CLIENT_MAX 4 + struct i2c_client *i2c_client[AF9035_I2C_CLIENT_MAX]; + struct i2c_adapter *i2c_adapter_demod; }; static const u32 clock_lut_af9035[] = { @@ -97,6 +102,7 @@ static const u32 clock_lut_it9135[] = { #define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw" #define AF9035_FIRMWARE_IT9135_V1 "dvb-usb-it9135-01.fw" #define AF9035_FIRMWARE_IT9135_V2 "dvb-usb-it9135-02.fw" +#define AF9035_FIRMWARE_IT9303 "dvb-usb-it9303-01.fw" /* * eeprom is memory mapped as read only. Writing that memory mapped address @@ -138,5 +144,7 @@ static const u32 clock_lut_it9135[] = { #define CMD_FW_DL_BEGIN 0x24 #define CMD_FW_DL_END 0x25 #define CMD_FW_SCATTER_WR 0x29 +#define CMD_GENERIC_I2C_RD 0x2a +#define CMD_GENERIC_I2C_WR 0x2b #endif diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index e4a2382..d3c5f23 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -332,7 +332,6 @@ static struct tda10023_config anysee_tda10023_tda18212_config = { }; static struct tda18212_config anysee_tda18212_config = { - .i2c_address = (0xc0 >> 1), .if_dvbt_6 = 4150, .if_dvbt_7 = 4150, .if_dvbt_8 = 4150, @@ -340,7 +339,6 @@ static struct tda18212_config anysee_tda18212_config = { }; static struct tda18212_config anysee_tda18212_config2 = { - .i2c_address = 0x60 /* (0xc0 >> 1) */, .if_dvbt_6 = 3550, .if_dvbt_7 = 3700, .if_dvbt_8 = 4150, @@ -632,6 +630,92 @@ error: return ret; } +static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, + void *platform_data) +{ + int ret, num; + struct anysee_state *state = d_to_priv(d); + struct i2c_client *client; + struct i2c_adapter *adapter = &d->i2c_adap; + struct i2c_board_info board_info = { + .addr = addr, + .platform_data = platform_data, + }; + + strlcpy(board_info.type, type, I2C_NAME_SIZE); + + /* find first free client */ + for (num = 0; num < ANYSEE_I2C_CLIENT_MAX; num++) { + if (state->i2c_client[num] == NULL) + break; + } + + dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + + if (num == ANYSEE_I2C_CLIENT_MAX) { + dev_err(&d->udev->dev, "%s: I2C client out of index\n", + KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + request_module(board_info.type); + + /* register I2C device */ + client = i2c_new_device(adapter, &board_info); + if (client == NULL || client->dev.driver == NULL) { + ret = -ENODEV; + goto err; + } + + /* increase I2C driver usage count */ + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + ret = -ENODEV; + goto err; + } + + state->i2c_client[num] = client; + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static void anysee_del_i2c_dev(struct dvb_usb_device *d) +{ + int num; + struct anysee_state *state = d_to_priv(d); + struct i2c_client *client; + + /* find last used client */ + num = ANYSEE_I2C_CLIENT_MAX; + while (num--) { + if (state->i2c_client[num] != NULL) + break; + } + + dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + + if (num == -1) { + dev_err(&d->udev->dev, "%s: I2C client out of index\n", + KBUILD_MODNAME); + goto err; + } + + client = state->i2c_client[num]; + + /* decrease I2C driver usage count */ + module_put(client->dev.driver->owner); + + /* unregister I2C device */ + i2c_unregister_device(client); + + state->i2c_client[num] = NULL; +err: + dev_dbg(&d->udev->dev, "%s: failed\n", __func__); +} + static int anysee_frontend_attach(struct dvb_usb_adapter *adap) { struct anysee_state *state = adap_to_priv(adap); @@ -640,12 +724,12 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) u8 tmp; struct i2c_msg msg[2] = { { - .addr = anysee_tda18212_config.i2c_address, + .addr = 0x60, .flags = 0, .len = 1, .buf = "\x00", }, { - .addr = anysee_tda18212_config.i2c_address, + .addr = 0x60, .flags = I2C_M_RD, .len = 1, .buf = &tmp, @@ -723,9 +807,11 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* probe TDA18212 */ tmp = 0; ret = i2c_transfer(&d->i2c_adap, msg, 2); - if (ret == 2 && tmp == 0xc7) + if (ret == 2 && tmp == 0xc7) { dev_dbg(&d->udev->dev, "%s: TDA18212 found\n", __func__); + state->has_tda18212 = true; + } else tmp = 0; @@ -939,46 +1025,63 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) * fails attach old simple PLL. */ /* attach tuner */ - fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, - &anysee_tda18212_config); + if (state->has_tda18212) { + struct tda18212_config tda18212_config = + anysee_tda18212_config; - if (fe && adap->fe[1]) { - /* attach tuner for 2nd FE */ - fe = dvb_attach(tda18212_attach, adap->fe[1], - &d->i2c_adap, &anysee_tda18212_config); - break; - } else if (fe) { - break; - } - - /* attach tuner */ - fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1), - &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + tda18212_config.fe = adap->fe[0]; + ret = anysee_add_i2c_dev(d, "tda18212", 0x60, + &tda18212_config); + if (ret) + goto err; + + /* copy tuner ops for 2nd FE as tuner is shared */ + if (adap->fe[1]) { + adap->fe[1]->tuner_priv = + adap->fe[0]->tuner_priv; + memcpy(&adap->fe[1]->ops.tuner_ops, + &adap->fe[0]->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } - if (fe && adap->fe[1]) { - /* attach tuner for 2nd FE */ - fe = dvb_attach(dvb_pll_attach, adap->fe[1], + return 0; + } else { + /* attach tuner */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1), &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + + if (fe && adap->fe[1]) { + /* attach tuner for 2nd FE */ + fe = dvb_attach(dvb_pll_attach, adap->fe[1], + (0xc0 >> 1), &d->i2c_adap, + DVB_PLL_SAMSUNG_DTOS403IH102A); + } } break; case ANYSEE_HW_508TC: /* 18 */ case ANYSEE_HW_508PTC: /* 21 */ + { /* E7 TC */ /* E7 PTC */ + struct tda18212_config tda18212_config = anysee_tda18212_config; - /* attach tuner */ - fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, - &anysee_tda18212_config); - - if (fe) { - /* attach tuner for 2nd FE */ - fe = dvb_attach(tda18212_attach, adap->fe[1], - &d->i2c_adap, &anysee_tda18212_config); + tda18212_config.fe = adap->fe[0]; + ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config); + if (ret) + goto err; + + /* copy tuner ops for 2nd FE as tuner is shared */ + if (adap->fe[1]) { + adap->fe[1]->tuner_priv = adap->fe[0]->tuner_priv; + memcpy(&adap->fe[1]->ops.tuner_ops, + &adap->fe[0]->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); } - break; + return 0; + } case ANYSEE_HW_508S2: /* 19 */ case ANYSEE_HW_508PS2: /* 22 */ /* E7 S2 */ @@ -997,13 +1100,18 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) break; case ANYSEE_HW_508T2C: /* 20 */ + { /* E7 T2C */ + struct tda18212_config tda18212_config = + anysee_tda18212_config2; - /* attach tuner */ - fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, - &anysee_tda18212_config2); + tda18212_config.fe = adap->fe[0]; + ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config); + if (ret) + goto err; - break; + return 0; + } default: fe = NULL; } @@ -1012,7 +1120,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) ret = 0; else ret = -ENODEV; - +err: return ret; } @@ -1270,6 +1378,11 @@ static int anysee_init(struct dvb_usb_device *d) static void anysee_exit(struct dvb_usb_device *d) { + struct anysee_state *state = d_to_priv(d); + + if (state->i2c_client[0]) + anysee_del_i2c_dev(d); + return anysee_ci_release(d); } diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h index 8f426d9..3ca2bca 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.h +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -55,8 +55,11 @@ struct anysee_state { u8 buf[64]; u8 seq; u8 hw; /* PCB ID */ + #define ANYSEE_I2C_CLIENT_MAX 1 + struct i2c_client *i2c_client[ANYSEE_I2C_CLIENT_MAX]; u8 fe_id:1; /* frondend ID */ u8 has_ci:1; + u8 has_tda18212:1; u8 ci_attached:1; struct dvb_ca_en50221 ci; unsigned long ci_cam_ready; /* jiffies */ diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 124b4ba..14e111e 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -214,6 +214,7 @@ struct dvb_usb_adapter_properties { * @read_config: called to resolve device configuration * @read_mac_address: called to resolve adapter mac-address * @frontend_attach: called to attach the possible frontends + * @frontend_detach: called to detach the possible frontends * @tuner_attach: called to attach the possible tuners * @frontend_ctrl: called to power on/off active frontend * @streaming_ctrl: called to start/stop the usb streaming of adapter @@ -254,7 +255,9 @@ struct dvb_usb_device_properties { int (*read_config) (struct dvb_usb_device *d); int (*read_mac_address) (struct dvb_usb_adapter *, u8 []); int (*frontend_attach) (struct dvb_usb_adapter *); + int (*frontend_detach)(struct dvb_usb_adapter *); int (*tuner_attach) (struct dvb_usb_adapter *); + int (*tuner_detach)(struct dvb_usb_adapter *); int (*frontend_ctrl) (struct dvb_frontend *, int); int (*streaming_ctrl) (struct dvb_frontend *, int); int (*init) (struct dvb_usb_device *); 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 2e90310..1950f37 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -21,7 +21,7 @@ #include "dvb_usb_common.h" -int dvb_usbv2_disable_rc_polling; +static int dvb_usbv2_disable_rc_polling; module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644); MODULE_PARM_DESC(disable_rc_polling, "disable remote control polling (default: 0)"); @@ -664,9 +664,10 @@ err: static int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap) { - int i; - dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__, - adap->id); + int ret, i; + struct dvb_usb_device *d = adap_to_d(adap); + + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id); for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { if (adap->fe[i]) { @@ -675,6 +676,23 @@ static int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap) } } + if (d->props->tuner_detach) { + ret = d->props->tuner_detach(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: tuner_detach() failed=%d\n", + __func__, ret); + } + } + + if (d->props->frontend_detach) { + ret = d->props->frontend_detach(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, + "%s: frontend_detach() failed=%d\n", + __func__, ret); + } + } + return 0; } @@ -762,9 +780,9 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d) for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { if (d->adapter[i].props) { - dvb_usbv2_adapter_frontend_exit(&d->adapter[i]); dvb_usbv2_adapter_dvb_exit(&d->adapter[i]); dvb_usbv2_adapter_stream_exit(&d->adapter[i]); + dvb_usbv2_adapter_frontend_exit(&d->adapter[i]); } } diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c index 33ff97e..22bdce1 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -26,7 +26,7 @@ static int dvb_usb_v2_generic_io(struct dvb_usb_device *d, { int ret, actual_length; - if (!d || !wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint || + if (!wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint || !d->props->generic_bulk_ctrl_endpoint_response) { dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, -EINVAL); return -EINVAL; diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c new file mode 100644 index 0000000..34688c8 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -0,0 +1,460 @@ +/* + * Driver for DVBSky USB2.0 receiver + * + * Copyright (C) 2013 Max nibble <nibble.max@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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb_usb.h" +#include "m88ds3103.h" +#include "m88ts2022.h" + +#define DVBSKY_MSG_DELAY 0/*2000*/ +#define DVBSKY_BUF_LEN 64 + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct dvbsky_state { + struct mutex stream_mutex; + u8 ibuf[DVBSKY_BUF_LEN]; + u8 obuf[DVBSKY_BUF_LEN]; + u8 last_lock; + struct i2c_client *i2c_client_tuner; + + /* fe hook functions*/ + int (*fe_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); + int (*fe_read_status)(struct dvb_frontend *fe, + fe_status_t *status); +}; + +static int dvbsky_usb_generic_rw(struct dvb_usb_device *d, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + int ret; + struct dvbsky_state *state = d_to_priv(d); + + mutex_lock(&d->usb_mutex); + if (wlen != 0) + memcpy(state->obuf, wbuf, wlen); + + ret = dvb_usbv2_generic_rw_locked(d, state->obuf, wlen, + state->ibuf, rlen); + + if (!ret && (rlen != 0)) + memcpy(rbuf, state->ibuf, rlen); + + mutex_unlock(&d->usb_mutex); + return ret; +} + +static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff) +{ + struct dvbsky_state *state = d_to_priv(d); + int ret; + u8 obuf_pre[3] = { 0x37, 0, 0 }; + u8 obuf_post[3] = { 0x36, 3, 0 }; + + mutex_lock(&state->stream_mutex); + ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0); + if (!ret && onoff) { + msleep(20); + ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0); + } + mutex_unlock(&state->stream_mutex); + return ret; +} + +static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_device *d = fe_to_d(fe); + + return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1); +} + +/* GPIO */ +static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value) +{ + int ret; + u8 obuf[3], ibuf[2]; + + obuf[0] = 0x0e; + obuf[1] = gport; + obuf[2] = value; + ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1); + if (ret) + dev_err(&d->udev->dev, "%s: %s() failed=%d\n", + KBUILD_MODNAME, __func__, ret); + return ret; +} + +/* I2C */ +static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0; + u8 ibuf[64], obuf[64]; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) { + dev_err(&d->udev->dev, + "dvbsky_usb: too many i2c messages[%d] than 2.", num); + ret = -EOPNOTSUPP; + goto i2c_error; + } + + if (num == 1) { + if (msg[0].len > 60) { + dev_err(&d->udev->dev, + "dvbsky_usb: too many i2c bytes[%d] than 60.", + msg[0].len); + ret = -EOPNOTSUPP; + goto i2c_error; + } + if (msg[0].flags & I2C_M_RD) { + /* single read */ + obuf[0] = 0x09; + obuf[1] = 0; + obuf[2] = msg[0].len; + obuf[3] = msg[0].addr; + ret = dvbsky_usb_generic_rw(d, obuf, 4, + ibuf, msg[0].len + 1); + if (ret) + dev_err(&d->udev->dev, "%s: %s() failed=%d\n", + KBUILD_MODNAME, __func__, ret); + if (!ret) + memcpy(msg[0].buf, &ibuf[1], msg[0].len); + } else { + /* write */ + obuf[0] = 0x08; + obuf[1] = msg[0].addr; + obuf[2] = msg[0].len; + memcpy(&obuf[3], msg[0].buf, msg[0].len); + ret = dvbsky_usb_generic_rw(d, obuf, + msg[0].len + 3, ibuf, 1); + if (ret) + dev_err(&d->udev->dev, "%s: %s() failed=%d\n", + KBUILD_MODNAME, __func__, ret); + } + } else { + if ((msg[0].len > 60) || (msg[1].len > 60)) { + dev_err(&d->udev->dev, + "dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.", + msg[0].len, msg[1].len); + ret = -EOPNOTSUPP; + goto i2c_error; + } + /* write then read */ + obuf[0] = 0x09; + obuf[1] = msg[0].len; + obuf[2] = msg[1].len; + obuf[3] = msg[0].addr; + memcpy(&obuf[4], msg[0].buf, msg[0].len); + ret = dvbsky_usb_generic_rw(d, obuf, + msg[0].len + 4, ibuf, msg[1].len + 1); + if (ret) + dev_err(&d->udev->dev, "%s: %s() failed=%d\n", + KBUILD_MODNAME, __func__, ret); + + if (!ret) + memcpy(msg[1].buf, &ibuf[1], msg[1].len); + } +i2c_error: + mutex_unlock(&d->i2c_mutex); + return (ret) ? ret : num; +} + +static u32 dvbsky_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dvbsky_i2c_algo = { + .master_xfer = dvbsky_i2c_xfer, + .functionality = dvbsky_i2c_func, +}; + +#if IS_ENABLED(CONFIG_RC_CORE) +static int dvbsky_rc_query(struct dvb_usb_device *d) +{ + u32 code = 0xffff, scancode; + u8 rc5_command, rc5_system; + u8 obuf[2], ibuf[2], toggle; + int ret; + + obuf[0] = 0x10; + ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2); + if (ret) + dev_err(&d->udev->dev, "%s: %s() failed=%d\n", + KBUILD_MODNAME, __func__, ret); + if (ret == 0) + code = (ibuf[0] << 8) | ibuf[1]; + if (code != 0xffff) { + dev_dbg(&d->udev->dev, "rc code: %x\n", code); + rc5_command = code & 0x3F; + rc5_system = (code & 0x7C0) >> 6; + toggle = (code & 0x800) ? 1 : 0; + scancode = rc5_system << 8 | rc5_command; + rc_keydown(d->rc_dev, RC_TYPE_RC5, scancode, toggle); + } + return 0; +} + +static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + rc->allowed_protos = RC_BIT_RC5; + rc->query = dvbsky_rc_query; + rc->interval = 300; + return 0; +} +#else + #define dvbsky_get_rc_config NULL +#endif + +static int dvbsky_usb_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct dvbsky_state *state = d_to_priv(d); + u8 value; + + if (voltage == SEC_VOLTAGE_OFF) + value = 0; + else + value = 1; + dvbsky_gpio_ctrl(d, 0x80, value); + + return state->fe_set_voltage(fe, voltage); +} + +static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6]) +{ + struct dvb_usb_device *d = adap_to_d(adap); + u8 obuf[] = { 0x1e, 0x00 }; + u8 ibuf[6] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = 0x51, + .flags = 0, + .buf = obuf, + .len = 2, + }, { + .addr = 0x51, + .flags = I2C_M_RD, + .buf = ibuf, + .len = 6, + } + }; + + if (i2c_transfer(&d->i2c_adap, msg, 2) == 2) + memcpy(mac, ibuf, 6); + + dev_info(&d->udev->dev, "dvbsky_usb MAC address=%pM\n", mac); + + return 0; +} + +static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct dvbsky_state *state = d_to_priv(d); + int ret; + + ret = state->fe_read_status(fe, status); + + /* it need resync slave fifo when signal change from unlock to lock.*/ + if ((*status & FE_HAS_LOCK) && (!state->last_lock)) + dvbsky_stream_ctrl(d, 1); + + state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0; + return ret; +} + +static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_CI, + .ts_clk = 16000, + .ts_clk_pol = 0, + .agc = 0x99, + .lnb_hv_pol = 1, + .lnb_en_pol = 1, +}; + +static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) +{ + struct dvbsky_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int ret = 0; + /* demod I2C adapter */ + struct i2c_adapter *i2c_adapter; + struct i2c_client *client; + struct i2c_board_info info; + struct m88ts2022_config m88ts2022_config = { + .clock = 27000000, + }; + memset(&info, 0, sizeof(struct i2c_board_info)); + + /* attach demod */ + adap->fe[0] = dvb_attach(m88ds3103_attach, + &dvbsky_s960_m88ds3103_config, + &d->i2c_adap, + &i2c_adapter); + if (!adap->fe[0]) { + dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n"); + ret = -ENODEV; + goto fail_attach; + } + + /* attach tuner */ + m88ts2022_config.fe = adap->fe[0]; + strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &m88ts2022_config; + request_module("m88ts2022"); + client = i2c_new_device(i2c_adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + dvb_frontend_detach(adap->fe[0]); + ret = -ENODEV; + goto fail_attach; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(adap->fe[0]); + ret = -ENODEV; + goto fail_attach; + } + + /* delegate signal strength measurement to tuner */ + adap->fe[0]->ops.read_signal_strength = + adap->fe[0]->ops.tuner_ops.get_rf_strength; + + /* hook fe: need to resync the slave fifo when signal locks. */ + state->fe_read_status = adap->fe[0]->ops.read_status; + adap->fe[0]->ops.read_status = dvbsky_usb_read_status; + + /* hook fe: LNB off/on is control by Cypress usb chip. */ + state->fe_set_voltage = adap->fe[0]->ops.set_voltage; + adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage; + + state->i2c_client_tuner = client; + +fail_attach: + return ret; +} + +static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) +{ + dvbsky_gpio_ctrl(d, 0x04, 1); + msleep(20); + dvbsky_gpio_ctrl(d, 0x83, 0); + dvbsky_gpio_ctrl(d, 0xc0, 1); + msleep(100); + dvbsky_gpio_ctrl(d, 0x83, 1); + dvbsky_gpio_ctrl(d, 0xc0, 0); + msleep(50); + + return WARM; +} + +static int dvbsky_init(struct dvb_usb_device *d) +{ + struct dvbsky_state *state = d_to_priv(d); + + /* use default interface */ + /* + ret = usb_set_interface(d->udev, 0, 0); + if (ret) + return ret; + */ + mutex_init(&state->stream_mutex); + + state->last_lock = 0; + + return 0; +} + +static void dvbsky_exit(struct dvb_usb_device *d) +{ + struct dvbsky_state *state = d_to_priv(d); + struct i2c_client *client; + + client = state->i2c_client_tuner; + /* remove I2C tuner */ + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties dvbsky_s960_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct dvbsky_state), + + .generic_bulk_ctrl_endpoint = 0x01, + .generic_bulk_ctrl_endpoint_response = 0x81, + .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, + + .i2c_algo = &dvbsky_i2c_algo, + .frontend_attach = dvbsky_s960_attach, + .init = dvbsky_init, + .get_rc_config = dvbsky_get_rc_config, + .streaming_ctrl = dvbsky_streaming_ctrl, + .identify_state = dvbsky_identify_state, + .exit = dvbsky_exit, + .read_mac_address = dvbsky_read_mac_addr, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), + } + } +}; + +static const struct usb_device_id dvbsky_id_table[] = { + { DVB_USB_DEVICE(0x0572, 0x6831, + &dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) }, + { } +}; +MODULE_DEVICE_TABLE(usb, dvbsky_id_table); + +static struct usb_driver dvbsky_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = dvbsky_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(dvbsky_usb_driver); + +MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>"); +MODULE_DESCRIPTION("Driver for DVBSky USB"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index e332af7..9f2c545 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -1252,7 +1252,7 @@ static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, /* Turn PID filter on the fly by module option */ if (pid_filter == 2) { - adap->pid_filtering = 1; + adap->pid_filtering = true; adap->max_feed_count = 15; } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index b8a707e..c3447ea 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -31,11 +31,11 @@ module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level " "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))."); -int dvb_usb_mxl111sf_isoc; +static int dvb_usb_mxl111sf_isoc; module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); -int dvb_usb_mxl111sf_spi; +static int dvb_usb_mxl111sf_spi; module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644); MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)."); @@ -43,7 +43,7 @@ MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)."); #define ANT_PATH_EXTERNAL 1 #define ANT_PATH_INTERNAL 2 -int dvb_usb_mxl111sf_rfswitch = +static int dvb_usb_mxl111sf_rfswitch = #if 0 ANT_PATH_AUTO; #else @@ -887,7 +887,7 @@ static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C; } -struct i2c_algorithm mxl111sf_i2c_algo = { +static struct i2c_algorithm mxl111sf_i2c_algo = { .master_xfer = mxl111sf_i2c_xfer, .functionality = mxl111sf_i2c_func, #ifdef NEED_ALGO_CONTROL diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 10aef21..41d3eb9 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -130,7 +130,7 @@ config DVB_USB_CXUSB Medion MD95700 hybrid USB2.0 device. DViCO FusionHDTV (Bluebird) USB2.0 devices - TechnoTrend TVStick CT2-4400 + TechnoTrend TVStick CT2-4400 and CT2-4650 CI devices config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index af176b6..3f4361e 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -30,7 +30,7 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." DVB_USB_DEBUG_STATUS); /* enable obnoxious led */ -bool dvb_usb_af9005_led = 1; +bool dvb_usb_af9005_led = true; module_param_named(led, dvb_usb_af9005_led, bool, 0644); MODULE_PARM_DESC(led, "enable led (default: 1)."); diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 16bc579..356abb3 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -44,6 +44,7 @@ #include "atbm8830.h" #include "si2168.h" #include "si2157.h" +#include "sp2.h" /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 80 @@ -175,7 +176,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], for (i = 0; i < num; i++) { - if (d->udev->descriptor.idVendor == USB_VID_MEDION) + if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_MEDION) switch (msg[i].addr) { case 0x63: cxusb_gpio_tuner(d, 0); @@ -672,6 +673,70 @@ static struct rc_map_table rc_map_d680_dmb_table[] = { { 0x0025, KEY_POWER }, }; +static int cxusb_tt_ct2_4400_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + u8 wbuf[2]; + u8 rbuf[6]; + int ret; + struct i2c_msg msg[] = { + { + .addr = 0x51, + .flags = 0, + .buf = wbuf, + .len = 2, + }, { + .addr = 0x51, + .flags = I2C_M_RD, + .buf = rbuf, + .len = 6, + } + }; + + wbuf[0] = 0x1e; + wbuf[1] = 0x00; + ret = cxusb_i2c_xfer(&d->i2c_adap, msg, 2); + + if (ret == 2) { + memcpy(mac, rbuf, 6); + return 0; + } else { + if (ret < 0) + return ret; + return -EIO; + } +} + +static int cxusb_tt_ct2_4650_ci_ctrl(void *priv, u8 read, int addr, + u8 data, int *mem) +{ + struct dvb_usb_device *d = priv; + u8 wbuf[3]; + u8 rbuf[2]; + int ret; + + wbuf[0] = (addr >> 8) & 0xff; + wbuf[1] = addr & 0xff; + + if (read) { + ret = cxusb_ctrl_msg(d, CMD_SP2_CI_READ, wbuf, 2, rbuf, 2); + } else { + wbuf[2] = data; + ret = cxusb_ctrl_msg(d, CMD_SP2_CI_WRITE, wbuf, 3, rbuf, 1); + } + + if (ret) + goto err; + + if (read) + *mem = rbuf[1]; + + return 0; +err: + deb_info("%s: ci usb write returned %d\n", __func__, ret); + return ret; + +} + static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; @@ -1350,9 +1415,12 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *adapter; struct i2c_client *client_demod; struct i2c_client *client_tuner; + struct i2c_client *client_ci; struct i2c_board_info info; struct si2168_config si2168_config; struct si2157_config si2157_config; + struct sp2_config sp2_config; + u8 o[2], i; /* reset the tuner */ if (cxusb_tt_ct2_4400_gpio_tuner(d, 0) < 0) { @@ -1369,6 +1437,7 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap) /* attach frontend */ si2168_config.i2c_adapter = &adapter; si2168_config.fe = &adap->fe_adap[0].fe; + si2168_config.ts_mode = SI2168_TS_PARALLEL; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2168", I2C_NAME_SIZE); info.addr = 0x64; @@ -1408,6 +1477,48 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap) st->i2c_client_tuner = client_tuner; + /* initialize CI */ + if (d->udev->descriptor.idProduct == + USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) { + + memcpy(o, "\xc0\x01", 2); + cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + msleep(100); + + memcpy(o, "\xc0\x00", 2); + cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + msleep(100); + + memset(&sp2_config, 0, sizeof(sp2_config)); + sp2_config.dvb_adap = &adap->dvb_adap; + sp2_config.priv = d; + sp2_config.ci_control = cxusb_tt_ct2_4650_ci_ctrl; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "sp2", I2C_NAME_SIZE); + info.addr = 0x40; + info.platform_data = &sp2_config; + request_module(info.type); + client_ci = i2c_new_device(&d->i2c_adap, &info); + if (client_ci == NULL || client_ci->dev.driver == NULL) { + module_put(client_tuner->dev.driver->owner); + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + return -ENODEV; + } + if (!try_module_get(client_ci->dev.driver->owner)) { + i2c_unregister_device(client_ci); + module_put(client_tuner->dev.driver->owner); + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + return -ENODEV; + } + + st->i2c_client_ci = client_ci; + + } + return 0; } @@ -1537,6 +1648,13 @@ static void cxusb_disconnect(struct usb_interface *intf) struct cxusb_state *st = d->priv; struct i2c_client *client; + /* remove I2C client for CI */ + client = st->i2c_client_ci; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + /* remove I2C client for tuner */ client = st->i2c_client_tuner; if (client) { @@ -1576,6 +1694,7 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_TVSTICK_CT2_4400) }, + { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -2230,6 +2349,8 @@ static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = { .size_of_priv = sizeof(struct cxusb_state), .num_adapters = 1, + .read_mac_address = cxusb_tt_ct2_4400_read_mac_address, + .adapter = { { .num_frontends = 1, @@ -2265,13 +2386,18 @@ static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = { .rc_interval = 150, }, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "TechnoTrend TVStick CT2-4400", { NULL }, { &cxusb_table[20], NULL }, }, + { + "TechnoTrend TT-connect CT2-4650 CI", + { NULL }, + { &cxusb_table[21], NULL }, + }, } }; diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 527ff79..29f3e2e 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -28,10 +28,14 @@ #define CMD_ANALOG 0x50 #define CMD_DIGITAL 0x51 +#define CMD_SP2_CI_WRITE 0x70 +#define CMD_SP2_CI_READ 0x71 + struct cxusb_state { u8 gpio_write_state[3]; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; + struct i2c_client *i2c_client_ci; }; #endif diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index ce47d3f..e1757b8 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -220,12 +220,21 @@ static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config[2] = { }; static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = { - 60000, 30000, - 1, 8, 3, 1, 0, - 0, 0, 1, 1, 2, - (3 << 14) | (1 << 12) | (524 << 0), - 0, - 20452225, + .internal = 60000, + .sampling = 30000, + .pll_prediv = 1, + .pll_ratio = 8, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = 0, + .timf = 20452225, }; static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = { @@ -342,57 +351,57 @@ static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) /* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */ static struct dibx000_agc_config xc3028_agc_config = { - BAND_VHF | BAND_UHF, /* band_caps */ - + .band_caps = BAND_VHF | BAND_UHF, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ - (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | - (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ - - 712, /* inv_gain */ - 21, /* time_stabiliz */ - - 0, /* alpha_level */ - 118, /* thlock */ - - 0, /* wbd_inv */ - 2867, /* wbd_ref */ - 0, /* wbd_sel */ - 2, /* wbd_alpha */ - - 0, /* agc1_max */ - 0, /* agc1_min */ - 39718, /* agc2_max */ - 9930, /* agc2_min */ - 0, /* agc1_pt1 */ - 0, /* agc1_pt2 */ - 0, /* agc1_pt3 */ - 0, /* agc1_slope1 */ - 0, /* agc1_slope2 */ - 0, /* agc2_pt1 */ - 128, /* agc2_pt2 */ - 29, /* agc2_slope1 */ - 29, /* agc2_slope2 */ - - 17, /* alpha_mant */ - 27, /* alpha_exp */ - 23, /* beta_mant */ - 51, /* beta_exp */ - - 1, /* perform_agc_softsplit */ + .setup = (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), + .inv_gain = 712, + .time_stabiliz = 21, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 2867, + .wbd_sel = 0, + .wbd_alpha = 2, + .agc1_max = 0, + .agc1_min = 0, + .agc2_max = 39718, + .agc2_min = 9930, + .agc1_pt1 = 0, + .agc1_pt2 = 0, + .agc1_pt3 = 0, + .agc1_slope1 = 0, + .agc1_slope2 = 0, + .agc2_pt1 = 0, + .agc2_pt2 = 128, + .agc2_slope1 = 29, + .agc2_slope2 = 29, + .alpha_mant = 17, + .alpha_exp = 27, + .beta_mant = 23, + .beta_exp = 51, + .perform_agc_softsplit = 1, }; /* PLL Configuration for COFDM BW_MHz = 8.00 with external clock = 30.00 */ static struct dibx000_bandwidth_config xc3028_bw_config = { - 60000, 30000, /* internal, sampling */ - 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ - 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, - modulo */ - (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ - (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ - 20452225, /* timf */ - 30000000, /* xtal_hz */ + .internal = 60000, + .sampling = 30000, + .pll_prediv = 1, + .pll_ratio = 8, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 0, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ + .ifreq = (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ + .timf = 20452225, + .xtal_hz = 30000000, }; static struct dib7000p_config stk7700ph_dib7700_xc3028_config = { @@ -614,59 +623,55 @@ static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { }; static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = { - BAND_UHF | BAND_VHF, - + .band_caps = BAND_UHF | BAND_VHF, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ - (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) - | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), - - 712, - 41, - - 0, - 118, - - 0, - 4095, - 0, - 0, - - 42598, - 16384, - 42598, - 0, - - 0, - 137, - 255, - - 0, - 255, - - 0, - 0, - - 0, - 41, - - 15, - 25, - - 28, - 48, - - 0, + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), + .inv_gain = 712, + .time_stabiliz = 41, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 4095, + .wbd_sel = 0, + .wbd_alpha = 0, + .agc1_max = 42598, + .agc1_min = 16384, + .agc2_max = 42598, + .agc2_min = 0, + .agc1_pt1 = 0, + .agc1_pt2 = 137, + .agc1_pt3 = 255, + .agc1_slope1 = 0, + .agc1_slope2 = 255, + .agc2_pt1 = 0, + .agc2_pt2 = 0, + .agc2_slope1 = 0, + .agc2_slope2 = 41, + .alpha_mant = 15, + .alpha_exp = 25, + .beta_mant = 28, + .beta_exp = 48, + .perform_agc_softsplit = 0, }; static struct dibx000_bandwidth_config stk7700p_pll_config = { - 60000, 30000, - 1, 8, 3, 1, 0, - 0, 0, 1, 1, 0, - (3 << 14) | (1 << 12) | (524 << 0), - 60258167, - 20452225, - 30000000, + .internal = 60000, + .sampling = 30000, + .pll_prediv = 1, + .pll_ratio = 8, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 0, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = 60258167, + .timf = 20452225, + .xtal_hz = 30000000, }; static struct dib7000m_config stk7700p_dib7000m_config = { @@ -758,45 +763,36 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) /* DIB7070 generic */ static struct dibx000_agc_config dib7070_agc_config = { - BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ - (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) - | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), - - 600, - 10, - - 0, - 118, - - 0, - 3530, - 1, - 5, - - 65535, - 0, - - 65535, - 0, - - 0, - 40, - 183, - 206, - 255, - 72, - 152, - 88, - 90, - - 17, - 27, - 23, - 51, - - 0, + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + .inv_gain = 600, + .time_stabiliz = 10, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, + .agc1_max = 65535, + .agc1_min = 0, + .agc2_max = 65535, + .agc2_min = 0, + .agc1_pt1 = 0, + .agc1_pt2 = 40, + .agc1_pt3 = 183, + .agc1_slope1 = 206, + .agc1_slope2 = 255, + .agc2_pt1 = 72, + .agc2_pt2 = 152, + .agc2_slope1 = 88, + .agc2_slope2 = 90, + .alpha_mant = 17, + .alpha_exp = 27, + .beta_mant = 23, + .beta_exp = 51, + .perform_agc_softsplit = 0, }; static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) @@ -952,13 +948,22 @@ static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) } static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { - 60000, 15000, - 1, 20, 3, 1, 0, - 0, 0, 1, 1, 2, - (3 << 14) | (1 << 12) | (524 << 0), - (0 << 25) | 0, - 20452225, - 12000000, + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 20, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20452225, + .xtal_hz = 12000000, }; static struct dib7000p_config dib7070p_dib7000p_config = { @@ -1169,14 +1174,22 @@ static struct dibx000_agc_config dib807x_agc_config[2] = { }; static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = { - 60000, 15000, /* internal, sampling*/ - 1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/ - 0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core, - ADClkSrc, modulo */ - (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ - (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ - 18179755, /* timf*/ - 12000000, /* xtal_hz*/ + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 20, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + .sad_cfg = (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ + .ifreq = (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ + .timf = 18179755, + .xtal_hz = 12000000, }; static struct dib8000_config dib807x_dib8000_config[2] = { @@ -1921,13 +1934,22 @@ static struct dibx000_agc_config dib8096p_agc_config[2] = { }; static struct dibx000_bandwidth_config dib8096p_clock_config_12_mhz = { - 108000, 13500, - 1, 9, 1, 0, 0, - 0, 0, 0, 0, 2, - (3 << 14) | (1 << 12) | (524 << 0), - (0 << 25) | 0, - 20199729, - 12000000, + .internal = 108000, + .sampling = 13500, + .pll_prediv = 1, + .pll_ratio = 9, + .pll_range = 1, + .pll_reset = 0, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 0, + .ADClkSrc = 0, + .modulo = 2, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20199729, + .xtal_hz = 12000000, }; static struct dib8000_config tfe8096p_dib8000_config = { @@ -2724,13 +2746,22 @@ static struct dibx000_agc_config dib7090_agc_config[2] = { }; static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = { - 60000, 15000, - 1, 5, 0, 0, 0, - 0, 0, 1, 1, 2, - (3 << 14) | (1 << 12) | (524 << 0), - (0 << 25) | 0, - 20452225, - 15000000, + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 5, + .pll_range = 0, + .pll_reset = 0, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20452225, + .xtal_hz = 15000000, }; static struct dib7000p_config nim7090_dib7000p_config = { @@ -3498,14 +3529,22 @@ static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = { }; static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = { - 60000, 30000, /* internal, sampling */ - 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ - 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, */ - /* ADClkSrc, modulo */ - (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */ - 39370534, /* ifreq */ - 20452225, /* timf */ - 30000000 /* xtal */ + .internal = 60000, + .sampling = 30000, + .pll_prediv = 1, + .pll_ratio = 8, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 0, + .sad_cfg = (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */ + .ifreq = 39370534, + .timf = 20452225, + .xtal_hz = 30000000 }; /* FIXME: none of these inputs are validated yet */ diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index 6d68af0..ef3a8f7 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -258,8 +258,8 @@ static struct dib3000mc_config mod3000p_dib3000p_config = { int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) { - if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && - adap->dev->udev->descriptor.idProduct == + if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_LITEON_DVB_T_WARM) { msleep(1000); } @@ -297,8 +297,8 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *tun_i2c; // First IF calibration for Liteon Sticks - if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && - adap->dev->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) { + if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_LITEON_DVB_T_WARM) { dibusb_read_eeprom_byte(adap->dev,0x7E,&a); dibusb_read_eeprom_byte(adap->dev,0x7F,&b); @@ -310,8 +310,8 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) else warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); - } else if (adap->dev->udev->descriptor.idVendor == USB_VID_DIBCOM && - adap->dev->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) { + } else if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_DIBCOM && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_DIBCOM_MOD3001_WARM) { u8 desc; dibusb_read_eeprom_byte(adap->dev, 7, &desc); if (desc == 2) { diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 2add8c5..1a3df10 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -667,7 +667,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); dw210x_op_rw(d->udev, - udev->descriptor.idProduct == + le16_to_cpu(udev->descriptor.idProduct) == 0x7500 ? 0x92 : 0x90, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); @@ -1598,7 +1598,7 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; - switch (dev->descriptor.idProduct) { + switch (le16_to_cpu(dev->descriptor.idProduct)) { case 0x2101: ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev); if (ret != 0) { @@ -1641,7 +1641,7 @@ static int dw2102_load_firmware(struct usb_device *dev, ret = -EINVAL; } /* init registers */ - switch (dev->descriptor.idProduct) { + switch (le16_to_cpu(dev->descriptor.idProduct)) { case USB_PID_TEVII_S650: dw2104_properties.rc.core.rc_codes = RC_MAP_TEVII_NEC; case USB_PID_DW2104: @@ -1901,14 +1901,14 @@ static struct dvb_usb_device_properties s6x0_properties = { } }; -struct dvb_usb_device_properties *p1100; +static struct dvb_usb_device_properties *p1100; static struct dvb_usb_device_description d1100 = { "Prof 1100 USB ", {&dw2102_table[PROF_1100], NULL}, {NULL}, }; -struct dvb_usb_device_properties *s660; +static struct dvb_usb_device_properties *s660; static struct dvb_usb_device_description d660 = { "TeVii S660 USB", {&dw2102_table[TEVII_S660], NULL}, @@ -1927,14 +1927,14 @@ static struct dvb_usb_device_description d480_2 = { {NULL}, }; -struct dvb_usb_device_properties *p7500; +static struct dvb_usb_device_properties *p7500; static struct dvb_usb_device_description d7500 = { "Prof 7500 USB DVB-S2", {&dw2102_table[PROF_7500], NULL}, {NULL}, }; -struct dvb_usb_device_properties *s421; +static struct dvb_usb_device_properties *s421; static struct dvb_usb_device_description d421 = { "TeVii S421 PCI", {&dw2102_table[TEVII_S421], NULL}, diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c index 16ba90a..14a2119 100644 --- a/drivers/media/usb/dvb-usb/opera1.c +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -554,8 +554,8 @@ static int opera1_probe(struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); - if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && - udev->descriptor.idVendor == USB_VID_OPERA1 && + if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM && + le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 && opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0 ) { return -EINVAL; diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index bdfe896..d17618f 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -883,7 +883,7 @@ static int pctv452e_frontend_attach(struct dvb_usb_adapter *a) if (!a->fe_adap[0].fe) return -ENODEV; if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe, - &a->dev->i2c_adap)) == 0) + &a->dev->i2c_adap)) == NULL) err("Cannot attach lnbp22\n"); id = a->dev->desc->warm_ids[0]; @@ -900,7 +900,7 @@ static int pctv452e_tuner_attach(struct dvb_usb_adapter *a) if (!a->fe_adap[0].fe) return -ENODEV; if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config, - &a->dev->i2c_adap) == 0) { + &a->dev->i2c_adap) == NULL) { err("%s failed\n", __func__); return -ENODEV; } @@ -965,7 +965,7 @@ static struct dvb_usb_device_properties pctv452e_properties = { .cold_ids = { NULL, NULL }, /* this is a warm only device */ .warm_ids = { &pctv452e_usb_table[0], NULL } }, - { 0 }, + { NULL }, } }; @@ -1023,7 +1023,7 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { .cold_ids = { NULL, NULL }, .warm_ids = { &pctv452e_usb_table[2], NULL } }, - { 0 }, + { NULL }, } }; diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index e881ef7..957c7ae 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -268,7 +268,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { if (!mutex_trylock(&dev->lock)) - return -EAGAIN; + return -EAGAIN; } else mutex_lock(&dev->lock); @@ -893,7 +893,7 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; int err; - if (!dev->has_alsa_audio) { + if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) { /* This device does not support the extension (in this case the device is expecting the snd-usb-audio module or doesn't have analog audio support at all) */ @@ -975,7 +975,7 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; - if (!dev->has_alsa_audio) { + if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) { /* This device does not support the extension (in this case the device is expecting the snd-usb-audio module or doesn't have analog audio support at all) */ @@ -1003,7 +1003,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) if (dev == NULL) return 0; - if (!dev->has_alsa_audio) + if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; em28xx_info("Suspending audio extension"); @@ -1017,7 +1017,7 @@ static int em28xx_audio_resume(struct em28xx *dev) if (dev == NULL) return 0; - if (!dev->has_alsa_audio) + if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; em28xx_info("Resuming audio extension"); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 9da812b..71fa51e 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2246,7 +2246,7 @@ struct em28xx_board em28xx_boards[] = { }; EXPORT_SYMBOL_GPL(em28xx_boards); -const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); +static const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); /* table of devices that work with this driver */ struct usb_device_id em28xx_id_table[] = { @@ -2931,9 +2931,9 @@ static void request_module_async(struct work_struct *work) #if defined(CONFIG_MODULES) && defined(MODULE) if (dev->has_video) request_module("em28xx-v4l"); - if (dev->has_audio_class) + if (dev->usb_audio_type == EM28XX_USB_AUDIO_CLASS) request_module("snd-usb-audio"); - else if (dev->has_alsa_audio) + else if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR) request_module("em28xx-alsa"); if (dev->board.has_dvb) request_module("em28xx-dvb"); @@ -3098,16 +3098,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, } } - if (dev->chip_id == CHIP_ID_EM2870 || - dev->chip_id == CHIP_ID_EM2874 || - dev->chip_id == CHIP_ID_EM28174 || - dev->chip_id == CHIP_ID_EM28178) { - /* Digital only device - don't load any alsa module */ - dev->audio_mode.has_audio = false; - dev->has_audio_class = false; - dev->has_alsa_audio = false; - } - if (chip_name != default_chip_name) printk(KERN_INFO DRIVER_NAME ": chip ID is %s\n", chip_name); @@ -3190,7 +3180,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct usb_device *udev; struct em28xx *dev = NULL; int retval; - bool has_audio = false, has_video = false, has_dvb = false; + bool has_vendor_audio = false, has_video = false, has_dvb = false; int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3272,7 +3262,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, break; case 0x83: if (usb_endpoint_xfer_isoc(e)) { - has_audio = true; + has_vendor_audio = true; } else { printk(KERN_INFO DRIVER_NAME ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); @@ -3328,7 +3318,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } - if (!(has_audio || has_video || has_dvb)) { + if (!(has_vendor_audio || has_video || has_dvb)) { retval = -ENODEV; goto err_free; } @@ -3375,26 +3365,27 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; - dev->is_audio_only = has_audio && !(has_video || has_dvb); - dev->has_alsa_audio = has_audio; - dev->audio_mode.has_audio = has_audio; + dev->is_audio_only = has_vendor_audio && !(has_video || has_dvb); dev->has_video = has_video; dev->ifnum = ifnum; - /* Checks if audio is provided by some interface */ + if (has_vendor_audio) { + printk(KERN_INFO DRIVER_NAME ": Audio interface %i found %s\n", + ifnum, "(Vendor Class)"); + dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR; + } + /* Checks if audio is provided by a USB Audio Class interface */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { struct usb_interface *uif = udev->config->interface[i]; if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev->has_audio_class = 1; + if (has_vendor_audio) + em28xx_err("em28xx: device seems to have vendor AND usb audio class interfaces !\n" + "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n"); + dev->usb_audio_type = EM28XX_USB_AUDIO_CLASS; break; } } - 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", diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 523d7e9..b5e52fe 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -279,7 +279,7 @@ int em28xx_read_ac97(struct em28xx *dev, u8 reg) { int ret; u8 addr = (reg & 0x7f) | 0x80; - u16 val; + __le16 val; ret = em28xx_is_ac97_ready(dev); if (ret < 0) @@ -433,7 +433,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) int ret, i; u8 xclk; - if (!dev->audio_mode.has_audio) + if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) return 0; /* It is assumed that all devices use master volume for output. @@ -505,37 +505,48 @@ int em28xx_audio_setup(struct em28xx *dev) { int vid1, vid2, feat, cfg; u32 vid; + u8 i2s_samplerates; - if (!dev->audio_mode.has_audio) + if (dev->chip_id == CHIP_ID_EM2870 || + dev->chip_id == CHIP_ID_EM2874 || + dev->chip_id == CHIP_ID_EM28174 || + dev->chip_id == CHIP_ID_EM28178) { + /* Digital only device - don't load any alsa module */ + dev->int_audio_type = EM28XX_INT_AUDIO_NONE; + dev->usb_audio_type = EM28XX_USB_AUDIO_NONE; return 0; + } /* See how this device is configured */ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); em28xx_info("Config register raw data: 0x%02x\n", cfg); - if (cfg < 0) { - /* Register read error? */ - cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ + if (cfg < 0) { /* Register read error */ + /* Be conservative */ + dev->int_audio_type = EM28XX_INT_AUDIO_AC97; } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) { /* The device doesn't have vendor audio at all */ - dev->has_alsa_audio = false; - dev->audio_mode.has_audio = false; + dev->int_audio_type = EM28XX_INT_AUDIO_NONE; + dev->usb_audio_type = EM28XX_USB_AUDIO_NONE; return 0; } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { + dev->int_audio_type = EM28XX_INT_AUDIO_I2S; if (dev->chip_id < CHIP_ID_EM2860 && (cfg & EM28XX_CHIPCFG_AUDIOMASK) == EM2820_CHIPCFG_I2S_1_SAMPRATE) - dev->audio_mode.i2s_samplerates = 1; + i2s_samplerates = 1; else if (dev->chip_id >= CHIP_ID_EM2860 && (cfg & EM28XX_CHIPCFG_AUDIOMASK) == EM2860_CHIPCFG_I2S_5_SAMPRATES) - dev->audio_mode.i2s_samplerates = 5; + i2s_samplerates = 5; else - dev->audio_mode.i2s_samplerates = 3; + i2s_samplerates = 3; em28xx_info("I2S Audio (%d sample rate(s))\n", - dev->audio_mode.i2s_samplerates); + i2s_samplerates); /* Skip the code that does AC97 vendor detection */ dev->audio_mode.ac97 = EM28XX_NO_AC97; goto init_audio; + } else { + dev->int_audio_type = EM28XX_INT_AUDIO_AC97; } dev->audio_mode.ac97 = EM28XX_AC97_OTHER; @@ -549,8 +560,9 @@ int em28xx_audio_setup(struct em28xx *dev) */ em28xx_warn("AC97 chip type couldn't be determined\n"); dev->audio_mode.ac97 = EM28XX_NO_AC97; - dev->has_alsa_audio = false; - dev->audio_mode.has_audio = false; + if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR) + dev->usb_audio_type = EM28XX_USB_AUDIO_NONE; + dev->int_audio_type = EM28XX_INT_AUDIO_NONE; goto init_audio; } @@ -559,15 +571,12 @@ int em28xx_audio_setup(struct em28xx *dev) goto init_audio; vid = vid1 << 16 | vid2; - - dev->audio_mode.ac97_vendor_id = vid; em28xx_warn("AC97 vendor ID = 0x%08x\n", vid); feat = em28xx_read_ac97(dev, AC97_RESET); if (feat < 0) goto init_audio; - dev->audio_mode.ac97_feat = feat; em28xx_warn("AC97 features = 0x%04x\n", feat); /* Try to identify what audio processor we have */ @@ -586,8 +595,8 @@ init_audio: em28xx_info("Empia 202 AC97 audio processor detected\n"); break; case EM28XX_AC97_SIGMATEL: - em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n", - dev->audio_mode.ac97_vendor_id & 0xff); + em28xx_info("Sigmatel audio processor detected (stac 97%02x)\n", + vid & 0xff); break; case EM28XX_AC97_OTHER: em28xx_warn("Unknown AC97 audio processor detected!\n"); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 3a3e243..9682c52 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -373,7 +373,6 @@ static struct tda18271_config kworld_ub435q_v2_config = { }; static struct tda18212_config kworld_ub435q_v3_config = { - .i2c_address = 0x60, .if_atsc_vsb = 3600, .if_atsc_qam = 3600, }; @@ -856,7 +855,9 @@ static const struct m88ds3103_config pctv_461e_m88ds3103_config = { .clock = 27000000, .i2c_wr_max = 33, .clock_out = 0, - .ts_mode = M88DS3103_TS_PARALLEL_16, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, .agc = 0x99, }; @@ -1435,6 +1436,15 @@ static int em28xx_dvb_init(struct em28xx *dev) } break; case EM2874_BOARD_KWORLD_UB435Q_V3: + { + struct i2c_client *client; + struct i2c_adapter *adapter = &dev->i2c_adap[dev->def_i2c_bus]; + struct i2c_board_info board_info = { + .type = "tda18212", + .addr = 0x60, + .platform_data = &kworld_ub435q_v3_config, + }; + dvb->fe[0] = dvb_attach(lgdt3305_attach, &em2874_lgdt3305_nogate_dev, &dev->i2c_adap[dev->def_i2c_bus]); @@ -1443,14 +1453,26 @@ static int em28xx_dvb_init(struct em28xx *dev) goto out_free; } - /* Attach the demodulator. */ - if (!dvb_attach(tda18212_attach, dvb->fe[0], - &dev->i2c_adap[dev->def_i2c_bus], - &kworld_ub435q_v3_config)) { - result = -EINVAL; + /* attach tuner */ + kworld_ub435q_v3_config.fe = dvb->fe[0]; + request_module("tda18212"); + client = i2c_new_device(adapter, &board_info); + if (client == NULL || client->dev.driver == NULL) { + dvb_frontend_detach(dvb->fe[0]); + result = -ENODEV; goto out_free; } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(dvb->fe[0]); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_tuner = client; break; + } case EM2874_BOARD_PCTV_HD_MINI_80E: dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0] != NULL) { @@ -1533,6 +1555,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach demod */ si2168_config.i2c_adapter = &adapter; si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_PARALLEL; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2168", I2C_NAME_SIZE); info.addr = 0x64; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ed843bd..581f6da 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -71,8 +71,7 @@ struct em28xx_IR { unsigned int last_readcount; u64 rc_type; - /* i2c slave address of external device (if used) */ - u16 i2c_dev_addr; + struct i2c_client *i2c_client; int (*get_key_i2c)(struct i2c_client *ir, enum rc_type *protocol, u32 *scancode); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); @@ -294,16 +293,11 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { - struct em28xx *dev = ir->dev; static u32 scancode; enum rc_type protocol; int rc; - struct i2c_client client; - - client.adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; - client.addr = ir->i2c_dev_addr; - rc = ir->get_key_i2c(&client, &protocol, &scancode); + rc = ir->get_key_i2c(ir->i2c_client, &protocol, &scancode); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; @@ -361,7 +355,7 @@ static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - if (ir->i2c_dev_addr) /* external i2c device */ + if (ir->i2c_client) /* external i2c device */ em28xx_i2c_ir_handle_key(ir); else /* internal device */ em28xx_ir_handle_key(ir); @@ -609,17 +603,17 @@ static int em28xx_register_snapshot_button(struct em28xx *dev) static void em28xx_init_buttons(struct em28xx *dev) { u8 i = 0, j = 0; - bool addr_new = 0; + bool addr_new = false; dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL; while (dev->board.buttons[i].role >= 0 && dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) { struct em28xx_button *button = &dev->board.buttons[i]; /* Check if polling address is already on the list */ - addr_new = 1; + addr_new = true; for (j = 0; j < dev->num_button_polling_addresses; j++) { if (button->reg_r == dev->button_polling_addresses[j]) { - addr_new = 0; + addr_new = false; break; } } @@ -756,7 +750,13 @@ static int em28xx_ir_init(struct em28xx *dev) goto error; } - ir->i2c_dev_addr = i2c_rc_dev_addr; + ir->i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!ir->i2c_client) + goto error; + ir->i2c_client->adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; + ir->i2c_client->addr = i2c_rc_dev_addr; + ir->i2c_client->flags = 0; + /* NOTE: all other fields of i2c_client are unused */ } else { /* internal device */ switch (dev->chip_id) { case CHIP_ID_EM2860: @@ -815,6 +815,7 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: + kfree(ir->i2c_client); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -841,6 +842,8 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); + kfree(ir->i2c_client); + /* done */ kfree(ir); dev->ir = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 6d7f657..34ee1e0 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -29,17 +29,6 @@ #include "em28xx.h" #include "em28xx-v4l.h" -static unsigned int vbibufs = 5; -module_param(vbibufs, int, 0644); -MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); - -static unsigned int vbi_debug; -module_param(vbi_debug, int, 0644); -MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); - -#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) - /* ------------------------------------------------------------------ */ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 29abc37..03d5ece 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -435,7 +435,10 @@ static inline void finish_buffer(struct em28xx *dev, em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++; - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + if (dev->v4l2->progressive) + buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + else + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); @@ -478,7 +481,7 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = lencopy > remain ? remain : lencopy; if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + em28xx_isocdbg("Overflow of %zu bytes past buffer end (1)\n", ((char *)startwrite + lencopy) - ((char *)buf->vb_buf + buf->length)); remain = (char *)buf->vb_buf + buf->length - @@ -504,7 +507,7 @@ static void em28xx_copy_video(struct em28xx *dev, if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end" + em28xx_isocdbg("Overflow of %zu bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - ((char *)buf->vb_buf + buf->length)); @@ -718,7 +721,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; struct em28xx_dmaqueue *dmaq = &dev->vidq; struct em28xx_v4l2 *v4l2 = dev->v4l2; - bool frame_end = 0; + bool frame_end = false; /* Check for header */ /* NOTE: at least with bulk transfers, only the first packet @@ -994,13 +997,16 @@ static void em28xx_stop_streaming(struct vb2_queue *vq) } spin_lock_irqsave(&dev->slock, flags); + if (dev->usb_ctl.vid_buf != NULL) { + vb2_buffer_done(&dev->usb_ctl.vid_buf->vb, VB2_BUF_STATE_ERROR); + dev->usb_ctl.vid_buf = NULL; + } 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); } @@ -1021,13 +1027,16 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq) } spin_lock_irqsave(&dev->slock, flags); + if (dev->usb_ctl.vbi_buf != NULL) { + vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb, VB2_BUF_STATE_ERROR); + dev->usb_ctl.vbi_buf = NULL; + } 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); } @@ -1711,7 +1720,7 @@ static int vidioc_querycap(struct file *file, void *priv, else cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - if (dev->audio_mode.has_audio) + if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) cap->device_caps |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) @@ -2296,7 +2305,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->v4l2_dev.ctrl_handler = hdl; if (dev->board.is_webcam) - v4l2->progressive = 1; + v4l2->progressive = true; /* * Default format, used for tvp5150 or saa711x output formats @@ -2502,7 +2511,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY); } - if (!dev->audio_mode.has_audio) { + if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) { v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO); v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO); } @@ -2532,7 +2541,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY); } - if (!dev->audio_mode.has_audio) { + if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) { v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO); v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO); } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 4360338..a21a746 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -309,13 +309,18 @@ enum em28xx_ac97_mode { struct em28xx_audio_mode { enum em28xx_ac97_mode ac97; +}; - u16 ac97_feat; - u32 ac97_vendor_id; - - unsigned int has_audio:1; +enum em28xx_int_audio_type { + EM28XX_INT_AUDIO_NONE = 0, + EM28XX_INT_AUDIO_AC97, + EM28XX_INT_AUDIO_I2S, +}; - u8 i2s_samplerates; +enum em28xx_usb_audio_type { + EM28XX_USB_AUDIO_NONE = 0, + EM28XX_USB_AUDIO_CLASS, + EM28XX_USB_AUDIO_VENDOR, }; /* em28xx has two audio inputs: tuner and line in. @@ -608,9 +613,9 @@ struct em28xx { unsigned int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */ unsigned char disconnected:1; /* device has been diconnected */ unsigned int has_video:1; - unsigned int has_audio_class:1; - unsigned int has_alsa_audio:1; unsigned int is_audio_only:1; + enum em28xx_int_audio_type int_audio_type; + enum em28xx_usb_audio_type usb_audio_type; struct em28xx_board board; diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index ece27ec..3f986e1 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -696,7 +696,7 @@ static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, sizeof(status_reg), timeout); if (r < 0) break; - status_reg = le16_to_cpu(*((u16 *)go->usb_buf)); + status_reg = le16_to_cpu(*((__le16 *)go->usb_buf)); if (!(status_reg & 0x0010)) break; msleep(10); @@ -751,7 +751,7 @@ static int go7007_usb_onboard_write_interrupt(struct go7007 *go, static void go7007_usb_readinterrupt_complete(struct urb *urb) { struct go7007 *go = (struct go7007 *)urb->context; - u16 *regs = (u16 *)urb->transfer_buffer; + __le16 *regs = (__le16 *)urb->transfer_buffer; int status = urb->status; if (status) { diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index e8cf23c..43d6505 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -876,9 +876,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ep_tb[0].alt = gspca_dev->alt; alt_idx = 1; } else { - - /* else, compute the minimum bandwidth - * and build the endpoint table */ + /* else, compute the minimum bandwidth + * and build the endpoint table */ alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index f06253c..d39adf9 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -235,6 +235,6 @@ int gspca_resume(struct usb_interface *intf); int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, - int avg_lum, int desired_avg_lum, int deadzone); + int avg_lum, int desired_avg_lum, int deadzone); #endif /* GSPCAV2_H */ diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 45bc1f5..3cb30a3 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -51,9 +51,9 @@ struct pkt_hdr { struct cam_hdr { uint8_t magic[2]; - uint16_t len; - uint16_t cmd; - uint16_t tag; + __le16 len; + __le16 cmd; + __le16 tag; }; /* specific webcam descriptor */ @@ -188,9 +188,9 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, rhdr->tag, chdr->tag); return -1; } - if (cpu_to_le16(rhdr->len) != (actual_len/2)) { + if (le16_to_cpu(rhdr->len) != (actual_len/2)) { pr_err("send_cmd: Bad len %04x != %04x\n", - cpu_to_le16(rhdr->len), (int)(actual_len/2)); + le16_to_cpu(rhdr->len), (int)(actual_len/2)); return -1; } @@ -211,7 +211,7 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, uint16_t data) { uint16_t reply[2]; - uint16_t cmd[2]; + __le16 cmd[2]; int res; cmd[0] = cpu_to_le16(reg); diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c index 41a9a89..d0ee899 100644 --- a/drivers/media/usb/gspca/sn9c20x.c +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -1297,7 +1297,7 @@ static void set_cmatrix(struct gspca_dev *gspca_dev, s32 hue_coord, hue_index = 180 + hue; u8 cmatrix[21]; - memset(cmatrix, 0, sizeof cmatrix); + memset(cmatrix, 0, sizeof(cmatrix)); cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; @@ -1787,8 +1787,9 @@ static int sd_init(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; u8 value; - u8 i2c_init[9] = - {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + u8 i2c_init[9] = { + 0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + }; for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { value = bridge_init[i][1]; @@ -2242,8 +2243,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int avg_lum, is_jpeg; - static const u8 frame_header[] = - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + static const u8 frame_header[] = { + 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96 + }; is_jpeg = (sd->fmt & 0x03) == 0; if (len >= 64 && memcmp(data, frame_header, 6) == 0) { diff --git a/drivers/media/usb/hackrf/Kconfig b/drivers/media/usb/hackrf/Kconfig new file mode 100644 index 0000000..937e6f5 --- /dev/null +++ b/drivers/media/usb/hackrf/Kconfig @@ -0,0 +1,10 @@ +config USB_HACKRF + tristate "HackRF" + depends on VIDEO_V4L2 + select VIDEOBUF2_VMALLOC + ---help--- + This is a video4linux2 driver for HackRF SDR device. + + To compile this driver as a module, choose M here: the + module will be called hackrf + diff --git a/drivers/media/usb/hackrf/Makefile b/drivers/media/usb/hackrf/Makefile new file mode 100644 index 0000000..73064a2 --- /dev/null +++ b/drivers/media/usb/hackrf/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_HACKRF) += hackrf.o diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c new file mode 100644 index 0000000..328b5ba --- /dev/null +++ b/drivers/media/usb/hackrf/hackrf.c @@ -0,0 +1,1142 @@ +/* + * HackRF driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-vmalloc.h> + +/* HackRF USB API commands (from HackRF Library) */ +enum { + CMD_SET_TRANSCEIVER_MODE = 0x01, + CMD_SAMPLE_RATE_SET = 0x06, + CMD_BASEBAND_FILTER_BANDWIDTH_SET = 0x07, + CMD_BOARD_ID_READ = 0x0e, + CMD_VERSION_STRING_READ = 0x0f, + CMD_SET_FREQ = 0x10, + CMD_SET_LNA_GAIN = 0x13, + CMD_SET_VGA_GAIN = 0x14, +}; + +/* + * bEndpointAddress 0x81 EP 1 IN + * Transfer Type Bulk + * wMaxPacketSize 0x0200 1x 512 bytes + */ +#define MAX_BULK_BUFS (6) +#define BULK_BUFFER_SIZE (128 * 512) + +static const struct v4l2_frequency_band bands_adc[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 200000, + .rangehigh = 24000000, + }, +}; + +static const struct v4l2_frequency_band bands_rf[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1, + .rangehigh = 4294967294LL, /* max u32, hw goes over 7GHz */ + }, +}; + +/* stream formats */ +struct hackrf_format { + char *name; + u32 pixelformat; + u32 buffersize; +}; + +/* format descriptions for capture and preview */ +static struct hackrf_format formats[] = { + { + .name = "Complex S8", + .pixelformat = V4L2_SDR_FMT_CS8, + .buffersize = BULK_BUFFER_SIZE, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct hackrf_frame_buf { + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; +}; + +struct hackrf_dev { +#define POWER_ON (1 << 1) +#define URB_BUF (1 << 2) +#define USB_STATE_URB_BUF (1 << 3) + unsigned long flags; + + struct device *dev; + struct usb_device *udev; + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + unsigned sequence; /* Buffer sequence counter */ + unsigned int vb_full; /* vb is full and packets dropped */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue */ + + struct urb *urb_list[MAX_BULK_BUFS]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_BULK_BUFS]; + dma_addr_t dma_addr[MAX_BULK_BUFS]; + int urbs_initialized; + int urbs_submitted; + + /* USB control message buffer */ + #define BUF_SIZE 24 + u8 buf[BUF_SIZE]; + + /* Current configuration */ + unsigned int f_adc; + unsigned int f_rf; + u32 pixelformat; + u32 buffersize; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + struct v4l2_ctrl *lna_gain; + struct v4l2_ctrl *if_gain; + + /* Sample rate calc */ + unsigned long jiffies_next; + unsigned int sample; + unsigned int sample_measured; +}; + +#define hackrf_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \ + char *_direction; \ + if (_t & USB_DIR_IN) \ + _direction = "<<<"; \ + else \ + _direction = ">>>"; \ + dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \ + _t, _r, _v & 0xff, _v >> 8, _i & 0xff, \ + _i >> 8, _l & 0xff, _l >> 8, _direction, _l, _b); \ +} + +/* execute firmware command */ +static int hackrf_ctrl_msg(struct hackrf_dev *dev, u8 request, u16 value, + u16 index, u8 *data, u16 size) +{ + int ret; + unsigned int pipe; + u8 requesttype; + + switch (request) { + case CMD_SET_TRANSCEIVER_MODE: + case CMD_SET_FREQ: + case CMD_SAMPLE_RATE_SET: + case CMD_BASEBAND_FILTER_BANDWIDTH_SET: + pipe = usb_sndctrlpipe(dev->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + break; + case CMD_BOARD_ID_READ: + case CMD_VERSION_STRING_READ: + case CMD_SET_LNA_GAIN: + case CMD_SET_VGA_GAIN: + pipe = usb_rcvctrlpipe(dev->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + break; + default: + dev_err(dev->dev, "Unknown command %02x\n", request); + ret = -EINVAL; + goto err; + } + + /* write request */ + if (!(requesttype & USB_DIR_IN)) + memcpy(dev->buf, data, size); + + ret = usb_control_msg(dev->udev, pipe, request, requesttype, value, + index, dev->buf, size, 1000); + hackrf_dbg_usb_control_msg(dev->dev, request, requesttype, value, + index, dev->buf, size); + if (ret < 0) { + dev_err(dev->dev, "usb_control_msg() failed %d request %02x\n", + ret, request); + goto err; + } + + /* read request */ + if (requesttype & USB_DIR_IN) + memcpy(data, dev->buf, size); + + return 0; +err: + return ret; +} + +/* Private functions */ +static struct hackrf_frame_buf *hackrf_get_next_fill_buf(struct hackrf_dev *dev) +{ + unsigned long flags; + struct hackrf_frame_buf *buf = NULL; + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + if (list_empty(&dev->queued_bufs)) + goto leave; + + buf = list_entry(dev->queued_bufs.next, struct hackrf_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); + return buf; +} + +static unsigned int hackrf_convert_stream(struct hackrf_dev *dev, + void *dst, void *src, unsigned int src_len) +{ + memcpy(dst, src, src_len); + + /* calculate sample rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(dev->jiffies_next))) { + #define MSECS 10000UL + unsigned int msecs = jiffies_to_msecs(jiffies - + dev->jiffies_next + msecs_to_jiffies(MSECS)); + unsigned int samples = dev->sample - dev->sample_measured; + + dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + dev->sample_measured = dev->sample; + dev_dbg(dev->dev, "slen=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); + } + + /* total number of samples */ + dev->sample += src_len / 2; + + return src_len; +} + +/* + * This gets called for the bulk stream pipe. This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void hackrf_urb_complete(struct urb *urb) +{ + struct hackrf_dev *dev = urb->context; + struct hackrf_frame_buf *fbuf; + + dev_dbg_ratelimited(dev->dev, "status=%d length=%d/%d errors=%d\n", + urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(dev->dev, "URB failed %d\n", urb->status); + break; + } + + if (likely(urb->actual_length > 0)) { + void *ptr; + unsigned int len; + /* get free framebuffer */ + fbuf = hackrf_get_next_fill_buf(dev); + if (unlikely(fbuf == NULL)) { + dev->vb_full++; + dev_notice_ratelimited(dev->dev, + "videobuf is full, %d packets dropped\n", + dev->vb_full); + goto skip; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + len = hackrf_convert_stream(dev, ptr, urb->transfer_buffer, + urb->actual_length); + vb2_set_plane_payload(&fbuf->vb, 0, len); + v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); + fbuf->vb.v4l2_buf.sequence = dev->sequence++; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + } +skip: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int hackrf_kill_urbs(struct hackrf_dev *dev) +{ + int i; + + for (i = dev->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(dev->dev, "kill urb=%d\n", i); + /* stop the URB */ + usb_kill_urb(dev->urb_list[i]); + } + dev->urbs_submitted = 0; + + return 0; +} + +static int hackrf_submit_urbs(struct hackrf_dev *dev) +{ + int i, ret; + + for (i = 0; i < dev->urbs_initialized; i++) { + dev_dbg(dev->dev, "submit urb=%d\n", i); + ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); + if (ret) { + dev_err(dev->dev, "Could not submit URB no. %d - get them all back\n", + i); + hackrf_kill_urbs(dev); + return ret; + } + dev->urbs_submitted++; + } + + return 0; +} + +static int hackrf_free_stream_bufs(struct hackrf_dev *dev) +{ + if (dev->flags & USB_STATE_URB_BUF) { + while (dev->buf_num) { + dev->buf_num--; + dev_dbg(dev->dev, "free buf=%d\n", dev->buf_num); + usb_free_coherent(dev->udev, dev->buf_size, + dev->buf_list[dev->buf_num], + dev->dma_addr[dev->buf_num]); + } + } + dev->flags &= ~USB_STATE_URB_BUF; + + return 0; +} + +static int hackrf_alloc_stream_bufs(struct hackrf_dev *dev) +{ + dev->buf_num = 0; + dev->buf_size = BULK_BUFFER_SIZE; + + dev_dbg(dev->dev, "all in all I will use %u bytes for streaming\n", + MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { + dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, + BULK_BUFFER_SIZE, GFP_ATOMIC, + &dev->dma_addr[dev->buf_num]); + if (!dev->buf_list[dev->buf_num]) { + dev_dbg(dev->dev, "alloc buf=%d failed\n", + dev->buf_num); + hackrf_free_stream_bufs(dev); + return -ENOMEM; + } + + dev_dbg(dev->dev, "alloc buf=%d %p (dma %llu)\n", dev->buf_num, + dev->buf_list[dev->buf_num], + (long long)dev->dma_addr[dev->buf_num]); + dev->flags |= USB_STATE_URB_BUF; + } + + return 0; +} + +static int hackrf_free_urbs(struct hackrf_dev *dev) +{ + int i; + + hackrf_kill_urbs(dev); + + for (i = dev->urbs_initialized - 1; i >= 0; i--) { + if (dev->urb_list[i]) { + dev_dbg(dev->dev, "free urb=%d\n", i); + /* free the URBs */ + usb_free_urb(dev->urb_list[i]); + } + } + dev->urbs_initialized = 0; + + return 0; +} + +static int hackrf_alloc_urbs(struct hackrf_dev *dev) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < MAX_BULK_BUFS; i++) { + dev_dbg(dev->dev, "alloc urb=%d\n", i); + dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!dev->urb_list[i]) { + dev_dbg(dev->dev, "failed\n"); + for (j = 0; j < i; j++) + usb_free_urb(dev->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb(dev->urb_list[i], + dev->udev, + usb_rcvbulkpipe(dev->udev, 0x81), + dev->buf_list[i], + BULK_BUFFER_SIZE, + hackrf_urb_complete, dev); + + dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + dev->urb_list[i]->transfer_dma = dev->dma_addr[i]; + dev->urbs_initialized++; + } + + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void hackrf_cleanup_queued_bufs(struct hackrf_dev *dev) +{ + unsigned long flags; + + dev_dbg(dev->dev, "\n"); + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + while (!list_empty(&dev->queued_bufs)) { + struct hackrf_frame_buf *buf; + + buf = list_entry(dev->queued_bufs.next, + struct hackrf_frame_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); +} + +/* The user yanked out the cable... */ +static void hackrf_disconnect(struct usb_interface *intf) +{ + struct v4l2_device *v = usb_get_intfdata(intf); + struct hackrf_dev *dev = container_of(v, struct hackrf_dev, v4l2_dev); + + dev_dbg(dev->dev, "\n"); + + mutex_lock(&dev->vb_queue_lock); + mutex_lock(&dev->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + dev->udev = NULL; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(&dev->vdev); + mutex_unlock(&dev->v4l2_lock); + mutex_unlock(&dev->vb_queue_lock); + + v4l2_device_put(&dev->v4l2_dev); +} + +/* Videobuf2 operations */ +static int hackrf_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) +{ + struct hackrf_dev *dev = vb2_get_drv_priv(vq); + + dev_dbg(dev->dev, "nbuffers=%d\n", *nbuffers); + + /* Need at least 8 buffers */ + if (vq->num_buffers + *nbuffers < 8) + *nbuffers = 8 - vq->num_buffers; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(dev->buffersize); + + dev_dbg(dev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); + return 0; +} + +static void hackrf_buf_queue(struct vb2_buffer *vb) +{ + struct hackrf_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct hackrf_frame_buf *buf = + container_of(vb, struct hackrf_frame_buf, vb); + unsigned long flags; + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &dev->queued_bufs); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); +} + +static int hackrf_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct hackrf_dev *dev = vb2_get_drv_priv(vq); + int ret; + + dev_dbg(dev->dev, "\n"); + + if (!dev->udev) + return -ENODEV; + + mutex_lock(&dev->v4l2_lock); + + dev->sequence = 0; + + set_bit(POWER_ON, &dev->flags); + + ret = hackrf_alloc_stream_bufs(dev); + if (ret) + goto err; + + ret = hackrf_alloc_urbs(dev); + if (ret) + goto err; + + ret = hackrf_submit_urbs(dev); + if (ret) + goto err; + + /* start hardware streaming */ + ret = hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, 1, 0, NULL, 0); + if (ret) + goto err; + + goto exit_mutex_unlock; +err: + hackrf_kill_urbs(dev); + hackrf_free_urbs(dev); + hackrf_free_stream_bufs(dev); + clear_bit(POWER_ON, &dev->flags); + + /* return all queued buffers to vb2 */ + { + struct hackrf_frame_buf *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->queued_bufs, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + } + +exit_mutex_unlock: + mutex_unlock(&dev->v4l2_lock); + + return ret; +} + +static void hackrf_stop_streaming(struct vb2_queue *vq) +{ + struct hackrf_dev *dev = vb2_get_drv_priv(vq); + + dev_dbg(dev->dev, "\n"); + + mutex_lock(&dev->v4l2_lock); + + /* stop hardware streaming */ + hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, 0, 0, NULL, 0); + + hackrf_kill_urbs(dev); + hackrf_free_urbs(dev); + hackrf_free_stream_bufs(dev); + + hackrf_cleanup_queued_bufs(dev); + + clear_bit(POWER_ON, &dev->flags); + + mutex_unlock(&dev->v4l2_lock); +} + +static struct vb2_ops hackrf_vb2_ops = { + .queue_setup = hackrf_queue_setup, + .buf_queue = hackrf_buf_queue, + .start_streaming = hackrf_start_streaming, + .stop_streaming = hackrf_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int hackrf_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct hackrf_dev *dev = video_drvdata(file); + + dev_dbg(dev->dev, "\n"); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, dev->vdev.name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int hackrf_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_queue; + int i; + + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (f->fmt.sdr.pixelformat == formats[i].pixelformat) { + dev->pixelformat = formats[i].pixelformat; + dev->buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + + return 0; +} + +static int hackrf_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&dev->pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + f->fmt.sdr.pixelformat = dev->pixelformat; + f->fmt.sdr.buffersize = dev->buffersize; + + return 0; +} + +static int hackrf_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + int i; + + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + + return 0; +} + +static int hackrf_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + + dev_dbg(dev->dev, "index=%d\n", f->index); + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int hackrf_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + + dev_dbg(dev->dev, "index=%d\n", v->index); + + if (v->index == 0) + ret = 0; + else if (v->index == 1) + ret = 0; + else + ret = -EINVAL; + + return ret; +} + +static int hackrf_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + + dev_dbg(dev->dev, "index=%d\n", v->index); + + if (v->index == 0) { + strlcpy(v->name, "HackRF ADC", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands_adc[0].rangelow; + v->rangehigh = bands_adc[0].rangehigh; + ret = 0; + } else if (v->index == 1) { + strlcpy(v->name, "HackRF RF", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands_rf[0].rangelow; + v->rangehigh = bands_rf[0].rangehigh; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int hackrf_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + unsigned int upper, lower; + u8 buf[8]; + + dev_dbg(dev->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); + + if (f->tuner == 0) { + dev->f_adc = clamp_t(unsigned int, f->frequency, + bands_adc[0].rangelow, bands_adc[0].rangehigh); + dev_dbg(dev->dev, "ADC frequency=%u Hz\n", dev->f_adc); + upper = dev->f_adc; + lower = 1; + buf[0] = (upper >> 0) & 0xff; + buf[1] = (upper >> 8) & 0xff; + buf[2] = (upper >> 16) & 0xff; + buf[3] = (upper >> 24) & 0xff; + buf[4] = (lower >> 0) & 0xff; + buf[5] = (lower >> 8) & 0xff; + buf[6] = (lower >> 16) & 0xff; + buf[7] = (lower >> 24) & 0xff; + ret = hackrf_ctrl_msg(dev, CMD_SAMPLE_RATE_SET, 0, 0, buf, 8); + } else if (f->tuner == 1) { + dev->f_rf = clamp_t(unsigned int, f->frequency, + bands_rf[0].rangelow, bands_rf[0].rangehigh); + dev_dbg(dev->dev, "RF frequency=%u Hz\n", dev->f_rf); + upper = dev->f_rf / 1000000; + lower = dev->f_rf % 1000000; + buf[0] = (upper >> 0) & 0xff; + buf[1] = (upper >> 8) & 0xff; + buf[2] = (upper >> 16) & 0xff; + buf[3] = (upper >> 24) & 0xff; + buf[4] = (lower >> 0) & 0xff; + buf[5] = (lower >> 8) & 0xff; + buf[6] = (lower >> 16) & 0xff; + buf[7] = (lower >> 24) & 0xff; + ret = hackrf_ctrl_msg(dev, CMD_SET_FREQ, 0, 0, buf, 8); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int hackrf_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + + dev_dbg(dev->dev, "tuner=%d type=%d\n", f->tuner, f->type); + + if (f->tuner == 0) { + f->type = V4L2_TUNER_ADC; + f->frequency = dev->f_adc; + ret = 0; + } else if (f->tuner == 1) { + f->type = V4L2_TUNER_RF; + f->frequency = dev->f_rf; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int hackrf_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + + dev_dbg(dev->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands_adc)) { + ret = -EINVAL; + } else { + *band = bands_adc[band->index]; + ret = 0; + } + } else if (band->tuner == 1) { + if (band->index >= ARRAY_SIZE(bands_rf)) { + ret = -EINVAL; + } else { + *band = bands_rf[band->index]; + ret = 0; + } + } else { + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops hackrf_ioctl_ops = { + .vidioc_querycap = hackrf_querycap, + + .vidioc_s_fmt_sdr_cap = hackrf_s_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = hackrf_g_fmt_sdr_cap, + .vidioc_enum_fmt_sdr_cap = hackrf_enum_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = hackrf_try_fmt_sdr_cap, + + .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_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_s_tuner = hackrf_s_tuner, + .vidioc_g_tuner = hackrf_g_tuner, + + .vidioc_s_frequency = hackrf_s_frequency, + .vidioc_g_frequency = hackrf_g_frequency, + .vidioc_enum_freq_bands = hackrf_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations hackrf_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device hackrf_template = { + .name = "HackRF One", + .release = video_device_release_empty, + .fops = &hackrf_fops, + .ioctl_ops = &hackrf_ioctl_ops, +}; + +static void hackrf_video_release(struct v4l2_device *v) +{ + struct hackrf_dev *dev = container_of(v, struct hackrf_dev, v4l2_dev); + + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); +} + +static int hackrf_set_bandwidth(struct hackrf_dev *dev) +{ + int ret, i; + u16 u16tmp, u16tmp2; + unsigned int bandwidth; + + static const struct { + u32 freq; + } bandwidth_lut[] = { + { 1750000}, /* 1.75 MHz */ + { 2500000}, /* 2.5 MHz */ + { 3500000}, /* 3.5 MHz */ + { 5000000}, /* 5 MHz */ + { 5500000}, /* 5.5 MHz */ + { 6000000}, /* 6 MHz */ + { 7000000}, /* 7 MHz */ + { 8000000}, /* 8 MHz */ + { 9000000}, /* 9 MHz */ + {10000000}, /* 10 MHz */ + {12000000}, /* 12 MHz */ + {14000000}, /* 14 MHz */ + {15000000}, /* 15 MHz */ + {20000000}, /* 20 MHz */ + {24000000}, /* 24 MHz */ + {28000000}, /* 28 MHz */ + }; + + dev_dbg(dev->dev, "bandwidth auto=%d->%d val=%d->%d f_adc=%u\n", + dev->bandwidth_auto->cur.val, + dev->bandwidth_auto->val, dev->bandwidth->cur.val, + dev->bandwidth->val, dev->f_adc); + + if (dev->bandwidth_auto->val == true) + bandwidth = dev->f_adc; + else + bandwidth = dev->bandwidth->val; + + for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { + if (bandwidth <= bandwidth_lut[i].freq) { + bandwidth = bandwidth_lut[i].freq; + break; + } + } + + dev->bandwidth->val = bandwidth; + dev->bandwidth->cur.val = bandwidth; + + dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq); + + u16tmp = 0; + u16tmp |= ((bandwidth >> 0) & 0xff) << 0; + u16tmp |= ((bandwidth >> 8) & 0xff) << 8; + u16tmp2 = 0; + u16tmp2 |= ((bandwidth >> 16) & 0xff) << 0; + u16tmp2 |= ((bandwidth >> 24) & 0xff) << 8; + + ret = hackrf_ctrl_msg(dev, CMD_BASEBAND_FILTER_BANDWIDTH_SET, + u16tmp, u16tmp2, NULL, 0); + if (ret) + dev_dbg(dev->dev, "failed=%d\n", ret); + + return ret; +} + +static int hackrf_set_lna_gain(struct hackrf_dev *dev) +{ + int ret; + u8 u8tmp; + + dev_dbg(dev->dev, "lna val=%d->%d\n", + dev->lna_gain->cur.val, dev->lna_gain->val); + + ret = hackrf_ctrl_msg(dev, CMD_SET_LNA_GAIN, 0, dev->lna_gain->val, + &u8tmp, 1); + if (ret) + dev_dbg(dev->dev, "failed=%d\n", ret); + + return ret; +} + +static int hackrf_set_if_gain(struct hackrf_dev *dev) +{ + int ret; + u8 u8tmp; + + dev_dbg(dev->dev, "val=%d->%d\n", + dev->if_gain->cur.val, dev->if_gain->val); + + ret = hackrf_ctrl_msg(dev, CMD_SET_VGA_GAIN, 0, dev->if_gain->val, + &u8tmp, 1); + if (ret) + dev_dbg(dev->dev, "failed=%d\n", ret); + + return ret; +} + +static int hackrf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hackrf_dev *dev = container_of(ctrl->handler, + struct hackrf_dev, hdl); + int ret; + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + ret = hackrf_set_bandwidth(dev); + break; + case V4L2_CID_RF_TUNER_LNA_GAIN: + ret = hackrf_set_lna_gain(dev); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ret = hackrf_set_if_gain(dev); + break; + default: + dev_dbg(dev->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops hackrf_ctrl_ops = { + .s_ctrl = hackrf_s_ctrl, +}; + +static int hackrf_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct hackrf_dev *dev; + int ret; + u8 u8tmp, buf[BUF_SIZE]; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + + mutex_init(&dev->v4l2_lock); + mutex_init(&dev->vb_queue_lock); + spin_lock_init(&dev->queued_bufs_lock); + INIT_LIST_HEAD(&dev->queued_bufs); + dev->dev = &intf->dev; + dev->udev = interface_to_usbdev(intf); + dev->f_adc = bands_adc[0].rangelow; + dev->f_rf = bands_rf[0].rangelow; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + + /* Detect device */ + ret = hackrf_ctrl_msg(dev, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); + if (ret == 0) + ret = hackrf_ctrl_msg(dev, CMD_VERSION_STRING_READ, 0, 0, + buf, BUF_SIZE); + if (ret) { + dev_err(dev->dev, "Could not detect board\n"); + goto err_free_mem; + } + + buf[BUF_SIZE - 1] = '\0'; + + dev_info(dev->dev, "Board ID: %02x\n", u8tmp); + dev_info(dev->dev, "Firmware version: %s\n", buf); + + /* Init videobuf2 queue structure */ + dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + dev->vb_queue.drv_priv = dev; + dev->vb_queue.buf_struct_size = sizeof(struct hackrf_frame_buf); + dev->vb_queue.ops = &hackrf_vb2_ops; + dev->vb_queue.mem_ops = &vb2_vmalloc_memops; + dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->vb_queue); + if (ret) { + dev_err(dev->dev, "Could not initialize vb2 queue\n"); + goto err_free_mem; + } + + /* Init video_device structure */ + dev->vdev = hackrf_template; + dev->vdev.queue = &dev->vb_queue; + dev->vdev.queue->lock = &dev->vb_queue_lock; + video_set_drvdata(&dev->vdev, dev); + + /* Register the v4l2_device structure */ + dev->v4l2_dev.release = hackrf_video_release; + ret = v4l2_device_register(&intf->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "Failed to register v4l2-device (%d)\n", ret); + goto err_free_mem; + } + + /* Register controls */ + v4l2_ctrl_handler_init(&dev->hdl, 4); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 1750000, 28000000, 50000, 1750000); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, 0, 40, 8, 0); + dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, 0, 62, 2, 0); + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(dev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + v4l2_ctrl_handler_setup(&dev->hdl); + + dev->v4l2_dev.ctrl_handler = &dev->hdl; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->v4l2_lock; + + ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(dev->dev, "Failed to register as video device (%d)\n", + ret); + goto err_unregister_v4l2_dev; + } + dev_info(dev->dev, "Registered as %s\n", + video_device_node_name(&dev->vdev)); + dev_notice(dev->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); + return 0; + +err_free_controls: + v4l2_ctrl_handler_free(&dev->hdl); +err_unregister_v4l2_dev: + v4l2_device_unregister(&dev->v4l2_dev); +err_free_mem: + kfree(dev); + return ret; +} + +/* USB device ID list */ +static struct usb_device_id hackrf_id_table[] = { + { USB_DEVICE(0x1d50, 0x6089) }, /* HackRF One */ + { } +}; +MODULE_DEVICE_TABLE(usb, hackrf_id_table); + +/* USB subsystem interface */ +static struct usb_driver hackrf_driver = { + .name = KBUILD_MODNAME, + .probe = hackrf_probe, + .disconnect = hackrf_disconnect, + .id_table = hackrf_id_table, +}; + +module_usb_driver(hackrf_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("HackRF"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c index 6053661..6e86032 100644 --- a/drivers/media/usb/hdpvr/hdpvr-control.c +++ b/drivers/media/usb/hdpvr/hdpvr-control.c @@ -59,13 +59,10 @@ int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vidinf) 1000); #ifdef HDPVR_DEBUG - if (hdpvr_debug & MSG_INFO) { - char print_buf[15]; - hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, - sizeof(print_buf), 0); + if (hdpvr_debug & MSG_INFO) v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "get video info returned: %d, %s\n", ret, print_buf); - } + "get video info returned: %d, %5ph\n", ret, + dev->usbc_buf); #endif mutex_unlock(&dev->usbc_mutex); @@ -82,9 +79,6 @@ int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vidinf) int get_input_lines_info(struct hdpvr_device *dev) { -#ifdef HDPVR_DEBUG - char print_buf[9]; -#endif int ret, lines; mutex_lock(&dev->usbc_mutex); @@ -96,13 +90,10 @@ int get_input_lines_info(struct hdpvr_device *dev) 1000); #ifdef HDPVR_DEBUG - if (hdpvr_debug & MSG_INFO) { - hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf, - sizeof(print_buf), 0); + if (hdpvr_debug & MSG_INFO) v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "get input lines info returned: %d, %s\n", ret, - print_buf); - } + "get input lines info returned: %d, %3ph\n", ret, + dev->usbc_buf); #else (void)ret; /* suppress compiler warning */ #endif diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index c563896..42b4cdf 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -124,14 +124,6 @@ static int device_authorization(struct hdpvr_device *dev) int ret, retval = -ENOMEM; char request_type = 0x38, rcv_request = 0x81; char *response; -#ifdef HDPVR_DEBUG - size_t buf_size = 46; - char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); - if (!print_buf) { - v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - return retval; - } -#endif mutex_lock(&dev->usbc_mutex); ret = usb_control_msg(dev->udev, @@ -147,11 +139,9 @@ static int device_authorization(struct hdpvr_device *dev) } #ifdef HDPVR_DEBUG else { - hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, - 5*buf_size+1, 0); v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "Status request returned, len %d: %s\n", - ret, print_buf); + "Status request returned, len %d: %46ph\n", + ret, dev->usbc_buf); } #endif @@ -189,15 +179,13 @@ static int device_authorization(struct hdpvr_device *dev) response = dev->usbc_buf+38; #ifdef HDPVR_DEBUG - hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", - print_buf); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %8ph\n", + response); #endif challenge(response); #ifdef HDPVR_DEBUG - hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n", - print_buf); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %8ph\n", + response); #endif msleep(100); @@ -213,9 +201,6 @@ static int device_authorization(struct hdpvr_device *dev) retval = ret != 8; unlock: mutex_unlock(&dev->usbc_mutex); -#ifdef HDPVR_DEBUG - kfree(print_buf); -#endif return retval; } diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 26b1334..efc761c 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -120,6 +120,7 @@ struct msi2500_frame_buf { }; struct msi2500_state { + struct device *dev; struct video_device vdev; struct v4l2_device v4l2_dev; struct v4l2_subdev *v4l2_subdev; @@ -153,14 +154,13 @@ struct msi2500_state { u32 next_sample; /* for track lost packets */ u32 sample; /* for sample rate calc */ unsigned long jiffies_next; - unsigned int sample_ctrl_bit[4]; }; /* Private functions */ static struct msi2500_frame_buf *msi2500_get_next_fill_buf( struct msi2500_state *s) { - unsigned long flags = 0; + unsigned long flags; struct msi2500_frame_buf *buf = NULL; spin_lock_irqsave(&s->queued_bufs_lock, flags); @@ -269,7 +269,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, sample[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; if (i == 0 && s->next_sample != sample[0]) { - dev_dbg_ratelimited(&s->udev->dev, + dev_dbg_ratelimited(s->dev, "%d samples lost, %d %08x:%08x\n", sample[0] - s->next_sample, src_len, s->next_sample, sample[0]); @@ -279,7 +279,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, * Dump all unknown 'garbage' data - maybe we will discover * someday if there is something rational... */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + dev_dbg_ratelimited(s->dev, "%*ph\n", 12, &src[4]); src += 16; /* skip header */ @@ -322,8 +322,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, } case MSI2500_PIX_FMT_SDR_MSI2500_384: /* 384 x IQ samples */ /* Dump unknown 'garbage' data */ - dev_dbg_ratelimited(&s->udev->dev, - "%*ph\n", 24, &src[1000]); + dev_dbg_ratelimited(s->dev, "%*ph\n", 24, &src[1000]); memcpy(dst, src, 984); src += 984 + 24; dst += 984; @@ -365,8 +364,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample = s->next_sample; - dev_dbg(&s->udev->dev, - "size=%u samples=%u msecs=%u sample rate=%lu\n", + dev_dbg(s->dev, "size=%u samples=%u msecs=%u sample rate=%lu\n", src_len, samples, msecs, samples * 1000UL / msecs); } @@ -387,19 +385,16 @@ static void msi2500_isoc_handler(struct urb *urb) if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { - dev_dbg(&s->udev->dev, "URB (%p) unlinked %ssynchronuously\n", + dev_dbg(s->dev, "URB (%p) unlinked %ssynchronuously\n", urb, urb->status == -ENOENT ? "" : "a"); return; } if (unlikely(urb->status != 0)) { - dev_dbg(&s->udev->dev, - "msi2500_isoc_handler() called with status %d\n", - urb->status); + dev_dbg(s->dev, "called with status %d\n", urb->status); /* Give up after a number of contiguous errors */ if (++s->isoc_errors > MAX_ISOC_ERRORS) - dev_dbg(&s->udev->dev, - "Too many ISOC errors, bailing out\n"); + dev_dbg(s->dev, "Too many ISOC errors, bailing out\n"); goto handler_end; } else { /* Reset ISOC error counter. We did get here, after all. */ @@ -413,7 +408,7 @@ static void msi2500_isoc_handler(struct urb *urb) /* Check frame error */ fstatus = urb->iso_frame_desc[i].status; if (unlikely(fstatus)) { - dev_dbg_ratelimited(&s->udev->dev, + dev_dbg_ratelimited(s->dev, "frame=%d/%d has error %d skipping\n", i, urb->number_of_packets, fstatus); continue; @@ -430,7 +425,7 @@ static void msi2500_isoc_handler(struct urb *urb) fbuf = msi2500_get_next_fill_buf(s); if (unlikely(fbuf == NULL)) { s->vb_full++; - dev_dbg_ratelimited(&s->udev->dev, + dev_dbg_ratelimited(s->dev, "videobuf is full, %d packets dropped\n", s->vb_full); continue; @@ -446,22 +441,19 @@ static void msi2500_isoc_handler(struct urb *urb) handler_end: i = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(i != 0)) - dev_dbg(&s->udev->dev, - "Error (%d) re-submitting urb in msi2500_isoc_handler\n", - i); + dev_dbg(s->dev, "Error (%d) re-submitting urb\n", i); } static void msi2500_iso_stop(struct msi2500_state *s) { int i; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); /* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (s->urbs[i]) { - dev_dbg(&s->udev->dev, "Unlinking URB %p\n", - s->urbs[i]); + dev_dbg(s->dev, "Unlinking URB %p\n", s->urbs[i]); usb_kill_urb(s->urbs[i]); } } @@ -471,12 +463,12 @@ static void msi2500_iso_free(struct msi2500_state *s) { int i; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); /* Freeing ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (s->urbs[i]) { - dev_dbg(&s->udev->dev, "Freeing URB\n"); + dev_dbg(s->dev, "Freeing URB\n"); if (s->urbs[i]->transfer_buffer) { usb_free_coherent(s->udev, s->urbs[i]->transfer_buffer_length, @@ -492,7 +484,7 @@ static void msi2500_iso_free(struct msi2500_state *s) /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static void msi2500_isoc_cleanup(struct msi2500_state *s) { - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); msi2500_iso_stop(s); msi2500_iso_free(s); @@ -501,14 +493,12 @@ static void msi2500_isoc_cleanup(struct msi2500_state *s) /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static int msi2500_isoc_init(struct msi2500_state *s) { - struct usb_device *udev; struct urb *urb; int i, j, ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); s->isoc_errors = 0; - udev = s->udev; ret = usb_set_interface(s->udev, 0, 1); if (ret) @@ -518,23 +508,22 @@ static int msi2500_isoc_init(struct msi2500_state *s) for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { - dev_err(&s->udev->dev, - "Failed to allocate urb %d\n", i); + dev_err(s->dev, "Failed to allocate urb %d\n", i); msi2500_isoc_cleanup(s); return -ENOMEM; } s->urbs[i] = urb; - dev_dbg(&s->udev->dev, "Allocated URB at 0x%p\n", urb); + dev_dbg(s->dev, "Allocated URB at 0x%p\n", urb); urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, 0x81); + urb->dev = s->udev; + urb->pipe = usb_rcvisocpipe(s->udev, 0x81); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(udev, ISO_BUFFER_SIZE, + urb->transfer_buffer = usb_alloc_coherent(s->udev, + ISO_BUFFER_SIZE, GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - dev_err(&s->udev->dev, - "Failed to allocate urb buffer %d\n", + dev_err(s->dev, "Failed to allocate urb buffer %d\n", i); msi2500_isoc_cleanup(s); return -ENOMEM; @@ -554,13 +543,12 @@ static int msi2500_isoc_init(struct msi2500_state *s) for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(s->urbs[i], GFP_KERNEL); if (ret) { - dev_err(&s->udev->dev, - "isoc_init() submit_urb %d failed with error %d\n", + dev_err(s->dev, "usb_submit_urb %d failed with error %d\n", i, ret); msi2500_isoc_cleanup(s); return ret; } - dev_dbg(&s->udev->dev, "URB 0x%p submitted.\n", s->urbs[i]); + dev_dbg(s->dev, "URB 0x%p submitted.\n", s->urbs[i]); } /* All is done... */ @@ -570,9 +558,9 @@ static int msi2500_isoc_init(struct msi2500_state *s) /* Must be called with vb_queue_lock hold */ static void msi2500_cleanup_queued_bufs(struct msi2500_state *s) { - unsigned long flags = 0; + unsigned long flags; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); spin_lock_irqsave(&s->queued_bufs_lock, flags); while (!list_empty(&s->queued_bufs)) { @@ -593,7 +581,7 @@ static void msi2500_disconnect(struct usb_interface *intf) struct msi2500_state *s = container_of(v, struct msi2500_state, v4l2_dev); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); mutex_lock(&s->vb_queue_lock); mutex_lock(&s->v4l2_lock); @@ -613,7 +601,7 @@ static int msi2500_querycap(struct file *file, void *fh, { struct msi2500_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); @@ -631,14 +619,13 @@ static int msi2500_queue_setup(struct vb2_queue *vq, { struct msi2500_state *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers); /* Absolute min and max number of buffers available for mmap() */ *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32); *nplanes = 1; sizes[0] = PAGE_ALIGN(s->buffersize); - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); + dev_dbg(s->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); return 0; } @@ -647,7 +634,7 @@ static void msi2500_buf_queue(struct vb2_buffer *vb) struct msi2500_state *s = vb2_get_drv_priv(vb->vb2_queue); struct msi2500_frame_buf *buf = container_of(vb, struct msi2500_frame_buf, vb); - unsigned long flags = 0; + unsigned long flags; /* Check the device has not disconnected between prep and queuing */ if (unlikely(!s->udev)) { @@ -665,16 +652,15 @@ static void msi2500_buf_queue(struct vb2_buffer *vb) #define CMD_STOP_STREAMING 0x45 #define CMD_READ_UNKNOW 0x48 -#define msi2500_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ +#define msi2500_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \ char *_direction; \ if (_t & USB_DIR_IN) \ _direction = "<<<"; \ else \ _direction = ">>>"; \ - dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ - _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ - _l, _b); \ + dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \ + _t, _r, _v & 0xff, _v >> 8, _i & 0xff, _i >> 8, \ + _l & 0xff, _l >> 8, _direction, _l, _b); \ } static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data) @@ -685,18 +671,16 @@ static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data) u16 value = (data >> 0) & 0xffff; u16 index = (data >> 16) & 0xffff; - msi2500_dbg_usb_control_msg(s->udev, + msi2500_dbg_usb_control_msg(s->dev, request, requesttype, value, index, NULL, 0); - ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), request, requesttype, value, index, NULL, 0, 2000); - if (ret) - dev_err(&s->udev->dev, "%s: failed %d, cmd %02x, data %04x\n", - __func__, ret, cmd, data); + dev_err(s->dev, "failed %d, cmd %02x, data %04x\n", + ret, cmd, data); return ret; -}; +} #define F_REF 24000000 #define DIV_R_IN 2 @@ -785,8 +769,7 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) for (div_r_out = 4; div_r_out < 16; div_r_out += 2) { f_vco = f_sr * div_r_out * 12; - dev_dbg(&s->udev->dev, "%s: div_r_out=%d f_vco=%d\n", - __func__, div_r_out, f_vco); + dev_dbg(s->dev, "div_r_out=%d f_vco=%d\n", div_r_out, f_vco); if (f_vco >= 202000000) break; } @@ -800,10 +783,8 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) reg3 |= ((fract >> 20) & 0x000001) << 15; /* [20] */ reg4 |= ((fract >> 0) & 0x0fffff) << 8; /* [19:0] */ - dev_dbg(&s->udev->dev, - "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", - __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, - reg4); + dev_dbg(s->dev, "f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", + f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00608008); if (ret) @@ -838,14 +819,14 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) goto err; err: return ret; -}; +} static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) { struct msi2500_state *s = vb2_get_drv_priv(vq); int ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); if (!s->udev) return -ENODEV; @@ -873,7 +854,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) { struct msi2500_state *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s:\n", __func__); + dev_dbg(s->dev, "\n"); mutex_lock(&s->v4l2_lock); @@ -909,7 +890,7 @@ static int msi2500_enum_fmt_sdr_cap(struct file *file, void *priv, { struct msi2500_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); + dev_dbg(s->dev, "index=%d\n", f->index); if (f->index >= s->num_formats) return -EINVAL; @@ -925,7 +906,7 @@ static int msi2500_g_fmt_sdr_cap(struct file *file, void *priv, { struct msi2500_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", (char *)&s->pixelformat); f->fmt.sdr.pixelformat = s->pixelformat; @@ -942,7 +923,7 @@ static int msi2500_s_fmt_sdr_cap(struct file *file, void *priv, struct vb2_queue *q = &s->vb_queue; int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", (char *)&f->fmt.sdr.pixelformat); if (vb2_is_busy(q)) @@ -972,7 +953,7 @@ static int msi2500_try_fmt_sdr_cap(struct file *file, void *priv, struct msi2500_state *s = video_drvdata(file); int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); @@ -995,7 +976,7 @@ static int msi2500_s_tuner(struct file *file, void *priv, struct msi2500_state *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + dev_dbg(s->dev, "index=%d\n", v->index); if (v->index == 0) ret = 0; @@ -1012,7 +993,7 @@ static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) struct msi2500_state *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + dev_dbg(s->dev, "index=%d\n", v->index); if (v->index == 0) { strlcpy(v->name, "Mirics MSi2500", sizeof(v->name)); @@ -1036,8 +1017,7 @@ static int msi2500_g_frequency(struct file *file, void *priv, struct msi2500_state *s = video_drvdata(file); int ret = 0; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); + dev_dbg(s->dev, "tuner=%d type=%d\n", f->tuner, f->type); if (f->tuner == 0) { f->frequency = s->f_adc; @@ -1058,15 +1038,14 @@ static int msi2500_s_frequency(struct file *file, void *priv, struct msi2500_state *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); + dev_dbg(s->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); if (f->tuner == 0) { s->f_adc = clamp_t(unsigned int, f->frequency, bands[0].rangelow, bands[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); + dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc); ret = msi2500_set_usb_adc(s); } else if (f->tuner == 1) { ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f); @@ -1083,8 +1062,8 @@ static int msi2500_enum_freq_bands(struct file *file, void *priv, struct msi2500_state *s = video_drvdata(file); int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); + dev_dbg(s->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->tuner == 0) { if (band->index >= ARRAY_SIZE(bands)) { @@ -1169,8 +1148,7 @@ static int msi2500_transfer_one_message(struct spi_master *master, u32 data; list_for_each_entry(t, &m->transfers, transfer_list) { - dev_dbg(&s->udev->dev, "%s: msg=%*ph\n", - __func__, t->len, t->tx_buf); + dev_dbg(s->dev, "msg=%*ph\n", t->len, t->tx_buf); data = 0x09; /* reg 9 is SPI adapter */ data |= ((u8 *)t->tx_buf)[0] << 8; data |= ((u8 *)t->tx_buf)[1] << 16; @@ -1186,8 +1164,7 @@ static int msi2500_transfer_one_message(struct spi_master *master, static int msi2500_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); - struct msi2500_state *s = NULL; + struct msi2500_state *s; struct v4l2_subdev *sd; struct spi_master *master; int ret; @@ -1200,7 +1177,7 @@ static int msi2500_probe(struct usb_interface *intf, s = kzalloc(sizeof(struct msi2500_state), GFP_KERNEL); if (s == NULL) { - pr_err("Could not allocate memory for msi2500_state\n"); + dev_err(&intf->dev, "Could not allocate memory for state\n"); return -ENOMEM; } @@ -1208,12 +1185,13 @@ static int msi2500_probe(struct usb_interface *intf, mutex_init(&s->vb_queue_lock); spin_lock_init(&s->queued_bufs_lock); INIT_LIST_HEAD(&s->queued_bufs); - s->udev = udev; + s->dev = &intf->dev; + s->udev = interface_to_usbdev(intf); s->f_adc = bands[0].rangelow; s->pixelformat = formats[0].pixelformat; s->buffersize = formats[0].buffersize; s->num_formats = NUM_FORMATS; - if (msi2500_emulated_fmt == false) + if (!msi2500_emulated_fmt) s->num_formats -= 2; /* Init videobuf2 queue structure */ @@ -1226,7 +1204,7 @@ static int msi2500_probe(struct usb_interface *intf, s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(&s->vb_queue); if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + dev_err(s->dev, "Could not initialize vb2 queue\n"); goto err_free_mem; } @@ -1240,13 +1218,12 @@ static int msi2500_probe(struct usb_interface *intf, s->v4l2_dev.release = msi2500_video_release; ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); + dev_err(s->dev, "Failed to register v4l2-device (%d)\n", ret); goto err_free_mem; } /* SPI master adapter */ - master = spi_alloc_master(&s->udev->dev, 0); + master = spi_alloc_master(s->dev, 0); if (master == NULL) { ret = -ENOMEM; goto err_unregister_v4l2_dev; @@ -1267,7 +1244,7 @@ static int msi2500_probe(struct usb_interface *intf, sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info); s->v4l2_subdev = sd; if (sd == NULL) { - dev_err(&s->udev->dev, "cannot get v4l2 subdevice\n"); + dev_err(s->dev, "cannot get v4l2 subdevice\n"); ret = -ENODEV; goto err_unregister_master; } @@ -1276,7 +1253,7 @@ static int msi2500_probe(struct usb_interface *intf, v4l2_ctrl_handler_init(&s->hdl, 0); if (s->hdl.error) { ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); + dev_err(s->dev, "Could not initialize controls\n"); goto err_free_controls; } @@ -1289,16 +1266,13 @@ static int msi2500_probe(struct usb_interface *intf, ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", + dev_err(s->dev, "Failed to register as video device (%d)\n", ret); goto err_unregister_v4l2_dev; } - dev_info(&s->udev->dev, "Registered as %s\n", + dev_info(s->dev, "Registered as %s\n", video_device_node_name(&s->vdev)); - dev_notice(&s->udev->dev, - "%s: SDR API is still slightly experimental and functionality changes may follow\n", - KBUILD_MODNAME); + dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); return 0; diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c index aa7449e..3d98798 100644 --- a/drivers/media/usb/pwc/pwc-v4l.c +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -52,7 +52,7 @@ enum { custom_autocontour, custom_contour, custom_noise_reduction, custom_awb_speed, custom_awb_delay, custom_save_user, custom_restore_user, custom_restore_factory }; -const char * const pwc_auto_whitebal_qmenu[] = { +static const char * const pwc_auto_whitebal_qmenu[] = { "Indoor (Incandescant Lighting) Mode", "Outdoor (Sunlight) Mode", "Indoor (Fluorescent Lighting) Mode", diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 2c90186..ccc0009 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2245,7 +2245,7 @@ static int s2255_probe(struct usb_interface *interface, } atomic_set(&dev->num_channels, 0); - dev->pid = le16_to_cpu(id->idProduct); + dev->pid = id->idProduct; dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) goto errorFWDATA1; diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 1836a41..94e10b1 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -277,14 +277,14 @@ static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id) rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), fw_buffer, fw->size, &dummy, 1000); - sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc); + sms_info("sent %zu(%d) bytes, rc %d", fw->size, dummy, rc); kfree(fw_buffer); } else { sms_err("failed to allocate firmware buffer"); rc = -ENOMEM; } - sms_info("read FW %s, size=%zd", fw_filename, fw->size); + sms_info("read FW %s, size=%zu", fw_filename, fw->size); release_firmware(fw); @@ -655,6 +655,8 @@ static const struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD }, { USB_DEVICE(0x3275, 0x0080), .driver_info = SMS1XXX_BOARD_SIANO_RIO }, + { USB_DEVICE(0x2013, 0x0257), + .driver_info = SMS1XXX_BOARD_PCTV_77E }, { } /* Terminating entry */ }; diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c index 5c45c9d..9c29552 100644 --- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -156,6 +156,9 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if (cmd->msg_len > sizeof(b) - 4) + return -EINVAL; + memcpy(&b[4], cmd->msg, cmd->msg_len); state->config->send_command(fe, 0x72, diff --git a/drivers/media/usb/usbtv/Kconfig b/drivers/media/usb/usbtv/Kconfig index 7c5b860..b833c5b 100644 --- a/drivers/media/usb/usbtv/Kconfig +++ b/drivers/media/usb/usbtv/Kconfig @@ -1,6 +1,7 @@ config VIDEO_USBTV tristate "USBTV007 video capture support" - depends on VIDEO_V4L2 + depends on VIDEO_V4L2 && SND + select SND_PCM select VIDEOBUF2_VMALLOC ---help--- diff --git a/drivers/media/usb/usbtv/Makefile b/drivers/media/usb/usbtv/Makefile index 775316a..f555cf8 100644 --- a/drivers/media/usb/usbtv/Makefile +++ b/drivers/media/usb/usbtv/Makefile @@ -1,4 +1,5 @@ usbtv-y := usbtv-core.o \ - usbtv-video.o + usbtv-video.o \ + usbtv-audio.o obj-$(CONFIG_VIDEO_USBTV) += usbtv.o diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c new file mode 100644 index 0000000..78c12d2 --- /dev/null +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -0,0 +1,385 @@ +/* + * Fushicai USBTV007 Audio-Video Grabber Driver + * + * Product web site: + * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html + * + * Copyright (c) 2013 Federico Simoncelli + * All rights reserved. + * No physical hardware was harmed running Windows during the + * reverse-engineering activity + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL"). + */ + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/ac97_codec.h> +#include <sound/pcm_params.h> + +#include "usbtv.h" + +static struct snd_pcm_hardware snd_usbtv_digital_hw = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 11059, + .period_bytes_max = 13516, + .periods_min = 2, + .periods_max = 98, + .buffer_bytes_max = 62720 * 8, /* value in usbaudio.c */ +}; + +static int snd_usbtv_pcm_open(struct snd_pcm_substream *substream) +{ + struct usbtv *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + chip->snd_substream = substream; + runtime->hw = snd_usbtv_digital_hw; + + return 0; +} + +static int snd_usbtv_pcm_close(struct snd_pcm_substream *substream) +{ + struct usbtv *chip = snd_pcm_substream_chip(substream); + + if (atomic_read(&chip->snd_stream)) { + atomic_set(&chip->snd_stream, 0); + schedule_work(&chip->snd_trigger); + } + + return 0; +} + +static int snd_usbtv_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int rv; + struct usbtv *chip = snd_pcm_substream_chip(substream); + + rv = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + + if (rv < 0) { + dev_warn(chip->dev, "pcm audio buffer allocation failure %i\n", + rv); + return rv; + } + + return 0; +} + +static int snd_usbtv_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_usbtv_prepare(struct snd_pcm_substream *substream) +{ + struct usbtv *chip = snd_pcm_substream_chip(substream); + + chip->snd_buffer_pos = 0; + chip->snd_period_pos = 0; + + return 0; +} + +static void usbtv_audio_urb_received(struct urb *urb) +{ + struct usbtv *chip = urb->context; + struct snd_pcm_substream *substream = chip->snd_substream; + struct snd_pcm_runtime *runtime = substream->runtime; + size_t i, frame_bytes, chunk_length, buffer_pos, period_pos; + int period_elapsed; + void *urb_current; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ENOENT: + case -EPROTO: + case -ECONNRESET: + case -ESHUTDOWN: + return; + default: + dev_warn(chip->dev, "unknown audio urb status %i\n", + urb->status); + } + + if (!atomic_read(&chip->snd_stream)) + return; + + frame_bytes = runtime->frame_bits >> 3; + chunk_length = USBTV_CHUNK / frame_bytes; + + buffer_pos = chip->snd_buffer_pos; + period_pos = chip->snd_period_pos; + period_elapsed = 0; + + for (i = 0; i < urb->actual_length; i += USBTV_CHUNK_SIZE) { + urb_current = urb->transfer_buffer + i + USBTV_AUDIO_HDRSIZE; + + if (buffer_pos + chunk_length >= runtime->buffer_size) { + size_t cnt = (runtime->buffer_size - buffer_pos) * + frame_bytes; + memcpy(runtime->dma_area + buffer_pos * frame_bytes, + urb_current, cnt); + memcpy(runtime->dma_area, urb_current + cnt, + chunk_length * frame_bytes - cnt); + } else { + memcpy(runtime->dma_area + buffer_pos * frame_bytes, + urb_current, chunk_length * frame_bytes); + } + + buffer_pos += chunk_length; + period_pos += chunk_length; + + if (buffer_pos >= runtime->buffer_size) + buffer_pos -= runtime->buffer_size; + + if (period_pos >= runtime->period_size) { + period_pos -= runtime->period_size; + period_elapsed = 1; + } + } + + snd_pcm_stream_lock(substream); + + chip->snd_buffer_pos = buffer_pos; + chip->snd_period_pos = period_pos; + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int usbtv_audio_start(struct usbtv *chip) +{ + unsigned int pipe; + static const u16 setup[][2] = { + /* These seem to enable the device. */ + { USBTV_BASE + 0x0008, 0x0001 }, + { USBTV_BASE + 0x01d0, 0x00ff }, + { USBTV_BASE + 0x01d9, 0x0002 }, + + { USBTV_BASE + 0x01da, 0x0013 }, + { USBTV_BASE + 0x01db, 0x0012 }, + { USBTV_BASE + 0x01e9, 0x0002 }, + { USBTV_BASE + 0x01ec, 0x006c }, + { USBTV_BASE + 0x0294, 0x0020 }, + { USBTV_BASE + 0x0255, 0x00cf }, + { USBTV_BASE + 0x0256, 0x0020 }, + { USBTV_BASE + 0x01eb, 0x0030 }, + { USBTV_BASE + 0x027d, 0x00a6 }, + { USBTV_BASE + 0x0280, 0x0011 }, + { USBTV_BASE + 0x0281, 0x0040 }, + { USBTV_BASE + 0x0282, 0x0011 }, + { USBTV_BASE + 0x0283, 0x0040 }, + { 0xf891, 0x0010 }, + + /* this sets the input from composite */ + { USBTV_BASE + 0x0284, 0x00aa }, + }; + + chip->snd_bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (chip->snd_bulk_urb == NULL) + goto err_alloc_urb; + + pipe = usb_rcvbulkpipe(chip->udev, USBTV_AUDIO_ENDP); + + chip->snd_bulk_urb->transfer_buffer = kzalloc( + USBTV_AUDIO_URBSIZE, GFP_KERNEL); + if (chip->snd_bulk_urb->transfer_buffer == NULL) + goto err_transfer_buffer; + + usb_fill_bulk_urb(chip->snd_bulk_urb, chip->udev, pipe, + chip->snd_bulk_urb->transfer_buffer, USBTV_AUDIO_URBSIZE, + usbtv_audio_urb_received, chip); + + /* starting the stream */ + usbtv_set_regs(chip, setup, ARRAY_SIZE(setup)); + + usb_clear_halt(chip->udev, pipe); + usb_submit_urb(chip->snd_bulk_urb, GFP_ATOMIC); + + return 0; + +err_transfer_buffer: + usb_free_urb(chip->snd_bulk_urb); + chip->snd_bulk_urb = NULL; + +err_alloc_urb: + return -ENOMEM; +} + +static int usbtv_audio_stop(struct usbtv *chip) +{ + static const u16 setup[][2] = { + /* The original windows driver sometimes sends also: + * { USBTV_BASE + 0x00a2, 0x0013 } + * but it seems useless and its real effects are untested at + * the moment. + */ + { USBTV_BASE + 0x027d, 0x0000 }, + { USBTV_BASE + 0x0280, 0x0010 }, + { USBTV_BASE + 0x0282, 0x0010 }, + }; + + if (chip->snd_bulk_urb) { + usb_kill_urb(chip->snd_bulk_urb); + kfree(chip->snd_bulk_urb->transfer_buffer); + usb_free_urb(chip->snd_bulk_urb); + chip->snd_bulk_urb = NULL; + } + + usbtv_set_regs(chip, setup, ARRAY_SIZE(setup)); + + return 0; +} + +void usbtv_audio_suspend(struct usbtv *usbtv) +{ + if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb) + usb_kill_urb(usbtv->snd_bulk_urb); +} + +void usbtv_audio_resume(struct usbtv *usbtv) +{ + if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb) + usb_submit_urb(usbtv->snd_bulk_urb, GFP_ATOMIC); +} + +static void snd_usbtv_trigger(struct work_struct *work) +{ + struct usbtv *chip = container_of(work, struct usbtv, snd_trigger); + + if (atomic_read(&chip->snd_stream)) + usbtv_audio_start(chip); + else + usbtv_audio_stop(chip); +} + +static int snd_usbtv_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct usbtv *chip = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + atomic_set(&chip->snd_stream, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + atomic_set(&chip->snd_stream, 0); + break; + default: + return -EINVAL; + } + + schedule_work(&chip->snd_trigger); + + return 0; +} + +static snd_pcm_uframes_t snd_usbtv_pointer(struct snd_pcm_substream *substream) +{ + struct usbtv *chip = snd_pcm_substream_chip(substream); + + return chip->snd_buffer_pos; +} + +static struct snd_pcm_ops snd_usbtv_pcm_ops = { + .open = snd_usbtv_pcm_open, + .close = snd_usbtv_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usbtv_hw_params, + .hw_free = snd_usbtv_hw_free, + .prepare = snd_usbtv_prepare, + .trigger = snd_usbtv_card_trigger, + .pointer = snd_usbtv_pointer, +}; + +int usbtv_audio_init(struct usbtv *usbtv) +{ + int rv; + struct snd_card *card; + struct snd_pcm *pcm; + + INIT_WORK(&usbtv->snd_trigger, snd_usbtv_trigger); + atomic_set(&usbtv->snd_stream, 0); + + rv = snd_card_new(&usbtv->udev->dev, SNDRV_DEFAULT_IDX1, "usbtv", + THIS_MODULE, 0, &card); + if (rv < 0) + return rv; + + strlcpy(card->driver, usbtv->dev->driver->name, sizeof(card->driver)); + strlcpy(card->shortname, "usbtv", sizeof(card->shortname)); + snprintf(card->longname, sizeof(card->longname), + "USBTV Audio at bus %d device %d", usbtv->udev->bus->busnum, + usbtv->udev->devnum); + + snd_card_set_dev(card, usbtv->dev); + + usbtv->snd = card; + + rv = snd_pcm_new(card, "USBTV Audio", 0, 0, 1, &pcm); + if (rv < 0) + goto err; + + strlcpy(pcm->name, "USBTV Audio Input", sizeof(pcm->name)); + pcm->info_flags = 0; + pcm->private_data = usbtv; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usbtv_pcm_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), USBTV_AUDIO_BUFFER, + USBTV_AUDIO_BUFFER); + + rv = snd_card_register(card); + if (rv) + goto err; + + return 0; + +err: + usbtv->snd = NULL; + snd_card_free(card); + + return rv; +} + +void usbtv_audio_free(struct usbtv *usbtv) +{ + if (usbtv->snd && usbtv->udev) { + snd_card_free(usbtv->snd); + usbtv->snd = NULL; + } +} diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 473fab8..29428be 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -1,5 +1,5 @@ /* - * Fushicai USBTV007 Video Grabber Driver + * Fushicai USBTV007 Audio-Video Grabber Driver * * Product web site: * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html @@ -84,12 +84,19 @@ static int usbtv_probe(struct usb_interface *intf, if (ret < 0) goto usbtv_video_fail; + ret = usbtv_audio_init(usbtv); + if (ret < 0) + goto usbtv_audio_fail; + /* for simplicity we exploit the v4l2_device reference counting */ v4l2_device_get(&usbtv->v4l2_dev); - dev_info(dev, "Fushicai USBTV007 Video Grabber\n"); + dev_info(dev, "Fushicai USBTV007 Audio-Video Grabber\n"); return 0; +usbtv_audio_fail: + usbtv_video_free(usbtv); + usbtv_video_fail: usb_set_intfdata(intf, NULL); usb_put_dev(usbtv->udev); @@ -101,11 +108,13 @@ usbtv_video_fail: static void usbtv_disconnect(struct usb_interface *intf) { struct usbtv *usbtv = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); if (!usbtv) return; + usbtv_audio_free(usbtv); usbtv_video_free(usbtv); usb_put_dev(usbtv->udev); @@ -122,8 +131,8 @@ static struct usb_device_id usbtv_id_table[] = { }; MODULE_DEVICE_TABLE(usb, usbtv_id_table); -MODULE_AUTHOR("Lubomir Rintel"); -MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver"); +MODULE_AUTHOR("Lubomir Rintel, Federico Simoncelli"); +MODULE_DESCRIPTION("Fushicai USBTV007 Audio-Video Grabber Driver"); MODULE_LICENSE("Dual BSD/GPL"); static struct usb_driver usbtv_usb_driver = { diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 030c585..9d3525f 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -1,5 +1,5 @@ /* - * Fushicai USBTV007 Video Grabber Driver + * Fushicai USBTV007 Audio-Video Grabber Driver * * Product web site: * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html @@ -79,7 +79,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input) { USBTV_BASE + 0x011f, 0x00f2 }, { USBTV_BASE + 0x0127, 0x0060 }, { USBTV_BASE + 0x00ae, 0x0010 }, - { USBTV_BASE + 0x0284, 0x00aa }, { USBTV_BASE + 0x0239, 0x0060 }, }; @@ -88,7 +87,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input) { USBTV_BASE + 0x011f, 0x00ff }, { USBTV_BASE + 0x0127, 0x0060 }, { USBTV_BASE + 0x00ae, 0x0030 }, - { USBTV_BASE + 0x0284, 0x0088 }, { USBTV_BASE + 0x0239, 0x0060 }, }; @@ -225,7 +223,6 @@ static int usbtv_setup_capture(struct usbtv *usbtv) { USBTV_BASE + 0x0159, 0x0006 }, { USBTV_BASE + 0x015d, 0x0000 }, - { USBTV_BASE + 0x0284, 0x0088 }, { USBTV_BASE + 0x0003, 0x0004 }, { USBTV_BASE + 0x0100, 0x00d3 }, { USBTV_BASE + 0x0115, 0x0015 }, @@ -256,7 +253,7 @@ static int usbtv_setup_capture(struct usbtv *usbtv) * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels. * Therefore, we break down the chunk into two halves before copyting, * so that we can interleave a line if needed. */ -static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd) +static void usbtv_chunk_to_vbuf(u32 *frame, __be32 *src, int chunk_no, int odd) { int half; @@ -266,6 +263,7 @@ static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd) int part_index = (line * 2 + !odd) * 3 + (part_no % 3); u32 *dst = &frame[part_index * USBTV_CHUNK/2]; + memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src)); src += USBTV_CHUNK/2; } @@ -274,7 +272,7 @@ static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd) /* Called for each 256-byte image chunk. * First word identifies the chunk, followed by 240 words of image * data and padding. */ -static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) +static void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk) { int frame_id, odd, chunk_no; u32 *frame; @@ -365,7 +363,7 @@ static void usbtv_iso_cb(struct urb *ip) for (offset = 0; USBTV_CHUNK_SIZE * offset < size; offset++) usbtv_image_chunk(usbtv, - (u32 *)&data[USBTV_CHUNK_SIZE * offset]); + (__be32 *)&data[USBTV_CHUNK_SIZE * offset]); } resubmit: @@ -410,6 +408,7 @@ static void usbtv_stop(struct usbtv *usbtv) /* Cancel running transfers. */ for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { struct urb *ip = usbtv->isoc_urbs[i]; + if (ip == NULL) continue; usb_kill_urb(ip); @@ -434,6 +433,8 @@ static int usbtv_start(struct usbtv *usbtv) int i; int ret; + usbtv_audio_suspend(usbtv); + ret = usb_set_interface(usbtv->udev, 0, 0); if (ret < 0) return ret; @@ -446,6 +447,8 @@ static int usbtv_start(struct usbtv *usbtv) if (ret < 0) return ret; + usbtv_audio_resume(usbtv); + for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) { struct urb *ip; @@ -559,6 +562,7 @@ static int usbtv_g_input(struct file *file, void *priv, unsigned int *i) static int usbtv_s_input(struct file *file, void *priv, unsigned int i) { struct usbtv *usbtv = video_drvdata(file); + return usbtv_select_input(usbtv, i); } diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h index cb1d388..9681195 100644 --- a/drivers/media/usb/usbtv/usbtv.h +++ b/drivers/media/usb/usbtv/usbtv.h @@ -1,5 +1,5 @@ /* - * Fushicai USBTV007 Video Grabber Driver + * Fushicai USBTV007 Audio-Video Grabber Driver * * Copyright (c) 2013 Lubomir Rintel * All rights reserved. @@ -28,6 +28,7 @@ /* Hardware. */ #define USBTV_VIDEO_ENDP 0x81 +#define USBTV_AUDIO_ENDP 0x83 #define USBTV_BASE 0xc000 #define USBTV_REQUEST_REG 12 @@ -39,6 +40,10 @@ #define USBTV_CHUNK_SIZE 256 #define USBTV_CHUNK 240 +#define USBTV_AUDIO_URBSIZE 20480 +#define USBTV_AUDIO_HDRSIZE 4 +#define USBTV_AUDIO_BUFFER 65536 + /* Chunk header. */ #define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \ == 0x88000000) @@ -91,9 +96,23 @@ struct usbtv { int iso_size; unsigned int sequence; struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS]; + + /* audio */ + struct snd_card *snd; + struct snd_pcm_substream *snd_substream; + atomic_t snd_stream; + struct work_struct snd_trigger; + struct urb *snd_bulk_urb; + size_t snd_buffer_pos; + size_t snd_period_pos; }; int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size); int usbtv_video_init(struct usbtv *usbtv); void usbtv_video_free(struct usbtv *usbtv); + +int usbtv_audio_init(struct usbtv *usbtv); +void usbtv_audio_free(struct usbtv *usbtv); +void usbtv_audio_suspend(struct usbtv *usbtv); +void usbtv_audio_resume(struct usbtv *usbtv); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 0eb82106..3e59b28 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -309,9 +309,8 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN - | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_GET_DEF + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE | UVC_CTRL_FLAG_AUTO_UPDATE, }, { @@ -391,6 +390,35 @@ static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, data[2] = min((int)abs(value), 0xff); } +static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + unsigned int first = mapping->offset / 8; + __s8 rel = (__s8)data[first]; + + switch (query) { + case UVC_GET_CUR: + return (rel == 0) ? 0 : (rel > 0 ? data[first+1] + : -data[first+1]); + case UVC_GET_MIN: + return -data[first+1]; + case UVC_GET_MAX: + case UVC_GET_RES: + case UVC_GET_DEF: + default: + return data[first+1]; + } +} + +static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + unsigned int first = mapping->offset / 8; + + data[first] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[first+1] = min_t(int, abs(value), 0xff); +} + static struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -677,6 +705,30 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_SIGNED, }, { + .id = V4L2_CID_PAN_SPEED, + .name = "Pan (Speed)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_rel_speed, + .set = uvc_ctrl_set_rel_speed, + }, + { + .id = V4L2_CID_TILT_SPEED, + .name = "Tilt (Speed)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_rel_speed, + .set = uvc_ctrl_set_rel_speed, + }, + { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, @@ -1795,7 +1847,7 @@ done: * - Handle restore order (Auto-Exposure Mode should be restored before * Exposure Time). */ -int uvc_ctrl_resume_device(struct uvc_device *dev) +int uvc_ctrl_restore_values(struct uvc_device *dev) { struct uvc_control *ctrl; struct uvc_entity *entity; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index f8135f4..7c8322d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2000,7 +2000,7 @@ static int __uvc_resume(struct usb_interface *intf, int reset) int ret = 0; if (reset) { - ret = uvc_ctrl_resume_device(dev); + ret = uvc_ctrl_restore_values(dev); if (ret < 0) return ret; } @@ -2175,6 +2175,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, + /* Logitech HD Pro Webcam C920 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x082d, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_RESTORE_CTRLS_ON_INIT }, /* Chicony CNF7129 (Asus EEE 100HE) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2229,6 +2238,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_DEF }, + /* Dell XPS M1330 (OmniVision OV7670 webcam) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05a9, + .idProduct = 0x7670, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 378ae02..60a8e2c 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -318,6 +318,7 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream, stream->ctrl = probe; stream->cur_format = format; stream->cur_frame = frame; + stream->frame_size = fmt->fmt.pix.sizeimage; done: mutex_unlock(&stream->mutex); diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 9144a2f..9ace520 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1143,7 +1143,7 @@ static int uvc_video_encode_data(struct uvc_streaming *stream, static void uvc_video_validate_buffer(const struct uvc_streaming *stream, struct uvc_buffer *buf) { - if (buf->length != buf->bytesused && + if (stream->frame_size != buf->bytesused && !(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)) buf->error = 1; } @@ -1463,7 +1463,7 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev, switch (dev->speed) { case USB_SPEED_SUPER: - return ep->ss_ep_comp.wBytesPerInterval; + return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); case USB_SPEED_HIGH: psize = usb_endpoint_maxp(&ep->desc); return (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); @@ -1678,6 +1678,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) } } + /* The Logitech C920 temporarily forgets that it should not be adjusting + * Exposure Absolute during init so restore controls to stored values. + */ + if (stream->dev->quirks & UVC_QUIRK_RESTORE_CTRLS_ON_INIT) + uvc_ctrl_restore_values(stream->dev); + return 0; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index b1f69a6..6f676c2 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -147,6 +147,7 @@ #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 #define UVC_QUIRK_PROBE_DEF 0x00000100 #define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 +#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -456,6 +457,8 @@ struct uvc_streaming { struct uvc_format *def_format; struct uvc_format *cur_format; struct uvc_frame *cur_frame; + size_t frame_size; + /* Protect access to ctrl, cur_format, cur_frame and hardware video * probe control. */ @@ -688,7 +691,7 @@ extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, const struct uvc_control_mapping *mapping); extern int uvc_ctrl_init_device(struct uvc_device *dev); extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); -extern int uvc_ctrl_resume_device(struct uvc_device *dev); +extern int uvc_ctrl_restore_values(struct uvc_device *dev); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 06c18ba..559f837 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -601,7 +601,7 @@ static int tuner_probe(struct i2c_client *client, t->name = "(tuner unset)"; t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; - t->standby = 1; + t->standby = true; t->radio_freq = 87.5 * 16000; /* Initial freq range */ t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */ @@ -1260,7 +1260,9 @@ static int tuner_suspend(struct device *dev) tuner_dbg("suspend\n"); - if (!t->standby && analog_ops->standby) + if (t->fe.ops.tuner_ops.suspend) + t->fe.ops.tuner_ops.suspend(&t->fe); + else if (!t->standby && analog_ops->standby) analog_ops->standby(&t->fe); return 0; @@ -1273,7 +1275,9 @@ static int tuner_resume(struct device *dev) tuner_dbg("resume\n"); - if (!t->standby) + if (t->fe.ops.tuner_ops.resume) + t->fe.ops.tuner_ops.resume(&t->fe); + else if (!t->standby) if (set_mode(t, t->mode) == 0) set_freq(t, 0); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index ccaa38f..2e9d81f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -435,16 +435,13 @@ static unsigned int clamp_align(unsigned int x, unsigned int min, /* Bits that must be zero to be aligned */ unsigned int mask = ~((1 << align) - 1); + /* Clamp to aligned min and max */ + x = clamp(x, (min + ~mask) & mask, max & mask); + /* Round to nearest aligned value */ if (align) x = (x + (1 << (align - 1))) & mask; - /* Clamp to aligned value of min and max */ - if (x < min) - x = (min + ~mask) & mask; - else if (x > max) - x = max & mask; - return x; } diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index cca6c2f..e502a5f 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -328,7 +328,7 @@ struct v4l2_buffer32 { __u32 reserved; }; -static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, +static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32, enum v4l2_memory memory) { void __user *up_pln; @@ -357,7 +357,7 @@ static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, return 0; } -static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, +static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32, enum v4l2_memory memory) { if (copy_in_user(up32, up, 2 * sizeof(__u32)) || @@ -427,7 +427,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user * by passing a very big num_planes value */ uplane = compat_alloc_user_space(num_planes * sizeof(struct v4l2_plane)); - kp->m.planes = uplane; + kp->m.planes = (__force struct v4l2_plane *)uplane; while (--num_planes >= 0) { ret = get_v4l2_plane32(uplane, uplane32, kp->memory); @@ -498,7 +498,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user if (num_planes == 0) return 0; - uplane = kp->m.planes; + uplane = (__force struct v4l2_plane __user *)kp->m.planes; if (get_user(p, &up->m.planes)) return -EFAULT; uplane32 = compat_ptr(p); @@ -562,7 +562,7 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame get_user(kp->flags, &up->flags) || copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) return -EFAULT; - kp->base = compat_ptr(tmp); + kp->base = (__force void *)compat_ptr(tmp); return 0; } @@ -667,11 +667,15 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext n * sizeof(struct v4l2_ext_control32))) return -EFAULT; kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); - kp->controls = kcontrols; + kp->controls = (__force struct v4l2_ext_control *)kcontrols; while (--n >= 0) { + u32 id; + if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) return -EFAULT; - if (ctrl_is_pointer(kcontrols->id)) { + if (get_user(id, &kcontrols->id)) + return -EFAULT; + if (ctrl_is_pointer(id)) { void __user *s; if (get_user(p, &ucontrols->string)) @@ -689,7 +693,8 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols = kp->controls; + struct v4l2_ext_control __user *kcontrols = + (__force struct v4l2_ext_control __user *)kp->controls; int n = kp->count; compat_caddr_t p; @@ -711,11 +716,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext while (--n >= 0) { unsigned size = sizeof(*ucontrols); + u32 id; + if (get_user(id, &kcontrols->id)) + return -EFAULT; /* Do not modify the pointer when copying a pointer control. The contents of the pointer was changed, not the pointer itself. */ - if (ctrl_is_pointer(kcontrols->id)) + if (ctrl_is_pointer(id)) size -= sizeof(ucontrols->value64); if (copy_in_user(ucontrols, kcontrols, size)) return -EFAULT; @@ -770,7 +778,7 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) get_user(tmp, &up->edid) || copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) return -EFAULT; - kp->edid = compat_ptr(tmp); + kp->edid = (__force u8 *)compat_ptr(tmp); return 0; } @@ -783,7 +791,7 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) put_user(kp->start_block, &up->start_block) || put_user(kp->blocks, &up->blocks) || put_user(tmp, &up->edid) || - copy_to_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) return -EFAULT; return 0; } diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f030d6a..8601214 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -796,6 +796,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop"; case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; + case V4L2_CID_PAN_SPEED: return "Pan, Speed"; + case V4L2_CID_TILT_SPEED: return "Tilt, Speed"; /* FM Radio Modulator controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -859,6 +861,10 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_VBLANK: return "Vertical Blanking"; case V4L2_CID_HBLANK: return "Horizontal Blanking"; case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; + case V4L2_CID_TEST_PATTERN_RED: return "Red Pixel Value"; + case V4L2_CID_TEST_PATTERN_GREENR: return "Green (Red) Pixel Value"; + case V4L2_CID_TEST_PATTERN_BLUE: return "Blue Pixel Value"; + case V4L2_CID_TEST_PATTERN_GREENB: return "Green (Blue) Pixel Value"; /* Image processing controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index ce1c9f5..b1d8dbb 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -164,7 +164,8 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, bt->width > cap->max_width || bt->pixelclock < cap->min_pixelclock || bt->pixelclock > cap->max_pixelclock || - (cap->standards && !(bt->standards & cap->standards)) || + (cap->standards && bt->standards && + !(bt->standards & cap->standards)) || (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) return false; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index d15e1673..9ccb19a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -562,7 +562,7 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) pr_cont("class=0x%x, count=%d, error_idx=%d", p->ctrl_class, p->count, p->error_idx); for (i = 0; i < p->count; i++) { - if (p->controls[i].size) + if (!p->controls[i].size) pr_cont(", id/val=0x%x/0x%x", p->controls[i].id, p->controls[i].value); else @@ -1153,9 +1153,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { - struct v4l2_clip *clips = p->fmt.win.clips; + struct v4l2_clip __user *clips = p->fmt.win.clips; u32 clipcount = p->fmt.win.clipcount; - void *bitmap = p->fmt.win.bitmap; + void __user *bitmap = p->fmt.win.bitmap; memset(&p->fmt, 0, sizeof(p->fmt)); p->fmt.win.clips = clips; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b4d235c..543631c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -501,11 +501,20 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { + /* The width, height and code must match. */ if (source_fmt->format.width != sink_fmt->format.width || source_fmt->format.height != sink_fmt->format.height || source_fmt->format.code != sink_fmt->format.code) return -EINVAL; + /* The field order must match, or the sink field order must be NONE + * to support interlaced hardware connected to bridges that support + * progressive formats only. + */ + if (source_fmt->format.field != sink_fmt->format.field && + sink_fmt->format.field != V4L2_FIELD_NONE) + return -EINVAL; + return 0; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index fb5ee5d..b91a266 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -441,11 +441,6 @@ int videobuf_reqbufs(struct videobuf_queue *q, unsigned int size, count; int retval; - if (req->count < 1) { - dprintk(1, "reqbufs: count invalid (%d)\n", req->count); - return -EINVAL; - } - if (req->memory != V4L2_MEMORY_MMAP && req->memory != V4L2_MEMORY_USERPTR && req->memory != V4L2_MEMORY_OVERLAY) { @@ -471,6 +466,12 @@ int videobuf_reqbufs(struct videobuf_queue *q, goto done; } + if (req->count == 0) { + dprintk(1, "reqbufs: count invalid (%d)\n", req->count); + retval = __videobuf_free(q); + goto done; + } + count = req->count; if (count > VIDEO_MAX_FRAME) count = VIDEO_MAX_FRAME; diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 3c8cc02..3ff15f1 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -253,9 +253,11 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, return 0; out_free_pages: while (i > 0) { - void *addr = page_address(dma->vaddr_pages[i]); - dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); + void *addr; + i--; + addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); } kfree(dma->dma_addr); dma->dma_addr = NULL; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 25d3ae2..f2e43de 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -36,7 +36,7 @@ module_param(debug, int, 0644); #define dprintk(level, fmt, arg...) \ do { \ if (debug >= level) \ - pr_debug("vb2: %s: " fmt, __func__, ## arg); \ + pr_info("vb2: %s: " fmt, __func__, ## arg); \ } while (0) #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -882,7 +882,9 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * We already have buffers allocated, so first check if they * are not in use and can be freed. */ + mutex_lock(&q->mmap_lock); if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { + mutex_unlock(&q->mmap_lock); dprintk(1, "memory in use, cannot free\n"); return -EBUSY; } @@ -894,6 +896,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) */ __vb2_queue_cancel(q); ret = __vb2_queue_free(q, q->num_buffers); + mutex_unlock(&q->mmap_lock); if (ret) return ret; @@ -955,6 +958,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) */ } + mutex_lock(&q->mmap_lock); q->num_buffers = allocated_buffers; if (ret < 0) { @@ -963,8 +967,10 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * from q->num_buffers. */ __vb2_queue_free(q, allocated_buffers); + mutex_unlock(&q->mmap_lock); return ret; } + mutex_unlock(&q->mmap_lock); /* * Return the number of successfully allocated buffers @@ -1063,6 +1069,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create */ } + mutex_lock(&q->mmap_lock); q->num_buffers += allocated_buffers; if (ret < 0) { @@ -1071,8 +1078,10 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create * from q->num_buffers. */ __vb2_queue_free(q, allocated_buffers); + mutex_unlock(&q->mmap_lock); return -ENOMEM; } + mutex_unlock(&q->mmap_lock); /* * Return the number of successfully allocated buffers @@ -1581,7 +1590,6 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; - struct rw_semaphore *mmap_sem; int ret; ret = __verify_length(vb, b); @@ -1618,26 +1626,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - /* - * In case of user pointer buffers vb2 allocators need to get - * direct access to userspace pages. This requires getting - * the mmap semaphore for read access in the current process - * structure. The same semaphore is taken before calling mmap - * operation, while both qbuf/prepare_buf and mmap are called - * by the driver or v4l2 core with the driver's lock held. - * To avoid an AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in qbuf/prepare_buf), - * the videobuf2 core releases the driver's lock, takes - * mmap_sem and then takes the driver's lock again. - */ - mmap_sem = ¤t->mm->mmap_sem; - call_void_qop(q, wait_prepare, q); - down_read(mmap_sem); - call_void_qop(q, wait_finish, q); - + down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - - up_read(mmap_sem); + up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); @@ -2504,7 +2495,9 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) return -EINVAL; } + mutex_lock(&q->mmap_lock); ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma); + mutex_unlock(&q->mmap_lock); if (ret) return ret; @@ -2523,6 +2516,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, unsigned long off = pgoff << PAGE_SHIFT; struct vb2_buffer *vb; unsigned int buffer, plane; + void *vaddr; int ret; if (q->memory != V4L2_MEMORY_MMAP) { @@ -2539,7 +2533,8 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, vb = q->bufs[buffer]; - return (unsigned long)vb2_plane_vaddr(vb, plane); + vaddr = vb2_plane_vaddr(vb, plane); + return vaddr ? (unsigned long)vaddr : -EINVAL; } EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); #endif @@ -2686,6 +2681,7 @@ int vb2_queue_init(struct vb2_queue *q) INIT_LIST_HEAD(&q->queued_list); INIT_LIST_HEAD(&q->done_list); spin_lock_init(&q->done_lock); + mutex_init(&q->mmap_lock); init_waitqueue_head(&q->done_wq); if (q->buf_struct_size == 0) @@ -2707,7 +2703,9 @@ void vb2_queue_release(struct vb2_queue *q) { __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); + mutex_lock(&q->mmap_lock); __vb2_queue_free(q, q->num_buffers); + mutex_unlock(&q->mmap_lock); } EXPORT_SYMBOL_GPL(vb2_queue_release); @@ -2985,6 +2983,12 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ buf->queued = 0; buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0) : vb2_plane_size(q->bufs[index], 0); + /* Compensate for data_offset on read in the multiplanar case. */ + if (is_multiplanar && read && + fileio->b.m.planes[0].data_offset < buf->size) { + buf->pos = fileio->b.m.planes[0].data_offset; + buf->size -= buf->pos; + } } else { buf = &fileio->bufs[index]; } @@ -3372,15 +3376,8 @@ EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int err; - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - err = vb2_mmap(vdev->queue, vma); - if (lock) - mutex_unlock(lock); - return err; + return vb2_mmap(vdev->queue, vma); } EXPORT_SYMBOL_GPL(vb2_fop_mmap); @@ -3499,15 +3496,8 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int ret; - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - ret = vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); - if (lock) - mutex_unlock(lock); - return ret; + return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); } EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); #endif |