diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 16:58:16 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 16:58:16 (GMT) |
commit | 240c3c3424366c8109babd2a0fe80855de511b35 (patch) | |
tree | 72eb8652c8e513715efee1e254644b4b670333fd /drivers/media | |
parent | 19b344efa35dbc253e2d10403dafe6aafda73c56 (diff) | |
parent | df90e2258950fd631cdbf322c1ee1f22068391aa (diff) | |
download | linux-fsl-qoriq-240c3c3424366c8109babd2a0fe80855de511b35.tar.xz |
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media update from Mauro Carvalho Chehab:
- OF documentation and patches at core and drivers, to be used by for
embedded media systems
- some I2C drivers used on go7007 were rewritten/promoted from staging:
sony-btf-mpx, tw2804, tw9903, tw9906, wis-ov7640, wis-uda1342
- add fimc-is driver (Exynos)
- add a new radio driver: radio-si476x
- add a two new tuners: r820t and tuner_it913x
- split camera code on em28xx driver and add more models
- the cypress firmware load is used outside dvb usb drivers. So, move
it to a common directory to make easier to re-use it
- siano media driver updated to work with sms2270 devices
- several work done in order to promote go7007 and solo6x1x out of
staging (still, there are some pending issues)
- several API compliance fixes at v4l2 drivers that don't behave as
expected
- as usual, lots of driver fixes, improvements, cleanups and new device
addition at the existing drivers.
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (831 commits)
[media] cx88: make core less verbose
[media] em28xx: fix oops at em28xx_dvb_bus_ctrl()
[media] s5c73m3: fix indentation of the help section in Kconfig
[media] cx25821-alsa: get rid of a __must_check warning
[media] cx25821-video: declare cx25821_vidioc_s_std as static
[media] cx25821-video: remove maxw from cx25821_vidioc_try_fmt_vid_cap
[media] r820t: Remove a warning for an unused value
[media] dib0090: Fix a warning at dib0090_set_EFUSE
[media] dib8000: fix a warning
[media] dib8000: Fix sub-channel range
[media] dib8000: store dtv_property_cache in a temp var
[media] dib8000: warning fix: declare internal functions as static
[media] r820t: quiet gcc warning on n_ring
[media] r820t: memory leak in release()
[media] r820t: precendence bug in r820t_xtal_check()
[media] videodev2.h: Remove the unused old V4L1 buffer types
[media] anysee: Grammar s/report the/report to/
[media] anysee: Initialize ret = 0 in anysee_frontend_attach()
[media] media: videobuf2: fix the length check for mmap
[media] em28xx: save isoc endpoint number for DVB only if endpoint has alt settings with xMaxPacketSize != 0
...
Diffstat (limited to 'drivers/media')
527 files changed, 32634 insertions, 19278 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 56c25e6..b85f88c 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -16,6 +16,10 @@ config VIDEO_TVEEPROM tristate depends on I2C +config CYPRESS_FIRMWARE + tristate "Cypress firmware helper routines" + depends on USB + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 8f8d187..d208de3 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -2,3 +2,4 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o diff --git a/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c index 850a6c6..7e14e90 100644 --- a/drivers/media/common/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c @@ -325,7 +325,7 @@ static int skystar2_rev27_attach(struct flexcop_device *fc, /* enable no_base_addr - no repeated start when reading */ fc->fc_i2c_adap[2].no_base_addr = 1; if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 1, 1)) { + 0x08, 1, 1, false)) { err("ISL6421 could NOT be attached"); goto fail_isl; } @@ -391,7 +391,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, fc->fc_i2c_adap[2].no_base_addr = 1; if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 0, 0)) { + 0x08, 0, 0, false)) { err("ISL6421 could NOT be attached"); fc->fc_i2c_adap[2].no_base_addr = 0; return 0; diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c b/drivers/media/common/cypress_firmware.c index 211df54..577e820 100644 --- a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c +++ b/drivers/media/common/cypress_firmware.c @@ -8,7 +8,10 @@ * */ -#include "dvb_usb.h" +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/firmware.h> #include "cypress_firmware.h" struct usb_cypress_controller { @@ -30,14 +33,42 @@ static const struct usb_cypress_controller cypress[] = { static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data, u8 len) { - dvb_usb_dbg_usb_control_msg(udev, - 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len); - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); } -int usbv2_cypress_load_firmware(struct usb_device *udev, +static int cypress_get_hexline(const struct firmware *fw, + struct hexline *hx, int *pos) +{ + u8 *b = (u8 *) &fw->data[*pos]; + int data_offs = 4; + + if (*pos >= fw->size) + return 0; + + memset(hx, 0, sizeof(struct hexline)); + hx->len = b[0]; + + if ((*pos + hx->len + 4) >= fw->size) + return -EINVAL; + + hx->addr = b[1] | (b[2] << 8); + hx->type = b[3]; + + if (hx->type == 0x04) { + /* b[4] and b[5] are the Extended linear address record data + * field */ + hx->addr |= (b[4] << 24) | (b[5] << 16); + } + + memcpy(hx->data, &b[data_offs], hx->len); + hx->chk = b[hx->len + data_offs]; + *pos += hx->len + 5; + + return *pos; +} + +int cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { struct hexline *hx; @@ -61,7 +92,7 @@ int usbv2_cypress_load_firmware(struct usb_device *udev, /* write firmware to memory */ for (;;) { - ret = dvb_usbv2_get_hexline(fw, hx, &pos); + ret = cypress_get_hexline(fw, hx, &pos); if (ret < 0) goto err_kfree; else if (ret == 0) @@ -71,9 +102,8 @@ int usbv2_cypress_load_firmware(struct usb_device *udev, if (ret < 0) { goto err_kfree; } else if (ret != hx->len) { - dev_err(&udev->dev, "%s: error while transferring " \ - "firmware (transferred size=%d, " \ - "block size=%d)\n", + dev_err(&udev->dev, + "%s: error while transferring firmware (transferred size=%d, block size=%d)\n", KBUILD_MODNAME, ret, hx->len); ret = -EIO; goto err_kfree; @@ -95,39 +125,7 @@ err_kfree: kfree(hx); return ret; } -EXPORT_SYMBOL(usbv2_cypress_load_firmware); - -int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx, - int *pos) -{ - u8 *b = (u8 *) &fw->data[*pos]; - int data_offs = 4; - - if (*pos >= fw->size) - return 0; - - memset(hx, 0, sizeof(struct hexline)); - hx->len = b[0]; - - if ((*pos + hx->len + 4) >= fw->size) - return -EINVAL; - - hx->addr = b[1] | (b[2] << 8); - hx->type = b[3]; - - if (hx->type == 0x04) { - /* b[4] and b[5] are the Extended linear address record data - * field */ - hx->addr |= (b[4] << 24) | (b[5] << 16); - } - - memcpy(hx->data, &b[data_offs], hx->len); - hx->chk = b[hx->len + data_offs]; - *pos += hx->len + 5; - - return *pos; -} -EXPORT_SYMBOL(dvb_usbv2_get_hexline); +EXPORT_SYMBOL(cypress_load_firmware); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Cypress firmware download"); diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.h b/drivers/media/common/cypress_firmware.h index 80085fd..e493cbc 100644 --- a/drivers/media/usb/dvb-usb-v2/cypress_firmware.h +++ b/drivers/media/common/cypress_firmware.h @@ -1,5 +1,4 @@ -/* cypress_firmware.h is part of the DVB USB library. - * +/* * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) * see dvb-usb-init.c for copyright information. * @@ -23,9 +22,7 @@ struct hexline { u8 data[255]; u8 chk; }; -extern int usbv2_cypress_load_firmware(struct usb_device *, - const struct firmware *, int); -extern int dvb_usbv2_get_hexline(const struct firmware *, - struct hexline *, int *); + +int cypress_load_firmware(struct usb_device *, const struct firmware *, int); #endif diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c index 4143d61..fe907f2 100644 --- a/drivers/media/common/saa7146/saa7146_video.c +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -832,7 +832,7 @@ static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) } */ -static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id) +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_vv *vv = dev->vv_data; @@ -856,7 +856,7 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id) } for (i = 0; i < dev->ext_vv_data->num_stds; i++) - if (*id & dev->ext_vv_data->stds[i].id) + if (id & dev->ext_vv_data->stds[i].id) break; if (i != dev->ext_vv_data->num_stds) { vv->standard = &dev->ext_vv_data->stds[i]; diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig index 68f0f60..f3f5ec4 100644 --- a/drivers/media/common/siano/Kconfig +++ b/drivers/media/common/siano/Kconfig @@ -17,3 +17,15 @@ config SMS_SIANO_RC default y ---help--- Choose Y to select Remote Controller support for Siano driver. + +config SMS_SIANO_DEBUGFS + bool "Enable debugfs for smsdvb" + depends on SMS_SIANO_MDTV + depends on DEBUG_FS + depends on SMS_USB_DRV + ---help--- + Choose Y to enable visualizing a dump of the frontend + statistics response packets via debugfs. Currently, works + only with Siano USB devices. + + Useful only for developers. In doubt, say N. diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile index 81b1e98..4c0567f 100644 --- a/drivers/media/common/siano/Makefile +++ b/drivers/media/common/siano/Makefile @@ -1,4 +1,5 @@ smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o +smsdvb-objs := smsdvb-main.o obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o @@ -6,6 +7,10 @@ ifeq ($(CONFIG_SMS_SIANO_RC),y) smsmdtv-objs += smsir.o endif +ifeq ($(CONFIG_SMS_SIANO_DEBUGFS),y) + smsdvb-objs += smsdvb-debugfs.o +endif + ccflags-y += -Idrivers/media/dvb-core ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c index 680c781..8276999 100644 --- a/drivers/media/common/siano/sms-cards.c +++ b/drivers/media/common/siano/sms-cards.c @@ -28,43 +28,53 @@ MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); static struct sms_board sms_boards[] = { [SMS_BOARD_UNKNOWN] = { .name = "Unknown board", + .type = SMS_UNKNOWN_TYPE, + .default_mode = DEVICE_MODE_NONE, }, [SMS1XXX_BOARD_SIANO_STELLAR] = { .name = "Siano Stellar Digital Receiver", .type = SMS_STELLAR, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_SIANO_NOVA_A] = { .name = "Siano Nova A Digital Receiver", .type = SMS_NOVA_A0, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_SIANO_NOVA_B] = { .name = "Siano Nova B Digital Receiver", .type = SMS_NOVA_B0, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_SIANO_VEGA] = { .name = "Siano Vega Digital Receiver", .type = SMS_VEGA, + .default_mode = DEVICE_MODE_CMMB, }, [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { .name = "Hauppauge Catamount", .type = SMS_STELLAR, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_STELLAR, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { .name = "Hauppauge Okemo-A", .type = SMS_NOVA_A0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_A, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { .name = "Hauppauge Okemo-B", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_B, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .fw[DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_HCW_55XXX, + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX, + .default_mode = DEVICE_MODE_DVBT_BDA, .rc_codes = RC_MAP_HAUPPAUGE, .board_cfg.leds_power = 26, .board_cfg.led0 = 27, @@ -77,7 +87,8 @@ static struct sms_board sms_boards[] = { [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = { .name = "Hauppauge WinTV MiniCard", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX, + .default_mode = DEVICE_MODE_DVBT_BDA, .lna_ctrl = 29, .board_cfg.foreign_lna0_ctrl = 29, .rf_switch = 17, @@ -86,18 +97,65 @@ static struct sms_board sms_boards[] = { [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = { .name = "Hauppauge WinTV MiniCard", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX, + .default_mode = DEVICE_MODE_DVBT_BDA, .lna_ctrl = -1, }, [SMS1XXX_BOARD_SIANO_NICE] = { - /* 11 */ .name = "Siano Nice Digital Receiver", .type = SMS_NOVA_B0, + .default_mode = DEVICE_MODE_DVBT_BDA, }, [SMS1XXX_BOARD_SIANO_VENICE] = { - /* 12 */ .name = "Siano Venice Digital Receiver", .type = SMS_VEGA, + .default_mode = DEVICE_MODE_CMMB, + }, + [SMS1XXX_BOARD_SIANO_STELLAR_ROM] = { + .name = "Siano Stellar Digital Receiver ROM", + .type = SMS_STELLAR, + .default_mode = DEVICE_MODE_DVBT_BDA, + .intf_num = 1, + }, + [SMS1XXX_BOARD_ZTE_DVB_DATA_CARD] = { + .name = "ZTE Data Card Digital Receiver", + .type = SMS_NOVA_B0, + .default_mode = DEVICE_MODE_DVBT_BDA, + .intf_num = 5, + .mtu = 15792, + }, + [SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD] = { + .name = "ONDA Data Card Digital Receiver", + .type = SMS_NOVA_B0, + .default_mode = DEVICE_MODE_DVBT_BDA, + .intf_num = 6, + .mtu = 15792, + }, + [SMS1XXX_BOARD_SIANO_MING] = { + .name = "Siano Ming Digital Receiver", + .type = SMS_MING, + .default_mode = DEVICE_MODE_CMMB, + }, + [SMS1XXX_BOARD_SIANO_PELE] = { + .name = "Siano Pele Digital Receiver", + .type = SMS_PELE, + .default_mode = DEVICE_MODE_ISDBT_BDA, + }, + [SMS1XXX_BOARD_SIANO_RIO] = { + .name = "Siano Rio Digital Receiver", + .type = SMS_RIO, + .default_mode = DEVICE_MODE_ISDBT_BDA, + }, + [SMS1XXX_BOARD_SIANO_DENVER_1530] = { + .name = "Siano Denver (ATSC-M/H) Digital Receiver", + .type = SMS_DENVER_1530, + .default_mode = DEVICE_MODE_ATSC, + .crystal = 2400, + }, + [SMS1XXX_BOARD_SIANO_DENVER_2160] = { + .name = "Siano Denver (TDMB) Digital Receiver", + .type = SMS_DENVER_2160, + .default_mode = DEVICE_MODE_DAB_TDMB, }, }; @@ -109,20 +167,21 @@ struct sms_board *sms_get_board(unsigned id) } EXPORT_SYMBOL_GPL(sms_get_board); static inline void sms_gpio_assign_11xx_default_led_config( - struct smscore_gpio_config *pGpioConfig) { - pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; - pGpioConfig->InputCharacteristics = - SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; - pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; - pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; - pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; + struct smscore_config_gpio *p_gpio_config) { + p_gpio_config->direction = SMS_GPIO_DIRECTION_OUTPUT; + p_gpio_config->inputcharacteristics = + SMS_GPIO_INPUTCHARACTERISTICS_NORMAL; + p_gpio_config->outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA; + p_gpio_config->outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; + p_gpio_config->pullupdown = SMS_GPIO_PULLUPDOWN_NONE; } int sms_board_event(struct smscore_device_t *coredev, - enum SMS_BOARD_EVENTS gevent) { - struct smscore_gpio_config MyGpioConfig; + enum SMS_BOARD_EVENTS gevent) +{ + struct smscore_config_gpio my_gpio_config; - sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); + sms_gpio_assign_11xx_default_led_config(&my_gpio_config); switch (gevent) { case BOARD_EVENT_POWER_INIT: /* including hotplug */ @@ -182,8 +241,8 @@ static int sms_set_gpio(struct smscore_device_t *coredev, int pin, int enable) .direction = SMS_GPIO_DIRECTION_OUTPUT, .pullupdown = SMS_GPIO_PULLUPDOWN_NONE, .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL, - .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST, - .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA, + .outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_FAST, + .outputdriving = SMS_GPIO_OUTPUTDRIVING_S_4mA, }; if (pin == 0) @@ -293,19 +352,7 @@ EXPORT_SYMBOL_GPL(sms_board_lna_control); int sms_board_load_modules(int id) { - switch (id) { - case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: - case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: - case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - request_module("smsdvb"); - break; - default: - /* do nothing */ - break; - } + request_module("smsdvb"); return 0; } EXPORT_SYMBOL_GPL(sms_board_load_modules); diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h index d8cdf75..c63b544 100644 --- a/drivers/media/common/siano/sms-cards.h +++ b/drivers/media/common/siano/sms-cards.h @@ -37,6 +37,14 @@ #define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 #define SMS1XXX_BOARD_SIANO_NICE 11 #define SMS1XXX_BOARD_SIANO_VENICE 12 +#define SMS1XXX_BOARD_SIANO_STELLAR_ROM 13 +#define SMS1XXX_BOARD_ZTE_DVB_DATA_CARD 14 +#define SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD 15 +#define SMS1XXX_BOARD_SIANO_MING 16 +#define SMS1XXX_BOARD_SIANO_PELE 17 +#define SMS1XXX_BOARD_SIANO_RIO 18 +#define SMS1XXX_BOARD_SIANO_DENVER_1530 19 +#define SMS1XXX_BOARD_SIANO_DENVER_2160 20 struct sms_board_gpio_cfg { int lna_vhf_exist; @@ -79,6 +87,12 @@ struct sms_board { /* gpios */ int led_power, led_hi, led_lo, lna_ctrl, rf_switch; + + char intf_num; + int default_mode; + unsigned int mtu; + unsigned int crystal; + struct sms_antenna_config_ST *antenna_config; }; struct sms_board *sms_get_board(unsigned id); diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index 1842e64..45ac9ee 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -37,7 +37,6 @@ #include "smscoreapi.h" #include "sms-cards.h" #include "smsir.h" -#include "smsendian.h" static int sms_dbg; module_param_named(debug, sms_dbg, int, 0644); @@ -58,11 +57,350 @@ struct smscore_client_t { struct list_head entry; struct smscore_device_t *coredev; void *context; - struct list_head idlist; - onresponse_t onresponse_handler; + struct list_head idlist; + onresponse_t onresponse_handler; onremove_t onremove_handler; }; +static char *siano_msgs[] = { + [MSG_TYPE_BASE_VAL - MSG_TYPE_BASE_VAL] = "MSG_TYPE_BASE_VAL", + [MSG_SMS_GET_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_REQ", + [MSG_SMS_GET_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_RES", + [MSG_SMS_MULTI_BRIDGE_CFG - MSG_TYPE_BASE_VAL] = "MSG_SMS_MULTI_BRIDGE_CFG", + [MSG_SMS_GPIO_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_REQ", + [MSG_SMS_GPIO_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_RES", + [MSG_SMS_GPIO_SET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_REQ", + [MSG_SMS_GPIO_SET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_RES", + [MSG_SMS_GPIO_GET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_REQ", + [MSG_SMS_GPIO_GET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_RES", + [MSG_SMS_EEPROM_BURN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_BURN_IND", + [MSG_SMS_LOG_ENABLE_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_REQ", + [MSG_SMS_LOG_ENABLE_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_RES", + [MSG_SMS_SET_MAX_TX_MSG_LEN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_REQ", + [MSG_SMS_SET_MAX_TX_MSG_LEN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_RES", + [MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE", + [MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST", + [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ", + [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES", + [MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND", + [MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND", + [MSG_SMS_CONFIGURE_RF_SWITCH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_REQ", + [MSG_SMS_CONFIGURE_RF_SWITCH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_RES", + [MSG_SMS_MRC_PATH_DISCONNECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_REQ", + [MSG_SMS_MRC_PATH_DISCONNECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_RES", + [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ", + [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES", + [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ", + [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES", + [MSG_WR_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_REQ", + [MSG_WR_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_RES", + [MSG_RD_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_REQ", + [MSG_RD_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_RES", + [MSG_RD_REG_ALL_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_REQ", + [MSG_RD_REG_ALL_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_RES", + [MSG_HELP_INT - MSG_TYPE_BASE_VAL] = "MSG_HELP_INT", + [MSG_RUN_SCRIPT_INT - MSG_TYPE_BASE_VAL] = "MSG_RUN_SCRIPT_INT", + [MSG_SMS_EWS_INBAND_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_REQ", + [MSG_SMS_EWS_INBAND_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_RES", + [MSG_SMS_RFS_SELECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_REQ", + [MSG_SMS_RFS_SELECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_RES", + [MSG_SMS_MB_GET_VER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_REQ", + [MSG_SMS_MB_GET_VER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_RES", + [MSG_SMS_MB_WRITE_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_REQ", + [MSG_SMS_MB_WRITE_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_RES", + [MSG_SMS_MB_READ_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_REQ", + [MSG_SMS_MB_READ_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_RES", + [MSG_SMS_RD_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_REQ", + [MSG_SMS_RD_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_RES", + [MSG_SMS_WR_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_REQ", + [MSG_SMS_WR_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_RES", + [MSG_SMS_UPDATE_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_REQ", + [MSG_SMS_UPDATE_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_RES", + [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ", + [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES", + [MSG_SMS_RF_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_REQ", + [MSG_SMS_RF_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_RES", + [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ", + [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES", + [MSG_SMS_ISDBT_SB_RECEPTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_REQ", + [MSG_SMS_ISDBT_SB_RECEPTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_RES", + [MSG_SMS_GENERIC_EPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_REQ", + [MSG_SMS_GENERIC_EPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_RES", + [MSG_SMS_GENERIC_EPROM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_REQ", + [MSG_SMS_GENERIC_EPROM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_RES", + [MSG_SMS_EEPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_REQ", + [MSG_SMS_EEPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_RES", + [MSG_SMS_CUSTOM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_REQ", + [MSG_SMS_CUSTOM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_RES", + [MSG_SMS_CUSTOM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_REQ", + [MSG_SMS_CUSTOM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_RES", + [MSG_SMS_INIT_DEVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_REQ", + [MSG_SMS_INIT_DEVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_RES", + [MSG_SMS_ATSC_SET_ALL_IP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_REQ", + [MSG_SMS_ATSC_SET_ALL_IP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_RES", + [MSG_SMS_ATSC_START_ENSEMBLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_REQ", + [MSG_SMS_ATSC_START_ENSEMBLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_RES", + [MSG_SMS_SET_OUTPUT_MODE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_REQ", + [MSG_SMS_SET_OUTPUT_MODE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_RES", + [MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ", + [MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES", + [MSG_SMS_SUB_CHANNEL_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_REQ", + [MSG_SMS_SUB_CHANNEL_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_RES", + [MSG_SMS_SUB_CHANNEL_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_REQ", + [MSG_SMS_SUB_CHANNEL_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_RES", + [MSG_SMS_ATSC_IP_FILTER_ADD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_REQ", + [MSG_SMS_ATSC_IP_FILTER_ADD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_RES", + [MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ", + [MSG_SMS_ATSC_IP_FILTER_REMOVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_RES", + [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ", + [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES", + [MSG_SMS_WAIT_CMD - MSG_TYPE_BASE_VAL] = "MSG_SMS_WAIT_CMD", + [MSG_SMS_ADD_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_REQ", + [MSG_SMS_ADD_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_RES", + [MSG_SMS_REMOVE_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_REQ", + [MSG_SMS_REMOVE_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_RES", + [MSG_SMS_FAST_INFORMATION_CHANNEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_REQ", + [MSG_SMS_FAST_INFORMATION_CHANNEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_RES", + [MSG_SMS_DAB_CHANNEL - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_CHANNEL", + [MSG_SMS_GET_PID_FILTER_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_REQ", + [MSG_SMS_GET_PID_FILTER_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_RES", + [MSG_SMS_POWER_DOWN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_REQ", + [MSG_SMS_POWER_DOWN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_RES", + [MSG_SMS_ATSC_SLT_EXIST_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SLT_EXIST_IND", + [MSG_SMS_ATSC_NO_SLT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_NO_SLT_IND", + [MSG_SMS_GET_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_REQ", + [MSG_SMS_GET_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_RES", + [MSG_SMS_SEND_DUMP - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_DUMP", + [MSG_SMS_SCAN_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_REQ", + [MSG_SMS_SCAN_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_RES", + [MSG_SMS_SCAN_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_REQ", + [MSG_SMS_SCAN_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_RES", + [MSG_SMS_SCAN_PROGRESS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_PROGRESS_IND", + [MSG_SMS_SCAN_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_COMPLETE_IND", + [MSG_SMS_LOG_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ITEM", + [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ", + [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES", + [MSG_SMS_HO_PER_SLICES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PER_SLICES_IND", + [MSG_SMS_HO_INBAND_POWER_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_INBAND_POWER_IND", + [MSG_SMS_MANUAL_DEMOD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MANUAL_DEMOD_REQ", + [MSG_SMS_HO_TUNE_ON_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_REQ", + [MSG_SMS_HO_TUNE_ON_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_RES", + [MSG_SMS_HO_TUNE_OFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_REQ", + [MSG_SMS_HO_TUNE_OFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_RES", + [MSG_SMS_HO_PEEK_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_REQ", + [MSG_SMS_HO_PEEK_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_RES", + [MSG_SMS_HO_PEEK_FREQ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_IND", + [MSG_SMS_MB_ATTEN_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_REQ", + [MSG_SMS_MB_ATTEN_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_RES", + [MSG_SMS_ENABLE_STAT_IN_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_REQ", + [MSG_SMS_ENABLE_STAT_IN_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_RES", + [MSG_SMS_SET_ANTENNA_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_REQ", + [MSG_SMS_SET_ANTENNA_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_RES", + [MSG_SMS_GET_STATISTICS_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_REQ", + [MSG_SMS_GET_STATISTICS_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_RES", + [MSG_SMS_SLEEP_RESUME_COMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLEEP_RESUME_COMP_IND", + [MSG_SMS_SWITCH_HOST_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_REQ", + [MSG_SMS_SWITCH_HOST_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_RES", + [MSG_SMS_DATA_DOWNLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_REQ", + [MSG_SMS_DATA_DOWNLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_RES", + [MSG_SMS_DATA_VALIDITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_REQ", + [MSG_SMS_DATA_VALIDITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_RES", + [MSG_SMS_SWDOWNLOAD_TRIGGER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_REQ", + [MSG_SMS_SWDOWNLOAD_TRIGGER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_RES", + [MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ", + [MSG_SMS_SWDOWNLOAD_BACKDOOR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_RES", + [MSG_SMS_GET_VERSION_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_REQ", + [MSG_SMS_GET_VERSION_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_RES", + [MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ", + [MSG_SMS_CLOCK_OUTPUT_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_RES", + [MSG_SMS_I2C_SET_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_REQ", + [MSG_SMS_I2C_SET_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_RES", + [MSG_SMS_GENERIC_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_REQ", + [MSG_SMS_GENERIC_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_RES", + [MSG_SMS_DVBT_BDA_DATA - MSG_TYPE_BASE_VAL] = "MSG_SMS_DVBT_BDA_DATA", + [MSG_SW_RELOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_REQ", + [MSG_SMS_DATA_MSG - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_MSG", + [MSG_TABLE_UPLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_REQ", + [MSG_TABLE_UPLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_RES", + [MSG_SW_RELOAD_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_REQ", + [MSG_SW_RELOAD_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_RES", + [MSG_SW_RELOAD_EXEC_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_REQ", + [MSG_SW_RELOAD_EXEC_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_RES", + [MSG_SMS_SPI_INT_LINE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_REQ", + [MSG_SMS_SPI_INT_LINE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_RES", + [MSG_SMS_GPIO_CONFIG_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_REQ", + [MSG_SMS_GPIO_CONFIG_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_RES", + [MSG_SMS_WATCHDOG_ACT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_REQ", + [MSG_SMS_WATCHDOG_ACT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_RES", + [MSG_SMS_LOOPBACK_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_REQ", + [MSG_SMS_LOOPBACK_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_RES", + [MSG_SMS_RAW_CAPTURE_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_REQ", + [MSG_SMS_RAW_CAPTURE_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_RES", + [MSG_SMS_RAW_CAPTURE_ABORT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_REQ", + [MSG_SMS_RAW_CAPTURE_ABORT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_RES", + [MSG_SMS_RAW_CAPTURE_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_COMPLETE_IND", + [MSG_SMS_DATA_PUMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_IND", + [MSG_SMS_DATA_PUMP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_REQ", + [MSG_SMS_DATA_PUMP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_RES", + [MSG_SMS_FLASH_DL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FLASH_DL_REQ", + [MSG_SMS_EXEC_TEST_1_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_REQ", + [MSG_SMS_EXEC_TEST_1_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_RES", + [MSG_SMS_ENBALE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_REQ", + [MSG_SMS_ENBALE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_RES", + [MSG_SMS_SPI_SET_BUS_WIDTH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_REQ", + [MSG_SMS_SPI_SET_BUS_WIDTH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_RES", + [MSG_SMS_SEND_EMM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_REQ", + [MSG_SMS_SEND_EMM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_RES", + [MSG_SMS_DISABLE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_REQ", + [MSG_SMS_DISABLE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_RES", + [MSG_SMS_IS_BUF_FREE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_REQ", + [MSG_SMS_IS_BUF_FREE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_RES", + [MSG_SMS_EXT_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_REQ", + [MSG_SMS_EXT_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_RES", + [MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE", + [MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE", + [MSG_SMS_BATTERY_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_REQ", + [MSG_SMS_BATTERY_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_RES", + [MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE", + [MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE", + [MSG_SMS_FM_RADIO_BLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_FM_RADIO_BLOCK_IND", + [MSG_SMS_HOST_NOTIFICATION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HOST_NOTIFICATION_IND", + [MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE", + [MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE", + [MSG_SMS_CMMB_GET_NETWORKS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_REQ", + [MSG_SMS_CMMB_GET_NETWORKS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_RES", + [MSG_SMS_CMMB_START_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_REQ", + [MSG_SMS_CMMB_START_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_RES", + [MSG_SMS_CMMB_STOP_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_REQ", + [MSG_SMS_CMMB_STOP_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_RES", + [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ", + [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES", + [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ", + [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES", + [MSG_SMS_CMMB_START_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_REQ", + [MSG_SMS_CMMB_START_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_RES", + [MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ", + [MSG_SMS_CMMB_STOP_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_RES", + [MSG_SMS_ISDBT_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_REQ", + [MSG_SMS_ISDBT_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_RES", + [MSG_SMS_TRANSMISSION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_TRANSMISSION_IND", + [MSG_SMS_PID_STATISTICS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_PID_STATISTICS_IND", + [MSG_SMS_POWER_DOWN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_IND", + [MSG_SMS_POWER_DOWN_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_CONF", + [MSG_SMS_POWER_UP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_IND", + [MSG_SMS_POWER_UP_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_CONF", + [MSG_SMS_POWER_MODE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_REQ", + [MSG_SMS_POWER_MODE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_RES", + [MSG_SMS_DEBUG_HOST_EVENT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_REQ", + [MSG_SMS_DEBUG_HOST_EVENT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_RES", + [MSG_SMS_NEW_CRYSTAL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_REQ", + [MSG_SMS_NEW_CRYSTAL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_RES", + [MSG_SMS_CONFIG_SPI_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_REQ", + [MSG_SMS_CONFIG_SPI_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_RES", + [MSG_SMS_I2C_SHORT_STAT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SHORT_STAT_IND", + [MSG_SMS_START_IR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_REQ", + [MSG_SMS_START_IR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_RES", + [MSG_SMS_IR_SAMPLES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_IR_SAMPLES_IND", + [MSG_SMS_CMMB_CA_SERVICE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_CA_SERVICE_IND", + [MSG_SMS_SLAVE_DEVICE_DETECTED - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLAVE_DEVICE_DETECTED", + [MSG_SMS_INTERFACE_LOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_LOCK_IND", + [MSG_SMS_INTERFACE_UNLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_UNLOCK_IND", + [MSG_SMS_SEND_ROSUM_BUFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_REQ", + [MSG_SMS_SEND_ROSUM_BUFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_RES", + [MSG_SMS_ROSUM_BUFF - MSG_TYPE_BASE_VAL] = "MSG_SMS_ROSUM_BUFF", + [MSG_SMS_SET_AES128_KEY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_REQ", + [MSG_SMS_SET_AES128_KEY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_RES", + [MSG_SMS_MBBMS_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_REQ", + [MSG_SMS_MBBMS_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_RES", + [MSG_SMS_MBBMS_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_READ_IND", + [MSG_SMS_IQ_STREAM_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_REQ", + [MSG_SMS_IQ_STREAM_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_RES", + [MSG_SMS_IQ_STREAM_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_REQ", + [MSG_SMS_IQ_STREAM_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_RES", + [MSG_SMS_IQ_STREAM_DATA_BLOCK - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_DATA_BLOCK", + [MSG_SMS_GET_EEPROM_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_REQ", + [MSG_SMS_GET_EEPROM_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_RES", + [MSG_SMS_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SIGNAL_DETECTED_IND", + [MSG_SMS_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_NO_SIGNAL_IND", + [MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ", + [MSG_SMS_MRC_SHUTDOWN_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_RES", + [MSG_SMS_MRC_BRINGUP_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_REQ", + [MSG_SMS_MRC_BRINGUP_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_RES", + [MSG_SMS_EXTERNAL_LNA_CTRL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_REQ", + [MSG_SMS_EXTERNAL_LNA_CTRL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_RES", + [MSG_SMS_SET_PERIODIC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_REQ", + [MSG_SMS_SET_PERIODIC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_RES", + [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ", + [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES", + [LOCAL_TUNE - MSG_TYPE_BASE_VAL] = "LOCAL_TUNE", + [LOCAL_IFFT_H_ICI - MSG_TYPE_BASE_VAL] = "LOCAL_IFFT_H_ICI", + [MSG_RESYNC_REQ - MSG_TYPE_BASE_VAL] = "MSG_RESYNC_REQ", + [MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ", + [MSG_SMS_CMMB_GET_MRC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_RES", + [MSG_SMS_LOG_EX_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_EX_ITEM", + [MSG_SMS_DEVICE_DATA_LOSS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEVICE_DATA_LOSS_IND", + [MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND", + [MSG_SMS_USER_MSG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_REQ", + [MSG_SMS_USER_MSG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_RES", + [MSG_SMS_SMART_CARD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_REQ", + [MSG_SMS_SMART_CARD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_RES", + [MSG_SMS_SMART_CARD_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_REQ", + [MSG_SMS_SMART_CARD_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_RES", + [MSG_SMS_SMART_CARD_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_READ_IND", + [MSG_SMS_TSE_ENABLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_REQ", + [MSG_SMS_TSE_ENABLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_RES", + [MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ", + [MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES", + [MSG_SMS_LED_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_REQ", + [MSG_SMS_LED_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_RES", + [MSG_PWM_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_REQ", + [MSG_PWM_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_RES", + [MSG_SMS_CMMB_SMD_SN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_REQ", + [MSG_SMS_CMMB_SMD_SN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_RES", + [MSG_SMS_CMMB_SET_CA_CW_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_REQ", + [MSG_SMS_CMMB_SET_CA_CW_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_RES", + [MSG_SMS_CMMB_SET_CA_SALT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_REQ", + [MSG_SMS_CMMB_SET_CA_SALT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_RES", + [MSG_SMS_NSCD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_REQ", + [MSG_SMS_NSCD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_RES", + [MSG_SMS_NSCD_PROCESS_SECTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_REQ", + [MSG_SMS_NSCD_PROCESS_SECTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_RES", + [MSG_SMS_DBD_CREATE_OBJECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_REQ", + [MSG_SMS_DBD_CREATE_OBJECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_RES", + [MSG_SMS_DBD_CONFIGURE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_REQ", + [MSG_SMS_DBD_CONFIGURE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_RES", + [MSG_SMS_DBD_SET_KEYS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_REQ", + [MSG_SMS_DBD_SET_KEYS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_RES", + [MSG_SMS_DBD_PROCESS_HEADER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_REQ", + [MSG_SMS_DBD_PROCESS_HEADER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_RES", + [MSG_SMS_DBD_PROCESS_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_REQ", + [MSG_SMS_DBD_PROCESS_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_RES", + [MSG_SMS_DBD_PROCESS_GET_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_REQ", + [MSG_SMS_DBD_PROCESS_GET_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_RES", + [MSG_SMS_NSCD_OPEN_SESSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_REQ", + [MSG_SMS_NSCD_OPEN_SESSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_RES", + [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ", + [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES", + [MSG_LAST_MSG_TYPE - MSG_TYPE_BASE_VAL] = "MSG_LAST_MSG_TYPE", +}; + +char *smscore_translate_msg(enum msg_types msgtype) +{ + int i = msgtype - MSG_TYPE_BASE_VAL; + char *msg; + + if (i < 0 || i >= ARRAY_SIZE(siano_msgs)) + return "Unknown msg type"; + + msg = siano_msgs[i]; + + if (!*msg) + return "Unknown msg type"; + + return msg; +} +EXPORT_SYMBOL_GPL(smscore_translate_msg); + void smscore_set_board_id(struct smscore_device_t *core, int id) { core->board_id = id; @@ -96,7 +434,7 @@ static struct mutex g_smscore_deviceslock; static struct list_head g_smscore_registry; static struct mutex g_smscore_registrylock; -static int default_mode = 4; +static int default_mode = DEVICE_MODE_NONE; module_param(default_mode, int, 0644); MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); @@ -151,10 +489,10 @@ static enum sms_device_type_st smscore_registry_gettype(char *devpath) else sms_err("No registry found."); - return -1; + return -EINVAL; } -void smscore_registry_setmode(char *devpath, int mode) +static void smscore_registry_setmode(char *devpath, int mode) { struct smscore_registry_entry_t *entry; @@ -294,10 +632,11 @@ static struct smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, dma_addr_t common_buffer_phys) { - struct smscore_buffer_t *cb = - kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); + struct smscore_buffer_t *cb; + + cb = kzalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); if (!cb) { - sms_info("kmalloc(...) failed"); + sms_info("kzalloc(...) failed"); return NULL; } @@ -344,6 +683,7 @@ int smscore_register_device(struct smsdevice_params_t *params, /* init completion events */ init_completion(&dev->version_ex_done); init_completion(&dev->data_download_done); + init_completion(&dev->data_validity_done); init_completion(&dev->trigger_done); init_completion(&dev->init_device_done); init_completion(&dev->reload_start_done); @@ -370,9 +710,10 @@ int smscore_register_device(struct smsdevice_params_t *params, for (buffer = dev->common_buffer; dev->num_buffers < params->num_buffers; dev->num_buffers++, buffer += params->buffer_size) { - struct smscore_buffer_t *cb = - smscore_createbuffer(buffer, dev->common_buffer, - dev->common_buffer_phys); + struct smscore_buffer_t *cb; + + cb = smscore_createbuffer(buffer, dev->common_buffer, + dev->common_buffer_phys); if (!cb) { smscore_unregister_device(dev); return -ENOMEM; @@ -384,6 +725,7 @@ int smscore_register_device(struct smsdevice_params_t *params, sms_info("allocated %d buffers", dev->num_buffers); dev->mode = DEVICE_MODE_NONE; + dev->board_id = SMS_BOARD_UNKNOWN; dev->context = params->context; dev->device = params->device; dev->setmode_handler = params->setmode_handler; @@ -413,7 +755,13 @@ EXPORT_SYMBOL_GPL(smscore_register_device); static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev, void *buffer, size_t size, struct completion *completion) { - int rc = coredev->sendrequest_handler(coredev->context, buffer, size); + int rc; + + if (completion == NULL) + return -EINVAL; + init_completion(completion); + + rc = coredev->sendrequest_handler(coredev->context, buffer, size); if (rc < 0) { sms_info("sendrequest returned error %d", rc); return rc; @@ -444,24 +792,22 @@ static int smscore_init_ir(struct smscore_device_t *coredev) if (rc != 0) sms_err("Error initialization DTV IR sub-module"); else { - buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + + buffer = kmalloc(sizeof(struct sms_msg_data2) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); if (buffer) { - struct SmsMsgData_ST2 *msg = - (struct SmsMsgData_ST2 *) + struct sms_msg_data2 *msg = + (struct sms_msg_data2 *) SMS_ALIGN_ADDRESS(buffer); - SMS_INIT_MSG(&msg->xMsgHeader, + SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_START_IR_REQ, - sizeof(struct SmsMsgData_ST2)); - msg->msgData[0] = coredev->ir.controller; - msg->msgData[1] = coredev->ir.timeout; + sizeof(struct sms_msg_data2)); + msg->msg_data[0] = coredev->ir.controller; + msg->msg_data[1] = coredev->ir.timeout; - smsendian_handle_tx_message( - (struct SmsMsgHdr_ST2 *)msg); rc = smscore_sendrequest_and_wait(coredev, msg, - msg->xMsgHeader. msgLength, + msg->x_msg_header. msg_length, &coredev->ir_init_done); kfree(buffer); @@ -476,21 +822,82 @@ static int smscore_init_ir(struct smscore_device_t *coredev) } /** + * configures device features according to board configuration structure. + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * + * @return 0 on success, <0 on error. + */ +static int smscore_configure_board(struct smscore_device_t *coredev) +{ + struct sms_board *board; + + board = sms_get_board(coredev->board_id); + if (!board) { + sms_err("no board configuration exist."); + return -EINVAL; + } + + if (board->mtu) { + struct sms_msg_data mtu_msg; + sms_debug("set max transmit unit %d", board->mtu); + + mtu_msg.x_msg_header.msg_src_id = 0; + mtu_msg.x_msg_header.msg_dst_id = HIF_TASK; + mtu_msg.x_msg_header.msg_flags = 0; + mtu_msg.x_msg_header.msg_type = MSG_SMS_SET_MAX_TX_MSG_LEN_REQ; + mtu_msg.x_msg_header.msg_length = sizeof(mtu_msg); + mtu_msg.msg_data[0] = board->mtu; + + coredev->sendrequest_handler(coredev->context, &mtu_msg, + sizeof(mtu_msg)); + } + + if (board->crystal) { + struct sms_msg_data crys_msg; + sms_debug("set crystal value %d", board->crystal); + + SMS_INIT_MSG(&crys_msg.x_msg_header, + MSG_SMS_NEW_CRYSTAL_REQ, + sizeof(crys_msg)); + crys_msg.msg_data[0] = board->crystal; + + coredev->sendrequest_handler(coredev->context, &crys_msg, + sizeof(crys_msg)); + } + + return 0; +} + +/** * sets initial device mode and notifies client hotplugs that device is ready * * @param coredev pointer to a coredev object returned by - * smscore_register_device + * smscore_register_device * * @return 0 on success, <0 on error. */ int smscore_start_device(struct smscore_device_t *coredev) { - int rc = smscore_set_device_mode( - coredev, smscore_registry_getmode(coredev->devpath)); + int rc; + int board_id = smscore_get_board_id(coredev); + int mode = smscore_registry_getmode(coredev->devpath); + + /* Device is initialized as DEVICE_MODE_NONE */ + if (board_id != SMS_BOARD_UNKNOWN && mode == DEVICE_MODE_NONE) + mode = sms_get_board(board_id)->default_mode; + + rc = smscore_set_device_mode(coredev, mode); if (rc < 0) { sms_info("set device mode faile , rc %d", rc); return rc; } + rc = smscore_configure_board(coredev); + if (rc < 0) { + sms_info("configure board failed , rc %d", rc); + return rc; + } kmutex_lock(&g_smscore_deviceslock); @@ -509,18 +916,19 @@ EXPORT_SYMBOL_GPL(smscore_start_device); static int smscore_load_firmware_family2(struct smscore_device_t *coredev, void *buffer, size_t size) { - struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; - struct SmsMsgHdr_ST *msg; - u32 mem_address; - u8 *payload = firmware->Payload; + struct sms_firmware *firmware = (struct sms_firmware *) buffer; + struct sms_msg_data4 *msg; + u32 mem_address, calc_checksum = 0; + u32 i, *ptr; + u8 *payload = firmware->payload; int rc = 0; - firmware->StartAddress = le32_to_cpu(firmware->StartAddress); - firmware->Length = le32_to_cpu(firmware->Length); + firmware->start_address = le32_to_cpu(firmware->start_address); + firmware->length = le32_to_cpu(firmware->length); - mem_address = firmware->StartAddress; + mem_address = firmware->start_address; sms_info("loading FW to addr 0x%x size %d", - mem_address, firmware->Length); + mem_address, firmware->length); if (coredev->preload_handler) { rc = coredev->preload_handler(coredev->context); if (rc < 0) @@ -534,35 +942,36 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev, if (coredev->mode != DEVICE_MODE_NONE) { sms_debug("sending reload command."); - SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, - sizeof(struct SmsMsgHdr_ST)); + SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_START_REQ, + sizeof(struct sms_msg_hdr)); rc = smscore_sendrequest_and_wait(coredev, msg, - msg->msgLength, + msg->x_msg_header.msg_length, &coredev->reload_start_done); + if (rc < 0) { + sms_err("device reload failed, rc %d", rc); + goto exit_fw_download; + } mem_address = *(u32 *) &payload[20]; } + for (i = 0, ptr = (u32 *)firmware->payload; i < firmware->length/4 ; + i++, ptr++) + calc_checksum += *ptr; + while (size && rc >= 0) { - struct SmsDataDownload_ST *DataMsg = - (struct SmsDataDownload_ST *) msg; - int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); + struct sms_data_download *data_msg = + (struct sms_data_download *) msg; + int payload_size = min_t(int, size, SMS_MAX_PAYLOAD_SIZE); - SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, - (u16)(sizeof(struct SmsMsgHdr_ST) + + SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_DOWNLOAD_REQ, + (u16)(sizeof(struct sms_msg_hdr) + sizeof(u32) + payload_size)); - DataMsg->MemAddr = mem_address; - memcpy(DataMsg->Payload, payload, payload_size); + data_msg->mem_addr = mem_address; + memcpy(data_msg->payload, payload, payload_size); - if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && - (coredev->mode == DEVICE_MODE_NONE)) - rc = coredev->sendrequest_handler( - coredev->context, DataMsg, - DataMsg->xMsgHeader.msgLength); - else - rc = smscore_sendrequest_and_wait( - coredev, DataMsg, - DataMsg->xMsgHeader.msgLength, + rc = smscore_sendrequest_and_wait(coredev, data_msg, + data_msg->x_msg_header.msg_length, &coredev->data_download_done); payload += payload_size; @@ -570,50 +979,158 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev, mem_address += payload_size; } - if (rc >= 0) { - if (coredev->mode == DEVICE_MODE_NONE) { - struct SmsMsgData_ST *TriggerMsg = - (struct SmsMsgData_ST *) msg; - - SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, - sizeof(struct SmsMsgHdr_ST) + - sizeof(u32) * 5); - - TriggerMsg->msgData[0] = firmware->StartAddress; - /* Entry point */ - TriggerMsg->msgData[1] = 5; /* Priority */ - TriggerMsg->msgData[2] = 0x200; /* Stack size */ - TriggerMsg->msgData[3] = 0; /* Parameter */ - TriggerMsg->msgData[4] = 4; /* Task ID */ - - if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { - rc = coredev->sendrequest_handler( - coredev->context, TriggerMsg, - TriggerMsg->xMsgHeader.msgLength); - msleep(100); - } else - rc = smscore_sendrequest_and_wait( - coredev, TriggerMsg, - TriggerMsg->xMsgHeader.msgLength, + if (rc < 0) + goto exit_fw_download; + + sms_err("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x", + calc_checksum); + SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_VALIDITY_REQ, + sizeof(msg->x_msg_header) + + sizeof(u32) * 3); + msg->msg_data[0] = firmware->start_address; + /* Entry point */ + msg->msg_data[1] = firmware->length; + msg->msg_data[2] = 0; /* Regular checksum*/ + rc = smscore_sendrequest_and_wait(coredev, msg, + msg->x_msg_header.msg_length, + &coredev->data_validity_done); + if (rc < 0) + goto exit_fw_download; + + if (coredev->mode == DEVICE_MODE_NONE) { + struct sms_msg_data *trigger_msg = + (struct sms_msg_data *) msg; + + sms_debug("sending MSG_SMS_SWDOWNLOAD_TRIGGER_REQ"); + SMS_INIT_MSG(&msg->x_msg_header, + MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, + sizeof(struct sms_msg_hdr) + + sizeof(u32) * 5); + + trigger_msg->msg_data[0] = firmware->start_address; + /* Entry point */ + trigger_msg->msg_data[1] = 6; /* Priority */ + trigger_msg->msg_data[2] = 0x200; /* Stack size */ + trigger_msg->msg_data[3] = 0; /* Parameter */ + trigger_msg->msg_data[4] = 4; /* Task ID */ + + rc = smscore_sendrequest_and_wait(coredev, trigger_msg, + trigger_msg->x_msg_header.msg_length, &coredev->trigger_done); - } else { - SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, - sizeof(struct SmsMsgHdr_ST)); - - rc = coredev->sendrequest_handler(coredev->context, - msg, msg->msgLength); - } - msleep(500); + } else { + SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_EXEC_REQ, + sizeof(struct sms_msg_hdr)); + rc = coredev->sendrequest_handler(coredev->context, msg, + msg->x_msg_header.msg_length); } - sms_debug("rc=%d, postload=%p ", rc, - coredev->postload_handler); + if (rc < 0) + goto exit_fw_download; + + /* + * backward compatibility - wait to device_ready_done for + * not more than 400 ms + */ + msleep(400); +exit_fw_download: kfree(msg); - return ((rc >= 0) && coredev->postload_handler) ? - coredev->postload_handler(coredev->context) : - rc; + if (coredev->postload_handler) { + sms_debug("rc=%d, postload=0x%p", rc, coredev->postload_handler); + if (rc >= 0) + return coredev->postload_handler(coredev->context); + } + + sms_debug("rc=%d", rc); + return rc; +} + +static char *smscore_fw_lkup[][DEVICE_MODE_MAX] = { + [SMS_NOVA_A0] = { + [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ, + [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ, + [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ, + [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ, + [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ, + [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ, + }, + [SMS_NOVA_B0] = { + [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ_B0, + [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ_B0, + [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ_B0, + [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0, + [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ_B0, + [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ_B0, + [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO, + [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO, + }, + [SMS_VEGA] = { + [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VEGA_12MHZ, + }, + [SMS_VENICE] = { + [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VENICE_12MHZ, + }, + [SMS_MING] = { + [DEVICE_MODE_CMMB] = SMS_FW_CMMB_MING_APP, + }, + [SMS_PELE] = { + [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_PELE, + [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_PELE, + }, + [SMS_RIO] = { + [DEVICE_MODE_DVBT] = SMS_FW_DVB_RIO, + [DEVICE_MODE_DVBH] = SMS_FW_DVBH_RIO, + [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_RIO, + [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_RIO, + [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_RIO, + [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO_RIO, + [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO_RIO, + }, + [SMS_DENVER_1530] = { + [DEVICE_MODE_ATSC] = SMS_FW_ATSC_DENVER, + }, + [SMS_DENVER_2160] = { + [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_DENVER, + }, +}; + +/** + * get firmware file name from one of the two mechanisms : sms_boards or + * smscore_fw_lkup. + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param mode requested mode of operation + * @param lookup if 1, always get the fw filename from smscore_fw_lkup + * table. if 0, try first to get from sms_boards + * + * @return 0 on success, <0 on error. + */ +static char *smscore_get_fw_filename(struct smscore_device_t *coredev, + int mode) +{ + char **fw; + int board_id = smscore_get_board_id(coredev); + enum sms_device_type_st type; + + type = smscore_registry_gettype(coredev->devpath); + + /* Prevent looking outside the smscore_fw_lkup table */ + if (type <= SMS_UNKNOWN_TYPE || type >= SMS_NUM_OF_DEVICE_TYPES) + return NULL; + if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) + return NULL; + + sms_debug("trying to get fw name from sms_boards board_id %d mode %d", + board_id, mode); + fw = sms_get_board(board_id)->fw; + if (!fw || !fw[mode]) { + sms_debug("cannot find fw name in sms_boards, getting from lookup table mode %d type %d", + mode, type); + return smscore_fw_lkup[type][mode]; + } + + return fw[mode]; } /** @@ -627,41 +1144,46 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev, * @return 0 on success, <0 on error. */ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, - char *filename, + int mode, loadfirmware_t loadfirmware_handler) { int rc = -ENOENT; + u8 *fw_buf; + u32 fw_buf_size; const struct firmware *fw; - u8 *fw_buffer; - if (loadfirmware_handler == NULL && !(coredev->device_flags & - SMS_DEVICE_FAMILY2)) + char *fw_filename = smscore_get_fw_filename(coredev, mode); + if (!fw_filename) { + sms_info("mode %d not supported on this device", mode); + return -ENOENT; + } + sms_debug("Firmware name: %s", fw_filename); + + if (loadfirmware_handler == NULL && !(coredev->device_flags + & SMS_DEVICE_FAMILY2)) return -EINVAL; - rc = request_firmware(&fw, filename, coredev->device); + rc = request_firmware(&fw, fw_filename, coredev->device); if (rc < 0) { - sms_info("failed to open \"%s\"", filename); + sms_info("failed to open \"%s\"", fw_filename); return rc; } - sms_info("read FW %s, size=%zd", filename, fw->size); - fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), - GFP_KERNEL | GFP_DMA); - if (fw_buffer) { - memcpy(fw_buffer, fw->data, fw->size); - - rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? - smscore_load_firmware_family2(coredev, - fw_buffer, - fw->size) : - loadfirmware_handler(coredev->context, - fw_buffer, fw->size); - - kfree(fw_buffer); - } else { + sms_info("read fw %s, buffer size=0x%zx", fw_filename, fw->size); + fw_buf = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), + GFP_KERNEL | GFP_DMA); + if (!fw_buf) { sms_info("failed to allocate firmware buffer"); - rc = -ENOMEM; + return -ENOMEM; } + memcpy(fw_buf, fw->data, fw->size); + fw_buf_size = fw->size; + + rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? + smscore_load_firmware_family2(coredev, fw_buf, fw_buf_size) + : loadfirmware_handler(coredev->context, fw_buf, + fw_buf_size); + kfree(fw_buf); release_firmware(fw); return rc; @@ -703,14 +1225,15 @@ void smscore_unregister_device(struct smscore_device_t *coredev) if (num_buffers == coredev->num_buffers) break; if (++retry > 10) { - sms_info("exiting although " - "not all buffers released."); + sms_info("exiting although not all buffers released."); break; } sms_info("waiting for %d buffer(s)", coredev->num_buffers - num_buffers); + kmutex_unlock(&g_smscore_deviceslock); msleep(100); + kmutex_lock(&g_smscore_deviceslock); } sms_info("freed %d buffers", num_buffers); @@ -719,8 +1242,7 @@ void smscore_unregister_device(struct smscore_device_t *coredev) dma_free_coherent(NULL, coredev->common_buffer_size, coredev->common_buffer, coredev->common_buffer_phys); - if (coredev->fw_buf != NULL) - kfree(coredev->fw_buf); + kfree(coredev->fw_buf); list_del(&coredev->entry); kfree(coredev); @@ -733,19 +1255,19 @@ EXPORT_SYMBOL_GPL(smscore_unregister_device); static int smscore_detect_mode(struct smscore_device_t *coredev) { - void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, + void *buffer = kmalloc(sizeof(struct sms_msg_hdr) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); - struct SmsMsgHdr_ST *msg = - (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); + struct sms_msg_hdr *msg = + (struct sms_msg_hdr *) SMS_ALIGN_ADDRESS(buffer); int rc; if (!buffer) return -ENOMEM; SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, - sizeof(struct SmsMsgHdr_ST)); + sizeof(struct sms_msg_hdr)); - rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, + rc = smscore_sendrequest_and_wait(coredev, msg, msg->msg_length, &coredev->version_ex_done); if (rc == -ETIME) { sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); @@ -753,11 +1275,11 @@ static int smscore_detect_mode(struct smscore_device_t *coredev) if (wait_for_completion_timeout(&coredev->resume_done, msecs_to_jiffies(5000))) { rc = smscore_sendrequest_and_wait( - coredev, msg, msg->msgLength, + coredev, msg, msg->msg_length, &coredev->version_ex_done); if (rc < 0) - sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " - "second try, rc %d", rc); + sms_err("MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d", + rc); } else rc = -ETIME; } @@ -767,31 +1289,39 @@ static int smscore_detect_mode(struct smscore_device_t *coredev) return rc; } -static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { - /*Stellar NOVA A0 Nova B0 VEGA*/ - /*DVBT*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*DVBH*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*TDMB*/ - {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, - /*DABIP*/ - {"none", "none", "none", "none"}, - /*BDA*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*ISDBT*/ - {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, - /*ISDBTBDA*/ - {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, - /*CMMB*/ - {"none", "none", "none", "cmmb_vega_12mhz.inp"} -}; - -static inline char *sms_get_fw_name(struct smscore_device_t *coredev, - int mode, enum sms_device_type_st type) +/** + * send init device request and wait for response + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param mode requested mode of operation + * + * @return 0 on success, <0 on error. + */ +static int smscore_init_device(struct smscore_device_t *coredev, int mode) { - char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; - return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; + void *buffer; + struct sms_msg_data *msg; + int rc = 0; + + buffer = kmalloc(sizeof(struct sms_msg_data) + + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + if (!buffer) { + sms_err("Could not allocate buffer for init device message."); + return -ENOMEM; + } + + msg = (struct sms_msg_data *)SMS_ALIGN_ADDRESS(buffer); + SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, + sizeof(struct sms_msg_data)); + msg->msg_data[0] = mode; + + rc = smscore_sendrequest_and_wait(coredev, msg, + msg->x_msg_header. msg_length, + &coredev->init_device_done); + + kfree(buffer); + return rc; } /** @@ -806,13 +1336,11 @@ static inline char *sms_get_fw_name(struct smscore_device_t *coredev, */ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) { - void *buffer; int rc = 0; - enum sms_device_type_st type; sms_debug("set device mode to %d", mode); if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { + if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) { sms_err("invalid mode specified %d", mode); return -EINVAL; } @@ -833,58 +1361,21 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) } if (!(coredev->modes_supported & (1 << mode))) { - char *fw_filename; - - type = smscore_registry_gettype(coredev->devpath); - fw_filename = sms_get_fw_name(coredev, mode, type); - rc = smscore_load_firmware_from_file(coredev, - fw_filename, NULL); - if (rc < 0) { - sms_warn("error %d loading firmware: %s, " - "trying again with default firmware", - rc, fw_filename); - - /* try again with the default firmware */ - fw_filename = smscore_fw_lkup[mode][type]; - rc = smscore_load_firmware_from_file(coredev, - fw_filename, NULL); - - if (rc < 0) { - sms_warn("error %d loading " - "firmware: %s", rc, - fw_filename); - return rc; - } - } - sms_log("firmware download success: %s", fw_filename); - } else - sms_info("mode %d supported by running " - "firmware", mode); - - buffer = kmalloc(sizeof(struct SmsMsgData_ST) + - SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); - if (buffer) { - struct SmsMsgData_ST *msg = - (struct SmsMsgData_ST *) - SMS_ALIGN_ADDRESS(buffer); - - SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, - sizeof(struct SmsMsgData_ST)); - msg->msgData[0] = mode; - - rc = smscore_sendrequest_and_wait( - coredev, msg, msg->xMsgHeader.msgLength, - &coredev->init_device_done); - - kfree(buffer); + mode, NULL); + if (rc >= 0) + sms_info("firmware download success"); } else { - sms_err("Could not allocate buffer for " - "init device message."); - rc = -ENOMEM; + sms_info("mode %d is already supported by running firmware", + mode); + } + if (coredev->fw_version >= 0x800) { + rc = smscore_init_device(coredev, mode); + if (rc < 0) + sms_err("device init failed, rc %d.", rc); } } else { - if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { + if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) { sms_err("invalid mode specified %d", mode); return -EINVAL; } @@ -900,12 +1391,32 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) } if (rc >= 0) { + char *buffer; coredev->mode = mode; coredev->device_flags &= ~SMS_DEVICE_NOT_READY; + + buffer = kmalloc(sizeof(struct sms_msg_data) + + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + if (buffer) { + struct sms_msg_data *msg = (struct sms_msg_data *) SMS_ALIGN_ADDRESS(buffer); + + SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, + sizeof(struct sms_msg_data)); + msg->msg_data[0] = mode; + + rc = smscore_sendrequest_and_wait( + coredev, msg, msg->x_msg_header.msg_length, + &coredev->init_device_done); + + kfree(buffer); + } } if (rc < 0) sms_err("return error code %d.", rc); + else + sms_debug("Success setting device mode."); + return rc; } @@ -971,7 +1482,7 @@ found: */ void smscore_onresponse(struct smscore_device_t *coredev, struct smscore_buffer_t *cb) { - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) ((u8 *) cb->p + cb->offset); struct smscore_client_t *client; int rc = -EBUSY; @@ -983,7 +1494,7 @@ void smscore_onresponse(struct smscore_device_t *coredev, last_sample_time = time_now; if (time_now - last_sample_time > 10000) { - sms_debug("\ndata rate %d bytes/secs", + sms_debug("data rate %d bytes/secs", (int)((data_total * 1000) / (time_now - last_sample_time))); @@ -993,14 +1504,14 @@ void smscore_onresponse(struct smscore_device_t *coredev, data_total += cb->size; /* Do we need to re-route? */ - if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) || - (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { + if ((phdr->msg_type == MSG_SMS_HO_PER_SLICES_IND) || + (phdr->msg_type == MSG_SMS_TRANSMISSION_IND)) { if (coredev->mode == DEVICE_MODE_DVBT_BDA) - phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; + phdr->msg_dst_id = DVBT_BDA_CONTROL_MSG_ID; } - client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); + client = smscore_find_client(coredev, phdr->msg_type, phdr->msg_dst_id); /* If no client registered for type & id, * check for control client where type is not registered */ @@ -1008,57 +1519,75 @@ void smscore_onresponse(struct smscore_device_t *coredev, rc = client->onresponse_handler(client->context, cb); if (rc < 0) { - switch (phdr->msgType) { + switch (phdr->msg_type) { + case MSG_SMS_ISDBT_TUNE_RES: + break; + case MSG_SMS_RF_TUNE_RES: + break; + case MSG_SMS_SIGNAL_DETECTED_IND: + break; + case MSG_SMS_NO_SIGNAL_IND: + break; + case MSG_SMS_SPI_INT_LINE_SET_RES: + break; + case MSG_SMS_INTERFACE_LOCK_IND: + break; + case MSG_SMS_INTERFACE_UNLOCK_IND: + break; case MSG_SMS_GET_VERSION_EX_RES: { - struct SmsVersionRes_ST *ver = - (struct SmsVersionRes_ST *) phdr; - sms_debug("MSG_SMS_GET_VERSION_EX_RES " - "id %d prots 0x%x ver %d.%d", - ver->FirmwareId, ver->SupportedProtocols, - ver->RomVersionMajor, ver->RomVersionMinor); - - coredev->mode = ver->FirmwareId == 255 ? - DEVICE_MODE_NONE : ver->FirmwareId; - coredev->modes_supported = ver->SupportedProtocols; + struct sms_version_res *ver = + (struct sms_version_res *) phdr; + sms_debug("Firmware id %d prots 0x%x ver %d.%d", + ver->firmware_id, ver->supported_protocols, + ver->rom_ver_major, ver->rom_ver_minor); + + coredev->mode = ver->firmware_id == 255 ? + DEVICE_MODE_NONE : ver->firmware_id; + coredev->modes_supported = ver->supported_protocols; + coredev->fw_version = ver->rom_ver_major << 8 | + ver->rom_ver_minor; complete(&coredev->version_ex_done); break; } case MSG_SMS_INIT_DEVICE_RES: - sms_debug("MSG_SMS_INIT_DEVICE_RES"); complete(&coredev->init_device_done); break; case MSG_SW_RELOAD_START_RES: - sms_debug("MSG_SW_RELOAD_START_RES"); complete(&coredev->reload_start_done); break; + case MSG_SMS_DATA_VALIDITY_RES: + { + struct sms_msg_data *validity = (struct sms_msg_data *) phdr; + + sms_err("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x", + validity->msg_data[0]); + complete(&coredev->data_validity_done); + break; + } case MSG_SMS_DATA_DOWNLOAD_RES: complete(&coredev->data_download_done); break; case MSG_SW_RELOAD_EXEC_RES: - sms_debug("MSG_SW_RELOAD_EXEC_RES"); break; case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: - sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES"); complete(&coredev->trigger_done); break; case MSG_SMS_SLEEP_RESUME_COMP_IND: complete(&coredev->resume_done); break; case MSG_SMS_GPIO_CONFIG_EX_RES: - sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); complete(&coredev->gpio_configuration_done); break; case MSG_SMS_GPIO_SET_LEVEL_RES: - sms_debug("MSG_SMS_GPIO_SET_LEVEL_RES"); complete(&coredev->gpio_set_level_done); break; case MSG_SMS_GPIO_GET_LEVEL_RES: { u32 *msgdata = (u32 *) phdr; coredev->gpio_get_res = msgdata[1]; - sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", + sms_debug("gpio level %d", coredev->gpio_get_res); complete(&coredev->gpio_get_level_done); break; @@ -1070,12 +1599,24 @@ void smscore_onresponse(struct smscore_device_t *coredev, sms_ir_event(coredev, (const char *) ((char *)phdr - + sizeof(struct SmsMsgHdr_ST)), - (int)phdr->msgLength - - sizeof(struct SmsMsgHdr_ST)); + + sizeof(struct sms_msg_hdr)), + (int)phdr->msg_length + - sizeof(struct sms_msg_hdr)); + break; + + case MSG_SMS_DVBT_BDA_DATA: + /* + * It can be received here, if the frontend is + * tuned into a valid channel and the proper firmware + * is loaded. That happens when the module got removed + * and re-inserted, without powering the device off + */ break; default: + sms_debug("message %s(%d) not handled.", + smscore_translate_msg(phdr->msg_type), + phdr->msg_type); break; } smscore_putbuffer(coredev, cb); @@ -1257,7 +1798,7 @@ int smsclient_sendrequest(struct smscore_client_t *client, void *buffer, size_t size) { struct smscore_device_t *coredev; - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer; int rc; if (client == NULL) { @@ -1274,7 +1815,7 @@ int smsclient_sendrequest(struct smscore_client_t *client, } rc = smscore_validate_client(client->coredev, client, 0, - phdr->msgSrcId); + phdr->msg_src_id); if (rc < 0) return rc; @@ -1288,16 +1829,16 @@ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, struct smscore_config_gpio *pinconfig) { struct { - struct SmsMsgHdr_ST hdr; + struct sms_msg_hdr hdr; u32 data[6]; } msg; if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - msg.hdr.msgDstId = HIF_TASK; - msg.hdr.msgFlags = 0; - msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; - msg.hdr.msgLength = sizeof(msg); + msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msg_dst_id = HIF_TASK; + msg.hdr.msg_flags = 0; + msg.hdr.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ; + msg.hdr.msg_length = sizeof(msg); msg.data[0] = pin; msg.data[1] = pinconfig->pullupdown; @@ -1306,16 +1847,16 @@ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; switch (pinconfig->outputdriving) { - case SMS_GPIO_OUTPUTDRIVING_16mA: + case SMS_GPIO_OUTPUTDRIVING_S_16mA: msg.data[3] = 7; /* Nova - 16mA */ break; - case SMS_GPIO_OUTPUTDRIVING_12mA: + case SMS_GPIO_OUTPUTDRIVING_S_12mA: msg.data[3] = 5; /* Nova - 11mA */ break; - case SMS_GPIO_OUTPUTDRIVING_8mA: + case SMS_GPIO_OUTPUTDRIVING_S_8mA: msg.data[3] = 3; /* Nova - 7mA */ break; - case SMS_GPIO_OUTPUTDRIVING_4mA: + case SMS_GPIO_OUTPUTDRIVING_S_4mA: default: msg.data[3] = 2; /* Nova - 4mA */ break; @@ -1333,18 +1874,18 @@ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) { struct { - struct SmsMsgHdr_ST hdr; + struct sms_msg_hdr hdr; u32 data[3]; } msg; if (pin > MAX_GPIO_PIN_NUMBER) return -EINVAL; - msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - msg.hdr.msgDstId = HIF_TASK; - msg.hdr.msgFlags = 0; - msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; - msg.hdr.msgLength = sizeof(msg); + msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msg_dst_id = HIF_TASK; + msg.hdr.msg_flags = 0; + msg.hdr.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ; + msg.hdr.msg_length = sizeof(msg); msg.data[0] = pin; msg.data[1] = level ? 1 : 0; @@ -1355,122 +1896,121 @@ int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) } /* new GPIO management implementation */ -static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, - u32 *pGroupNum, u32 *pGroupCfg) { - - *pGroupCfg = 1; - - if (PinNum <= 1) { - *pTranslatedPinNum = 0; - *pGroupNum = 9; - *pGroupCfg = 2; - } else if (PinNum >= 2 && PinNum <= 6) { - *pTranslatedPinNum = 2; - *pGroupNum = 0; - *pGroupCfg = 2; - } else if (PinNum >= 7 && PinNum <= 11) { - *pTranslatedPinNum = 7; - *pGroupNum = 1; - } else if (PinNum >= 12 && PinNum <= 15) { - *pTranslatedPinNum = 12; - *pGroupNum = 2; - *pGroupCfg = 3; - } else if (PinNum == 16) { - *pTranslatedPinNum = 16; - *pGroupNum = 23; - } else if (PinNum >= 17 && PinNum <= 24) { - *pTranslatedPinNum = 17; - *pGroupNum = 3; - } else if (PinNum == 25) { - *pTranslatedPinNum = 25; - *pGroupNum = 6; - } else if (PinNum >= 26 && PinNum <= 28) { - *pTranslatedPinNum = 26; - *pGroupNum = 4; - } else if (PinNum == 29) { - *pTranslatedPinNum = 29; - *pGroupNum = 5; - *pGroupCfg = 2; - } else if (PinNum == 30) { - *pTranslatedPinNum = 30; - *pGroupNum = 8; - } else if (PinNum == 31) { - *pTranslatedPinNum = 31; - *pGroupNum = 17; +static int get_gpio_pin_params(u32 pin_num, u32 *p_translatedpin_num, + u32 *p_group_num, u32 *p_group_cfg) { + + *p_group_cfg = 1; + + if (pin_num <= 1) { + *p_translatedpin_num = 0; + *p_group_num = 9; + *p_group_cfg = 2; + } else if (pin_num >= 2 && pin_num <= 6) { + *p_translatedpin_num = 2; + *p_group_num = 0; + *p_group_cfg = 2; + } else if (pin_num >= 7 && pin_num <= 11) { + *p_translatedpin_num = 7; + *p_group_num = 1; + } else if (pin_num >= 12 && pin_num <= 15) { + *p_translatedpin_num = 12; + *p_group_num = 2; + *p_group_cfg = 3; + } else if (pin_num == 16) { + *p_translatedpin_num = 16; + *p_group_num = 23; + } else if (pin_num >= 17 && pin_num <= 24) { + *p_translatedpin_num = 17; + *p_group_num = 3; + } else if (pin_num == 25) { + *p_translatedpin_num = 25; + *p_group_num = 6; + } else if (pin_num >= 26 && pin_num <= 28) { + *p_translatedpin_num = 26; + *p_group_num = 4; + } else if (pin_num == 29) { + *p_translatedpin_num = 29; + *p_group_num = 5; + *p_group_cfg = 2; + } else if (pin_num == 30) { + *p_translatedpin_num = 30; + *p_group_num = 8; + } else if (pin_num == 31) { + *p_translatedpin_num = 31; + *p_group_num = 17; } else return -1; - *pGroupCfg <<= 24; + *p_group_cfg <<= 24; return 0; } -int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, - struct smscore_gpio_config *pGpioConfig) { +int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num, + struct smscore_config_gpio *p_gpio_config) { - u32 totalLen; - u32 TranslatedPinNum = 0; - u32 GroupNum = 0; - u32 ElectricChar; - u32 groupCfg; + u32 total_len; + u32 translatedpin_num = 0; + u32 group_num = 0; + u32 electric_char; + u32 group_cfg; void *buffer; int rc; - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[6]; - } *pMsg; + struct set_gpio_msg { + struct sms_msg_hdr x_msg_header; + u32 msg_data[6]; + } *p_msg; - if (PinNum > MAX_GPIO_PIN_NUMBER) + if (pin_num > MAX_GPIO_PIN_NUMBER) return -EINVAL; - if (pGpioConfig == NULL) + if (p_gpio_config == NULL) return -EINVAL; - totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); + total_len = sizeof(struct sms_msg_hdr) + (sizeof(u32) * 6); - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); if (!buffer) return -ENOMEM; - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer); - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; + p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + p_msg->x_msg_header.msg_dst_id = HIF_TASK; + p_msg->x_msg_header.msg_flags = 0; + p_msg->x_msg_header.msg_length = (u16) total_len; + p_msg->msg_data[0] = pin_num; if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; - if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, - &groupCfg) != 0) { + p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_REQ; + if (get_gpio_pin_params(pin_num, &translatedpin_num, &group_num, + &group_cfg) != 0) { rc = -EINVAL; goto free; } - pMsg->msgData[1] = TranslatedPinNum; - pMsg->msgData[2] = GroupNum; - ElectricChar = (pGpioConfig->PullUpDown) - | (pGpioConfig->InputCharacteristics << 2) - | (pGpioConfig->OutputSlewRate << 3) - | (pGpioConfig->OutputDriving << 4); - pMsg->msgData[3] = ElectricChar; - pMsg->msgData[4] = pGpioConfig->Direction; - pMsg->msgData[5] = groupCfg; + p_msg->msg_data[1] = translatedpin_num; + p_msg->msg_data[2] = group_num; + electric_char = (p_gpio_config->pullupdown) + | (p_gpio_config->inputcharacteristics << 2) + | (p_gpio_config->outputslewrate << 3) + | (p_gpio_config->outputdriving << 4); + p_msg->msg_data[3] = electric_char; + p_msg->msg_data[4] = p_gpio_config->direction; + p_msg->msg_data[5] = group_cfg; } else { - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; - pMsg->msgData[1] = pGpioConfig->PullUpDown; - pMsg->msgData[2] = pGpioConfig->OutputSlewRate; - pMsg->msgData[3] = pGpioConfig->OutputDriving; - pMsg->msgData[4] = pGpioConfig->Direction; - pMsg->msgData[5] = 0; + p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ; + p_msg->msg_data[1] = p_gpio_config->pullupdown; + p_msg->msg_data[2] = p_gpio_config->outputslewrate; + p_msg->msg_data[3] = p_gpio_config->outputdriving; + p_msg->msg_data[4] = p_gpio_config->direction; + p_msg->msg_data[5] = 0; } - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len, &coredev->gpio_configuration_done); if (rc != 0) { @@ -1485,42 +2025,41 @@ free: return rc; } -int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, - u8 NewLevel) { +int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num, + u8 new_level) { - u32 totalLen; + u32 total_len; int rc; void *buffer; - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[3]; /* keep it 3 ! */ - } *pMsg; + struct set_gpio_msg { + struct sms_msg_hdr x_msg_header; + u32 msg_data[3]; /* keep it 3 ! */ + } *p_msg; - if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) + if ((new_level > 1) || (pin_num > MAX_GPIO_PIN_NUMBER)) return -EINVAL; - totalLen = sizeof(struct SmsMsgHdr_ST) + + total_len = sizeof(struct sms_msg_hdr) + (3 * sizeof(u32)); /* keep it 3 ! */ - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); if (!buffer) return -ENOMEM; - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer); - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; - pMsg->msgData[1] = NewLevel; + p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + p_msg->x_msg_header.msg_dst_id = HIF_TASK; + p_msg->x_msg_header.msg_flags = 0; + p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ; + p_msg->x_msg_header.msg_length = (u16) total_len; + p_msg->msg_data[0] = pin_num; + p_msg->msg_data[1] = new_level; /* Send message to SMS */ - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len, &coredev->gpio_set_level_done); if (rc != 0) { @@ -1534,42 +2073,41 @@ int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, return rc; } -int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, +int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num, u8 *level) { - u32 totalLen; + u32 total_len; int rc; void *buffer; - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[2]; - } *pMsg; + struct set_gpio_msg { + struct sms_msg_hdr x_msg_header; + u32 msg_data[2]; + } *p_msg; - if (PinNum > MAX_GPIO_PIN_NUMBER) + if (pin_num > MAX_GPIO_PIN_NUMBER) return -EINVAL; - totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); + total_len = sizeof(struct sms_msg_hdr) + (2 * sizeof(u32)); - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); if (!buffer) return -ENOMEM; - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer); - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; - pMsg->msgData[1] = 0; + p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + p_msg->x_msg_header.msg_dst_id = HIF_TASK; + p_msg->x_msg_header.msg_flags = 0; + p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_GET_LEVEL_REQ; + p_msg->x_msg_header.msg_length = (u16) total_len; + p_msg->msg_data[0] = pin_num; + p_msg->msg_data[1] = 0; /* Send message to SMS */ - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len, &coredev->gpio_get_level_done); if (rc != 0) { @@ -1635,3 +2173,27 @@ module_exit(smscore_module_exit); MODULE_DESCRIPTION("Siano MDTV Core module"); MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); MODULE_LICENSE("GPL"); + +/* This should match what's defined at smscoreapi.h */ +MODULE_FIRMWARE(SMS_FW_ATSC_DENVER); +MODULE_FIRMWARE(SMS_FW_CMMB_MING_APP); +MODULE_FIRMWARE(SMS_FW_CMMB_VEGA_12MHZ); +MODULE_FIRMWARE(SMS_FW_CMMB_VENICE_12MHZ); +MODULE_FIRMWARE(SMS_FW_DVBH_RIO); +MODULE_FIRMWARE(SMS_FW_DVB_NOVA_12MHZ_B0); +MODULE_FIRMWARE(SMS_FW_DVB_NOVA_12MHZ); +MODULE_FIRMWARE(SMS_FW_DVB_RIO); +MODULE_FIRMWARE(SMS_FW_FM_RADIO); +MODULE_FIRMWARE(SMS_FW_FM_RADIO_RIO); +MODULE_FIRMWARE(SMS_FW_DVBT_HCW_55XXX); +MODULE_FIRMWARE(SMS_FW_ISDBT_HCW_55XXX); +MODULE_FIRMWARE(SMS_FW_ISDBT_NOVA_12MHZ_B0); +MODULE_FIRMWARE(SMS_FW_ISDBT_NOVA_12MHZ); +MODULE_FIRMWARE(SMS_FW_ISDBT_PELE); +MODULE_FIRMWARE(SMS_FW_ISDBT_RIO); +MODULE_FIRMWARE(SMS_FW_DVBT_NOVA_A); +MODULE_FIRMWARE(SMS_FW_DVBT_NOVA_B); +MODULE_FIRMWARE(SMS_FW_DVBT_STELLAR); +MODULE_FIRMWARE(SMS_FW_TDMB_DENVER); +MODULE_FIRMWARE(SMS_FW_TDMB_NOVA_12MHZ_B0); +MODULE_FIRMWARE(SMS_FW_TDMB_NOVA_12MHZ); diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index c592ae0..d0799e3 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -40,9 +40,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define kmutex_trylock(_p_) mutex_trylock(_p_) #define kmutex_unlock(_p_) mutex_unlock(_p_) -#ifndef min -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif +/* + * Define the firmware names used by the driver. + * Those should match what's used at smscoreapi.c and sms-cards.c + * including the MODULE_FIRMWARE() macros at the end of smscoreapi.c + */ +#define SMS_FW_ATSC_DENVER "atsc_denver.inp" +#define SMS_FW_CMMB_MING_APP "cmmb_ming_app.inp" +#define SMS_FW_CMMB_VEGA_12MHZ "cmmb_vega_12mhz.inp" +#define SMS_FW_CMMB_VENICE_12MHZ "cmmb_venice_12mhz.inp" +#define SMS_FW_DVBH_RIO "dvbh_rio.inp" +#define SMS_FW_DVB_NOVA_12MHZ_B0 "dvb_nova_12mhz_b0.inp" +#define SMS_FW_DVB_NOVA_12MHZ "dvb_nova_12mhz.inp" +#define SMS_FW_DVB_RIO "dvb_rio.inp" +#define SMS_FW_FM_RADIO "fm_radio.inp" +#define SMS_FW_FM_RADIO_RIO "fm_radio_rio.inp" +#define SMS_FW_DVBT_HCW_55XXX "sms1xxx-hcw-55xxx-dvbt-02.fw" +#define SMS_FW_ISDBT_HCW_55XXX "sms1xxx-hcw-55xxx-isdbt-02.fw" +#define SMS_FW_ISDBT_NOVA_12MHZ_B0 "isdbt_nova_12mhz_b0.inp" +#define SMS_FW_ISDBT_NOVA_12MHZ "isdbt_nova_12mhz.inp" +#define SMS_FW_ISDBT_PELE "isdbt_pele.inp" +#define SMS_FW_ISDBT_RIO "isdbt_rio.inp" +#define SMS_FW_DVBT_NOVA_A "sms1xxx-nova-a-dvbt-01.fw" +#define SMS_FW_DVBT_NOVA_B "sms1xxx-nova-b-dvbt-01.fw" +#define SMS_FW_DVBT_STELLAR "sms1xxx-stellar-dvbt-01.fw" +#define SMS_FW_TDMB_DENVER "tdmb_denver.inp" +#define SMS_FW_TDMB_NOVA_12MHZ_B0 "tdmb_nova_12mhz_b0.inp" +#define SMS_FW_TDMB_NOVA_12MHZ "tdmb_nova_12mhz.inp" #define SMS_PROTOCOL_MAX_RAOUNDTRIP_MS (10000) #define SMS_ALLOC_ALIGNMENT 128 @@ -50,18 +74,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define SMS_ALIGN_ADDRESS(addr) \ ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1)) +#define SMS_DEVICE_FAMILY1 0 #define SMS_DEVICE_FAMILY2 1 #define SMS_ROM_NO_RESPONSE 2 #define SMS_DEVICE_NOT_READY 0x8000000 enum sms_device_type_st { + SMS_UNKNOWN_TYPE = -1, SMS_STELLAR = 0, SMS_NOVA_A0, SMS_NOVA_B0, SMS_VEGA, + SMS_VENICE, + SMS_MING, + SMS_PELE, + SMS_RIO, + SMS_DENVER_1530, + SMS_DENVER_2160, SMS_NUM_OF_DEVICE_TYPES }; +enum sms_power_mode_st { + SMS_POWER_MODE_ACTIVE, + SMS_POWER_MODE_SUSPENDED +}; + struct smscore_device_t; struct smscore_client_t; struct smscore_buffer_t; @@ -149,6 +186,7 @@ struct smscore_device_t { /* host <--> device messages */ struct completion version_ex_done, data_download_done, trigger_done; + struct completion data_validity_done, device_ready_done; struct completion init_device_done, reload_start_done, resume_done; struct completion gpio_configuration_done, gpio_set_level_done; struct completion gpio_get_level_done, ir_init_done; @@ -165,10 +203,17 @@ struct smscore_device_t { /* Firmware */ u8 *fw_buf; u32 fw_buf_size; + u16 fw_version; /* Infrared (IR) */ struct ir_t ir; + /* + * Identify if device is USB or not. + * Used by smsdvb-sysfs to know the root node for debugfs + */ + bool is_usb_device; + int led_state; }; @@ -176,81 +221,363 @@ struct smscore_device_t { #define SMS_ANTENNA_GPIO_0 1 #define SMS_ANTENNA_GPIO_1 0 -#define BW_8_MHZ 0 -#define BW_7_MHZ 1 -#define BW_6_MHZ 2 -#define BW_5_MHZ 3 -#define BW_ISDBT_1SEG 4 -#define BW_ISDBT_3SEG 5 +enum sms_bandwidth_mode { + BW_8_MHZ = 0, + BW_7_MHZ = 1, + BW_6_MHZ = 2, + BW_5_MHZ = 3, + BW_ISDBT_1SEG = 4, + BW_ISDBT_3SEG = 5, + BW_2_MHZ = 6, + BW_FM_RADIO = 7, + BW_ISDBT_13SEG = 8, + BW_1_5_MHZ = 15, + BW_UNKNOWN = 0xffff +}; + #define MSG_HDR_FLAG_SPLIT_MSG 4 #define MAX_GPIO_PIN_NUMBER 31 #define HIF_TASK 11 +#define HIF_TASK_SLAVE 22 +#define HIF_TASK_SLAVE2 33 +#define HIF_TASK_SLAVE3 44 #define SMS_HOST_LIB 150 #define DVBT_BDA_CONTROL_MSG_ID 201 #define SMS_MAX_PAYLOAD_SIZE 240 #define SMS_TUNE_TIMEOUT 500 -#define MSG_SMS_GPIO_CONFIG_REQ 507 -#define MSG_SMS_GPIO_CONFIG_RES 508 -#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 -#define MSG_SMS_GPIO_SET_LEVEL_RES 510 -#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 -#define MSG_SMS_GPIO_GET_LEVEL_RES 512 -#define MSG_SMS_RF_TUNE_REQ 561 -#define MSG_SMS_RF_TUNE_RES 562 -#define MSG_SMS_INIT_DEVICE_REQ 578 -#define MSG_SMS_INIT_DEVICE_RES 579 -#define MSG_SMS_ADD_PID_FILTER_REQ 601 -#define MSG_SMS_ADD_PID_FILTER_RES 602 -#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 -#define MSG_SMS_REMOVE_PID_FILTER_RES 604 -#define MSG_SMS_DAB_CHANNEL 607 -#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 -#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 -#define MSG_SMS_GET_STATISTICS_RES 616 -#define MSG_SMS_GET_STATISTICS_REQ 615 -#define MSG_SMS_HO_PER_SLICES_IND 630 -#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 -#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 -#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 -#define MSG_SMS_DATA_DOWNLOAD_REQ 660 -#define MSG_SMS_DATA_DOWNLOAD_RES 661 -#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 -#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 -#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 -#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 -#define MSG_SMS_GET_VERSION_EX_REQ 668 -#define MSG_SMS_GET_VERSION_EX_RES 669 -#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 -#define MSG_SMS_I2C_SET_FREQ_REQ 685 -#define MSG_SMS_GENERIC_I2C_REQ 687 -#define MSG_SMS_GENERIC_I2C_RES 688 -#define MSG_SMS_DVBT_BDA_DATA 693 -#define MSG_SW_RELOAD_REQ 697 -#define MSG_SMS_DATA_MSG 699 -#define MSG_SW_RELOAD_START_REQ 702 -#define MSG_SW_RELOAD_START_RES 703 -#define MSG_SW_RELOAD_EXEC_REQ 704 -#define MSG_SW_RELOAD_EXEC_RES 705 -#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 -#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 -#define MSG_SMS_GPIO_CONFIG_EX_RES 713 -#define MSG_SMS_ISDBT_TUNE_REQ 776 -#define MSG_SMS_ISDBT_TUNE_RES 777 -#define MSG_SMS_TRANSMISSION_IND 782 -#define MSG_SMS_START_IR_REQ 800 -#define MSG_SMS_START_IR_RES 801 -#define MSG_SMS_IR_SAMPLES_IND 802 -#define MSG_SMS_SIGNAL_DETECTED_IND 827 -#define MSG_SMS_NO_SIGNAL_IND 828 +enum msg_types { + MSG_TYPE_BASE_VAL = 500, + MSG_SMS_GET_VERSION_REQ = 503, + MSG_SMS_GET_VERSION_RES = 504, + MSG_SMS_MULTI_BRIDGE_CFG = 505, + MSG_SMS_GPIO_CONFIG_REQ = 507, + MSG_SMS_GPIO_CONFIG_RES = 508, + MSG_SMS_GPIO_SET_LEVEL_REQ = 509, + MSG_SMS_GPIO_SET_LEVEL_RES = 510, + MSG_SMS_GPIO_GET_LEVEL_REQ = 511, + MSG_SMS_GPIO_GET_LEVEL_RES = 512, + MSG_SMS_EEPROM_BURN_IND = 513, + MSG_SMS_LOG_ENABLE_CHANGE_REQ = 514, + MSG_SMS_LOG_ENABLE_CHANGE_RES = 515, + MSG_SMS_SET_MAX_TX_MSG_LEN_REQ = 516, + MSG_SMS_SET_MAX_TX_MSG_LEN_RES = 517, + MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE = 518, + MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST = 519, + MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ = 520, + MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES = 521, + MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND = 522, + MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND = 523, + MSG_SMS_CONFIGURE_RF_SWITCH_REQ = 524, + MSG_SMS_CONFIGURE_RF_SWITCH_RES = 525, + MSG_SMS_MRC_PATH_DISCONNECT_REQ = 526, + MSG_SMS_MRC_PATH_DISCONNECT_RES = 527, + MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ = 528, + MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES = 529, + MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ = 530, + MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES = 531, + MSG_WR_REG_RFT_REQ = 533, + MSG_WR_REG_RFT_RES = 534, + MSG_RD_REG_RFT_REQ = 535, + MSG_RD_REG_RFT_RES = 536, + MSG_RD_REG_ALL_RFT_REQ = 537, + MSG_RD_REG_ALL_RFT_RES = 538, + MSG_HELP_INT = 539, + MSG_RUN_SCRIPT_INT = 540, + MSG_SMS_EWS_INBAND_REQ = 541, + MSG_SMS_EWS_INBAND_RES = 542, + MSG_SMS_RFS_SELECT_REQ = 543, + MSG_SMS_RFS_SELECT_RES = 544, + MSG_SMS_MB_GET_VER_REQ = 545, + MSG_SMS_MB_GET_VER_RES = 546, + MSG_SMS_MB_WRITE_CFGFILE_REQ = 547, + MSG_SMS_MB_WRITE_CFGFILE_RES = 548, + MSG_SMS_MB_READ_CFGFILE_REQ = 549, + MSG_SMS_MB_READ_CFGFILE_RES = 550, + MSG_SMS_RD_MEM_REQ = 552, + MSG_SMS_RD_MEM_RES = 553, + MSG_SMS_WR_MEM_REQ = 554, + MSG_SMS_WR_MEM_RES = 555, + MSG_SMS_UPDATE_MEM_REQ = 556, + MSG_SMS_UPDATE_MEM_RES = 557, + MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ = 558, + MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES = 559, + MSG_SMS_RF_TUNE_REQ = 561, + MSG_SMS_RF_TUNE_RES = 562, + MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ = 563, + MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES = 564, + MSG_SMS_ISDBT_SB_RECEPTION_REQ = 565, + MSG_SMS_ISDBT_SB_RECEPTION_RES = 566, + MSG_SMS_GENERIC_EPROM_WRITE_REQ = 567, + MSG_SMS_GENERIC_EPROM_WRITE_RES = 568, + MSG_SMS_GENERIC_EPROM_READ_REQ = 569, + MSG_SMS_GENERIC_EPROM_READ_RES = 570, + MSG_SMS_EEPROM_WRITE_REQ = 571, + MSG_SMS_EEPROM_WRITE_RES = 572, + MSG_SMS_CUSTOM_READ_REQ = 574, + MSG_SMS_CUSTOM_READ_RES = 575, + MSG_SMS_CUSTOM_WRITE_REQ = 576, + MSG_SMS_CUSTOM_WRITE_RES = 577, + MSG_SMS_INIT_DEVICE_REQ = 578, + MSG_SMS_INIT_DEVICE_RES = 579, + MSG_SMS_ATSC_SET_ALL_IP_REQ = 580, + MSG_SMS_ATSC_SET_ALL_IP_RES = 581, + MSG_SMS_ATSC_START_ENSEMBLE_REQ = 582, + MSG_SMS_ATSC_START_ENSEMBLE_RES = 583, + MSG_SMS_SET_OUTPUT_MODE_REQ = 584, + MSG_SMS_SET_OUTPUT_MODE_RES = 585, + MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ = 586, + MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES = 587, + MSG_SMS_SUB_CHANNEL_START_REQ = 589, + MSG_SMS_SUB_CHANNEL_START_RES = 590, + MSG_SMS_SUB_CHANNEL_STOP_REQ = 591, + MSG_SMS_SUB_CHANNEL_STOP_RES = 592, + MSG_SMS_ATSC_IP_FILTER_ADD_REQ = 593, + MSG_SMS_ATSC_IP_FILTER_ADD_RES = 594, + MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ = 595, + MSG_SMS_ATSC_IP_FILTER_REMOVE_RES = 596, + MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ = 597, + MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES = 598, + MSG_SMS_WAIT_CMD = 599, + MSG_SMS_ADD_PID_FILTER_REQ = 601, + MSG_SMS_ADD_PID_FILTER_RES = 602, + MSG_SMS_REMOVE_PID_FILTER_REQ = 603, + MSG_SMS_REMOVE_PID_FILTER_RES = 604, + MSG_SMS_FAST_INFORMATION_CHANNEL_REQ = 605, + MSG_SMS_FAST_INFORMATION_CHANNEL_RES = 606, + MSG_SMS_DAB_CHANNEL = 607, + MSG_SMS_GET_PID_FILTER_LIST_REQ = 608, + MSG_SMS_GET_PID_FILTER_LIST_RES = 609, + MSG_SMS_POWER_DOWN_REQ = 610, + MSG_SMS_POWER_DOWN_RES = 611, + MSG_SMS_ATSC_SLT_EXIST_IND = 612, + MSG_SMS_ATSC_NO_SLT_IND = 613, + MSG_SMS_GET_STATISTICS_REQ = 615, + MSG_SMS_GET_STATISTICS_RES = 616, + MSG_SMS_SEND_DUMP = 617, + MSG_SMS_SCAN_START_REQ = 618, + MSG_SMS_SCAN_START_RES = 619, + MSG_SMS_SCAN_STOP_REQ = 620, + MSG_SMS_SCAN_STOP_RES = 621, + MSG_SMS_SCAN_PROGRESS_IND = 622, + MSG_SMS_SCAN_COMPLETE_IND = 623, + MSG_SMS_LOG_ITEM = 624, + MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ = 628, + MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES = 629, + MSG_SMS_HO_PER_SLICES_IND = 630, + MSG_SMS_HO_INBAND_POWER_IND = 631, + MSG_SMS_MANUAL_DEMOD_REQ = 632, + MSG_SMS_HO_TUNE_ON_REQ = 636, + MSG_SMS_HO_TUNE_ON_RES = 637, + MSG_SMS_HO_TUNE_OFF_REQ = 638, + MSG_SMS_HO_TUNE_OFF_RES = 639, + MSG_SMS_HO_PEEK_FREQ_REQ = 640, + MSG_SMS_HO_PEEK_FREQ_RES = 641, + MSG_SMS_HO_PEEK_FREQ_IND = 642, + MSG_SMS_MB_ATTEN_SET_REQ = 643, + MSG_SMS_MB_ATTEN_SET_RES = 644, + MSG_SMS_ENABLE_STAT_IN_I2C_REQ = 649, + MSG_SMS_ENABLE_STAT_IN_I2C_RES = 650, + MSG_SMS_SET_ANTENNA_CONFIG_REQ = 651, + MSG_SMS_SET_ANTENNA_CONFIG_RES = 652, + MSG_SMS_GET_STATISTICS_EX_REQ = 653, + MSG_SMS_GET_STATISTICS_EX_RES = 654, + MSG_SMS_SLEEP_RESUME_COMP_IND = 655, + MSG_SMS_SWITCH_HOST_INTERFACE_REQ = 656, + MSG_SMS_SWITCH_HOST_INTERFACE_RES = 657, + MSG_SMS_DATA_DOWNLOAD_REQ = 660, + MSG_SMS_DATA_DOWNLOAD_RES = 661, + MSG_SMS_DATA_VALIDITY_REQ = 662, + MSG_SMS_DATA_VALIDITY_RES = 663, + MSG_SMS_SWDOWNLOAD_TRIGGER_REQ = 664, + MSG_SMS_SWDOWNLOAD_TRIGGER_RES = 665, + MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ = 666, + MSG_SMS_SWDOWNLOAD_BACKDOOR_RES = 667, + MSG_SMS_GET_VERSION_EX_REQ = 668, + MSG_SMS_GET_VERSION_EX_RES = 669, + MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ = 670, + MSG_SMS_CLOCK_OUTPUT_CONFIG_RES = 671, + MSG_SMS_I2C_SET_FREQ_REQ = 685, + MSG_SMS_I2C_SET_FREQ_RES = 686, + MSG_SMS_GENERIC_I2C_REQ = 687, + MSG_SMS_GENERIC_I2C_RES = 688, + MSG_SMS_DVBT_BDA_DATA = 693, + MSG_SW_RELOAD_REQ = 697, + MSG_SMS_DATA_MSG = 699, + MSG_TABLE_UPLOAD_REQ = 700, + MSG_TABLE_UPLOAD_RES = 701, + MSG_SW_RELOAD_START_REQ = 702, + MSG_SW_RELOAD_START_RES = 703, + MSG_SW_RELOAD_EXEC_REQ = 704, + MSG_SW_RELOAD_EXEC_RES = 705, + MSG_SMS_SPI_INT_LINE_SET_REQ = 710, + MSG_SMS_SPI_INT_LINE_SET_RES = 711, + MSG_SMS_GPIO_CONFIG_EX_REQ = 712, + MSG_SMS_GPIO_CONFIG_EX_RES = 713, + MSG_SMS_WATCHDOG_ACT_REQ = 716, + MSG_SMS_WATCHDOG_ACT_RES = 717, + MSG_SMS_LOOPBACK_REQ = 718, + MSG_SMS_LOOPBACK_RES = 719, + MSG_SMS_RAW_CAPTURE_START_REQ = 720, + MSG_SMS_RAW_CAPTURE_START_RES = 721, + MSG_SMS_RAW_CAPTURE_ABORT_REQ = 722, + MSG_SMS_RAW_CAPTURE_ABORT_RES = 723, + MSG_SMS_RAW_CAPTURE_COMPLETE_IND = 728, + MSG_SMS_DATA_PUMP_IND = 729, + MSG_SMS_DATA_PUMP_REQ = 730, + MSG_SMS_DATA_PUMP_RES = 731, + MSG_SMS_FLASH_DL_REQ = 732, + MSG_SMS_EXEC_TEST_1_REQ = 734, + MSG_SMS_EXEC_TEST_1_RES = 735, + MSG_SMS_ENBALE_TS_INTERFACE_REQ = 736, + MSG_SMS_ENBALE_TS_INTERFACE_RES = 737, + MSG_SMS_SPI_SET_BUS_WIDTH_REQ = 738, + MSG_SMS_SPI_SET_BUS_WIDTH_RES = 739, + MSG_SMS_SEND_EMM_REQ = 740, + MSG_SMS_SEND_EMM_RES = 741, + MSG_SMS_DISABLE_TS_INTERFACE_REQ = 742, + MSG_SMS_DISABLE_TS_INTERFACE_RES = 743, + MSG_SMS_IS_BUF_FREE_REQ = 744, + MSG_SMS_IS_BUF_FREE_RES = 745, + MSG_SMS_EXT_ANTENNA_REQ = 746, + MSG_SMS_EXT_ANTENNA_RES = 747, + MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE = 748, + MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE = 749, + MSG_SMS_BATTERY_LEVEL_REQ = 750, + MSG_SMS_BATTERY_LEVEL_RES = 751, + MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE = 752, + MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE = 753, + MSG_SMS_FM_RADIO_BLOCK_IND = 754, + MSG_SMS_HOST_NOTIFICATION_IND = 755, + MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE = 756, + MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE = 757, + MSG_SMS_CMMB_GET_NETWORKS_REQ = 760, + MSG_SMS_CMMB_GET_NETWORKS_RES = 761, + MSG_SMS_CMMB_START_SERVICE_REQ = 762, + MSG_SMS_CMMB_START_SERVICE_RES = 763, + MSG_SMS_CMMB_STOP_SERVICE_REQ = 764, + MSG_SMS_CMMB_STOP_SERVICE_RES = 765, + MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ = 768, + MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES = 769, + MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ = 770, + MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES = 771, + MSG_SMS_CMMB_START_CONTROL_INFO_REQ = 772, + MSG_SMS_CMMB_START_CONTROL_INFO_RES = 773, + MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ = 774, + MSG_SMS_CMMB_STOP_CONTROL_INFO_RES = 775, + MSG_SMS_ISDBT_TUNE_REQ = 776, + MSG_SMS_ISDBT_TUNE_RES = 777, + MSG_SMS_TRANSMISSION_IND = 782, + MSG_SMS_PID_STATISTICS_IND = 783, + MSG_SMS_POWER_DOWN_IND = 784, + MSG_SMS_POWER_DOWN_CONF = 785, + MSG_SMS_POWER_UP_IND = 786, + MSG_SMS_POWER_UP_CONF = 787, + MSG_SMS_POWER_MODE_SET_REQ = 790, + MSG_SMS_POWER_MODE_SET_RES = 791, + MSG_SMS_DEBUG_HOST_EVENT_REQ = 792, + MSG_SMS_DEBUG_HOST_EVENT_RES = 793, + MSG_SMS_NEW_CRYSTAL_REQ = 794, + MSG_SMS_NEW_CRYSTAL_RES = 795, + MSG_SMS_CONFIG_SPI_REQ = 796, + MSG_SMS_CONFIG_SPI_RES = 797, + MSG_SMS_I2C_SHORT_STAT_IND = 798, + MSG_SMS_START_IR_REQ = 800, + MSG_SMS_START_IR_RES = 801, + MSG_SMS_IR_SAMPLES_IND = 802, + MSG_SMS_CMMB_CA_SERVICE_IND = 803, + MSG_SMS_SLAVE_DEVICE_DETECTED = 804, + MSG_SMS_INTERFACE_LOCK_IND = 805, + MSG_SMS_INTERFACE_UNLOCK_IND = 806, + MSG_SMS_SEND_ROSUM_BUFF_REQ = 810, + MSG_SMS_SEND_ROSUM_BUFF_RES = 811, + MSG_SMS_ROSUM_BUFF = 812, + MSG_SMS_SET_AES128_KEY_REQ = 815, + MSG_SMS_SET_AES128_KEY_RES = 816, + MSG_SMS_MBBMS_WRITE_REQ = 817, + MSG_SMS_MBBMS_WRITE_RES = 818, + MSG_SMS_MBBMS_READ_IND = 819, + MSG_SMS_IQ_STREAM_START_REQ = 820, + MSG_SMS_IQ_STREAM_START_RES = 821, + MSG_SMS_IQ_STREAM_STOP_REQ = 822, + MSG_SMS_IQ_STREAM_STOP_RES = 823, + MSG_SMS_IQ_STREAM_DATA_BLOCK = 824, + MSG_SMS_GET_EEPROM_VERSION_REQ = 825, + MSG_SMS_GET_EEPROM_VERSION_RES = 826, + MSG_SMS_SIGNAL_DETECTED_IND = 827, + MSG_SMS_NO_SIGNAL_IND = 828, + MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ = 830, + MSG_SMS_MRC_SHUTDOWN_SLAVE_RES = 831, + MSG_SMS_MRC_BRINGUP_SLAVE_REQ = 832, + MSG_SMS_MRC_BRINGUP_SLAVE_RES = 833, + MSG_SMS_EXTERNAL_LNA_CTRL_REQ = 834, + MSG_SMS_EXTERNAL_LNA_CTRL_RES = 835, + MSG_SMS_SET_PERIODIC_STATISTICS_REQ = 836, + MSG_SMS_SET_PERIODIC_STATISTICS_RES = 837, + MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ = 838, + MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES = 839, + LOCAL_TUNE = 850, + LOCAL_IFFT_H_ICI = 851, + MSG_RESYNC_REQ = 852, + MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ = 853, + MSG_SMS_CMMB_GET_MRC_STATISTICS_RES = 854, + MSG_SMS_LOG_EX_ITEM = 855, + MSG_SMS_DEVICE_DATA_LOSS_IND = 856, + MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND = 857, + MSG_SMS_USER_MSG_REQ = 858, + MSG_SMS_USER_MSG_RES = 859, + MSG_SMS_SMART_CARD_INIT_REQ = 860, + MSG_SMS_SMART_CARD_INIT_RES = 861, + MSG_SMS_SMART_CARD_WRITE_REQ = 862, + MSG_SMS_SMART_CARD_WRITE_RES = 863, + MSG_SMS_SMART_CARD_READ_IND = 864, + MSG_SMS_TSE_ENABLE_REQ = 866, + MSG_SMS_TSE_ENABLE_RES = 867, + MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ = 868, + MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES = 869, + MSG_SMS_LED_CONFIG_REQ = 870, + MSG_SMS_LED_CONFIG_RES = 871, + MSG_PWM_ANTENNA_REQ = 872, + MSG_PWM_ANTENNA_RES = 873, + MSG_SMS_CMMB_SMD_SN_REQ = 874, + MSG_SMS_CMMB_SMD_SN_RES = 875, + MSG_SMS_CMMB_SET_CA_CW_REQ = 876, + MSG_SMS_CMMB_SET_CA_CW_RES = 877, + MSG_SMS_CMMB_SET_CA_SALT_REQ = 878, + MSG_SMS_CMMB_SET_CA_SALT_RES = 879, + MSG_SMS_NSCD_INIT_REQ = 880, + MSG_SMS_NSCD_INIT_RES = 881, + MSG_SMS_NSCD_PROCESS_SECTION_REQ = 882, + MSG_SMS_NSCD_PROCESS_SECTION_RES = 883, + MSG_SMS_DBD_CREATE_OBJECT_REQ = 884, + MSG_SMS_DBD_CREATE_OBJECT_RES = 885, + MSG_SMS_DBD_CONFIGURE_REQ = 886, + MSG_SMS_DBD_CONFIGURE_RES = 887, + MSG_SMS_DBD_SET_KEYS_REQ = 888, + MSG_SMS_DBD_SET_KEYS_RES = 889, + MSG_SMS_DBD_PROCESS_HEADER_REQ = 890, + MSG_SMS_DBD_PROCESS_HEADER_RES = 891, + MSG_SMS_DBD_PROCESS_DATA_REQ = 892, + MSG_SMS_DBD_PROCESS_DATA_RES = 893, + MSG_SMS_DBD_PROCESS_GET_DATA_REQ = 894, + MSG_SMS_DBD_PROCESS_GET_DATA_RES = 895, + MSG_SMS_NSCD_OPEN_SESSION_REQ = 896, + MSG_SMS_NSCD_OPEN_SESSION_RES = 897, + MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ = 898, + MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES = 899, + MSG_LAST_MSG_TYPE = 900, +}; #define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ - (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ - (ptr)->msgLength = len; (ptr)->msgFlags = 0; \ + (ptr)->msg_type = type; \ + (ptr)->msg_src_id = src; \ + (ptr)->msg_dst_id = dst; \ + (ptr)->msg_length = len; \ + (ptr)->msg_flags = 0; \ } while (0) #define SMS_INIT_MSG(ptr, type, len) \ @@ -277,228 +604,296 @@ enum SMS_DEVICE_MODE { DEVICE_MODE_ISDBT_BDA, DEVICE_MODE_CMMB, DEVICE_MODE_RAW_TUNER, + DEVICE_MODE_FM_RADIO, + DEVICE_MODE_FM_RADIO_BDA, + DEVICE_MODE_ATSC, DEVICE_MODE_MAX, }; -struct SmsMsgHdr_ST { - u16 msgType; - u8 msgSrcId; - u8 msgDstId; - u16 msgLength; /* Length of entire message, including header */ - u16 msgFlags; +struct sms_msg_hdr { + u16 msg_type; + u8 msg_src_id; + u8 msg_dst_id; + u16 msg_length; /* length of entire message, including header */ + u16 msg_flags; +}; + +struct sms_msg_data { + struct sms_msg_hdr x_msg_header; + u32 msg_data[1]; }; -struct SmsMsgData_ST { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[1]; +struct sms_msg_data2 { + struct sms_msg_hdr x_msg_header; + u32 msg_data[2]; }; -struct SmsMsgData_ST2 { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[2]; +struct sms_msg_data4 { + struct sms_msg_hdr x_msg_header; + u32 msg_data[4]; }; -struct SmsDataDownload_ST { - struct SmsMsgHdr_ST xMsgHeader; - u32 MemAddr; - u8 Payload[SMS_MAX_PAYLOAD_SIZE]; +struct sms_data_download { + struct sms_msg_hdr x_msg_header; + u32 mem_addr; + u8 payload[SMS_MAX_PAYLOAD_SIZE]; }; -struct SmsVersionRes_ST { - struct SmsMsgHdr_ST xMsgHeader; +struct sms_version_res { + struct sms_msg_hdr x_msg_header; - u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ - u8 Step; /* 0 - Step A */ - u8 MetalFix; /* 0 - Metal 0 */ + u16 chip_model; /* e.g. 0x1102 for SMS-1102 "Nova" */ + u8 step; /* 0 - step A */ + u8 metal_fix; /* 0 - Metal 0 */ - /* FirmwareId 0xFF if ROM, otherwise the + /* firmware_id 0xFF if ROM, otherwise the * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ - u8 FirmwareId; - /* SupportedProtocols Bitwise OR combination of + u8 firmware_id; + /* supported_protocols Bitwise OR combination of * supported protocols */ - u8 SupportedProtocols; + u8 supported_protocols; - u8 VersionMajor; - u8 VersionMinor; - u8 VersionPatch; - u8 VersionFieldPatch; + u8 version_major; + u8 version_minor; + u8 version_patch; + u8 version_field_patch; - u8 RomVersionMajor; - u8 RomVersionMinor; - u8 RomVersionPatch; - u8 RomVersionFieldPatch; + u8 rom_ver_major; + u8 rom_ver_minor; + u8 rom_ver_patch; + u8 rom_ver_field_patch; u8 TextLabel[34]; }; -struct SmsFirmware_ST { - u32 CheckSum; - u32 Length; - u32 StartAddress; - u8 Payload[1]; +struct sms_firmware { + u32 check_sum; + u32 length; + u32 start_address; + u8 payload[1]; }; -/* Statistics information returned as response for - * SmsHostApiGetStatistics_Req */ -struct SMSHOSTLIB_STATISTICS_ST { - u32 Reserved; /* Reserved */ +/* statistics information returned as response for + * SmsHostApiGetstatistics_Req */ +struct sms_stats { + u32 reserved; /* reserved */ /* Common parameters */ - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */ /* Reception quality */ s32 SNR; /* dB */ - u32 BER; /* Post Viterbi BER [1E-5] */ + u32 ber; /* Post Viterbi ber [1E-5] */ u32 FIB_CRC; /* CRC errors percentage, valid only for DAB */ - u32 TS_PER; /* Transport stream PER, + u32 ts_per; /* Transport stream PER, 0xFFFFFFFF indicate N/A, valid only for DVB-T/H */ u32 MFER; /* DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H */ s32 RSSI; /* dBm */ - s32 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in bin/1024 */ + s32 in_band_pwr; /* In band power in dBM */ + s32 carrier_offset; /* Carrier Offset in bin/1024 */ /* Transmission parameters */ - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ - u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, + u32 frequency; /* frequency in Hz */ + u32 bandwidth; /* bandwidth in MHz, valid only for DVB-T/H */ + u32 transmission_mode; /* Transmission Mode, for DAB modes 1-4, for DVB-T/H FFT mode carriers in Kilos */ - u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, + u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, valid only for DVB-T/H */ - u32 GuardInterval; /* Guard Interval from - SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ - u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, + u32 guard_interval; /* Guard Interval from + SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ + u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, valid only for DVB-T/H */ - u32 LPCodeRate; /* Low Priority Code Rate from + u32 lp_code_rate; /* Low Priority Code Rate from SMSHOSTLIB_CODE_RATE_ET, valid only for DVB-T/H */ - u32 Hierarchy; /* Hierarchy from SMSHOSTLIB_HIERARCHY_ET, + u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET, valid only for DVB-T/H */ - u32 Constellation; /* Constellation from + u32 constellation; /* constellation from SMSHOSTLIB_CONSTELLATION_ET, valid only for DVB-T/H */ /* Burst parameters, valid only for DVB-H */ - u32 BurstSize; /* Current burst size in bytes, + u32 burst_size; /* Current burst size in bytes, valid only for DVB-H */ - u32 BurstDuration; /* Current burst duration in mSec, + u32 burst_duration; /* Current burst duration in mSec, valid only for DVB-H */ - u32 BurstCycleTime; /* Current burst cycle time in mSec, + u32 burst_cycle_time; /* Current burst cycle time in mSec, valid only for DVB-H */ - u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, + u32 calc_burst_cycle_time;/* Current burst cycle time in mSec, as calculated by demodulator, valid only for DVB-H */ - u32 NumOfRows; /* Number of rows in MPE table, + u32 num_of_rows; /* Number of rows in MPE table, valid only for DVB-H */ - u32 NumOfPaddCols; /* Number of padding columns in MPE table, + u32 num_of_padd_cols; /* Number of padding columns in MPE table, valid only for DVB-H */ - u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, + u32 num_of_punct_cols; /* Number of puncturing columns in MPE table, valid only for DVB-H */ - u32 ErrorTSPackets; /* Number of erroneous + u32 error_ts_packets; /* Number of erroneous transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ - u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include + u32 total_ts_packets; /* Total number of transport-stream packets */ + u32 num_of_valid_mpe_tlbs; /* Number of MPE tables which do not include errors after MPE RS decoding */ - u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors + u32 num_of_invalid_mpe_tlbs;/* Number of MPE tables which include errors after MPE RS decoding */ - u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were + u32 num_of_corrected_mpe_tlbs;/* Number of MPE tables which were corrected by MPE RS decoding */ /* Common params */ - u32 BERErrorCount; /* Number of errornous SYNC bits. */ - u32 BERBitCount; /* Total number of SYNC bits. */ + u32 ber_error_count; /* Number of errornous SYNC bits. */ + u32 ber_bit_count; /* Total number of SYNC bits. */ /* Interface information */ - u32 SmsToHostTxErrors; /* Total number of transmission errors. */ + u32 sms_to_host_tx_errors; /* Total number of transmission errors. */ /* DAB/T-DMB */ - u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ + u32 pre_ber; /* DAB/T-DMB only: Pre Viterbi ber [1E-5] */ /* DVB-H TPS parameters */ - u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; + u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered */ - u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - + u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - + u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 NumMPEReceived; /* DVB-H, Num MPE section received */ + u32 num_mpe_received; /* DVB-H, Num MPE section received */ - u32 ReservedFields[10]; /* Reserved */ + u32 reservedFields[10]; /* reserved */ }; -struct SmsMsgStatisticsInfo_ST { - u32 RequestResult; +struct sms_msg_statistics_info { + u32 request_result; - struct SMSHOSTLIB_STATISTICS_ST Stat; + struct sms_stats stat; /* Split the calc of the SNR in DAB */ - u32 Signal; /* dB */ - u32 Noise; /* dB */ + u32 signal; /* dB */ + u32 noise; /* dB */ }; -struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { +struct sms_isdbt_layer_stats { /* Per-layer information */ - u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, + u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, * 255 means layer does not exist */ - u32 Constellation; /* Constellation from SMSHOSTLIB_CONSTELLATION_ET, + u32 constellation; /* constellation from SMSHOSTLIB_CONSTELLATION_ET, * 255 means layer does not exist */ - u32 BER; /* Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ - u32 BERErrorCount; /* Post Viterbi Error Bits Count */ - u32 BERBitCount; /* Post Viterbi Total Bits Count */ - u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ - u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ - u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ - u32 TILdepthI; /* Time interleaver depth I parameter, + u32 ber; /* Post Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */ + u32 ber_error_count; /* Post Viterbi Error Bits Count */ + u32 ber_bit_count; /* Post Viterbi Total Bits Count */ + u32 pre_ber; /* Pre Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */ + u32 ts_per; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ + u32 error_ts_packets; /* Number of erroneous transport-stream packets */ + u32 total_ts_packets; /* Total number of transport-stream packets */ + u32 ti_ldepth_i; /* Time interleaver depth I parameter, * 255 means layer does not exist */ - u32 NumberOfSegments; /* Number of segments in layer A, + u32 number_of_segments; /* Number of segments in layer A, * 255 means layer does not exist */ - u32 TMCCErrors; /* TMCC errors */ + u32 tmcc_errors; /* TMCC errors */ }; -struct SMSHOSTLIB_STATISTICS_ISDBT_ST { - u32 StatisticsType; /* Enumerator identifying the type of the +struct sms_isdbt_stats { + u32 statistics_type; /* Enumerator identifying the type of the * structure. Values are the same as * SMSHOSTLIB_DEVICE_MODES_E * * This field MUST always be first in any * statistics structure */ - u32 FullSize; /* Total size of the structure returned by the modem. + u32 full_size; /* Total size of the structure returned by the modem. * If the size requested by the host is smaller than - * FullSize, the struct will be truncated */ + * full_size, the struct will be truncated */ /* Common parameters */ - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */ /* Reception quality */ s32 SNR; /* dB */ s32 RSSI; /* dBm */ - s32 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in Hz */ + s32 in_band_pwr; /* In band power in dBM */ + s32 carrier_offset; /* Carrier Offset in Hz */ /* Transmission parameters */ - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz */ - u32 TransmissionMode; /* ISDB-T transmission mode */ - u32 ModemState; /* 0 - Acquisition, 1 - Locked */ - u32 GuardInterval; /* Guard Interval, 1 divided by value */ - u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ - u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ - u32 NumOfLayers; /* Number of ISDB-T layers in the network */ + u32 frequency; /* frequency in Hz */ + u32 bandwidth; /* bandwidth in MHz */ + u32 transmission_mode; /* ISDB-T transmission mode */ + u32 modem_state; /* 0 - Acquisition, 1 - Locked */ + u32 guard_interval; /* Guard Interval, 1 divided by value */ + u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ + u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */ + u32 num_of_layers; /* Number of ISDB-T layers in the network */ /* Per-layer information */ /* Layers A, B and C */ - struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; - /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ + struct sms_isdbt_layer_stats layer_info[3]; + /* Per-layer statistics, see sms_isdbt_layer_stats */ /* Interface information */ - u32 SmsToHostTxErrors; /* Total number of transmission errors. */ + u32 sms_to_host_tx_errors; /* Total number of transmission errors. */ }; -struct PID_STATISTICS_DATA_S { +struct sms_isdbt_stats_ex { + u32 statistics_type; /* Enumerator identifying the type of the + * structure. Values are the same as + * SMSHOSTLIB_DEVICE_MODES_E + * + * This field MUST always be first in any + * statistics structure */ + + u32 full_size; /* Total size of the structure returned by the modem. + * If the size requested by the host is smaller than + * full_size, the struct will be truncated */ + + /* Common parameters */ + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */ + + /* Reception quality */ + s32 SNR; /* dB */ + s32 RSSI; /* dBm */ + s32 in_band_pwr; /* In band power in dBM */ + s32 carrier_offset; /* Carrier Offset in Hz */ + + /* Transmission parameters */ + u32 frequency; /* frequency in Hz */ + u32 bandwidth; /* bandwidth in MHz */ + u32 transmission_mode; /* ISDB-T transmission mode */ + u32 modem_state; /* 0 - Acquisition, 1 - Locked */ + u32 guard_interval; /* Guard Interval, 1 divided by value */ + u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ + u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */ + u32 num_of_layers; /* Number of ISDB-T layers in the network */ + + u32 segment_number; /* Segment number for ISDB-Tsb */ + u32 tune_bw; /* Tuned bandwidth - BW_ISDBT_1SEG / BW_ISDBT_3SEG */ + + /* Per-layer information */ + /* Layers A, B and C */ + struct sms_isdbt_layer_stats layer_info[3]; + /* Per-layer statistics, see sms_isdbt_layer_stats */ + + /* Interface information */ + u32 reserved1; /* Was sms_to_host_tx_errors - obsolete . */ + /* Proprietary information */ + u32 ext_antenna; /* Obsolete field. */ + u32 reception_quality; + u32 ews_alert_active; /* signals if EWS alert is currently on */ + u32 lna_on_off; /* Internal LNA state: 0: OFF, 1: ON */ + + u32 rf_agc_level; /* RF AGC Level [linear units], full gain = 65535 (20dB) */ + u32 bb_agc_level; /* Baseband AGC level [linear units], full gain = 65535 (71.5dB) */ + u32 fw_errors_counter; /* Application errors - should be always zero */ + u8 FwErrorsHistoryArr[8]; /* Last FW errors IDs - first is most recent, last is oldest */ + + s32 MRC_SNR; /* dB */ + u32 snr_full_res; /* dB x 65536 */ + u32 reserved4[4]; +}; + + +struct sms_pid_stats_data { struct PID_BURST_S { u32 size; u32 padding_cols; @@ -513,112 +908,155 @@ struct PID_STATISTICS_DATA_S { u32 tot_cor_tbl; }; -struct PID_DATA_S { +struct sms_pid_data { u32 pid; u32 num_rows; - struct PID_STATISTICS_DATA_S pid_statistics; + struct sms_pid_stats_data pid_statistics; }; #define CORRECT_STAT_RSSI(_stat) ((_stat).RSSI *= -1) -#define CORRECT_STAT_BANDWIDTH(_stat) (_stat.Bandwidth = 8 - _stat.Bandwidth) +#define CORRECT_STAT_BANDWIDTH(_stat) (_stat.bandwidth = 8 - _stat.bandwidth) #define CORRECT_STAT_TRANSMISSON_MODE(_stat) \ - if (_stat.TransmissionMode == 0) \ - _stat.TransmissionMode = 2; \ - else if (_stat.TransmissionMode == 1) \ - _stat.TransmissionMode = 8; \ + if (_stat.transmission_mode == 0) \ + _stat.transmission_mode = 2; \ + else if (_stat.transmission_mode == 1) \ + _stat.transmission_mode = 8; \ else \ - _stat.TransmissionMode = 4; + _stat.transmission_mode = 4; -struct TRANSMISSION_STATISTICS_S { - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz */ - u32 TransmissionMode; /* FFT mode carriers in Kilos */ - u32 GuardInterval; /* Guard Interval from +struct sms_tx_stats { + u32 frequency; /* frequency in Hz */ + u32 bandwidth; /* bandwidth in MHz */ + u32 transmission_mode; /* FFT mode carriers in Kilos */ + u32 guard_interval; /* Guard Interval from SMSHOSTLIB_GUARD_INTERVALS_ET */ - u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ - u32 LPCodeRate; /* Low Priority Code Rate from + u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ + u32 lp_code_rate; /* Low Priority Code Rate from SMSHOSTLIB_CODE_RATE_ET */ - u32 Hierarchy; /* Hierarchy from SMSHOSTLIB_HIERARCHY_ET */ - u32 Constellation; /* Constellation from + u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET */ + u32 constellation; /* constellation from SMSHOSTLIB_CONSTELLATION_ET */ /* DVB-H TPS parameters */ - u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; + u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered */ - u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - + u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - + u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ +}; + +struct sms_rx_stats { + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */ + + u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ + s32 SNR; /* dB */ + u32 ber; /* Post Viterbi ber [1E-5] */ + u32 ber_error_count; /* Number of erronous SYNC bits. */ + u32 ber_bit_count; /* Total number of SYNC bits. */ + u32 ts_per; /* Transport stream PER, + 0xFFFFFFFF indicate N/A */ + u32 MFER; /* DVB-H frame error rate in percentage, + 0xFFFFFFFF indicate N/A, valid only for DVB-H */ + s32 RSSI; /* dBm */ + s32 in_band_pwr; /* In band power in dBM */ + s32 carrier_offset; /* Carrier Offset in bin/1024 */ + u32 error_ts_packets; /* Number of erroneous + transport-stream packets */ + u32 total_ts_packets; /* Total number of transport-stream packets */ + + s32 MRC_SNR; /* dB */ + s32 MRC_RSSI; /* dBm */ + s32 mrc_in_band_pwr; /* In band power in dBM */ }; -struct RECEPTION_STATISTICS_S { - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ +struct sms_rx_stats_ex { + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */ - u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ + u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ s32 SNR; /* dB */ - u32 BER; /* Post Viterbi BER [1E-5] */ - u32 BERErrorCount; /* Number of erronous SYNC bits. */ - u32 BERBitCount; /* Total number of SYNC bits. */ - u32 TS_PER; /* Transport stream PER, + u32 ber; /* Post Viterbi ber [1E-5] */ + u32 ber_error_count; /* Number of erronous SYNC bits. */ + u32 ber_bit_count; /* Total number of SYNC bits. */ + u32 ts_per; /* Transport stream PER, 0xFFFFFFFF indicate N/A */ u32 MFER; /* DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H */ s32 RSSI; /* dBm */ - s32 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in bin/1024 */ - u32 ErrorTSPackets; /* Number of erroneous + s32 in_band_pwr; /* In band power in dBM */ + s32 carrier_offset; /* Carrier Offset in bin/1024 */ + u32 error_ts_packets; /* Number of erroneous transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ + u32 total_ts_packets; /* Total number of transport-stream packets */ + + s32 ref_dev_ppm; + s32 freq_dev_hz; s32 MRC_SNR; /* dB */ s32 MRC_RSSI; /* dBm */ - s32 MRC_InBandPwr; /* In band power in dBM */ + s32 mrc_in_band_pwr; /* In band power in dBM */ }; -/* Statistics information returned as response for - * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ -struct SMSHOSTLIB_STATISTICS_DVB_S { +/* statistics information returned as response for + * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */ +struct sms_stats_dvb { /* Reception */ - struct RECEPTION_STATISTICS_S ReceptionData; + struct sms_rx_stats reception_data; /* Transmission parameters */ - struct TRANSMISSION_STATISTICS_S TransmissionData; + struct sms_tx_stats transmission_data; /* Burst parameters, valid only for DVB-H */ #define SRVM_MAX_PID_FILTERS 8 - struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; + struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS]; }; -struct SRVM_SIGNAL_STATUS_S { +/* statistics information returned as response for + * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */ +struct sms_stats_dvb_ex { + /* Reception */ + struct sms_rx_stats_ex reception_data; + + /* Transmission parameters */ + struct sms_tx_stats transmission_data; + + /* Burst parameters, valid only for DVB-H */ +#define SRVM_MAX_PID_FILTERS 8 + struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS]; +}; + +struct sms_srvm_signal_status { u32 result; u32 snr; - u32 tsPackets; - u32 etsPackets; + u32 ts_packets; + u32 ets_packets; u32 constellation; - u32 hpCode; - u32 tpsSrvIndLP; - u32 tpsSrvIndHP; - u32 cellId; + u32 hp_code; + u32 tps_srv_ind_lp; + u32 tps_srv_ind_hp; + u32 cell_id; u32 reason; - s32 inBandPower; - u32 requestId; + s32 in_band_power; + u32 request_id; }; -struct SMSHOSTLIB_I2C_REQ_ST { - u32 DeviceAddress; /* I2c device address */ - u32 WriteCount; /* number of bytes to write */ - u32 ReadCount; /* number of bytes to read */ +struct sms_i2c_req { + u32 device_address; /* I2c device address */ + u32 write_count; /* number of bytes to write */ + u32 read_count; /* number of bytes to read */ u8 Data[1]; }; -struct SMSHOSTLIB_I2C_RES_ST { - u32 Status; /* non-zero value in case of failure */ - u32 ReadCount; /* number of bytes read */ +struct sms_i2c_res { + u32 status; /* non-zero value in case of failure */ + u32 read_count; /* number of bytes read */ u8 Data[1]; }; @@ -638,59 +1076,39 @@ struct smscore_config_gpio { #define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1 u8 inputcharacteristics; -#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 -#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 - u8 outputslewrate; - -#define SMS_GPIO_OUTPUTDRIVING_4mA 0 -#define SMS_GPIO_OUTPUTDRIVING_8mA 1 -#define SMS_GPIO_OUTPUTDRIVING_12mA 2 -#define SMS_GPIO_OUTPUTDRIVING_16mA 3 - u8 outputdriving; -}; - -struct smscore_gpio_config { -#define SMS_GPIO_DIRECTION_INPUT 0 -#define SMS_GPIO_DIRECTION_OUTPUT 1 - u8 Direction; - -#define SMS_GPIO_PULL_UP_DOWN_NONE 0 -#define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 -#define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 -#define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 - u8 PullUpDown; - -#define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 -#define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 - u8 InputCharacteristics; - -#define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ + /* 10xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 +#define SMS_GPIO_OUTPUT_SLEW_WRATE_SLOW 1 + /* 11xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 +#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 +#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 +#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 -#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ - u8 OutputSlewRate; + u8 outputslewrate; -#define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ + /* 10xx */ +#define SMS_GPIO_OUTPUTDRIVING_S_4mA 0 +#define SMS_GPIO_OUTPUTDRIVING_S_8mA 1 +#define SMS_GPIO_OUTPUTDRIVING_S_12mA 2 +#define SMS_GPIO_OUTPUTDRIVING_S_16mA 3 + + /* 11xx*/ +#define SMS_GPIO_OUTPUTDRIVING_1_5mA 0 +#define SMS_GPIO_OUTPUTDRIVING_2_8mA 1 +#define SMS_GPIO_OUTPUTDRIVING_4mA 2 +#define SMS_GPIO_OUTPUTDRIVING_7mA 3 +#define SMS_GPIO_OUTPUTDRIVING_10mA 4 +#define SMS_GPIO_OUTPUTDRIVING_11mA 5 +#define SMS_GPIO_OUTPUTDRIVING_14mA 6 +#define SMS_GPIO_OUTPUTDRIVING_16mA 7 -#define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ - u8 OutputDriving; + u8 outputdriving; }; -extern void smscore_registry_setmode(char *devpath, int mode); +char *smscore_translate_msg(enum msg_types msgtype); + extern int smscore_registry_getmode(char *devpath); extern int smscore_register_hotplug(hotplug_t hotplug); @@ -721,8 +1139,6 @@ extern void smscore_onresponse(struct smscore_device_t *coredev, extern int smscore_get_common_buffer_size(struct smscore_device_t *coredev); extern int smscore_map_common_buffer(struct smscore_device_t *coredev, struct vm_area_struct *vma); -extern int smscore_get_fw_filename(struct smscore_device_t *coredev, - int mode, char *filename); extern int smscore_send_fw_file(struct smscore_device_t *coredev, u8 *ufwbuf, int size); @@ -737,11 +1153,11 @@ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); /* new GPIO management */ -extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, - struct smscore_gpio_config *pGpioConfig); -extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, - u8 NewLevel); -extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, +extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num, + struct smscore_config_gpio *p_gpio_config); +extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num, + u8 new_level); +extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num, u8 *level); void smscore_set_board_id(struct smscore_device_t *core, int id); @@ -760,7 +1176,8 @@ int smscore_led_state(struct smscore_device_t *core, int led); #define dprintk(kern, lvl, fmt, arg...) do {\ if (sms_dbg & lvl) \ - sms_printk(kern, fmt, ##arg); } while (0) + sms_printk(kern, fmt, ##arg); \ +} while (0) #define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) #define sms_err(fmt, arg...) \ diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c new file mode 100644 index 0000000..0bb4430 --- /dev/null +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -0,0 +1,551 @@ +/*********************************************************************** + * + * Copyright(c) 2013 Mauro Carvalho Chehab <mchehab@redhat.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, see <http://www.gnu.org/licenses/>. + * + ***********************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/spinlock.h> +#include <linux/usb.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "smscoreapi.h" + +#include "smsdvb.h" + +static struct dentry *smsdvb_debugfs_usb_root; + +struct smsdvb_debugfs { + struct kref refcount; + spinlock_t lock; + + char stats_data[PAGE_SIZE]; + unsigned stats_count; + bool stats_was_read; + + wait_queue_head_t stats_queue; +}; + +static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data, + struct sms_stats *p) +{ + int n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_rf_locked = %d\n", p->is_rf_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_demod_locked = %d\n", p->is_demod_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_external_lna_on = %d\n", p->is_external_lna_on); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d\n", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ber = %d\n", p->ber); + n += snprintf(&buf[n], PAGE_SIZE - n, + "FIB_CRC = %d\n", p->FIB_CRC); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ts_per = %d\n", p->ts_per); + n += snprintf(&buf[n], PAGE_SIZE - n, + "MFER = %d\n", p->MFER); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d\n", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "in_band_pwr = %d\n", p->in_band_pwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "carrier_offset = %d\n", p->carrier_offset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "modem_state = %d\n", p->modem_state); + n += snprintf(&buf[n], PAGE_SIZE - n, + "frequency = %d\n", p->frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "bandwidth = %d\n", p->bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "transmission_mode = %d\n", p->transmission_mode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "modem_state = %d\n", p->modem_state); + n += snprintf(&buf[n], PAGE_SIZE - n, + "guard_interval = %d\n", p->guard_interval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "code_rate = %d\n", p->code_rate); + n += snprintf(&buf[n], PAGE_SIZE - n, + "lp_code_rate = %d\n", p->lp_code_rate); + n += snprintf(&buf[n], PAGE_SIZE - n, + "hierarchy = %d\n", p->hierarchy); + n += snprintf(&buf[n], PAGE_SIZE - n, + "constellation = %d\n", p->constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, + "burst_size = %d\n", p->burst_size); + n += snprintf(&buf[n], PAGE_SIZE - n, + "burst_duration = %d\n", p->burst_duration); + n += snprintf(&buf[n], PAGE_SIZE - n, + "burst_cycle_time = %d\n", p->burst_cycle_time); + n += snprintf(&buf[n], PAGE_SIZE - n, + "calc_burst_cycle_time = %d\n", + p->calc_burst_cycle_time); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_rows = %d\n", p->num_of_rows); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_padd_cols = %d\n", p->num_of_padd_cols); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_punct_cols = %d\n", p->num_of_punct_cols); + n += snprintf(&buf[n], PAGE_SIZE - n, + "error_ts_packets = %d\n", p->error_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, + "total_ts_packets = %d\n", p->total_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_corrected_mpe_tlbs = %d\n", p->num_of_corrected_mpe_tlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ber_error_count = %d\n", p->ber_error_count); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ber_bit_count = %d\n", p->ber_bit_count); + n += snprintf(&buf[n], PAGE_SIZE - n, + "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); + n += snprintf(&buf[n], PAGE_SIZE - n, + "pre_ber = %d\n", p->pre_ber); + n += snprintf(&buf[n], PAGE_SIZE - n, + "cell_id = %d\n", p->cell_id); + n += snprintf(&buf[n], PAGE_SIZE - n, + "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp); + n += snprintf(&buf[n], PAGE_SIZE - n, + "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_mpe_received = %d\n", p->num_mpe_received); + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + wake_up(&debug_data->stats_queue); +} + +static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, + struct sms_isdbt_stats *p) +{ + int i, n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "statistics_type = %d\t", p->statistics_type); + n += snprintf(&buf[n], PAGE_SIZE - n, + "full_size = %d\n", p->full_size); + + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_rf_locked = %d\t\t", p->is_rf_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_demod_locked = %d\t", p->is_demod_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_external_lna_on = %d\n", p->is_external_lna_on); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d dB\t\t", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d dBm\t\t", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "in_band_pwr = %d dBm\n", p->in_band_pwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "carrier_offset = %d\t", p->carrier_offset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "bandwidth = %d\t\t", p->bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "frequency = %d Hz\n", p->frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "transmission_mode = %d\t", p->transmission_mode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "modem_state = %d\t\t", p->modem_state); + n += snprintf(&buf[n], PAGE_SIZE - n, + "guard_interval = %d\n", p->guard_interval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "system_type = %d\t\t", p->system_type); + n += snprintf(&buf[n], PAGE_SIZE - n, + "partial_reception = %d\t", p->partial_reception); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_layers = %d\n", p->num_of_layers); + n += snprintf(&buf[n], PAGE_SIZE - n, + "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); + + for (i = 0; i < 3; i++) { + if (p->layer_info[i].number_of_segments < 1 || + p->layer_info[i].number_of_segments > 13) + continue; + + n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", + p->layer_info[i].code_rate); + n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", + p->layer_info[i].constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", + p->layer_info[i].ber); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t", + p->layer_info[i].ber_error_count); + n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", + p->layer_info[i].ber_bit_count); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", + p->layer_info[i].pre_ber); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", + p->layer_info[i].ts_per); + n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t", + p->layer_info[i].error_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t", + p->layer_info[i].total_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", + p->layer_info[i].ti_ldepth_i); + n += snprintf(&buf[n], PAGE_SIZE - n, + "\tnumber_of_segments = %d\t", + p->layer_info[i].number_of_segments); + n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", + p->layer_info[i].tmcc_errors); + } + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + wake_up(&debug_data->stats_queue); +} + +static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, + struct sms_isdbt_stats_ex *p) +{ + int i, n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "statistics_type = %d\t", p->statistics_type); + n += snprintf(&buf[n], PAGE_SIZE - n, + "full_size = %d\n", p->full_size); + + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_rf_locked = %d\t\t", p->is_rf_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_demod_locked = %d\t", p->is_demod_locked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "is_external_lna_on = %d\n", p->is_external_lna_on); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d dB\t\t", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d dBm\t\t", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "in_band_pwr = %d dBm\n", p->in_band_pwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "carrier_offset = %d\t", p->carrier_offset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "bandwidth = %d\t\t", p->bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "frequency = %d Hz\n", p->frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "transmission_mode = %d\t", p->transmission_mode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "modem_state = %d\t\t", p->modem_state); + n += snprintf(&buf[n], PAGE_SIZE - n, + "guard_interval = %d\n", p->guard_interval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "system_type = %d\t\t", p->system_type); + n += snprintf(&buf[n], PAGE_SIZE - n, + "partial_reception = %d\t", p->partial_reception); + n += snprintf(&buf[n], PAGE_SIZE - n, + "num_of_layers = %d\n", p->num_of_layers); + n += snprintf(&buf[n], PAGE_SIZE - n, "segment_number = %d\t", + p->segment_number); + n += snprintf(&buf[n], PAGE_SIZE - n, "tune_bw = %d\n", + p->tune_bw); + + for (i = 0; i < 3; i++) { + if (p->layer_info[i].number_of_segments < 1 || + p->layer_info[i].number_of_segments > 13) + continue; + + n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", + p->layer_info[i].code_rate); + n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", + p->layer_info[i].constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", + p->layer_info[i].ber); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t", + p->layer_info[i].ber_error_count); + n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", + p->layer_info[i].ber_bit_count); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", + p->layer_info[i].pre_ber); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", + p->layer_info[i].ts_per); + n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t", + p->layer_info[i].error_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t", + p->layer_info[i].total_ts_packets); + n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", + p->layer_info[i].ti_ldepth_i); + n += snprintf(&buf[n], PAGE_SIZE - n, + "\tnumber_of_segments = %d\t", + p->layer_info[i].number_of_segments); + n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", + p->layer_info[i].tmcc_errors); + } + + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + + wake_up(&debug_data->stats_queue); +} + +static int smsdvb_stats_open(struct inode *inode, struct file *file) +{ + struct smsdvb_client_t *client = inode->i_private; + struct smsdvb_debugfs *debug_data = client->debug_data; + + kref_get(&debug_data->refcount); + + spin_lock(&debug_data->lock); + debug_data->stats_count = 0; + debug_data->stats_was_read = false; + spin_unlock(&debug_data->lock); + + file->private_data = debug_data; + + return 0; +} + +static void smsdvb_debugfs_data_release(struct kref *ref) +{ + struct smsdvb_debugfs *debug_data; + + debug_data = container_of(ref, struct smsdvb_debugfs, refcount); + kfree(debug_data); +} + +static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data) +{ + int rc = 1; + + spin_lock(&debug_data->lock); + + if (debug_data->stats_was_read) + goto exit; + + rc = debug_data->stats_count; + +exit: + spin_unlock(&debug_data->lock); + return rc; +} + +static unsigned int smsdvb_stats_poll(struct file *file, poll_table *wait) +{ + struct smsdvb_debugfs *debug_data = file->private_data; + int rc; + + kref_get(&debug_data->refcount); + + poll_wait(file, &debug_data->stats_queue, wait); + + rc = smsdvb_stats_wait_read(debug_data); + if (rc > 0) + rc = POLLIN | POLLRDNORM; + + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); + + return rc; +} + +static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, + size_t nbytes, loff_t *ppos) +{ + int rc = 0, len; + struct smsdvb_debugfs *debug_data = file->private_data; + + kref_get(&debug_data->refcount); + + if (file->f_flags & O_NONBLOCK) { + rc = smsdvb_stats_wait_read(debug_data); + if (!rc) { + rc = -EWOULDBLOCK; + goto ret; + } + } else { + rc = wait_event_interruptible(debug_data->stats_queue, + smsdvb_stats_wait_read(debug_data)); + if (rc < 0) + goto ret; + } + + if (debug_data->stats_was_read) { + rc = 0; /* EOF */ + goto ret; + } + + len = debug_data->stats_count - *ppos; + if (len >= 0) + rc = simple_read_from_buffer(user_buf, nbytes, ppos, + debug_data->stats_data, len); + else + rc = 0; + + if (*ppos >= debug_data->stats_count) { + spin_lock(&debug_data->lock); + debug_data->stats_was_read = true; + spin_unlock(&debug_data->lock); + } +ret: + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); + return rc; +} + +static int smsdvb_stats_release(struct inode *inode, struct file *file) +{ + struct smsdvb_debugfs *debug_data = file->private_data; + + spin_lock(&debug_data->lock); + debug_data->stats_was_read = true; /* return EOF to read() */ + spin_unlock(&debug_data->lock); + wake_up_interruptible_sync(&debug_data->stats_queue); + + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations debugfs_stats_ops = { + .open = smsdvb_stats_open, + .poll = smsdvb_stats_poll, + .read = smsdvb_stats_read, + .release = smsdvb_stats_release, + .llseek = generic_file_llseek, +}; + +/* + * Functions used by smsdvb, in order to create the interfaces + */ + +int smsdvb_debugfs_create(struct smsdvb_client_t *client) +{ + struct smscore_device_t *coredev = client->coredev; + struct dentry *d; + struct smsdvb_debugfs *debug_data; + + if (!smsdvb_debugfs_usb_root || !coredev->is_usb_device) + return -ENODEV; + + client->debugfs = debugfs_create_dir(coredev->devpath, + smsdvb_debugfs_usb_root); + if (IS_ERR_OR_NULL(client->debugfs)) { + pr_info("Unable to create debugfs %s directory.\n", + coredev->devpath); + return -ENODEV; + } + + d = debugfs_create_file("stats", S_IRUGO | S_IWUSR, client->debugfs, + client, &debugfs_stats_ops); + if (!d) { + debugfs_remove(client->debugfs); + return -ENOMEM; + } + + debug_data = kzalloc(sizeof(*client->debug_data), GFP_KERNEL); + if (!debug_data) + return -ENOMEM; + + client->debug_data = debug_data; + client->prt_dvb_stats = smsdvb_print_dvb_stats; + client->prt_isdb_stats = smsdvb_print_isdb_stats; + client->prt_isdb_stats_ex = smsdvb_print_isdb_stats_ex; + + init_waitqueue_head(&debug_data->stats_queue); + spin_lock_init(&debug_data->lock); + kref_init(&debug_data->refcount); + + return 0; +} + +void smsdvb_debugfs_release(struct smsdvb_client_t *client) +{ + if (!client->debugfs) + return; + + client->prt_dvb_stats = NULL; + client->prt_isdb_stats = NULL; + client->prt_isdb_stats_ex = NULL; + + debugfs_remove_recursive(client->debugfs); + kref_put(&client->debug_data->refcount, smsdvb_debugfs_data_release); + + client->debug_data = NULL; + client->debugfs = NULL; +} + +int smsdvb_debugfs_register(void) +{ + struct dentry *d; + + /* + * FIXME: This was written to debug Siano USB devices. So, it creates + * the debugfs node under <debugfs>/usb. + * A similar logic would be needed for Siano sdio devices, but, in that + * case, usb_debug_root is not a good choice. + * + * Perhaps the right fix here would be to create another sysfs root + * node for sdio-based boards, but this may need some logic at sdio + * subsystem. + */ + d = debugfs_create_dir("smsdvb", usb_debug_root); + if (IS_ERR_OR_NULL(d)) { + sms_err("Couldn't create sysfs node for smsdvb"); + return PTR_ERR(d); + } else { + smsdvb_debugfs_usb_root = d; + } + return 0; +} + +void smsdvb_debugfs_unregister(void) +{ + debugfs_remove_recursive(smsdvb_debugfs_usb_root); + smsdvb_debugfs_usb_root = NULL; +} diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c new file mode 100644 index 0000000..297f1b2 --- /dev/null +++ b/drivers/media/common/siano/smsdvb-main.c @@ -0,0 +1,1230 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <asm/div64.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "smscoreapi.h" +#include "sms-cards.h" + +#include "smsdvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct list_head g_smsdvb_clients; +static struct mutex g_smsdvb_clientslock; + +static int sms_dbg; +module_param_named(debug, sms_dbg, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + + +u32 sms_to_guard_interval_table[] = { + [0] = GUARD_INTERVAL_1_32, + [1] = GUARD_INTERVAL_1_16, + [2] = GUARD_INTERVAL_1_8, + [3] = GUARD_INTERVAL_1_4, +}; + +u32 sms_to_code_rate_table[] = { + [0] = FEC_1_2, + [1] = FEC_2_3, + [2] = FEC_3_4, + [3] = FEC_5_6, + [4] = FEC_7_8, +}; + + +u32 sms_to_hierarchy_table[] = { + [0] = HIERARCHY_NONE, + [1] = HIERARCHY_1, + [2] = HIERARCHY_2, + [3] = HIERARCHY_4, +}; + +u32 sms_to_modulation_table[] = { + [0] = QPSK, + [1] = QAM_16, + [2] = QAM_64, + [3] = DQPSK, +}; + + +/* Events that may come from DVB v3 adapter */ +static void sms_board_dvb3_event(struct smsdvb_client_t *client, + enum SMS_DVB3_EVENTS event) { + + struct smscore_device_t *coredev = client->coredev; + switch (event) { + case DVB3_EVENT_INIT: + sms_debug("DVB3_EVENT_INIT"); + sms_board_event(coredev, BOARD_EVENT_BIND); + break; + case DVB3_EVENT_SLEEP: + sms_debug("DVB3_EVENT_SLEEP"); + sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); + break; + case DVB3_EVENT_HOTPLUG: + sms_debug("DVB3_EVENT_HOTPLUG"); + sms_board_event(coredev, BOARD_EVENT_POWER_INIT); + break; + case DVB3_EVENT_FE_LOCK: + if (client->event_fe_state != DVB3_EVENT_FE_LOCK) { + client->event_fe_state = DVB3_EVENT_FE_LOCK; + sms_debug("DVB3_EVENT_FE_LOCK"); + sms_board_event(coredev, BOARD_EVENT_FE_LOCK); + } + break; + case DVB3_EVENT_FE_UNLOCK: + if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) { + client->event_fe_state = DVB3_EVENT_FE_UNLOCK; + sms_debug("DVB3_EVENT_FE_UNLOCK"); + sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK); + } + break; + case DVB3_EVENT_UNC_OK: + if (client->event_unc_state != DVB3_EVENT_UNC_OK) { + client->event_unc_state = DVB3_EVENT_UNC_OK; + sms_debug("DVB3_EVENT_UNC_OK"); + sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK); + } + break; + case DVB3_EVENT_UNC_ERR: + if (client->event_unc_state != DVB3_EVENT_UNC_ERR) { + client->event_unc_state = DVB3_EVENT_UNC_ERR; + sms_debug("DVB3_EVENT_UNC_ERR"); + sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); + } + break; + + default: + sms_err("Unknown dvb3 api event"); + break; + } +} + +static void smsdvb_stats_not_ready(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i, n_layers; + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + n_layers = 4; + default: + n_layers = 1; + } + + /* Global stats */ + c->strength.len = 1; + c->cnr.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + /* Per-layer stats */ + c->post_bit_error.len = n_layers; + c->post_bit_count.len = n_layers; + c->block_error.len = n_layers; + c->block_count.len = n_layers; + + /* + * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically + * changed when the stats become available. + */ + for (i = 0; i < n_layers; i++) { + c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static inline int sms_to_mode(u32 mode) +{ + switch (mode) { + case 2: + return TRANSMISSION_MODE_2K; + case 4: + return TRANSMISSION_MODE_4K; + case 8: + return TRANSMISSION_MODE_8K; + } + return TRANSMISSION_MODE_AUTO; +} + +static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked) +{ + if (is_demod_locked) + return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + + if (is_rf_locked) + return FE_HAS_SIGNAL | FE_HAS_CARRIER; + + return 0; +} + +static inline u32 sms_to_bw(u32 value) +{ + return value * 1000000; +} + +#define convert_from_table(value, table, defval) ({ \ + u32 __ret; \ + if (value < ARRAY_SIZE(table)) \ + __ret = table[value]; \ + else \ + __ret = defval; \ + __ret; \ +}) + +#define sms_to_guard_interval(value) \ + convert_from_table(value, sms_to_guard_interval_table, \ + GUARD_INTERVAL_AUTO); + +#define sms_to_code_rate(value) \ + convert_from_table(value, sms_to_code_rate_table, \ + FEC_NONE); + +#define sms_to_hierarchy(value) \ + convert_from_table(value, sms_to_hierarchy_table, \ + FEC_NONE); + +#define sms_to_modulation(value) \ + convert_from_table(value, sms_to_modulation_table, \ + FEC_NONE); + +static void smsdvb_update_tx_params(struct smsdvb_client_t *client, + struct sms_tx_stats *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + c->frequency = p->frequency; + client->fe_status = sms_to_status(p->is_demod_locked, 0); + c->bandwidth_hz = sms_to_bw(p->bandwidth); + c->transmission_mode = sms_to_mode(p->transmission_mode); + c->guard_interval = sms_to_guard_interval(p->guard_interval); + c->code_rate_HP = sms_to_code_rate(p->code_rate); + c->code_rate_LP = sms_to_code_rate(p->lp_code_rate); + c->hierarchy = sms_to_hierarchy(p->hierarchy); + c->modulation = sms_to_modulation(p->constellation); +} + +static void smsdvb_update_per_slices(struct smsdvb_client_t *client, + struct RECEPTION_STATISTICS_PER_SLICES_S *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u64 tmp; + + client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked); + c->modulation = sms_to_modulation(p->constellation); + + /* signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->in_band_power * 1000; + + /* Carrier to noise ratio, in DB */ + c->cnr.stat[0].svalue = p->snr * 1000; + + /* PER/BER requires demod lock */ + if (!p->is_demod_locked) + return; + + /* TS PER */ + client->last_per = c->block_error.stat[0].uvalue; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += p->ets_packets; + c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets; + + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += p->ber_error_count; + c->post_bit_count.stat[0].uvalue += p->ber_bit_count; + + /* Legacy PER/BER */ + tmp = p->ets_packets * 65535; + do_div(tmp, p->ts_packets + p->ets_packets); + client->legacy_per = tmp; +} + +static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client, + struct sms_stats *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (client->prt_dvb_stats) + client->prt_dvb_stats(client->debug_data, p); + + client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked); + + /* Update DVB modulation parameters */ + c->frequency = p->frequency; + client->fe_status = sms_to_status(p->is_demod_locked, 0); + c->bandwidth_hz = sms_to_bw(p->bandwidth); + c->transmission_mode = sms_to_mode(p->transmission_mode); + c->guard_interval = sms_to_guard_interval(p->guard_interval); + c->code_rate_HP = sms_to_code_rate(p->code_rate); + c->code_rate_LP = sms_to_code_rate(p->lp_code_rate); + c->hierarchy = sms_to_hierarchy(p->hierarchy); + c->modulation = sms_to_modulation(p->constellation); + + /* update reception data */ + c->lna = p->is_external_lna_on ? 1 : 0; + + /* Carrier to noise ratio, in DB */ + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->in_band_pwr * 1000; + + /* PER/BER requires demod lock */ + if (!p->is_demod_locked) + return; + + /* TS PER */ + client->last_per = c->block_error.stat[0].uvalue; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += p->error_ts_packets; + c->block_count.stat[0].uvalue += p->total_ts_packets; + + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += p->ber_error_count; + c->post_bit_count.stat[0].uvalue += p->ber_bit_count; + + /* Legacy PER/BER */ + client->legacy_ber = p->ber; +}; + +static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client, + struct sms_isdbt_stats *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct sms_isdbt_layer_stats *lr; + int i, n_layers; + + if (client->prt_isdb_stats) + client->prt_isdb_stats(client->debug_data, p); + + client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked); + + /* + * Firmware 2.1 seems to report only lock status and + * signal strength. The signal strength indicator is at the + * wrong field. + */ + if (p->statistics_type == 0) { + c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + /* Update ISDB-T transmission parameters */ + c->frequency = p->frequency; + c->bandwidth_hz = sms_to_bw(p->bandwidth); + c->transmission_mode = sms_to_mode(p->transmission_mode); + c->guard_interval = sms_to_guard_interval(p->guard_interval); + c->isdbt_partial_reception = p->partial_reception ? 1 : 0; + n_layers = p->num_of_layers; + if (n_layers < 1) + n_layers = 1; + if (n_layers > 3) + n_layers = 3; + c->isdbt_layer_enabled = 0; + + /* update reception data */ + c->lna = p->is_external_lna_on ? 1 : 0; + + /* Carrier to noise ratio, in DB */ + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->in_band_pwr * 1000; + + /* PER/BER and per-layer stats require demod lock */ + if (!p->is_demod_locked) + return; + + client->last_per = c->block_error.stat[0].uvalue; + + /* Clears global counters, as the code below will sum it again */ + c->block_error.stat[0].uvalue = 0; + c->block_count.stat[0].uvalue = 0; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_count.stat[0].uvalue = 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + + for (i = 0; i < n_layers; i++) { + lr = &p->layer_info[i]; + + /* Update per-layer transmission parameters */ + if (lr->number_of_segments > 0 && lr->number_of_segments < 13) { + c->isdbt_layer_enabled |= 1 << i; + c->layer[i].segment_count = lr->number_of_segments; + } else { + continue; + } + c->layer[i].modulation = sms_to_modulation(lr->constellation); + + /* TS PER */ + c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER; + c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER; + c->block_error.stat[i + 1].uvalue += lr->error_ts_packets; + c->block_count.stat[i + 1].uvalue += lr->total_ts_packets; + + /* Update global PER counter */ + c->block_error.stat[0].uvalue += lr->error_ts_packets; + c->block_count.stat[0].uvalue += lr->total_ts_packets; + + /* BER */ + c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count; + c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count; + + /* Update global BER counter */ + c->post_bit_error.stat[0].uvalue += lr->ber_error_count; + c->post_bit_count.stat[0].uvalue += lr->ber_bit_count; + } +} + +static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client, + struct sms_isdbt_stats_ex *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct sms_isdbt_layer_stats *lr; + int i, n_layers; + + if (client->prt_isdb_stats_ex) + client->prt_isdb_stats_ex(client->debug_data, p); + + /* Update ISDB-T transmission parameters */ + c->frequency = p->frequency; + client->fe_status = sms_to_status(p->is_demod_locked, 0); + c->bandwidth_hz = sms_to_bw(p->bandwidth); + c->transmission_mode = sms_to_mode(p->transmission_mode); + c->guard_interval = sms_to_guard_interval(p->guard_interval); + c->isdbt_partial_reception = p->partial_reception ? 1 : 0; + n_layers = p->num_of_layers; + if (n_layers < 1) + n_layers = 1; + if (n_layers > 3) + n_layers = 3; + c->isdbt_layer_enabled = 0; + + /* update reception data */ + c->lna = p->is_external_lna_on ? 1 : 0; + + /* Carrier to noise ratio, in DB */ + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->in_band_pwr * 1000; + + /* PER/BER and per-layer stats require demod lock */ + if (!p->is_demod_locked) + return; + + client->last_per = c->block_error.stat[0].uvalue; + + /* Clears global counters, as the code below will sum it again */ + c->block_error.stat[0].uvalue = 0; + c->block_count.stat[0].uvalue = 0; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_count.stat[0].uvalue = 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + + c->post_bit_error.len = n_layers + 1; + c->post_bit_count.len = n_layers + 1; + c->block_error.len = n_layers + 1; + c->block_count.len = n_layers + 1; + for (i = 0; i < n_layers; i++) { + lr = &p->layer_info[i]; + + /* Update per-layer transmission parameters */ + if (lr->number_of_segments > 0 && lr->number_of_segments < 13) { + c->isdbt_layer_enabled |= 1 << i; + c->layer[i].segment_count = lr->number_of_segments; + } else { + continue; + } + c->layer[i].modulation = sms_to_modulation(lr->constellation); + + /* TS PER */ + c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER; + c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER; + c->block_error.stat[i + 1].uvalue += lr->error_ts_packets; + c->block_count.stat[i + 1].uvalue += lr->total_ts_packets; + + /* Update global PER counter */ + c->block_error.stat[0].uvalue += lr->error_ts_packets; + c->block_count.stat[0].uvalue += lr->total_ts_packets; + + /* ber */ + c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count; + c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count; + + /* Update global ber counter */ + c->post_bit_error.stat[0].uvalue += lr->ber_error_count; + c->post_bit_count.stat[0].uvalue += lr->ber_bit_count; + } +} + +static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) +{ + struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p) + + cb->offset); + void *p = phdr + 1; + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + bool is_status_update = false; + + switch (phdr->msg_type) { + case MSG_SMS_DVBT_BDA_DATA: + /* + * Only feed data to dvb demux if are there any feed listening + * to it and if the device has tuned + */ + if (client->feed_users && client->has_tuned) + dvb_dmx_swfilter(&client->demux, p, + cb->size - sizeof(struct sms_msg_hdr)); + break; + + case MSG_SMS_RF_TUNE_RES: + case MSG_SMS_ISDBT_TUNE_RES: + complete(&client->tune_done); + break; + + case MSG_SMS_SIGNAL_DETECTED_IND: + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + is_status_update = true; + break; + + case MSG_SMS_NO_SIGNAL_IND: + client->fe_status = 0; + + is_status_update = true; + break; + + case MSG_SMS_TRANSMISSION_IND: + smsdvb_update_tx_params(client, p); + + is_status_update = true; + break; + + case MSG_SMS_HO_PER_SLICES_IND: + smsdvb_update_per_slices(client, p); + + is_status_update = true; + break; + + case MSG_SMS_GET_STATISTICS_RES: + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + smsdvb_update_isdbt_stats(client, p); + break; + default: + /* Skip sms_msg_statistics_info:request_result field */ + smsdvb_update_dvb_stats(client, p + sizeof(u32)); + } + + is_status_update = true; + break; + + /* Only for ISDB-T */ + case MSG_SMS_GET_STATISTICS_EX_RES: + /* Skip sms_msg_statistics_info:request_result field? */ + smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32)); + is_status_update = true; + break; + default: + sms_info("message not handled"); + } + smscore_putbuffer(client->coredev, cb); + + if (is_status_update) { + if (client->fe_status & FE_HAS_LOCK) { + sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); + if (client->last_per == c->block_error.stat[0].uvalue) + sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); + else + sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR); + client->has_tuned = true; + } else { + smsdvb_stats_not_ready(fe); + client->has_tuned = false; + sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); + } + complete(&client->stats_done); + } + + return 0; +} + +static void smsdvb_unregister_client(struct smsdvb_client_t *client) +{ + /* must be called under clientslock */ + + list_del(&client->entry); + + smsdvb_debugfs_release(client); + smscore_unregister_client(client->smsclient); + dvb_unregister_frontend(&client->frontend); + dvb_dmxdev_release(&client->dmxdev); + dvb_dmx_release(&client->demux); + dvb_unregister_adapter(&client->adapter); + kfree(client); +} + +static void smsdvb_onremove(void *context) +{ + kmutex_lock(&g_smsdvb_clientslock); + + smsdvb_unregister_client((struct smsdvb_client_t *) context); + + kmutex_unlock(&g_smsdvb_clientslock); +} + +static int smsdvb_start_feed(struct dvb_demux_feed *feed) +{ + struct smsdvb_client_t *client = + container_of(feed->demux, struct smsdvb_client_t, demux); + struct sms_msg_data pid_msg; + + sms_debug("add pid %d(%x)", + feed->pid, feed->pid); + + client->feed_users++; + + pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + pid_msg.x_msg_header.msg_dst_id = HIF_TASK; + pid_msg.x_msg_header.msg_flags = 0; + pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ; + pid_msg.x_msg_header.msg_length = sizeof(pid_msg); + pid_msg.msg_data[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, + &pid_msg, sizeof(pid_msg)); +} + +static int smsdvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct smsdvb_client_t *client = + container_of(feed->demux, struct smsdvb_client_t, demux); + struct sms_msg_data pid_msg; + + sms_debug("remove pid %d(%x)", + feed->pid, feed->pid); + + client->feed_users--; + + pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + pid_msg.x_msg_header.msg_dst_id = HIF_TASK; + pid_msg.x_msg_header.msg_flags = 0; + pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ; + pid_msg.x_msg_header.msg_length = sizeof(pid_msg); + pid_msg.msg_data[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, + &pid_msg, sizeof(pid_msg)); +} + +static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, + void *buffer, size_t size, + struct completion *completion) +{ + int rc; + + rc = smsclient_sendrequest(client->smsclient, buffer, size); + if (rc < 0) + return rc; + + return wait_for_completion_timeout(completion, + msecs_to_jiffies(2000)) ? + 0 : -ETIME; +} + +static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) +{ + int rc; + struct sms_msg_hdr msg; + + /* Don't request stats too fast */ + if (client->get_stats_jiffies && + (!time_after(jiffies, client->get_stats_jiffies))) + return 0; + client->get_stats_jiffies = jiffies + msecs_to_jiffies(100); + + msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + msg.msg_dst_id = HIF_TASK; + msg.msg_flags = 0; + msg.msg_length = sizeof(msg); + + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + /* + * Check for firmware version, to avoid breaking for old cards + */ + if (client->coredev->fw_version >= 0x800) + msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ; + else + msg.msg_type = MSG_SMS_GET_STATISTICS_REQ; + break; + default: + msg.msg_type = MSG_SMS_GET_STATISTICS_REQ; + } + + rc = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), + &client->stats_done); + + return rc; +} + +static inline int led_feedback(struct smsdvb_client_t *client) +{ + if (!(client->fe_status & FE_HAS_LOCK)) + return sms_board_led_feedback(client->coredev, SMS_LED_OFF); + + return sms_board_led_feedback(client->coredev, + (client->legacy_ber == 0) ? + SMS_LED_HI : SMS_LED_LO); +} + +static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + int rc; + struct smsdvb_client_t *client; + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *stat = client->fe_status; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + int rc; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *ber = client->legacy_ber; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + s32 power = (s32) c->strength.stat[0].uvalue; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + if (power < -95) + *strength = 0; + else if (power > -29) + *strength = 65535; + else + *strength = (power + 95) * 65535 / 66; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + /* Preferred scale for SNR with legacy API: 0.1 dB */ + *snr = ((u32)c->cnr.stat[0].svalue) / 100; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + int rc; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *ucblocks = c->block_error.stat[0].uvalue; + + led_feedback(client); + + return rc; +} + +static int smsdvb_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + sms_debug(""); + + tune->min_delay_ms = 400; + tune->step_size = 250000; + tune->max_drift = 0; + return 0; +} + +static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + struct { + struct sms_msg_hdr msg; + u32 Data[3]; + } msg; + + int ret; + + client->fe_status = 0; + client->event_fe_state = -1; + client->event_unc_state = -1; + fe->dtv_property_cache.delivery_system = SYS_DVBT; + + msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + msg.msg.msg_dst_id = HIF_TASK; + msg.msg.msg_flags = 0; + msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ; + msg.msg.msg_length = sizeof(msg); + msg.Data[0] = c->frequency; + msg.Data[2] = 12000000; + + sms_info("%s: freq %d band %d", __func__, c->frequency, + c->bandwidth_hz); + + switch (c->bandwidth_hz / 1000000) { + case 8: + msg.Data[1] = BW_8_MHZ; + break; + case 7: + msg.Data[1] = BW_7_MHZ; + break; + case 6: + msg.Data[1] = BW_6_MHZ; + break; + case 0: + return -EOPNOTSUPP; + default: + return -EINVAL; + } + /* Disable LNA, if any. An error is returned if no LNA is present */ + ret = sms_board_lna_control(client->coredev, 0); + if (ret == 0) { + fe_status_t status; + + /* tune with LNA off at first */ + ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), + &client->tune_done); + + smsdvb_read_status(fe, &status); + + if (status & FE_HAS_LOCK) + return ret; + + /* previous tune didn't lock - enable LNA and tune again */ + sms_board_lna_control(client->coredev, 1); + } + + return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), + &client->tune_done); +} + +static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int board_id = smscore_get_board_id(client->coredev); + struct sms_board *board = sms_get_board(board_id); + enum sms_device_type_st type = board->type; + int ret; + + struct { + struct sms_msg_hdr msg; + u32 Data[4]; + } msg; + + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + + msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; + msg.msg.msg_dst_id = HIF_TASK; + msg.msg.msg_flags = 0; + msg.msg.msg_type = MSG_SMS_ISDBT_TUNE_REQ; + msg.msg.msg_length = sizeof(msg); + + if (c->isdbt_sb_segment_idx == -1) + c->isdbt_sb_segment_idx = 0; + + if (!c->isdbt_layer_enabled) + c->isdbt_layer_enabled = 7; + + msg.Data[0] = c->frequency; + msg.Data[1] = BW_ISDBT_1SEG; + msg.Data[2] = 12000000; + msg.Data[3] = c->isdbt_sb_segment_idx; + + if (c->isdbt_partial_reception) { + if ((type == SMS_PELE || type == SMS_RIO) && + c->isdbt_sb_segment_count > 3) + msg.Data[1] = BW_ISDBT_13SEG; + else if (c->isdbt_sb_segment_count > 1) + msg.Data[1] = BW_ISDBT_3SEG; + } else if (type == SMS_PELE || type == SMS_RIO) + msg.Data[1] = BW_ISDBT_13SEG; + + c->bandwidth_hz = 6000000; + + sms_info("%s: freq %d segwidth %d segindex %d", __func__, + c->frequency, c->isdbt_sb_segment_count, + c->isdbt_sb_segment_idx); + + /* Disable LNA, if any. An error is returned if no LNA is present */ + ret = sms_board_lna_control(client->coredev, 0); + if (ret == 0) { + fe_status_t status; + + /* tune with LNA off at first */ + ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), + &client->tune_done); + + smsdvb_read_status(fe, &status); + + if (status & FE_HAS_LOCK) + return ret; + + /* previous tune didn't lock - enable LNA and tune again */ + sms_board_lna_control(client->coredev, 1); + } + return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), + &client->tune_done); +} + +static int smsdvb_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + + smsdvb_stats_not_ready(fe); + c->strength.stat[0].uvalue = 0; + c->cnr.stat[0].uvalue = 0; + + client->has_tuned = false; + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + return smsdvb_dvbt_set_frontend(fe); + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + return smsdvb_isdbt_set_frontend(fe); + default: + return -EINVAL; + } +} + +/* Nothing to do here, as stats are automatically updated */ +static int smsdvb_get_frontend(struct dvb_frontend *fe) +{ + return 0; +} + +static int smsdvb_init(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_power(client->coredev, 1); + + sms_board_dvb3_event(client, DVB3_EVENT_INIT); + return 0; +} + +static int smsdvb_sleep(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_led_feedback(client->coredev, SMS_LED_OFF); + sms_board_power(client->coredev, 0); + + sms_board_dvb3_event(client, DVB3_EVENT_SLEEP); + + return 0; +} + +static void smsdvb_release(struct dvb_frontend *fe) +{ + /* do nothing */ +} + +static struct dvb_frontend_ops smsdvb_fe_ops = { + .info = { + .name = "Siano Mobile Digital MDTV Receiver", + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .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, + }, + + .release = smsdvb_release, + + .set_frontend = smsdvb_set_frontend, + .get_frontend = smsdvb_get_frontend, + .get_tune_settings = smsdvb_get_tune_settings, + + .read_status = smsdvb_read_status, + .read_ber = smsdvb_read_ber, + .read_signal_strength = smsdvb_read_signal_strength, + .read_snr = smsdvb_read_snr, + .read_ucblocks = smsdvb_read_ucblocks, + + .init = smsdvb_init, + .sleep = smsdvb_sleep, +}; + +static int smsdvb_hotplug(struct smscore_device_t *coredev, + struct device *device, int arrival) +{ + struct smsclient_params_t params; + struct smsdvb_client_t *client; + int rc; + + /* device removal handled by onremove callback */ + if (!arrival) + return 0; + client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); + if (!client) { + sms_err("kmalloc() failed"); + return -ENOMEM; + } + + /* register dvb adapter */ + rc = dvb_register_adapter(&client->adapter, + sms_get_board( + smscore_get_board_id(coredev))->name, + THIS_MODULE, device, adapter_nr); + if (rc < 0) { + sms_err("dvb_register_adapter() failed %d", rc); + goto adapter_error; + } + + /* init dvb demux */ + client->demux.dmx.capabilities = DMX_TS_FILTERING; + client->demux.filternum = 32; /* todo: nova ??? */ + client->demux.feednum = 32; + client->demux.start_feed = smsdvb_start_feed; + client->demux.stop_feed = smsdvb_stop_feed; + + rc = dvb_dmx_init(&client->demux); + if (rc < 0) { + sms_err("dvb_dmx_init failed %d", rc); + goto dvbdmx_error; + } + + /* init dmxdev */ + client->dmxdev.filternum = 32; + client->dmxdev.demux = &client->demux.dmx; + client->dmxdev.capabilities = 0; + + rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); + if (rc < 0) { + sms_err("dvb_dmxdev_init failed %d", rc); + goto dmxdev_error; + } + + /* init and register frontend */ + memcpy(&client->frontend.ops, &smsdvb_fe_ops, + sizeof(struct dvb_frontend_ops)); + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + client->frontend.ops.delsys[0] = SYS_DVBT; + break; + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + client->frontend.ops.delsys[0] = SYS_ISDBT; + break; + } + + rc = dvb_register_frontend(&client->adapter, &client->frontend); + if (rc < 0) { + sms_err("frontend registration failed %d", rc); + goto frontend_error; + } + + params.initial_id = 1; + params.data_type = MSG_SMS_DVBT_BDA_DATA; + params.onresponse_handler = smsdvb_onresponse; + params.onremove_handler = smsdvb_onremove; + params.context = client; + + rc = smscore_register_client(coredev, ¶ms, &client->smsclient); + if (rc < 0) { + sms_err("smscore_register_client() failed %d", rc); + goto client_error; + } + + client->coredev = coredev; + + init_completion(&client->tune_done); + init_completion(&client->stats_done); + + kmutex_lock(&g_smsdvb_clientslock); + + list_add(&client->entry, &g_smsdvb_clients); + + kmutex_unlock(&g_smsdvb_clientslock); + + client->event_fe_state = -1; + client->event_unc_state = -1; + sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); + + sms_info("success"); + sms_board_setup(coredev); + + if (smsdvb_debugfs_create(client) < 0) + sms_info("failed to create debugfs node"); + + return 0; + +client_error: + dvb_unregister_frontend(&client->frontend); + +frontend_error: + dvb_dmxdev_release(&client->dmxdev); + +dmxdev_error: + dvb_dmx_release(&client->demux); + +dvbdmx_error: + dvb_unregister_adapter(&client->adapter); + +adapter_error: + kfree(client); + return rc; +} + +static int __init smsdvb_module_init(void) +{ + int rc; + + INIT_LIST_HEAD(&g_smsdvb_clients); + kmutex_init(&g_smsdvb_clientslock); + + smsdvb_debugfs_register(); + + rc = smscore_register_hotplug(smsdvb_hotplug); + + sms_debug(""); + + return rc; +} + +static void __exit smsdvb_module_exit(void) +{ + smscore_unregister_hotplug(smsdvb_hotplug); + + kmutex_lock(&g_smsdvb_clientslock); + + while (!list_empty(&g_smsdvb_clients)) + smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next); + + smsdvb_debugfs_unregister(); + + kmutex_unlock(&g_smsdvb_clientslock); +} + +module_init(smsdvb_module_init); +module_exit(smsdvb_module_exit); + +MODULE_DESCRIPTION("SMS DVB subsystem adaptation module"); +MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c deleted file mode 100644 index aa77e54..0000000 --- a/drivers/media/common/siano/smsdvb.c +++ /dev/null @@ -1,1078 +0,0 @@ -/**************************************************************** - -Siano Mobile Silicon, Inc. -MDTV receiver kernel modules. -Copyright (C) 2006-2008, Uri Shkolnik - -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, see <http://www.gnu.org/licenses/>. - -****************************************************************/ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" - -#include "smscoreapi.h" -#include "smsendian.h" -#include "sms-cards.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct smsdvb_client_t { - struct list_head entry; - - struct smscore_device_t *coredev; - struct smscore_client_t *smsclient; - - struct dvb_adapter adapter; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dvb_frontend frontend; - - fe_status_t fe_status; - - struct completion tune_done; - - struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; - int event_fe_state; - int event_unc_state; -}; - -static struct list_head g_smsdvb_clients; -static struct mutex g_smsdvb_clientslock; - -static int sms_dbg; -module_param_named(debug, sms_dbg, int, 0644); -MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); - -/* Events that may come from DVB v3 adapter */ -static void sms_board_dvb3_event(struct smsdvb_client_t *client, - enum SMS_DVB3_EVENTS event) { - - struct smscore_device_t *coredev = client->coredev; - switch (event) { - case DVB3_EVENT_INIT: - sms_debug("DVB3_EVENT_INIT"); - sms_board_event(coredev, BOARD_EVENT_BIND); - break; - case DVB3_EVENT_SLEEP: - sms_debug("DVB3_EVENT_SLEEP"); - sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); - break; - case DVB3_EVENT_HOTPLUG: - sms_debug("DVB3_EVENT_HOTPLUG"); - sms_board_event(coredev, BOARD_EVENT_POWER_INIT); - break; - case DVB3_EVENT_FE_LOCK: - if (client->event_fe_state != DVB3_EVENT_FE_LOCK) { - client->event_fe_state = DVB3_EVENT_FE_LOCK; - sms_debug("DVB3_EVENT_FE_LOCK"); - sms_board_event(coredev, BOARD_EVENT_FE_LOCK); - } - break; - case DVB3_EVENT_FE_UNLOCK: - if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) { - client->event_fe_state = DVB3_EVENT_FE_UNLOCK; - sms_debug("DVB3_EVENT_FE_UNLOCK"); - sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK); - } - break; - case DVB3_EVENT_UNC_OK: - if (client->event_unc_state != DVB3_EVENT_UNC_OK) { - client->event_unc_state = DVB3_EVENT_UNC_OK; - sms_debug("DVB3_EVENT_UNC_OK"); - sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK); - } - break; - case DVB3_EVENT_UNC_ERR: - if (client->event_unc_state != DVB3_EVENT_UNC_ERR) { - client->event_unc_state = DVB3_EVENT_UNC_ERR; - sms_debug("DVB3_EVENT_UNC_ERR"); - sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); - } - break; - - default: - sms_err("Unknown dvb3 api event"); - break; - } -} - - -static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, - struct SMSHOSTLIB_STATISTICS_ST *p) -{ - if (sms_dbg & 2) { - printk(KERN_DEBUG "Reserved = %d", p->Reserved); - printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); - printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); - printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); - printk(KERN_DEBUG "SNR = %d", p->SNR); - printk(KERN_DEBUG "BER = %d", p->BER); - printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); - printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); - printk(KERN_DEBUG "MFER = %d", p->MFER); - printk(KERN_DEBUG "RSSI = %d", p->RSSI); - printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); - printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); - printk(KERN_DEBUG "Frequency = %d", p->Frequency); - printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); - printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); - printk(KERN_DEBUG "ModemState = %d", p->ModemState); - printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); - printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); - printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); - printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); - printk(KERN_DEBUG "Constellation = %d", p->Constellation); - printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); - printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); - printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); - printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); - printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); - printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); - printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); - printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); - printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); - printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); - printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); - printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); - printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); - printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); - printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); - printk(KERN_DEBUG "PreBER = %d", p->PreBER); - printk(KERN_DEBUG "CellId = %d", p->CellId); - printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); - printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); - printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); - } - - pReceptionData->IsDemodLocked = p->IsDemodLocked; - - pReceptionData->SNR = p->SNR; - pReceptionData->BER = p->BER; - pReceptionData->BERErrorCount = p->BERErrorCount; - pReceptionData->InBandPwr = p->InBandPwr; - pReceptionData->ErrorTSPackets = p->ErrorTSPackets; -}; - - -static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, - struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) -{ - int i; - - if (sms_dbg & 2) { - printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); - printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); - printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); - printk(KERN_DEBUG "SNR = %d", p->SNR); - printk(KERN_DEBUG "RSSI = %d", p->RSSI); - printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); - printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); - printk(KERN_DEBUG "Frequency = %d", p->Frequency); - printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); - printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); - printk(KERN_DEBUG "ModemState = %d", p->ModemState); - printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); - printk(KERN_DEBUG "SystemType = %d", p->SystemType); - printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); - printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); - printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); - - for (i = 0; i < 3; i++) { - printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); - printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); - printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); - printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); - printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); - printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); - printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); - printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); - printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); - printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); - printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); - printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); - } - } - - pReceptionData->IsDemodLocked = p->IsDemodLocked; - - pReceptionData->SNR = p->SNR; - pReceptionData->InBandPwr = p->InBandPwr; - - pReceptionData->ErrorTSPackets = 0; - pReceptionData->BER = 0; - pReceptionData->BERErrorCount = 0; - for (i = 0; i < 3; i++) { - pReceptionData->BER += p->LayerInfo[i].BER; - pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; - pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; - } -} - -static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) -{ - struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) - + cb->offset); - u32 *pMsgData = (u32 *) phdr + 1; - /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ - bool is_status_update = false; - - smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); - - switch (phdr->msgType) { - case MSG_SMS_DVBT_BDA_DATA: - dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), - cb->size - sizeof(struct SmsMsgHdr_ST)); - break; - - case MSG_SMS_RF_TUNE_RES: - case MSG_SMS_ISDBT_TUNE_RES: - complete(&client->tune_done); - break; - - case MSG_SMS_SIGNAL_DETECTED_IND: - sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); - client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; - is_status_update = true; - break; - - case MSG_SMS_NO_SIGNAL_IND: - sms_info("MSG_SMS_NO_SIGNAL_IND"); - client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; - is_status_update = true; - break; - - case MSG_SMS_TRANSMISSION_IND: { - sms_info("MSG_SMS_TRANSMISSION_IND"); - - pMsgData++; - memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, - sizeof(struct TRANSMISSION_STATISTICS_S)); - - /* Mo need to correct guard interval - * (as opposed to old statistics message). - */ - CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); - CORRECT_STAT_TRANSMISSON_MODE( - client->sms_stat_dvb.TransmissionData); - is_status_update = true; - break; - } - case MSG_SMS_HO_PER_SLICES_IND: { - struct RECEPTION_STATISTICS_S *pReceptionData = - &client->sms_stat_dvb.ReceptionData; - struct SRVM_SIGNAL_STATUS_S SignalStatusData; - - /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ - pMsgData++; - SignalStatusData.result = pMsgData[0]; - SignalStatusData.snr = pMsgData[1]; - SignalStatusData.inBandPower = (s32) pMsgData[2]; - SignalStatusData.tsPackets = pMsgData[3]; - SignalStatusData.etsPackets = pMsgData[4]; - SignalStatusData.constellation = pMsgData[5]; - SignalStatusData.hpCode = pMsgData[6]; - SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; - SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; - SignalStatusData.cellId = pMsgData[9] & 0xFFFF; - SignalStatusData.reason = pMsgData[10]; - SignalStatusData.requestId = pMsgData[11]; - pReceptionData->IsRfLocked = pMsgData[16]; - pReceptionData->IsDemodLocked = pMsgData[17]; - pReceptionData->ModemState = pMsgData[12]; - pReceptionData->SNR = pMsgData[1]; - pReceptionData->BER = pMsgData[13]; - pReceptionData->RSSI = pMsgData[14]; - CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); - - pReceptionData->InBandPwr = (s32) pMsgData[2]; - pReceptionData->CarrierOffset = (s32) pMsgData[15]; - pReceptionData->TotalTSPackets = pMsgData[3]; - pReceptionData->ErrorTSPackets = pMsgData[4]; - - /* TS PER */ - if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) - > 0) { - pReceptionData->TS_PER = (SignalStatusData.etsPackets - * 100) / (SignalStatusData.tsPackets - + SignalStatusData.etsPackets); - } else { - pReceptionData->TS_PER = 0; - } - - pReceptionData->BERBitCount = pMsgData[18]; - pReceptionData->BERErrorCount = pMsgData[19]; - - pReceptionData->MRC_SNR = pMsgData[20]; - pReceptionData->MRC_InBandPwr = pMsgData[21]; - pReceptionData->MRC_RSSI = pMsgData[22]; - - is_status_update = true; - break; - } - case MSG_SMS_GET_STATISTICS_RES: { - union { - struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; - struct SmsMsgStatisticsInfo_ST dvb; - } *p = (void *) (phdr + 1); - struct RECEPTION_STATISTICS_S *pReceptionData = - &client->sms_stat_dvb.ReceptionData; - - sms_info("MSG_SMS_GET_STATISTICS_RES"); - - is_status_update = true; - - switch (smscore_get_device_mode(client->coredev)) { - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); - break; - default: - smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); - } - if (!pReceptionData->IsDemodLocked) { - pReceptionData->SNR = 0; - pReceptionData->BER = 0; - pReceptionData->BERErrorCount = 0; - pReceptionData->InBandPwr = 0; - pReceptionData->ErrorTSPackets = 0; - } - - complete(&client->tune_done); - break; - } - default: - sms_info("Unhandled message %d", phdr->msgType); - - } - smscore_putbuffer(client->coredev, cb); - - if (is_status_update) { - if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { - client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER - | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); - if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets - == 0) - sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); - else - sms_board_dvb3_event(client, - DVB3_EVENT_UNC_ERR); - - } else { - if (client->sms_stat_dvb.ReceptionData.IsRfLocked) - client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - else - client->fe_status = 0; - sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); - } - } - - return 0; -} - -static void smsdvb_unregister_client(struct smsdvb_client_t *client) -{ - /* must be called under clientslock */ - - list_del(&client->entry); - - smscore_unregister_client(client->smsclient); - dvb_unregister_frontend(&client->frontend); - dvb_dmxdev_release(&client->dmxdev); - dvb_dmx_release(&client->demux); - dvb_unregister_adapter(&client->adapter); - kfree(client); -} - -static void smsdvb_onremove(void *context) -{ - kmutex_lock(&g_smsdvb_clientslock); - - smsdvb_unregister_client((struct smsdvb_client_t *) context); - - kmutex_unlock(&g_smsdvb_clientslock); -} - -static int smsdvb_start_feed(struct dvb_demux_feed *feed) -{ - struct smsdvb_client_t *client = - container_of(feed->demux, struct smsdvb_client_t, demux); - struct SmsMsgData_ST PidMsg; - - sms_debug("add pid %d(%x)", - feed->pid, feed->pid); - - PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - PidMsg.xMsgHeader.msgDstId = HIF_TASK; - PidMsg.xMsgHeader.msgFlags = 0; - PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; - PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); - PidMsg.msgData[0] = feed->pid; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); - return smsclient_sendrequest(client->smsclient, - &PidMsg, sizeof(PidMsg)); -} - -static int smsdvb_stop_feed(struct dvb_demux_feed *feed) -{ - struct smsdvb_client_t *client = - container_of(feed->demux, struct smsdvb_client_t, demux); - struct SmsMsgData_ST PidMsg; - - sms_debug("remove pid %d(%x)", - feed->pid, feed->pid); - - PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - PidMsg.xMsgHeader.msgDstId = HIF_TASK; - PidMsg.xMsgHeader.msgFlags = 0; - PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; - PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); - PidMsg.msgData[0] = feed->pid; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); - return smsclient_sendrequest(client->smsclient, - &PidMsg, sizeof(PidMsg)); -} - -static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, - void *buffer, size_t size, - struct completion *completion) -{ - int rc; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); - rc = smsclient_sendrequest(client->smsclient, buffer, size); - if (rc < 0) - return rc; - - return wait_for_completion_timeout(completion, - msecs_to_jiffies(2000)) ? - 0 : -ETIME; -} - -static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) -{ - int rc; - struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, - DVBT_BDA_CONTROL_MSG_ID, - HIF_TASK, - sizeof(struct SmsMsgHdr_ST), 0 }; - - rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_done); - - return rc; -} - -static inline int led_feedback(struct smsdvb_client_t *client) -{ - if (client->fe_status & FE_HAS_LOCK) - return sms_board_led_feedback(client->coredev, - (client->sms_stat_dvb.ReceptionData.BER - == 0) ? SMS_LED_HI : SMS_LED_LO); - else - return sms_board_led_feedback(client->coredev, SMS_LED_OFF); -} - -static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *stat = client->fe_status; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *ber = client->sms_stat_dvb.ReceptionData.BER; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - int rc; - - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) - *strength = 0; - else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) - *strength = 100; - else - *strength = - (client->sms_stat_dvb.ReceptionData.InBandPwr - + 95) * 3 / 2; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *snr = client->sms_stat_dvb.ReceptionData.SNR; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; - - led_feedback(client); - - return rc; -} - -static int smsdvb_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *tune) -{ - sms_debug(""); - - tune->min_delay_ms = 400; - tune->step_size = 250000; - tune->max_drift = 0; - return 0; -} - -static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - - struct { - struct SmsMsgHdr_ST Msg; - u32 Data[3]; - } Msg; - - int ret; - - client->fe_status = FE_HAS_SIGNAL; - client->event_fe_state = -1; - client->event_unc_state = -1; - fe->dtv_property_cache.delivery_system = SYS_DVBT; - - Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - Msg.Msg.msgDstId = HIF_TASK; - Msg.Msg.msgFlags = 0; - Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; - Msg.Msg.msgLength = sizeof(Msg); - Msg.Data[0] = c->frequency; - Msg.Data[2] = 12000000; - - sms_info("%s: freq %d band %d", __func__, c->frequency, - c->bandwidth_hz); - - switch (c->bandwidth_hz / 1000000) { - case 8: - Msg.Data[1] = BW_8_MHZ; - break; - case 7: - Msg.Data[1] = BW_7_MHZ; - break; - case 6: - Msg.Data[1] = BW_6_MHZ; - break; - case 0: - return -EOPNOTSUPP; - default: - return -EINVAL; - } - /* Disable LNA, if any. An error is returned if no LNA is present */ - ret = sms_board_lna_control(client->coredev, 0); - if (ret == 0) { - fe_status_t status; - - /* tune with LNA off at first */ - ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_done); - - smsdvb_read_status(fe, &status); - - if (status & FE_HAS_LOCK) - return ret; - - /* previous tune didn't lock - enable LNA and tune again */ - sms_board_lna_control(client->coredev, 1); - } - - return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_done); -} - -static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - - struct { - struct SmsMsgHdr_ST Msg; - u32 Data[4]; - } Msg; - - fe->dtv_property_cache.delivery_system = SYS_ISDBT; - - Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - Msg.Msg.msgDstId = HIF_TASK; - Msg.Msg.msgFlags = 0; - Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; - Msg.Msg.msgLength = sizeof(Msg); - - if (c->isdbt_sb_segment_idx == -1) - c->isdbt_sb_segment_idx = 0; - - switch (c->isdbt_sb_segment_count) { - case 3: - Msg.Data[1] = BW_ISDBT_3SEG; - break; - case 1: - Msg.Data[1] = BW_ISDBT_1SEG; - break; - case 0: /* AUTO */ - switch (c->bandwidth_hz / 1000000) { - case 8: - case 7: - c->isdbt_sb_segment_count = 3; - Msg.Data[1] = BW_ISDBT_3SEG; - break; - case 6: - c->isdbt_sb_segment_count = 1; - Msg.Data[1] = BW_ISDBT_1SEG; - break; - default: /* Assumes 6 MHZ bw */ - c->isdbt_sb_segment_count = 1; - c->bandwidth_hz = 6000; - Msg.Data[1] = BW_ISDBT_1SEG; - break; - } - break; - default: - sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); - return -EINVAL; - } - - Msg.Data[0] = c->frequency; - Msg.Data[2] = 12000000; - Msg.Data[3] = c->isdbt_sb_segment_idx; - - sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, - c->frequency, c->isdbt_sb_segment_count, - c->isdbt_sb_segment_idx); - - return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_done); -} - -static int smsdvb_set_frontend(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - return smsdvb_dvbt_set_frontend(fe); - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - return smsdvb_isdbt_set_frontend(fe); - default: - return -EINVAL; - } -} - -static int smsdvb_get_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - struct TRANSMISSION_STATISTICS_S *td = - &client->sms_stat_dvb.TransmissionData; - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - fep->frequency = td->Frequency; - - switch (td->Bandwidth) { - case 6: - fep->bandwidth_hz = 6000000; - break; - case 7: - fep->bandwidth_hz = 7000000; - break; - case 8: - fep->bandwidth_hz = 8000000; - break; - } - - switch (td->TransmissionMode) { - case 2: - fep->transmission_mode = TRANSMISSION_MODE_2K; - break; - case 8: - fep->transmission_mode = TRANSMISSION_MODE_8K; - } - - switch (td->GuardInterval) { - case 0: - fep->guard_interval = GUARD_INTERVAL_1_32; - break; - case 1: - fep->guard_interval = GUARD_INTERVAL_1_16; - break; - case 2: - fep->guard_interval = GUARD_INTERVAL_1_8; - break; - case 3: - fep->guard_interval = GUARD_INTERVAL_1_4; - break; - } - - switch (td->CodeRate) { - case 0: - fep->code_rate_HP = FEC_1_2; - break; - case 1: - fep->code_rate_HP = FEC_2_3; - break; - case 2: - fep->code_rate_HP = FEC_3_4; - break; - case 3: - fep->code_rate_HP = FEC_5_6; - break; - case 4: - fep->code_rate_HP = FEC_7_8; - break; - } - - switch (td->LPCodeRate) { - case 0: - fep->code_rate_LP = FEC_1_2; - break; - case 1: - fep->code_rate_LP = FEC_2_3; - break; - case 2: - fep->code_rate_LP = FEC_3_4; - break; - case 3: - fep->code_rate_LP = FEC_5_6; - break; - case 4: - fep->code_rate_LP = FEC_7_8; - break; - } - - switch (td->Constellation) { - case 0: - fep->modulation = QPSK; - break; - case 1: - fep->modulation = QAM_16; - break; - case 2: - fep->modulation = QAM_64; - break; - } - - switch (td->Hierarchy) { - case 0: - fep->hierarchy = HIERARCHY_NONE; - break; - case 1: - fep->hierarchy = HIERARCHY_1; - break; - case 2: - fep->hierarchy = HIERARCHY_2; - break; - case 3: - fep->hierarchy = HIERARCHY_4; - break; - } - - fep->inversion = INVERSION_AUTO; - break; - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - fep->frequency = td->Frequency; - fep->bandwidth_hz = 6000000; - /* todo: retrive the other parameters */ - break; - default: - return -EINVAL; - } - - return 0; -} - -static int smsdvb_init(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - - sms_board_power(client->coredev, 1); - - sms_board_dvb3_event(client, DVB3_EVENT_INIT); - return 0; -} - -static int smsdvb_sleep(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - - sms_board_led_feedback(client->coredev, SMS_LED_OFF); - sms_board_power(client->coredev, 0); - - sms_board_dvb3_event(client, DVB3_EVENT_SLEEP); - - return 0; -} - -static void smsdvb_release(struct dvb_frontend *fe) -{ - /* do nothing */ -} - -static struct dvb_frontend_ops smsdvb_fe_ops = { - .info = { - .name = "Siano Mobile Digital MDTV Receiver", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, - .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, - }, - - .release = smsdvb_release, - - .set_frontend = smsdvb_set_frontend, - .get_frontend = smsdvb_get_frontend, - .get_tune_settings = smsdvb_get_tune_settings, - - .read_status = smsdvb_read_status, - .read_ber = smsdvb_read_ber, - .read_signal_strength = smsdvb_read_signal_strength, - .read_snr = smsdvb_read_snr, - .read_ucblocks = smsdvb_read_ucblocks, - - .init = smsdvb_init, - .sleep = smsdvb_sleep, -}; - -static int smsdvb_hotplug(struct smscore_device_t *coredev, - struct device *device, int arrival) -{ - struct smsclient_params_t params; - struct smsdvb_client_t *client; - int rc; - - /* device removal handled by onremove callback */ - if (!arrival) - return 0; - client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); - if (!client) { - sms_err("kmalloc() failed"); - return -ENOMEM; - } - - /* register dvb adapter */ - rc = dvb_register_adapter(&client->adapter, - sms_get_board( - smscore_get_board_id(coredev))->name, - THIS_MODULE, device, adapter_nr); - if (rc < 0) { - sms_err("dvb_register_adapter() failed %d", rc); - goto adapter_error; - } - - /* init dvb demux */ - client->demux.dmx.capabilities = DMX_TS_FILTERING; - client->demux.filternum = 32; /* todo: nova ??? */ - client->demux.feednum = 32; - client->demux.start_feed = smsdvb_start_feed; - client->demux.stop_feed = smsdvb_stop_feed; - - rc = dvb_dmx_init(&client->demux); - if (rc < 0) { - sms_err("dvb_dmx_init failed %d", rc); - goto dvbdmx_error; - } - - /* init dmxdev */ - client->dmxdev.filternum = 32; - client->dmxdev.demux = &client->demux.dmx; - client->dmxdev.capabilities = 0; - - rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); - if (rc < 0) { - sms_err("dvb_dmxdev_init failed %d", rc); - goto dmxdev_error; - } - - /* init and register frontend */ - memcpy(&client->frontend.ops, &smsdvb_fe_ops, - sizeof(struct dvb_frontend_ops)); - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - client->frontend.ops.delsys[0] = SYS_DVBT; - break; - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - client->frontend.ops.delsys[0] = SYS_ISDBT; - break; - } - - rc = dvb_register_frontend(&client->adapter, &client->frontend); - if (rc < 0) { - sms_err("frontend registration failed %d", rc); - goto frontend_error; - } - - params.initial_id = 1; - params.data_type = MSG_SMS_DVBT_BDA_DATA; - params.onresponse_handler = smsdvb_onresponse; - params.onremove_handler = smsdvb_onremove; - params.context = client; - - rc = smscore_register_client(coredev, ¶ms, &client->smsclient); - if (rc < 0) { - sms_err("smscore_register_client() failed %d", rc); - goto client_error; - } - - client->coredev = coredev; - - init_completion(&client->tune_done); - - kmutex_lock(&g_smsdvb_clientslock); - - list_add(&client->entry, &g_smsdvb_clients); - - kmutex_unlock(&g_smsdvb_clientslock); - - client->event_fe_state = -1; - client->event_unc_state = -1; - sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); - - sms_info("success"); - sms_board_setup(coredev); - - return 0; - -client_error: - dvb_unregister_frontend(&client->frontend); - -frontend_error: - dvb_dmxdev_release(&client->dmxdev); - -dmxdev_error: - dvb_dmx_release(&client->demux); - -dvbdmx_error: - dvb_unregister_adapter(&client->adapter); - -adapter_error: - kfree(client); - return rc; -} - -static int __init smsdvb_module_init(void) -{ - int rc; - - INIT_LIST_HEAD(&g_smsdvb_clients); - kmutex_init(&g_smsdvb_clientslock); - - rc = smscore_register_hotplug(smsdvb_hotplug); - - sms_debug(""); - - return rc; -} - -static void __exit smsdvb_module_exit(void) -{ - smscore_unregister_hotplug(smsdvb_hotplug); - - kmutex_lock(&g_smsdvb_clientslock); - - while (!list_empty(&g_smsdvb_clients)) - smsdvb_unregister_client( - (struct smsdvb_client_t *) g_smsdvb_clients.next); - - kmutex_unlock(&g_smsdvb_clientslock); -} - -module_init(smsdvb_module_init); -module_exit(smsdvb_module_exit); - -MODULE_DESCRIPTION("SMS DVB subsystem adaptation module"); -MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h new file mode 100644 index 0000000..92c413b --- /dev/null +++ b/drivers/media/common/siano/smsdvb.h @@ -0,0 +1,130 @@ +/*********************************************************************** + * + * 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, see <http://www.gnu.org/licenses/>. + * + ***********************************************************************/ + +struct smsdvb_debugfs; +struct smsdvb_client_t; + +typedef void (*sms_prt_dvb_stats_t)(struct smsdvb_debugfs *debug_data, + struct sms_stats *p); + +typedef void (*sms_prt_isdb_stats_t)(struct smsdvb_debugfs *debug_data, + struct sms_isdbt_stats *p); + +typedef void (*sms_prt_isdb_stats_ex_t) + (struct smsdvb_debugfs *debug_data, + struct sms_isdbt_stats_ex *p); + + +struct smsdvb_client_t { + struct list_head entry; + + struct smscore_device_t *coredev; + struct smscore_client_t *smsclient; + + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dvb_frontend frontend; + + fe_status_t fe_status; + + struct completion tune_done; + struct completion stats_done; + + int last_per; + + int legacy_ber, legacy_per; + + int event_fe_state; + int event_unc_state; + + unsigned long get_stats_jiffies; + + int feed_users; + bool has_tuned; + + /* stats debugfs data */ + struct dentry *debugfs; + + struct smsdvb_debugfs *debug_data; + + sms_prt_dvb_stats_t prt_dvb_stats; + sms_prt_isdb_stats_t prt_isdb_stats; + sms_prt_isdb_stats_ex_t prt_isdb_stats_ex; +}; + +/* + * This struct is a mix of struct sms_rx_stats_ex and + * struct sms_srvm_signal_status. + * It was obtained by comparing the way it was filled by the original code + */ +struct RECEPTION_STATISTICS_PER_SLICES_S { + u32 result; + u32 snr; + s32 in_band_power; + u32 ts_packets; + u32 ets_packets; + u32 constellation; + u32 hp_code; + u32 tps_srv_ind_lp; + u32 tps_srv_ind_hp; + u32 cell_id; + u32 reason; + u32 request_id; + u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ + + u32 ber; /* Post Viterbi BER [1E-5] */ + s32 RSSI; /* dBm */ + s32 carrier_offset; /* Carrier Offset in bin/1024 */ + + u32 is_rf_locked; /* 0 - not locked, 1 - locked */ + u32 is_demod_locked; /* 0 - not locked, 1 - locked */ + + u32 ber_bit_count; /* Total number of SYNC bits. */ + u32 ber_error_count; /* Number of erronous SYNC bits. */ + + s32 MRC_SNR; /* dB */ + s32 mrc_in_band_pwr; /* In band power in dBM */ + s32 MRC_RSSI; /* dBm */ +}; + +/* From smsdvb-debugfs.c */ +#ifdef CONFIG_SMS_SIANO_DEBUGFS + +int smsdvb_debugfs_create(struct smsdvb_client_t *client); +void smsdvb_debugfs_release(struct smsdvb_client_t *client); +int smsdvb_debugfs_register(void); +void smsdvb_debugfs_unregister(void); + +#else + +static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client) +{ + return 0; +} + +static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {} + +static inline int smsdvb_debugfs_register(void) +{ + return 0; +}; + +static inline void smsdvb_debugfs_unregister(void) {}; + +#endif + diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c index e2657c2..bfe831c 100644 --- a/drivers/media/common/siano/smsendian.c +++ b/drivers/media/common/siano/smsendian.c @@ -28,23 +28,23 @@ void smsendian_handle_tx_message(void *buffer) { #ifdef __BIG_ENDIAN - struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; + struct sms_msg_data *msg = (struct sms_msg_data *)buffer; int i; - int msgWords; + int msg_words; - switch (msg->xMsgHeader.msgType) { + switch (msg->x_msg_header.msg_type) { case MSG_SMS_DATA_DOWNLOAD_REQ: { - msg->msgData[0] = le32_to_cpu(msg->msgData[0]); + msg->msg_data[0] = le32_to_cpu(msg->msg_data[0]); break; } default: - msgWords = (msg->xMsgHeader.msgLength - - sizeof(struct SmsMsgHdr_ST))/4; + msg_words = (msg->x_msg_header.msg_length - + sizeof(struct sms_msg_hdr))/4; - for (i = 0; i < msgWords; i++) - msg->msgData[i] = le32_to_cpu(msg->msgData[i]); + for (i = 0; i < msg_words; i++) + msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]); break; } @@ -55,16 +55,16 @@ EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); void smsendian_handle_rx_message(void *buffer) { #ifdef __BIG_ENDIAN - struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; + struct sms_msg_data *msg = (struct sms_msg_data *)buffer; int i; - int msgWords; + int msg_words; - switch (msg->xMsgHeader.msgType) { + switch (msg->x_msg_header.msg_type) { case MSG_SMS_GET_VERSION_EX_RES: { - struct SmsVersionRes_ST *ver = - (struct SmsVersionRes_ST *) msg; - ver->ChipModel = le16_to_cpu(ver->ChipModel); + struct sms_version_res *ver = + (struct sms_version_res *) msg; + ver->chip_model = le16_to_cpu(ver->chip_model); break; } @@ -77,11 +77,11 @@ void smsendian_handle_rx_message(void *buffer) default: { - msgWords = (msg->xMsgHeader.msgLength - - sizeof(struct SmsMsgHdr_ST))/4; + msg_words = (msg->x_msg_header.msg_length - + sizeof(struct sms_msg_hdr))/4; - for (i = 0; i < msgWords; i++) - msg->msgData[i] = le32_to_cpu(msg->msgData[i]); + for (i = 0; i < msg_words; i++) + msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]); break; } @@ -93,11 +93,11 @@ EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); void smsendian_handle_message_header(void *msg) { #ifdef __BIG_ENDIAN - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)msg; - phdr->msgType = le16_to_cpu(phdr->msgType); - phdr->msgLength = le16_to_cpu(phdr->msgLength); - phdr->msgFlags = le16_to_cpu(phdr->msgFlags); + phdr->msg_type = le16_to_cpu(phdr->msg_type); + phdr->msg_length = le16_to_cpu(phdr->msg_length); + phdr->msg_flags = le16_to_cpu(phdr->msg_flags); #endif /* __BIG_ENDIAN */ } EXPORT_SYMBOL_GPL(smsendian_handle_message_header); diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h index 69b59b9..fc8b792 100644 --- a/drivers/media/common/siano/smsir.h +++ b/drivers/media/common/siano/smsir.h @@ -40,7 +40,6 @@ struct ir_t { char phys[32]; char *rc_codes; - u64 protocol; u32 timeout; u32 controller; diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h index eb91fd8..833191bc 100644 --- a/drivers/media/dvb-core/demux.h +++ b/drivers/media/dvb-core/demux.h @@ -83,45 +83,6 @@ enum dmx_success { #define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to the demux device, not to the dvr device */ -/* PES type for filters which write to built-in decoder */ -/* these should be kept identical to the types in dmx.h */ - -enum dmx_ts_pes -{ /* also send packets to decoder (if it exists) */ - DMX_TS_PES_AUDIO0, - DMX_TS_PES_VIDEO0, - DMX_TS_PES_TELETEXT0, - DMX_TS_PES_SUBTITLE0, - DMX_TS_PES_PCR0, - - DMX_TS_PES_AUDIO1, - DMX_TS_PES_VIDEO1, - DMX_TS_PES_TELETEXT1, - DMX_TS_PES_SUBTITLE1, - DMX_TS_PES_PCR1, - - DMX_TS_PES_AUDIO2, - DMX_TS_PES_VIDEO2, - DMX_TS_PES_TELETEXT2, - DMX_TS_PES_SUBTITLE2, - DMX_TS_PES_PCR2, - - DMX_TS_PES_AUDIO3, - DMX_TS_PES_VIDEO3, - DMX_TS_PES_TELETEXT3, - DMX_TS_PES_SUBTITLE3, - DMX_TS_PES_PCR3, - - DMX_TS_PES_OTHER -}; - -#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0 -#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0 -#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0 -#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0 -#define DMX_TS_PES_PCR DMX_TS_PES_PCR0 - - struct dmx_ts_feed { int is_filtering; /* Set to non-zero when filtering in progress */ struct dmx_demux *parent; /* Back-pointer */ diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index d81dbb2..a1a3a51 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -569,7 +569,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, dmx_output_t otype; int ret; int ts_type; - dmx_pes_type_t ts_pes; + enum dmx_ts_pes ts_pes; struct dmx_ts_feed *tsfeed; feed->ts = NULL; @@ -852,7 +852,8 @@ static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter, struct dmx_sct_filter_params *params) { - dprintk("function : %s\n", __func__); + dprintk("function : %s, PID=0x%04x, flags=%02x, timeout=%d\n", + __func__, params->pid, params->flags, params->timeout); dvb_dmxdev_filter_stop(dmxdevfilter); diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 399e104..335a8f4 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -124,8 +124,7 @@ #define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DIBCOM_NIM7090 0x1bb2 #define USB_PID_DIBCOM_TFE7090PVR 0x1bb4 -#define USB_PID_DIBCOM_TFE7090E 0x1bb7 -#define USB_PID_DIBCOM_TFE7790E 0x1e6e +#define USB_PID_DIBCOM_TFE7790P 0x1e6e #define USB_PID_DIBCOM_NIM9090M 0x2383 #define USB_PID_DIBCOM_NIM9090MD 0x2384 #define USB_PID_DPOSH_M9206_COLD 0x9206 diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index d319717..3485655 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -440,20 +440,22 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) if (!dvb_demux_feed_err_pkts) return; } else /* if TEI bit is set, pid may be wrong- skip pkt counter */ - if (demux->cnt_storage && dvb_demux_tscheck) { - /* check pkt counter */ - if (pid < MAX_PID) { - if ((buf[3] & 0xf) != demux->cnt_storage[pid]) - dprintk_tscheck("TS packet counter mismatch. " - "PID=0x%x expected 0x%x " - "got 0x%x\n", + if (demux->cnt_storage && dvb_demux_tscheck) { + /* check pkt counter */ + if (pid < MAX_PID) { + if (buf[3] & 0x10) + demux->cnt_storage[pid] = + (demux->cnt_storage[pid] + 1) & 0xf; + + if ((buf[3] & 0xf) != demux->cnt_storage[pid]) { + dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n", pid, demux->cnt_storage[pid], buf[3] & 0xf); - - demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; + demux->cnt_storage[pid] = buf[3] & 0xf; + } + } + /* end check */ } - /* end check */ - } list_for_each_entry(feed, &demux->feed_list, list_head) { if ((feed->pid != pid) && (feed->pid != 0x2000)) @@ -672,7 +674,7 @@ static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, return -ERESTARTSYS; if (ts_type & TS_DECODER) { - if (pes_type >= DMX_TS_PES_OTHER) { + if (pes_type >= DMX_PES_OTHER) { mutex_unlock(&demux->mutex); return -EINVAL; } @@ -844,7 +846,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, feed->pid = 0xffff; - if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER) + if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_PES_OTHER) demux->pesfilter[feed->pes_type] = NULL; mutex_unlock(&demux->mutex); @@ -1266,7 +1268,7 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux) INIT_LIST_HEAD(&dvbdemux->frontend_list); - for (i = 0; i < DMX_TS_PES_OTHER; i++) { + for (i = 0; i < DMX_PES_OTHER; i++) { dvbdemux->pesfilter[i] = NULL; dvbdemux->pids[i] = 0xffff; } diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h index fa7188a..ae7fc33 100644 --- a/drivers/media/dvb-core/dvb_demux.h +++ b/drivers/media/dvb-core/dvb_demux.h @@ -119,8 +119,8 @@ struct dvb_demux { struct list_head frontend_list; - struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER]; - u16 pids[DMX_TS_PES_OTHER]; + struct dvb_demux_feed *pesfilter[DMX_PES_OTHER]; + u16 pids[DMX_PES_OTHER]; int playing; int recording; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 6e50a75..57601c0 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -920,7 +920,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) u32 delsys; delsys = c->delivery_system; - memset(c, 0, sizeof(struct dtv_frontend_properties)); + memset(c, 0, offsetof(struct dtv_frontend_properties, strength)); c->delivery_system = delsys; c->state = DTV_CLEAR; @@ -1509,9 +1509,74 @@ static bool is_dvbv3_delsys(u32 delsys) return status; } -static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) +/** + * emulate_delivery_system - emulate a DVBv5 delivery system with a DVBv3 type + * @fe: struct frontend; + * @delsys: DVBv5 type that will be used for emulation + * + * Provides emulation for delivery systems that are compatible with the old + * DVBv3 call. Among its usages, it provices support for ISDB-T, and allows + * using a DVB-S2 only frontend just like it were a DVB-S, if the frontent + * parameters are compatible with DVB-S spec. + */ +static int emulate_delivery_system(struct dvb_frontend *fe, u32 delsys) { - int ncaps, i; + int i; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + c->delivery_system = delsys; + + /* + * If the call is for ISDB-T, put it into full-seg, auto mode, TV + */ + if (c->delivery_system == SYS_ISDBT) { + dev_dbg(fe->dvb->device, + "%s: Using defaults for SYS_ISDBT\n", + __func__); + + if (!c->bandwidth_hz) + c->bandwidth_hz = 6000000; + + c->isdbt_partial_reception = 0; + c->isdbt_sb_mode = 0; + c->isdbt_sb_subchannel = 0; + c->isdbt_sb_segment_idx = 0; + c->isdbt_sb_segment_count = 0; + c->isdbt_layer_enabled = 7; + for (i = 0; i < 3; i++) { + c->layer[i].fec = FEC_AUTO; + c->layer[i].modulation = QAM_AUTO; + c->layer[i].interleaving = 0; + c->layer[i].segment_count = 0; + } + } + dev_dbg(fe->dvb->device, "%s: change delivery system on cache to %d\n", + __func__, c->delivery_system); + + return 0; +} + +/** + * dvbv5_set_delivery_system - Sets the delivery system for a DVBv5 API call + * @fe: frontend struct + * @desired_system: delivery system requested by the user + * + * A DVBv5 call know what's the desired system it wants. So, set it. + * + * There are, however, a few known issues with early DVBv5 applications that + * are also handled by this logic: + * + * 1) Some early apps use SYS_UNDEFINED as the desired delivery system. + * This is an API violation, but, as we don't want to break userspace, + * convert it to the first supported delivery system. + * 2) Some apps might be using a DVBv5 call in a wrong way, passing, for + * example, SYS_DVBT instead of SYS_ISDBT. This is because early usage of + * ISDB-T provided backward compat with DVB-T. + */ +static int dvbv5_set_delivery_system(struct dvb_frontend *fe, + u32 desired_system) +{ + int ncaps; u32 delsys = SYS_UNDEFINED; struct dtv_frontend_properties *c = &fe->dtv_property_cache; enum dvbv3_emulation_type type; @@ -1522,166 +1587,136 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) * assume that the application wants to use the first supported * delivery system. */ - if (c->delivery_system == SYS_UNDEFINED) - c->delivery_system = fe->ops.delsys[0]; + if (desired_system == SYS_UNDEFINED) + desired_system = fe->ops.delsys[0]; - if (desired_system == SYS_UNDEFINED) { - /* - * A DVBv3 call doesn't know what's the desired system. - * Also, DVBv3 applications don't know that ops.info->type - * could be changed, and they simply dies when it doesn't - * match. - * So, don't change the current delivery system, as it - * may be trying to do the wrong thing, like setting an - * ISDB-T frontend as DVB-T. Instead, find the closest - * DVBv3 system that matches the delivery system. - */ - if (is_dvbv3_delsys(c->delivery_system)) { + /* + * This is a DVBv5 call. So, it likely knows the supported + * delivery systems. So, check if the desired delivery system is + * supported + */ + ncaps = 0; + while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { + if (fe->ops.delsys[ncaps] == desired_system) { + c->delivery_system = desired_system; dev_dbg(fe->dvb->device, - "%s: Using delivery system to %d\n", - __func__, c->delivery_system); - return 0; - } - type = dvbv3_type(c->delivery_system); - switch (type) { - case DVBV3_QPSK: - desired_system = SYS_DVBS; - break; - case DVBV3_QAM: - desired_system = SYS_DVBC_ANNEX_A; - break; - case DVBV3_ATSC: - desired_system = SYS_ATSC; - break; - case DVBV3_OFDM: - desired_system = SYS_DVBT; - break; - default: - dev_dbg(fe->dvb->device, "%s: This frontend doesn't support DVBv3 calls\n", - __func__); - return -EINVAL; - } - /* - * Get a delivery system that is compatible with DVBv3 - * NOTE: in order for this to work with softwares like Kaffeine that - * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to - * DVB-S, drivers that support both should put the SYS_DVBS entry - * before the SYS_DVBS2, otherwise it won't switch back to DVB-S. - * The real fix is that userspace applications should not use DVBv3 - * and not trust on calling FE_SET_FRONTEND to switch the delivery - * system. - */ - ncaps = 0; - while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { - if (fe->ops.delsys[ncaps] == desired_system) { - delsys = desired_system; - break; - } - ncaps++; - } - if (delsys == SYS_UNDEFINED) { - dev_dbg(fe->dvb->device, "%s: Couldn't find a delivery system that matches %d\n", + "%s: Changing delivery system to %d\n", __func__, desired_system); + return 0; } - } else { - /* - * This is a DVBv5 call. So, it likely knows the supported - * delivery systems. - */ + ncaps++; + } - /* Check if the desired delivery system is supported */ - ncaps = 0; - while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { - if (fe->ops.delsys[ncaps] == desired_system) { - c->delivery_system = desired_system; - dev_dbg(fe->dvb->device, - "%s: Changing delivery system to %d\n", - __func__, desired_system); - return 0; - } - ncaps++; - } - type = dvbv3_type(desired_system); + /* + * The requested delivery system isn't supported. Maybe userspace + * is requesting a DVBv3 compatible delivery system. + * + * The emulation only works if the desired system is one of the + * delivery systems supported by DVBv3 API + */ + if (!is_dvbv3_delsys(desired_system)) { + dev_dbg(fe->dvb->device, + "%s: Delivery system %d not supported.\n", + __func__, desired_system); + return -EINVAL; + } - /* - * The delivery system is not supported. See if it can be - * emulated. - * The emulation only works if the desired system is one of the - * DVBv3 delivery systems - */ - if (!is_dvbv3_delsys(desired_system)) { - dev_dbg(fe->dvb->device, - "%s: can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n", - __func__); - return -EINVAL; - } + type = dvbv3_type(desired_system); - /* - * Get the last non-DVBv3 delivery system that has the same type - * of the desired system - */ - ncaps = 0; - while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { - if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) && - !is_dvbv3_delsys(fe->ops.delsys[ncaps])) - delsys = fe->ops.delsys[ncaps]; - ncaps++; - } - /* There's nothing compatible with the desired delivery system */ - if (delsys == SYS_UNDEFINED) { - dev_dbg(fe->dvb->device, - "%s: Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n", - __func__); - return -EINVAL; - } + /* + * Get the last non-DVBv3 delivery system that has the same type + * of the desired system + */ + ncaps = 0; + while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { + if (dvbv3_type(fe->ops.delsys[ncaps]) == type) + delsys = fe->ops.delsys[ncaps]; + ncaps++; } - c->delivery_system = delsys; + /* There's nothing compatible with the desired delivery system */ + if (delsys == SYS_UNDEFINED) { + dev_dbg(fe->dvb->device, + "%s: Delivery system %d not supported on emulation mode.\n", + __func__, desired_system); + return -EINVAL; + } + + dev_dbg(fe->dvb->device, + "%s: Using delivery system %d emulated as if it were %d\n", + __func__, delsys, desired_system); + + return emulate_delivery_system(fe, desired_system); +} + +/** + * dvbv3_set_delivery_system - Sets the delivery system for a DVBv3 API call + * @fe: frontend struct + * + * A DVBv3 call doesn't know what's the desired system it wants. It also + * doesn't allow to switch between different types. Due to that, userspace + * should use DVBv5 instead. + * However, in order to avoid breaking userspace API, limited backward + * compatibility support is provided. + * + * There are some delivery systems that are incompatible with DVBv3 calls. + * + * This routine should work fine for frontends that support just one delivery + * system. + * + * For frontends that support multiple frontends: + * 1) It defaults to use the first supported delivery system. There's an + * userspace application that allows changing it at runtime; + * + * 2) If the current delivery system is not compatible with DVBv3, it gets + * the first one that it is compatible. + * + * NOTE: in order for this to work with applications like Kaffeine that + * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to + * DVB-S, drivers that support both DVB-S and DVB-S2 should have the + * SYS_DVBS entry before the SYS_DVBS2, otherwise it won't switch back + * to DVB-S. + */ +static int dvbv3_set_delivery_system(struct dvb_frontend *fe) +{ + int ncaps; + u32 delsys = SYS_UNDEFINED; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* If not set yet, defaults to the first supported delivery system */ + if (c->delivery_system == SYS_UNDEFINED) + c->delivery_system = fe->ops.delsys[0]; /* - * The DVBv3 or DVBv5 call is requesting a different system. So, - * emulation is needed. - * - * Emulate newer delivery systems like ISDBT, DVBT and DTMB - * for older DVBv5 applications. The emulation will try to use - * the auto mode for most things, and will assume that the desired - * delivery system is the last one at the ops.delsys[] array + * Trivial case: just use the current one, if it already a DVBv3 + * delivery system */ - dev_dbg(fe->dvb->device, - "%s: Using delivery system %d emulated as if it were a %d\n", - __func__, delsys, desired_system); + if (is_dvbv3_delsys(c->delivery_system)) { + dev_dbg(fe->dvb->device, + "%s: Using delivery system to %d\n", + __func__, c->delivery_system); + return 0; + } /* - * For now, handles ISDB-T calls. More code may be needed here for the - * other emulated stuff + * Seek for the first delivery system that it is compatible with a + * DVBv3 standard */ - if (type == DVBV3_OFDM) { - if (c->delivery_system == SYS_ISDBT) { - dev_dbg(fe->dvb->device, - "%s: Using defaults for SYS_ISDBT\n", - __func__); - - if (!c->bandwidth_hz) - c->bandwidth_hz = 6000000; - - c->isdbt_partial_reception = 0; - c->isdbt_sb_mode = 0; - c->isdbt_sb_subchannel = 0; - c->isdbt_sb_segment_idx = 0; - c->isdbt_sb_segment_count = 0; - c->isdbt_layer_enabled = 0; - for (i = 0; i < 3; i++) { - c->layer[i].fec = FEC_AUTO; - c->layer[i].modulation = QAM_AUTO; - c->layer[i].interleaving = 0; - c->layer[i].segment_count = 0; - } + ncaps = 0; + while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { + if (dvbv3_type(fe->ops.delsys[ncaps]) != DVBV3_UNKNOWN) { + delsys = fe->ops.delsys[ncaps]; + break; } + ncaps++; } - dev_dbg(fe->dvb->device, "%s: change delivery system on cache to %d\n", - __func__, c->delivery_system); - - return 0; + if (delsys == SYS_UNDEFINED) { + dev_dbg(fe->dvb->device, + "%s: Couldn't find a delivery system that works with FE_SET_FRONTEND\n", + __func__); + return -EINVAL; + } + return emulate_delivery_system(fe, delsys); } static int dtv_property_process_set(struct dvb_frontend *fe, @@ -1742,7 +1777,7 @@ static int dtv_property_process_set(struct dvb_frontend *fe, c->rolloff = tvp->u.data; break; case DTV_DELIVERY_SYSTEM: - r = set_delivery_system(fe, tvp->u.data); + r = dvbv5_set_delivery_system(fe, tvp->u.data); break; case DTV_VOLTAGE: c->voltage = tvp->u.data; @@ -2335,7 +2370,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, break; case FE_SET_FRONTEND: - err = set_delivery_system(fe, SYS_UNDEFINED); + err = dvbv3_set_delivery_system(fe); if (err) break; @@ -2594,7 +2629,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb, * first supported delivery system (ops->delsys[0]) */ - fe->dtv_property_cache.delivery_system = fe->ops.delsys[0]; + fe->dtv_property_cache.delivery_system = fe->ops.delsys[0]; dvb_frontend_clear_cache(fe); mutex_unlock(&frontend_mutex); diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index b34922a..371b6ca 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -245,8 +245,8 @@ struct analog_demod_ops { void (*set_params)(struct dvb_frontend *fe, struct analog_parameters *params); - int (*has_signal)(struct dvb_frontend *fe); - int (*get_afc)(struct dvb_frontend *fe); + int (*has_signal)(struct dvb_frontend *fe, u16 *signal); + int (*get_afc)(struct dvb_frontend *fe, s32 *afc); void (*tuner_status)(struct dvb_frontend *fe); void (*standby)(struct dvb_frontend *fe); void (*release)(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index 44225b1..e17cb85 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1044,7 +1044,7 @@ static int dvb_net_feed_start(struct net_device *dev) ret = priv->tsfeed->set(priv->tsfeed, priv->pid, /* pid */ TS_PACKET, /* type */ - DMX_TS_PES_OTHER, /* pes type */ + DMX_PES_OTHER, /* pes type */ 32768, /* circular buffer size */ timeout /* timeout */ ); diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 6f809a7..0e2ec6f 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -210,7 +210,7 @@ config DVB_SI21XX config DVB_TS2020 tristate "Montage Tehnology TS2020 based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h index ed29e55..b6ef642 100644 --- a/drivers/media/dvb-frontends/a8293.h +++ b/drivers/media/dvb-frontends/a8293.h @@ -21,12 +21,13 @@ #ifndef A8293_H #define A8293_H +#include <linux/kconfig.h> + struct a8293_config { u8 i2c_addr; }; -#if defined(CONFIG_DVB_A8293) || \ - (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_A8293) extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct a8293_config *cfg); #else diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h index dc837d9..09273b2 100644 --- a/drivers/media/dvb-frontends/af9013.h +++ b/drivers/media/dvb-frontends/af9013.h @@ -25,6 +25,7 @@ #ifndef AF9013_H #define AF9013_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> /* AF9013/5 GPIOs (mostly guessed) @@ -102,8 +103,7 @@ struct af9013_config { u8 gpio[4]; }; -#if defined(CONFIG_DVB_AF9013) || \ - (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_AF9013) extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index c9cad989..a777b4b 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -156,6 +156,37 @@ static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, return 0; } +/* write reg val table using reg addr auto increment */ +static int af9033_wr_reg_val_tab(struct af9033_state *state, + const struct reg_val *tab, int tab_len) +{ + int ret, i, j; + u8 buf[tab_len]; + + dev_dbg(&state->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); + + for (i = 0, j = 0; i < tab_len; i++) { + 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); + if (ret < 0) + goto err; + + j = 0; + } else { + j++; + } + } + + return 0; + +err: + dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x) { u32 r = 0, c = 0, i; @@ -223,6 +254,7 @@ static int af9033_init(struct dvb_frontend *fe) { 0x80f986, state->ts_mode_parallel, 0x01 }, { 0x00d827, 0x00, 0xff }, { 0x00d829, 0x00, 0xff }, + { 0x800045, state->cfg.adc_multiplier, 0xff }, }; /* program clock control */ @@ -286,14 +318,29 @@ static int af9033_init(struct dvb_frontend *fe) /* load OFSM settings */ dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__); - len = ARRAY_SIZE(ofsm_init); - init = ofsm_init; - for (i = 0; i < len; i++) { - ret = af9033_wr_reg(state, init[i].reg, init[i].val); - if (ret < 0) - goto err; + switch (state->cfg.tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + len = ARRAY_SIZE(ofsm_init_it9135_v1); + init = ofsm_init_it9135_v1; + break; + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + len = ARRAY_SIZE(ofsm_init_it9135_v2); + init = ofsm_init_it9135_v2; + break; + default: + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + break; } + ret = af9033_wr_reg_val_tab(state, init, len); + if (ret < 0) + goto err; + /* load tuner specific settings */ dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n", __func__); @@ -322,6 +369,30 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_fc0012); init = tuner_init_fc0012; break; + case AF9033_TUNER_IT9135_38: + len = ARRAY_SIZE(tuner_init_it9135_38); + init = tuner_init_it9135_38; + break; + case AF9033_TUNER_IT9135_51: + len = ARRAY_SIZE(tuner_init_it9135_51); + init = tuner_init_it9135_51; + break; + case AF9033_TUNER_IT9135_52: + len = ARRAY_SIZE(tuner_init_it9135_52); + init = tuner_init_it9135_52; + break; + case AF9033_TUNER_IT9135_60: + len = ARRAY_SIZE(tuner_init_it9135_60); + init = tuner_init_it9135_60; + break; + case AF9033_TUNER_IT9135_61: + len = ARRAY_SIZE(tuner_init_it9135_61); + init = tuner_init_it9135_61; + break; + case AF9033_TUNER_IT9135_62: + len = ARRAY_SIZE(tuner_init_it9135_62); + init = tuner_init_it9135_62; + break; default: dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); @@ -329,11 +400,9 @@ static int af9033_init(struct dvb_frontend *fe) goto err; } - for (i = 0; i < len; i++) { - ret = af9033_wr_reg(state, init[i].reg, init[i].val); - if (ret < 0) - goto err; - } + ret = af9033_wr_reg_val_tab(state, 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); @@ -349,6 +418,15 @@ static int af9033_init(struct dvb_frontend *fe) goto err; } + switch (state->cfg.tuner) { + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + ret = af9033_wr_reg(state, 0x800000, 0x01); + if (ret < 0) + goto err; + } + state->bandwidth_hz = 0; /* force to program all parameters */ return 0; @@ -415,7 +493,8 @@ err: static int af9033_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *fesettings) { - fesettings->min_delay_ms = 800; + /* 800 => 2000 because IT9135 v2 is slow to gain lock */ + fesettings->min_delay_ms = 2000; fesettings->step_size = 0; fesettings->max_drift = 0; @@ -498,17 +577,17 @@ static int af9033_set_frontend(struct dvb_frontend *fe) if (spec_inv == -1) freq_cw = 0x800000 - freq_cw; - /* get adc multiplies */ - ret = af9033_rd_reg(state, 0x800045, &tmp); - if (ret < 0) - goto err; - - if (tmp == 1) + if (state->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X) freq_cw /= 2; buf[0] = (freq_cw >> 0) & 0xff; buf[1] = (freq_cw >> 8) & 0xff; buf[2] = (freq_cw >> 16) & 0x7f; + + /* FIXME: there seems to be calculation error here... */ + if (if_frequency == 0) + buf[2] = 0; + ret = af9033_wr_regs(state, 0x800029, buf, 3); if (ret < 0) goto err; @@ -934,13 +1013,24 @@ struct dvb_frontend *af9033_attach(const struct af9033_config *config, buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); /* sleep */ - ret = af9033_wr_reg(state, 0x80004c, 1); - if (ret < 0) - goto err; + 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; - ret = af9033_wr_reg(state, 0x800000, 0); - if (ret < 0) - goto err; + ret = af9033_wr_reg(state, 0x800000, 0); + if (ret < 0) + goto err; + } /* configure internal TS mode */ switch (state->cfg.ts_mode) { diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index 82bd8c1..c286e8f 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -22,6 +22,8 @@ #ifndef AF9033_H #define AF9033_H +#include <linux/kconfig.h> + struct af9033_config { /* * I2C address @@ -36,6 +38,13 @@ struct af9033_config { u32 clock; /* + * ADC multiplier + */ +#define AF9033_ADC_MULTIPLIER_1X 0 +#define AF9033_ADC_MULTIPLIER_2X 1 + u8 adc_multiplier; + + /* * tuner */ #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ @@ -44,6 +53,14 @@ struct af9033_config { #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ #define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ #define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ +/* 50-5f Omega */ +#define AF9033_TUNER_IT9135_38 0x38 /* Omega */ +#define AF9033_TUNER_IT9135_51 0x51 /* Omega LNA config 1 */ +#define AF9033_TUNER_IT9135_52 0x52 /* Omega LNA config 2 */ +/* 60-6f Omega v2 */ +#define AF9033_TUNER_IT9135_60 0x60 /* Omega v2 */ +#define AF9033_TUNER_IT9135_61 0x61 /* Omega v2 LNA config 1 */ +#define AF9033_TUNER_IT9135_62 0x62 /* Omega v2 LNA config 2 */ u8 tuner; /* @@ -61,8 +78,7 @@ struct af9033_config { }; -#if defined(CONFIG_DVB_AF9033) || \ - (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_AF9033) extern struct dvb_frontend *af9033_attach(const struct af9033_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index e9bd782..fc2ad58 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -547,5 +547,1509 @@ static const struct reg_val tuner_init_fc2580[] = { { 0x80f1e6, 0x01 }, }; -#endif /* AF9033_PRIV_H */ +static const struct reg_val ofsm_init_it9135_v1[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c2, 0x05 }, + { 0x8000c6, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* ITE Tech IT9135 Omega tuner init + AF9033_TUNER_IT9135_38 = 0x38 */ +static const struct reg_val tuner_init_it9135_38[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x38 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xc8 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800083, 0x02 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xc8 }, + { 0x800087, 0xb8 }, + { 0x800088, 0xd0 }, + { 0x800089, 0xc3 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x32 }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x02 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x77 }, + { 0x800103, 0x00 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc8 }, + { 0x800106, 0x05 }, + { 0x800107, 0x7b }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xc8 }, + { 0x80011b, 0x7b }, + { 0x80011c, 0x8a }, + { 0x80011d, 0xa0 }, + { 0x800122, 0x02 }, + { 0x800123, 0x18 }, + { 0x800124, 0xc3 }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x01 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xc8 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0x00 }, + { 0x80f086, 0x02 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* ITE Tech IT9135 Omega LNA config 1 tuner init + AF9033_TUNER_IT9135_51 = 0x51 */ +static const struct reg_val tuner_init_it9135_51[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x51 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x800070, 0x0a }, + { 0x800071, 0x06 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xc8 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800083, 0x02 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xc0 }, + { 0x800087, 0x96 }, + { 0x800088, 0xcf }, + { 0x800089, 0xc3 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3c }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7a }, + { 0x800102, 0x77 }, + { 0x800103, 0x01 }, + { 0x800104, 0x02 }, + { 0x800105, 0xb0 }, + { 0x800106, 0x02 }, + { 0x800107, 0x7a }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xc0 }, + { 0x80011b, 0x7a }, + { 0x80011c, 0xac }, + { 0x80011d, 0x8c }, + { 0x800122, 0x02 }, + { 0x800123, 0x70 }, + { 0x800124, 0xa4 }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x01 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xc0 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0xc0 }, + { 0x80f086, 0x01 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* ITE Tech IT9135 Omega LNA config 2 tuner init + AF9033_TUNER_IT9135_52 = 0x52 */ +static const struct reg_val tuner_init_it9135_52[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x52 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x10 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xa0 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x17 }, + { 0x800083, 0x03 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xb3 }, + { 0x800087, 0x97 }, + { 0x800088, 0xc0 }, + { 0x800089, 0x9e }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5c }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3c }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x91 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x74 }, + { 0x800102, 0x77 }, + { 0x800103, 0x02 }, + { 0x800104, 0x02 }, + { 0x800105, 0xa4 }, + { 0x800106, 0x02 }, + { 0x800107, 0x6e }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xcd }, + { 0x80011b, 0x62 }, + { 0x80011c, 0xa4 }, + { 0x80011d, 0x8c }, + { 0x800122, 0x03 }, + { 0x800123, 0x18 }, + { 0x800124, 0x9e }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x00 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xb6 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0xc0 }, + { 0x80f086, 0x01 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +static const struct reg_val ofsm_init_it9135_v2[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c2, 0x05 }, + { 0x8000c6, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* ITE Tech IT9135 Omega v2 tuner init + AF9033_TUNER_IT9135_60 = 0x60 */ +static const struct reg_val tuner_init_it9135_60[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x60 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x8c }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x18 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xbe }, + { 0x800087, 0xa0 }, + { 0x800088, 0xc6 }, + { 0x800089, 0xb6 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0xa0 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x0a }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x8c }, + { 0x800103, 0x00 }, + { 0x800104, 0x02 }, + { 0x800105, 0xbe }, + { 0x800106, 0x00 }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xbe }, + { 0x800124, 0xae }, + { 0x800127, 0x00 }, + { 0x80012a, 0x56 }, + { 0x80012b, 0x50 }, + { 0x80012c, 0x47 }, + { 0x80012d, 0x42 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x08 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x19 }, + { 0x800144, 0x00 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x6e }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x8c }, + { 0x80014d, 0xac }, + { 0x80014e, 0xc6 }, + { 0x80014f, 0x03 }, + { 0x800151, 0x1e }, + { 0x800153, 0xbc }, + { 0x800178, 0x09 }, + { 0x800181, 0x94 }, + { 0x800182, 0x6e }, + { 0x800185, 0x24 }, + { 0x800189, 0xbe }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xa0 }, + { 0x800190, 0x5a }, + { 0x80ed02, 0xff }, + { 0x80ee42, 0xff }, + { 0x80ee82, 0xff }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; +/* ITE Tech IT9135 Omega v2 LNA config 1 tuner init + AF9033_TUNER_IT9135_61 = 0x61 */ +static const struct reg_val tuner_init_it9135_61[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x61 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x06 }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x90 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xbc }, + { 0x800087, 0x9c }, + { 0x800088, 0xcc }, + { 0x800089, 0xa8 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5c }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0xa0 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x08 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x8c }, + { 0x800103, 0x01 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc8 }, + { 0x800106, 0x00 }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xc6 }, + { 0x800124, 0xa8 }, + { 0x800127, 0x00 }, + { 0x80012a, 0x59 }, + { 0x80012b, 0x50 }, + { 0x80012c, 0x47 }, + { 0x80012d, 0x42 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x05 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x59 }, + { 0x800144, 0x01 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x7b }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x8c }, + { 0x80014d, 0xa8 }, + { 0x80014e, 0xc6 }, + { 0x80014f, 0x03 }, + { 0x800151, 0x28 }, + { 0x800153, 0xcc }, + { 0x800178, 0x09 }, + { 0x800181, 0x9c }, + { 0x800182, 0x76 }, + { 0x800185, 0x28 }, + { 0x800189, 0xaa }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xfb }, + { 0x800190, 0x5c }, + { 0x80ed02, 0xff }, + { 0x80ee42, 0xff }, + { 0x80ee82, 0xff }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* ITE Tech IT9135 Omega v2 LNA config 2 tuner init + AF9033_TUNER_IT9135_62 = 0x62 */ +static const struct reg_val tuner_init_it9135_62[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x62 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x8c }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xb8 }, + { 0x800087, 0x9c }, + { 0x800088, 0xb2 }, + { 0x800089, 0xa6 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x02 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x6e }, + { 0x800102, 0x8c }, + { 0x800103, 0x02 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc2 }, + { 0x800106, 0x00 }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xb8 }, + { 0x800124, 0xa8 }, + { 0x800127, 0x00 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x05 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x59 }, + { 0x800144, 0x00 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x7b }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x70 }, + { 0x80014d, 0x96 }, + { 0x80014e, 0xd0 }, + { 0x80014f, 0x03 }, + { 0x800151, 0x28 }, + { 0x800153, 0xb2 }, + { 0x800178, 0x09 }, + { 0x800181, 0x9c }, + { 0x800182, 0x6e }, + { 0x800185, 0x24 }, + { 0x800189, 0xb8 }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xfb }, + { 0x800190, 0x5a }, + { 0x80ed02, 0xff }, + { 0x80ee42, 0xff }, + { 0x80ee82, 0xff }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +#endif /* AF9033_PRIV_H */ diff --git a/drivers/media/dvb-frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h index 0242733..8e0ac98 100644 --- a/drivers/media/dvb-frontends/atbm8830.h +++ b/drivers/media/dvb-frontends/atbm8830.h @@ -22,6 +22,7 @@ #ifndef __ATBM8830_H__ #define __ATBM8830_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include <linux/i2c.h> @@ -60,8 +61,7 @@ struct atbm8830_config { u8 agc_hold_loop; }; -#if defined(CONFIG_DVB_ATBM8830) || \ - (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ATBM8830) extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h index 565dcf3..83fe9a6 100644 --- a/drivers/media/dvb-frontends/au8522.h +++ b/drivers/media/dvb-frontends/au8522.h @@ -22,6 +22,7 @@ #ifndef __AU8522_H__ #define __AU8522_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> enum au8522_if_freq { @@ -60,8 +61,7 @@ struct au8522_config { enum au8522_if_freq qam_if; }; -#if defined(CONFIG_DVB_AU8522) || \ - (defined(CONFIG_DVB_AU8522_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_AU8522_DTV) extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 5243ba6..2099f21 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -229,15 +229,11 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) /* Provide reasonable defaults for picture tuning values */ au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07); au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed); - state->brightness = 0xed - 128; au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79); - state->contrast = 0x79; au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); - state->saturation = 0x80; au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); - state->hue = 0x00; /* Other decoder registers */ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); @@ -489,75 +485,32 @@ static void set_audio_input(struct au8522_state *state, int aud_input) /* ----------------------------------------------------------------------- */ -static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int au8522_s_ctrl(struct v4l2_ctrl *ctrl) { - struct au8522_state *state = to_state(sd); + struct au8522_state *state = + container_of(ctrl->handler, struct au8522_state, hdl); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - state->brightness = ctrl->value; au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, - ctrl->value - 128); + ctrl->val - 128); break; case V4L2_CID_CONTRAST: - state->contrast = ctrl->value; au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, - ctrl->value); + ctrl->val); break; case V4L2_CID_SATURATION: - state->saturation = ctrl->value; au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, - ctrl->value); + ctrl->val); au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, - ctrl->value); + ctrl->val); break; case V4L2_CID_HUE: - state->hue = ctrl->value; au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, - ctrl->value >> 8); + ctrl->val >> 8); au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, - ctrl->value & 0xFF); - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - /* Not yet implemented */ - default: - return -EINVAL; - } - - return 0; -} - -static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct au8522_state *state = to_state(sd); - - /* Note that we are using values cached in the state structure instead - of reading the registers due to issues with i2c reads not working - properly/consistently yet on the HVR-950q */ - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = state->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = state->contrast; + ctrl->val & 0xFF); break; - case V4L2_CID_SATURATION: - ctrl->value = state->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = state->hue; - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - /* Not yet supported */ default: return -EINVAL; } @@ -583,7 +536,7 @@ static int au8522_g_register(struct v4l2_subdev *sd, } static int au8522_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct au8522_state *state = to_state(sd); @@ -616,26 +569,6 @@ static int au8522_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, - AU8522_TVDEC_CONTRAST_REG00BH_CVBS); - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0); - default: - break; - } - - qc->type = 0; - return -EINVAL; -} - static int au8522_reset(struct v4l2_subdev *sd, u32 val) { struct au8522_state *state = to_state(sd); @@ -712,20 +645,11 @@ static int au8522_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); } -static int au8522_log_status(struct v4l2_subdev *sd) -{ - /* FIXME: Add some status info here */ - return 0; -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops au8522_core_ops = { - .log_status = au8522_log_status, + .log_status = v4l2_ctrl_subdev_log_status, .g_chip_ident = au8522_g_chip_ident, - .g_ctrl = au8522_g_ctrl, - .s_ctrl = au8522_s_ctrl, - .queryctrl = au8522_queryctrl, .reset = au8522_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = au8522_g_register, @@ -753,12 +677,17 @@ static const struct v4l2_subdev_ops au8522_ops = { .video = &au8522_video_ops, }; +static const struct v4l2_ctrl_ops au8522_ctrl_ops = { + .s_ctrl = au8522_s_ctrl, +}; + /* ----------------------------------------------------------------------- */ static int au8522_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct au8522_state *state; + struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; int instance; struct au8522_config *demod_config; @@ -799,6 +728,27 @@ static int au8522_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &au8522_ops); + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 109); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, + AU8522_TVDEC_CONTRAST_REG00BH_CVBS); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_HUE, -32768, 32767, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(demod_config); + kfree(state); + return err; + } + state->c = client; state->vid_input = AU8522_COMPOSITE_CH1; state->aud_input = AU8522_AUDIO_NONE; @@ -815,6 +765,7 @@ static int au8522_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); au8522_release_state(to_state(sd)); return 0; } diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index 0529699..aa0f16d 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -29,6 +29,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <linux/i2c.h> #include "dvb_frontend.h" #include "au8522.h" @@ -65,10 +66,7 @@ struct au8522_state { int aud_input; u32 id; u32 rev; - u8 brightness; - u8 contrast; - u8 saturation; - s16 hue; + struct v4l2_ctrl_handler hdl; }; /* These are routines shared by both the VSB/QAM demodulator and the analog diff --git a/drivers/media/dvb-frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h index f154e1f..0b1a6c2 100644 --- a/drivers/media/dvb-frontends/cx22702.h +++ b/drivers/media/dvb-frontends/cx22702.h @@ -28,6 +28,7 @@ #ifndef CX22702_H #define CX22702_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct cx22702_config { @@ -40,8 +41,7 @@ struct cx22702_config { u8 output_mode; }; -#if defined(CONFIG_DVB_CX22702) || (defined(CONFIG_DVB_CX22702_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX22702) extern struct dvb_frontend *cx22702_attach( const struct cx22702_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h index 01eb7b9..782711b 100644 --- a/drivers/media/dvb-frontends/cx24113.h +++ b/drivers/media/dvb-frontends/cx24113.h @@ -22,6 +22,8 @@ #ifndef CX24113_H #define CX24113_H +#include <linux/kconfig.h> + struct dvb_frontend; struct cx24113_config { @@ -30,8 +32,7 @@ struct cx24113_config { u32 xtal_khz; }; -#if defined(CONFIG_DVB_TUNER_CX24113) || \ - (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_CX24113) extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, const struct cx24113_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h index 7d90ab9..2ec84fa 100644 --- a/drivers/media/dvb-frontends/cx24116.h +++ b/drivers/media/dvb-frontends/cx24116.h @@ -21,6 +21,7 @@ #ifndef CX24116_H #define CX24116_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct cx24116_config { @@ -40,8 +41,7 @@ struct cx24116_config { u16 i2c_wr_max; }; -#if defined(CONFIG_DVB_CX24116) || \ - (defined(CONFIG_DVB_CX24116_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX24116) extern struct dvb_frontend *cx24116_attach( const struct cx24116_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index 68c88ab..a771da3 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <asm/div64.h> #include "dvb_frontend.h" #include "cx24123.h" @@ -452,7 +453,8 @@ static u32 cx24123_int_log2(u32 a, u32 b) static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) { - u32 tmp, sample_rate, ratio, sample_gain; + u64 tmp; + u32 sample_rate, ratio, sample_gain; u8 pll_mult; /* check if symbol rate is within limits */ @@ -482,27 +484,11 @@ static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) sample_rate = pll_mult * XTAL; - /* - SYSSymbolRate[21:0] = (srate << 23) / sample_rate - - We have to use 32 bit unsigned arithmetic without precision loss. - The maximum srate is 45000000 or 0x02AEA540. This number has - only 6 clear bits on top, hence we can shift it left only 6 bits - at a time. Borrowed from cx24110.c - */ - - tmp = srate << 6; - ratio = tmp / sample_rate; - - tmp = (tmp % sample_rate) << 6; - ratio = (ratio << 6) + (tmp / sample_rate); - - tmp = (tmp % sample_rate) << 6; - ratio = (ratio << 6) + (tmp / sample_rate); - - tmp = (tmp % sample_rate) << 5; - ratio = (ratio << 5) + (tmp / sample_rate); + /* SYSSymbolRate[21:0] = (srate << 23) / sample_rate */ + tmp = ((u64)srate) << 23; + do_div(tmp, sample_rate); + ratio = (u32) tmp; cx24123_writereg(state, 0x01, pll_mult * 6); diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h index 51ae866..102e70d 100644 --- a/drivers/media/dvb-frontends/cx24123.h +++ b/drivers/media/dvb-frontends/cx24123.h @@ -21,6 +21,7 @@ #ifndef CX24123_H #define CX24123_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct cx24123_config { @@ -38,8 +39,7 @@ struct cx24123_config { void (*agc_callback) (struct dvb_frontend *); }; -#if defined(CONFIG_DVB_CX24123) || (defined(CONFIG_DVB_CX24123_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX24123) extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, struct i2c_adapter *i2c); extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *); diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h index 6acc21c..82b3d93 100644 --- a/drivers/media/dvb-frontends/cxd2820r.h +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -22,6 +22,7 @@ #ifndef CXD2820R_H #define CXD2820R_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #define CXD2820R_GPIO_D (0 << 0) /* disable */ @@ -65,8 +66,7 @@ struct cxd2820r_config { }; -#if defined(CONFIG_DVB_CXD2820R) || \ - (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CXD2820R) extern struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 9b658c1..7ca5c69 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -660,7 +660,8 @@ static const struct dvb_frontend_ops cxd2820r_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 }, .release = cxd2820r_release, diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index e82d82a..2ba130e 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -124,6 +124,23 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) buf[1] = ((if_ctl >> 8) & 0xff); buf[2] = ((if_ctl >> 0) & 0xff); + /* PLP filtering */ + if (c->stream_id > 255) { + dev_dbg(&priv->i2c->dev, "%s: Disable PLP filtering\n", __func__); + ret = cxd2820r_wr_reg(priv, 0x023ad , 0); + if (ret) + goto error; + } else { + dev_dbg(&priv->i2c->dev, "%s: Enable PLP filtering = %d\n", __func__, + c->stream_id); + ret = cxd2820r_wr_reg(priv, 0x023af , c->stream_id & 0xFF); + if (ret) + goto error; + ret = cxd2820r_wr_reg(priv, 0x023ad , 1); + if (ret) + goto error; + } + ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3); if (ret) goto error; diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index d9fe60b..3ee22ff 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -528,20 +528,19 @@ static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_ u16 PllCfg, i, v; HARD_RESET(state); - dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); - dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ + if (cfg->in_soc) + return; - if (!cfg->in_soc) { - /* adcClkOutRatio=8->7, release reset */ - dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); - if (cfg->clkoutdrive != 0) - dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) - | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); - else - dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) - | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); - } + dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ + /* adcClkOutRatio=8->7, release reset */ + dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); + if (cfg->clkoutdrive != 0) + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + else + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); /* Read Pll current config * */ PllCfg = dib0090_read_reg(state, 0x21); @@ -694,192 +693,174 @@ void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) EXPORT_SYMBOL(dib0090_dcc_freq); static const u16 bb_ramp_pwm_normal_socs[] = { - 550, /* max BB gain in 10th of dB */ - (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 550, /* max BB gain in 10th of dB */ + (1<<9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ 440, - (4 << 9) | 0, /* BB_RAMP3 = 26dB */ - (0 << 9) | 208, /* BB_RAMP4 */ - (4 << 9) | 208, /* BB_RAMP5 = 29dB */ - (0 << 9) | 440, /* BB_RAMP6 */ + (4 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (4 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ }; -static const u16 rf_ramp_pwm_cband_7090[] = { - 280, /* max RF gain in 10th of dB */ - 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 504, /* ramp_max = maximum X used on the ramp */ - (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ - (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ - (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ - (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ - (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ - (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ - (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ - (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ +static const u16 rf_ramp_pwm_cband_7090p[] = { + 280, /* max RF gain in 10th of dB */ + 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 504, /* ramp_max = maximum X used on the ramp */ + (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ + (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ + (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ + (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ + (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ + (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ + (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ + (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ }; -static const uint16_t rf_ramp_pwm_cband_7090e_sensitivity[] = { - 186, - 40, - 746, - (10 << 10) | 345, - (0 << 10) | 746, - (0 << 10) | 0, - (0 << 10) | 0, - (28 << 10) | 200, - (0 << 10) | 345, - (20 << 10) | 0, - (0 << 10) | 200, +static const u16 rf_ramp_pwm_cband_7090e_sensitivity[] = { + 186, /* max RF gain in 10th of dB */ + 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 746, /* ramp_max = maximum X used on the ramp */ + (10 << 10) | 345, /* RF_RAMP5, LNA 1 = 10dB */ + (0 << 10) | 746, /* RF_RAMP6, LNA 1 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ + (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ + (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ + (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ + (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ }; -static const uint16_t rf_ramp_pwm_cband_7090e_aci[] = { - 86, - 40, - 345, - (0 << 10) | 0, - (0 << 10) | 0, - (0 << 10) | 0, - (0 << 10) | 0, - (28 << 10) | 200, - (0 << 10) | 345, - (20 << 10) | 0, - (0 << 10) | 200, +static const u16 rf_ramp_pwm_cband_7090e_aci[] = { + 86, /* max RF gain in 10th of dB */ + 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 345, /* ramp_max = maximum X used on the ramp */ + (0 << 10) | 0, /* RF_RAMP5, LNA 1 = 8dB */ /* 7.47 dB */ + (0 << 10) | 0, /* RF_RAMP6, LNA 1 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ + (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ + (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ + (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ + (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ }; static const u16 rf_ramp_pwm_cband_8090[] = { - 345, /* max RF gain in 10th of dB */ - 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 1000, /* ramp_max = maximum X used on the ramp */ - (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ - (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ - (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ - (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ - (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ - (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ - (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ - (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ + 345, /* max RF gain in 10th of dB */ + 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1000, /* ramp_max = maximum X used on the ramp */ + (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ + (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ + (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ + (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ + (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ + (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ + (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf_7090[] = { - 407, /* max RF gain in 10th of dB */ - 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 529, /* ramp_max = maximum X used on the ramp */ - (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ - (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ - (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ - (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ - (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ - (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ - (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ - (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ + 407, /* max RF gain in 10th of dB */ + 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 529, /* ramp_max = maximum X used on the ramp */ + (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ + (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ + (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ + (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ + (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf_8090[] = { - 388, /* max RF gain in 10th of dB */ - 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 1008, /* ramp_max = maximum X used on the ramp */ - (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ - (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ - (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ - (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ - (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ - (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ - (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ - (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ + 388, /* max RF gain in 10th of dB */ + 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1008, /* ramp_max = maximum X used on the ramp */ + (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ + (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ + (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ + (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ }; -static const u16 rf_ramp_pwm_cband[] = { - 0, /* max RF gain in 10th of dB */ - 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ - 0, /* ramp_max = maximum X used on the ramp */ - (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */ - (0 << 10) | 0, /* 0x2d, LNA 1 */ - (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */ - (0 << 10) | 0, /* 0x2f, LNA 2 */ - (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */ - (0 << 10) | 0, /* 0x31, LNA 3 */ - (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ - (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ -}; - -static const u16 rf_ramp_vhf[] = { - 412, /* max RF gain in 10th of dB */ - 132, 307, 127, /* LNA1, 13.2dB */ - 105, 412, 255, /* LNA2, 10.5dB */ - 50, 50, 127, /* LNA3, 5dB */ - 125, 175, 127, /* LNA4, 12.5dB */ - 0, 0, 127, /* CBAND, 0dB */ -}; - -static const u16 rf_ramp_uhf[] = { - 412, /* max RF gain in 10th of dB */ - 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */ - 105, 412, 255, /* LNA2 : 10.5 dB */ - 50, 50, 127, /* LNA3 : 5.0 dB */ - 125, 175, 127, /* LNA4 : 12.5 dB */ - 0, 0, 127, /* CBAND : 0.0 dB */ +/* GENERAL PWM ramp definition for all other Krosus */ +static const u16 bb_ramp_pwm_normal[] = { + 500, /* max BB gain in 10th of dB */ + 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 400, + (2 << 9) | 0, /* BB_RAMP3 = 21dB */ + (0 << 9) | 168, /* BB_RAMP4 */ + (2 << 9) | 168, /* BB_RAMP5 = 29dB */ + (0 << 9) | 400, /* BB_RAMP6 */ }; -static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */ -{ - 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */ - 84, 314, 127, /* LNA1 */ - 80, 230, 255, /* LNA2 */ - 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */ - 70, 70, 127, /* LNA4 */ - 0, 0, 127, /* CBAND */ +static const u16 bb_ramp_pwm_boost[] = { + 550, /* max BB gain in 10th of dB */ + 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 440, + (2 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (2 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ }; -static const u16 rf_ramp_cband[] = { - 332, /* max RF gain in 10th of dB */ - 132, 252, 127, /* LNA1, dB */ - 80, 332, 255, /* LNA2, dB */ - 0, 0, 127, /* LNA3, dB */ - 0, 0, 127, /* LNA4, dB */ - 120, 120, 127, /* LT1 CBAND */ +static const u16 rf_ramp_pwm_cband[] = { + 314, /* max RF gain in 10th of dB */ + 33, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1023, /* ramp_max = maximum X used on the ramp */ + (8 << 10) | 743, /* RF_RAMP3, LNA 1 = 0dB */ + (0 << 10) | 1023, /* RF_RAMP4, LNA 1 */ + (15 << 10) | 469, /* RF_RAMP5, LNA 2 = 0dB */ + (0 << 10) | 742, /* RF_RAMP6, LNA 2 */ + (9 << 10) | 234, /* RF_RAMP7, LNA 3 = 0dB */ + (0 << 10) | 468, /* RF_RAMP8, LNA 3 */ + (9 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ + (0 << 10) | 233, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_vhf[] = { - 404, /* max RF gain in 10th of dB */ - 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ - 1011, /* ramp_max = maximum X used on the ramp */ - (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ - (0 << 10) | 756, /* 0x2d, LNA 1 */ - (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ - (0 << 10) | 1011, /* 0x2f, LNA 2 */ - (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */ - (0 << 10) | 417, /* 0x31, LNA 3 */ - (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */ - (0 << 10) | 290, /* GAIN_4_2, LNA 4 */ + 398, /* max RF gain in 10th of dB */ + 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 954, /* ramp_max = maximum X used on the ramp */ + (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ + (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ + (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ + (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ + (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ + (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ + (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf[] = { - 404, /* max RF gain in 10th of dB */ - 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ - 1011, /* ramp_max = maximum X used on the ramp */ - (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ - (0 << 10) | 756, /* 0x2d, LNA 1 */ - (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ - (0 << 10) | 1011, /* 0x2f, LNA 2 */ - (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */ - (0 << 10) | 127, /* 0x31, LNA 3 */ - (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */ - (0 << 10) | 417, /* GAIN_4_2, LNA 4 */ + 398, /* max RF gain in 10th of dB */ + 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 954, /* ramp_max = maximum X used on the ramp */ + (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ + (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ + (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ + (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ + (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ + (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ + (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ }; -static const u16 bb_ramp_boost[] = { - 550, /* max BB gain in 10th of dB */ - 260, 260, 26, /* BB1, 26dB */ - 290, 550, 29, /* BB2, 29dB */ -}; - -static const u16 bb_ramp_pwm_normal[] = { - 500, /* max RF gain in 10th of dB */ - 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */ - 400, - (2 << 9) | 0, /* 0x35 = 21dB */ - (0 << 9) | 168, /* 0x36 */ - (2 << 9) | 168, /* 0x37 = 29dB */ - (0 << 9) | 400, /* 0x38 */ +static const u16 rf_ramp_pwm_sband[] = { + 253, /* max RF gain in 10th of dB */ + 38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 961, + (4 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.1dB */ + (0 << 10) | 508, /* RF_RAMP4, LNA 1 */ + (9 << 10) | 508, /* RF_RAMP5, LNA 2 = 11.2dB */ + (0 << 10) | 961, /* RF_RAMP6, LNA 2 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 3 = 0dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 3 */ + (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ + (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ }; struct slope { @@ -1089,70 +1070,69 @@ static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg) void dib0090_pwm_gain_reset(struct dvb_frontend *fe) { struct dib0090_state *state = fe->tuner_priv; - /* reset the AGC */ + u16 *bb_ramp = (u16 *)&bb_ramp_pwm_normal; /* default baseband config */ + u16 *rf_ramp = NULL; + u8 en_pwm_rf_mux = 1; + /* reset the AGC */ if (state->config->use_pwm_agc) { -#ifdef CONFIG_BAND_SBAND - if (state->current_band == BAND_SBAND) { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost); - } else -#endif -#ifdef CONFIG_BAND_CBAND if (state->current_band == BAND_CBAND) { if (state->identity.in_soc) { - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) - dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090); - else if (state->identity.version == SOC_7090_P1G_11R1 - || state->identity.version == SOC_7090_P1G_21R1) { + rf_ramp = (u16 *)&rf_ramp_pwm_cband_8090; + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) { if (state->config->is_dib7090e) { if (state->rf_ramp == NULL) - dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090e_sensitivity); + rf_ramp = (u16 *)&rf_ramp_pwm_cband_7090e_sensitivity; else - dib0090_set_rframp_pwm(state, state->rf_ramp); + rf_ramp = (u16 *)state->rf_ramp; } else - dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090); + rf_ramp = (u16 *)&rf_ramp_pwm_cband_7090p; } - } else { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); - } + } else + rf_ramp = (u16 *)&rf_ramp_pwm_cband; } else -#endif -#ifdef CONFIG_BAND_VHF - if (state->current_band == BAND_VHF) { - if (state->identity.in_soc) { - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); - } else { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + + if (state->current_band == BAND_VHF) { + if (state->identity.in_soc) { + bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; + /* rf_ramp = &rf_ramp_pwm_vhf_socs; */ /* TODO */ + } else + rf_ramp = (u16 *)&rf_ramp_pwm_vhf; + } else if (state->current_band == BAND_UHF) { + if (state->identity.in_soc) { + bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + rf_ramp = (u16 *)&rf_ramp_pwm_uhf_8090; + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + rf_ramp = (u16 *)&rf_ramp_pwm_uhf_7090; + } else + rf_ramp = (u16 *)&rf_ramp_pwm_uhf; } + if (rf_ramp) + dib0090_set_rframp_pwm(state, rf_ramp); + dib0090_set_bbramp_pwm(state, bb_ramp); + + /* activate the ramp generator using PWM control */ + dprintk("ramp RF gain = %d BAND = %s version = %d", state->rf_ramp[0], (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", state->identity.version & 0x1f); + + if ((state->rf_ramp[0] == 0) || (state->current_band == BAND_CBAND && (state->identity.version & 0x1f) <= P1D_E_F)) { + dprintk("DE-Engage mux for direct gain reg control"); + en_pwm_rf_mux = 0; } else -#endif - { - if (state->identity.in_soc) { - if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) - dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090); - else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) - dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); - } else { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); - } - } + dprintk("Engage mux for PWM control"); - if (state->rf_ramp[0] != 0) - dib0090_write_reg(state, 0x32, (3 << 11)); - else - dib0090_write_reg(state, 0x32, (0 << 11)); + dib0090_write_reg(state, 0x32, (en_pwm_rf_mux << 12) | (en_pwm_rf_mux << 11)); - dib0090_write_reg(state, 0x04, 0x03); - dib0090_write_reg(state, 0x39, (1 << 10)); + /* Set fast servo cutoff to start AGC; 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast*/ + if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + dib0090_write_reg(state, 0x04, 3); + else + dib0090_write_reg(state, 0x04, 1); + dib0090_write_reg(state, 0x39, (1 << 10)); /* 0 gain by default */ } } - EXPORT_SYMBOL(dib0090_pwm_gain_reset); void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) @@ -1193,22 +1173,22 @@ int dib0090_gain_control(struct dvb_frontend *fe) #endif #ifdef CONFIG_BAND_VHF if (state->current_band == BAND_VHF && !state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_vhf); - dib0090_set_bbramp(state, bb_ramp_boost); + dib0090_set_rframp(state, rf_ramp_pwm_vhf); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); } else #endif #ifdef CONFIG_BAND_CBAND if (state->current_band == BAND_CBAND && !state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_cband); - dib0090_set_bbramp(state, bb_ramp_boost); + dib0090_set_rframp(state, rf_ramp_pwm_cband); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); } else #endif if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_cband_broadmatching); - dib0090_set_bbramp(state, bb_ramp_boost); + dib0090_set_rframp(state, rf_ramp_pwm_cband_7090p); + dib0090_set_bbramp(state, bb_ramp_pwm_normal_socs); } else { - dib0090_set_rframp(state, rf_ramp_uhf); - dib0090_set_bbramp(state, bb_ramp_boost); + dib0090_set_rframp(state, rf_ramp_pwm_uhf); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); } dib0090_write_reg(state, 0x32, 0); @@ -1553,14 +1533,16 @@ static void dib0090_set_EFUSE(struct dib0090_state *state) if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) c = 32; + else + c += 14; if ((h >= HR_MAX) || (h <= HR_MIN)) h = 34; if ((n >= POLY_MAX) || (n <= POLY_MIN)) n = 3; - dib0090_write_reg(state, 0x13, (h << 10)) ; - e2 = (n<<11) | ((h>>2)<<6) | (c); - dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */ + dib0090_write_reg(state, 0x13, (h << 10)); + e2 = (n << 11) | ((h >> 2)<<6) | c; + dib0090_write_reg(state, 0x2, e2); /* Load the BB_2 */ } } diff --git a/drivers/media/dvb-frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h index d75ffad..129d142 100644 --- a/drivers/media/dvb-frontends/dib3000mc.h +++ b/drivers/media/dvb-frontends/dib3000mc.h @@ -13,6 +13,8 @@ #ifndef DIB3000MC_H #define DIB3000MC_H +#include <linux/kconfig.h> + #include "dibx000_common.h" struct dib3000mc_config { @@ -39,8 +41,7 @@ struct dib3000mc_config { #define DEFAULT_DIB3000MC_I2C_ADDRESS 16 #define DEFAULT_DIB3000P_I2C_ADDRESS 24 -#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB3000MC) extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg); diff --git a/drivers/media/dvb-frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h index 81fcf22..b585413 100644 --- a/drivers/media/dvb-frontends/dib7000m.h +++ b/drivers/media/dvb-frontends/dib7000m.h @@ -1,6 +1,8 @@ #ifndef DIB7000M_H #define DIB7000M_H +#include <linux/kconfig.h> + #include "dibx000_common.h" struct dib7000m_config { @@ -38,8 +40,7 @@ struct dib7000m_config { #define DEFAULT_DIB7000M_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB7000M) || (defined(CONFIG_DVB_DIB7000M_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB7000M) extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg); diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 3e1eefa..effb87f 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -429,6 +429,13 @@ int dib7000p_get_agc_values(struct dvb_frontend *fe, } EXPORT_SYMBOL(dib7000p_get_agc_values); +int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) +{ + struct dib7000p_state *state = fe->demodulator_priv; + return dib7000p_write_word(state, 108, v); +} +EXPORT_SYMBOL(dib7000p_set_agc1_min); + static void dib7000p_reset_pll(struct dib7000p_state *state) { struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; @@ -821,6 +828,7 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod) u8 agc_split; u16 reg; u32 upd_demod_gain_period = 0x1000; + s32 frequency_offset = 0; switch (state->agc_state) { case 0: @@ -841,7 +849,14 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod) if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) return -1; - dib7000p_set_dds(state, 0); + if (demod->ops.tuner_ops.get_frequency) { + u32 frequency_tuner; + + demod->ops.tuner_ops.get_frequency(demod, &frequency_tuner); + frequency_offset = (s32)frequency_tuner / 1000 - ch->frequency / 1000; + } + + dib7000p_set_dds(state, frequency_offset); ret = 7; (*agc_state)++; break; diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h index b61b03a..d08cdff 100644 --- a/drivers/media/dvb-frontends/dib7000p.h +++ b/drivers/media/dvb-frontends/dib7000p.h @@ -1,6 +1,8 @@ #ifndef DIB7000P_H #define DIB7000P_H +#include <linux/kconfig.h> + #include "dibx000_common.h" struct dib7000p_config { @@ -44,8 +46,7 @@ struct dib7000p_config { #define DEFAULT_DIB7000P_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB7000P) extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); @@ -62,6 +63,7 @@ extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); extern int dib7090_slave_reset(struct dvb_frontend *fe); extern int dib7000p_get_agc_values(struct dvb_frontend *fe, u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); +extern int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v); #else static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { @@ -153,6 +155,12 @@ static inline int dib7000p_get_agc_values(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } + +static inline int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif #endif diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 1f3bcb5..a54182d 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -23,8 +23,8 @@ #define LAYER_B 2 #define LAYER_C 3 -#define FE_CALLBACK_TIME_NEVER 0xffffffff #define MAX_NUMBER_OF_FRONTENDS 6 +/* #define DIB8000_AGC_FREEZE */ static int debug; module_param(debug, int, 0644); @@ -32,8 +32,6 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) -#define FE_STATUS_TUNE_FAILED 0 - struct i2c_device { struct i2c_adapter *adap; u8 addr; @@ -42,6 +40,23 @@ struct i2c_device { struct mutex *i2c_buffer_lock; }; +enum param_loop_step { + LOOP_TUNE_1, + LOOP_TUNE_2 +}; + +enum dib8000_autosearch_step { + AS_START = 0, + AS_SEARCHING_FFT, + AS_SEARCHING_GUARD, + AS_DONE = 100, +}; + +enum timeout_mode { + SYMBOL_DEPENDENT_OFF = 0, + SYMBOL_DEPENDENT_ON, +}; + struct dib8000_state { struct dib8000_config cfg; @@ -72,7 +87,7 @@ struct dib8000_state { u16 revision; u8 isdbt_cfg_loaded; enum frontend_tune_state tune_state; - u32 status; + s32 status; struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; @@ -85,6 +100,30 @@ struct dib8000_state { u16 tuner_enable; struct i2c_adapter dib8096p_tuner_adap; + u16 current_demod_bw; + + u16 seg_mask; + u16 seg_diff_mask; + u16 mode; + u8 layer_b_nb_seg; + u8 layer_c_nb_seg; + + u8 channel_parameters_set; + u16 autosearch_state; + u16 found_nfft; + u16 found_guard; + u8 subchannel; + u8 symbol_duration; + u32 timeout; + u8 longest_intlv_layer; + u16 output_mode; + +#ifdef DIB8000_AGC_FREEZE + u16 agc1_max; + u16 agc1_min; + u16 agc2_max; + u16 agc2_min; +#endif }; enum dib8000_power_mode { @@ -338,9 +377,9 @@ static void dib8000_set_acquisition_mode(struct dib8000_state *state) static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) { struct dib8000_state *state = fe->demodulator_priv; - u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ + state->output_mode = mode; outreg = 0; fifo_threshold = 1792; smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); @@ -399,8 +438,9 @@ static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) { struct dib8000_state *state = fe->demodulator_priv; - u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; + u16 tmp, sync_wait = dib8000_read_word(state, 273) & 0xfff0; + dprintk("set diversity input to %i", onoff); if (!state->differential_constellation) { dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 @@ -424,6 +464,13 @@ static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) dib8000_write_word(state, 271, 1); break; } + + if (state->revision == 0x8002) { + tmp = dib8000_read_word(state, 903); + dib8000_write_word(state, 903, tmp & ~(1 << 3)); + msleep(30); + dib8000_write_word(state, 903, tmp | (1 << 3)); + } return 0; } @@ -468,27 +515,6 @@ static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_pow dib8000_write_word(state, 1280, reg_1280); } -static int dib8000_init_sdram(struct dib8000_state *state) -{ - u16 reg = 0; - dprintk("Init sdram"); - - reg = dib8000_read_word(state, 274)&0xfff0; - /* P_dintlv_delay_ram = 7 because of MobileSdram */ - dib8000_write_word(state, 274, reg | 0x7); - - dib8000_write_word(state, 1803, (7<<2)); - - reg = dib8000_read_word(state, 1280); - /* force restart P_restart_sdram */ - dib8000_write_word(state, 1280, reg | (1<<2)); - - /* release restart P_restart_sdram */ - dib8000_write_word(state, 1280, reg); - - return 0; -} - static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) { int ret = 0; @@ -584,18 +610,23 @@ static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) static int dib8000_sad_calib(struct dib8000_state *state) { + u8 sad_sel = 3; + if (state->revision == 0x8090) { - dprintk("%s: the sad calibration is not needed for the dib8096P", - __func__); - return 0; - } - /* internal */ - dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); - dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 + dib8000_write_word(state, 922, (sad_sel << 2)); + dib8000_write_word(state, 923, 2048); + + dib8000_write_word(state, 922, (sad_sel << 2) | 0x1); + dib8000_write_word(state, 922, (sad_sel << 2)); + } else { + /* internal */ + dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); + dib8000_write_word(state, 924, 776); - /* do the calibration */ - dib8000_write_word(state, 923, (1 << 0)); - dib8000_write_word(state, 923, (0 << 0)); + /* do the calibration */ + dib8000_write_word(state, 923, (1 << 0)); + dib8000_write_word(state, 923, (0 << 0)); + } msleep(1); return 0; @@ -609,8 +640,8 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) state->wbd_ref = value; return dib8000_write_word(state, 106, value); } - EXPORT_SYMBOL(dib8000_set_wbd_ref); + static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) { dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); @@ -685,20 +716,23 @@ static void dib8000_reset_pll(struct dib8000_state *state) } int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll) + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) { struct dib8000_state *state = fe->demodulator_priv; u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856); - u8 loopdiv, prediv; + u8 loopdiv, prediv, oldprediv = state->cfg.pll->pll_prediv ; u32 internal, xtal; /* get back old values */ prediv = reg_1856 & 0x3f; loopdiv = (reg_1856 >> 6) & 0x3f; - if ((pll != NULL) && (pll->pll_prediv != prediv || - pll->pll_ratio != loopdiv)) { - dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); + if ((pll == NULL) || (pll->pll_prediv == prediv && + pll->pll_ratio == loopdiv)) + return -EINVAL; + + dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); + if (state->revision == 0x8090) { reg_1856 &= 0xf000; reg_1857 = dib8000_read_word(state, 1857); /* disable PLL */ @@ -729,10 +763,33 @@ int dib8000_update_pll(struct dvb_frontend *fe, reg_1856 = dib8000_read_word(state, 1856); dprintk("PLL Updated with prediv = %d and loopdiv = %d", reg_1856&0x3f, (reg_1856>>6)&0x3f); + } else { + if (bw != state->current_demod_bw) { + /** Bandwidth change => force PLL update **/ + dprintk("PLL: Bandwidth Change %d MHz -> %d MHz (prediv: %d->%d)", state->current_demod_bw / 1000, bw / 1000, oldprediv, state->cfg.pll->pll_prediv); + + if (state->cfg.pll->pll_prediv != oldprediv) { + /** Full PLL change only if prediv is changed **/ + + /** full update => bypass and reconfigure **/ + dprintk("PLL: New Setting for %d MHz Bandwidth (prediv: %d, ratio: %d)", bw/1000, state->cfg.pll->pll_prediv, state->cfg.pll->pll_ratio); + dib8000_write_word(state, 902, dib8000_read_word(state, 902) | (1<<3)); /* bypass PLL */ + dib8000_reset_pll(state); + dib8000_write_word(state, 898, 0x0004); /* sad */ + } else + ratio = state->cfg.pll->pll_ratio; - return 0; - } - return -EINVAL; + state->current_demod_bw = bw; + } + + if (ratio != 0) { + /** ratio update => only change ratio **/ + dprintk("PLL: Update ratio (prediv: %d, ratio: %d)", state->cfg.pll->pll_prediv, ratio); + dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */ + } +} + + return 0; } EXPORT_SYMBOL(dib8000_update_pll); @@ -928,7 +985,7 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_power_mode(state, DIB8000_POWER_ALL); /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ - dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); + dib8000_set_adc_state(state, DIBX000_ADC_OFF); /* restart all parts */ dib8000_write_word(state, 770, 0xffff); @@ -992,12 +1049,11 @@ static int dib8000_reset(struct dvb_frontend *fe) l = *n++; } } - if (state->revision != 0x8090) - dib8000_write_word(state, 903, (0 << 4) | 2); + state->isdbt_cfg_loaded = 0; //div_cfg override for special configs - if (state->cfg.div_cfg != 0) + if ((state->revision != 8090) && (state->cfg.div_cfg != 0)) dib8000_write_word(state, 903, state->cfg.div_cfg); /* unforce divstr regardless whether i2c enumeration was done or not */ @@ -1006,10 +1062,12 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_bandwidth(fe, 6000); dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); - if (state->revision != 0x8090) { - dib8000_sad_calib(state); + dib8000_sad_calib(state); + if (state->revision != 0x8090) dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); - } + + /* ber_rs_len = 3 */ + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); @@ -1441,6 +1499,7 @@ static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) u8 prefer_mpeg_mux_use = 1; int ret = 0; + state->output_mode = mode; dib8096p_host_bus_drive(state, 1); fifo_threshold = 1792; @@ -1879,782 +1938,643 @@ static const u16 adc_target_16dB[11] = { }; static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; -static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 max_constellation) { - u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; - u8 guard, crate, constellation, timeI; - u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled - const s16 *ncoeff = NULL, *ana_fe; - u16 tmcc_pow = 0; - u16 coff_pow = 0x2800; - u16 init_prbs = 0xfff; - u16 ana_gain = 0; - - if (state->revision == 0x8090) - dib8000_init_sdram(state); + u8 cr, constellation, time_intlv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; - if (state->ber_monitored_layer != LAYER_ALL) - dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); - else - dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); - - i = dib8000_read_word(state, 26) & 1; // P_dds_invspec - dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i); - - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - //compute new dds_freq for the seg and adjust prbs - int seg_offset = - state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - - (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - - (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); - int clk = state->cfg.pll->internal; - u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) - int dds_offset = seg_offset * segtodds; - int new_dds, sub_channel; - if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - dds_offset -= (int)(segtodds / 2); - - if (state->cfg.pll->ifreq == 0) { - if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) { - dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); - new_dds = dds_offset; - } else - new_dds = dds_offset; - - // We shift tuning frequency if the wanted segment is : - // - the segment of center frequency with an odd total number of segments - // - the segment to the left of center frequency with an even total number of segments - // - the segment to the right of center frequency with an even total number of segments - if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) - && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) - && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) - && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == - ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) - || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2))) - || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == - ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) - )) { - new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) - } - } else { - if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) - new_dds = state->cfg.pll->ifreq - dds_offset; - else - new_dds = state->cfg.pll->ifreq + dds_offset; - } - dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); - dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); - if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) - sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; - else - sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; - sub_channel -= 6; - - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K - || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { - dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 - dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 - } else { - dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 - dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 - } - - switch (state->fe[0]->dtv_property_cache.transmission_mode) { - case TRANSMISSION_MODE_2K: - switch (sub_channel) { - case -6: - init_prbs = 0x0; - break; // 41, 0, 1 - case -5: - init_prbs = 0x423; - break; // 02~04 - case -4: - init_prbs = 0x9; - break; // 05~07 - case -3: - init_prbs = 0x5C7; - break; // 08~10 - case -2: - init_prbs = 0x7A6; - break; // 11~13 - case -1: - init_prbs = 0x3D8; - break; // 14~16 - case 0: - init_prbs = 0x527; - break; // 17~19 - case 1: - init_prbs = 0x7FF; - break; // 20~22 - case 2: - init_prbs = 0x79B; - break; // 23~25 - case 3: - init_prbs = 0x3D6; - break; // 26~28 - case 4: - init_prbs = 0x3A2; - break; // 29~31 - case 5: - init_prbs = 0x53B; - break; // 32~34 - case 6: - init_prbs = 0x2F4; - break; // 35~37 - default: - case 7: - init_prbs = 0x213; - break; // 38~40 - } - break; - - case TRANSMISSION_MODE_4K: - switch (sub_channel) { - case -6: - init_prbs = 0x0; - break; // 41, 0, 1 - case -5: - init_prbs = 0x208; - break; // 02~04 - case -4: - init_prbs = 0xC3; - break; // 05~07 - case -3: - init_prbs = 0x7B9; - break; // 08~10 - case -2: - init_prbs = 0x423; - break; // 11~13 - case -1: - init_prbs = 0x5C7; - break; // 14~16 - case 0: - init_prbs = 0x3D8; - break; // 17~19 - case 1: - init_prbs = 0x7FF; - break; // 20~22 - case 2: - init_prbs = 0x3D6; - break; // 23~25 - case 3: - init_prbs = 0x53B; - break; // 26~28 - case 4: - init_prbs = 0x213; - break; // 29~31 - case 5: - init_prbs = 0x29; - break; // 32~34 - case 6: - init_prbs = 0xD0; - break; // 35~37 - default: - case 7: - init_prbs = 0x48E; - break; // 38~40 - } - break; - - default: - case TRANSMISSION_MODE_8K: - switch (sub_channel) { - case -6: - init_prbs = 0x0; - break; // 41, 0, 1 - case -5: - init_prbs = 0x740; - break; // 02~04 - case -4: - init_prbs = 0x069; - break; // 05~07 - case -3: - init_prbs = 0x7DD; - break; // 08~10 - case -2: - init_prbs = 0x208; - break; // 11~13 - case -1: - init_prbs = 0x7B9; - break; // 14~16 - case 0: - init_prbs = 0x5C7; - break; // 17~19 - case 1: - init_prbs = 0x7FF; - break; // 20~22 - case 2: - init_prbs = 0x53B; - break; // 23~25 - case 3: - init_prbs = 0x29; - break; // 26~28 - case 4: - init_prbs = 0x48E; - break; // 29~31 - case 5: - init_prbs = 0x4C4; - break; // 32~34 - case 6: - init_prbs = 0x367; - break; // 33~37 - default: - case 7: - init_prbs = 0x684; - break; // 38~40 - } - break; - } - } else { - dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); - dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); - dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); - } - /*P_mode == ?? */ - dib8000_write_word(state, 10, (seq << 4)); - // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); - - switch (state->fe[0]->dtv_property_cache.guard_interval) { - case GUARD_INTERVAL_1_32: - guard = 0; - break; - case GUARD_INTERVAL_1_16: - guard = 1; - break; - case GUARD_INTERVAL_1_8: - guard = 2; - break; - case GUARD_INTERVAL_1_4: - default: - guard = 3; - break; - } - - dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 - - max_constellation = DQPSK; - for (i = 0; i < 3; i++) { - switch (state->fe[0]->dtv_property_cache.layer[i].modulation) { - case DQPSK: + switch (c->layer[layer_index].modulation) { + case DQPSK: constellation = 0; break; - case QPSK: + case QPSK: constellation = 1; break; - case QAM_16: + case QAM_16: constellation = 2; break; - case QAM_64: - default: + case QAM_64: + default: constellation = 3; break; - } + } - switch (state->fe[0]->dtv_property_cache.layer[i].fec) { - case FEC_1_2: - crate = 1; + switch (c->layer[layer_index].fec) { + case FEC_1_2: + cr = 1; break; - case FEC_2_3: - crate = 2; + case FEC_2_3: + cr = 2; break; - case FEC_3_4: - crate = 3; + case FEC_3_4: + cr = 3; break; - case FEC_5_6: - crate = 5; + case FEC_5_6: + cr = 5; break; - case FEC_7_8: - default: - crate = 7; + case FEC_7_8: + default: + cr = 7; break; - } + } - if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) && - ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) || - (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)) - ) - timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving; - else - timeI = 0; - dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) | - (crate << 3) | timeI); - if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { - switch (max_constellation) { - case DQPSK: - case QPSK: - if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 || - state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) - max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; + if ((c->layer[layer_index].interleaving > 0) && ((c->layer[layer_index].interleaving <= 3) || (c->layer[layer_index].interleaving == 4 && c->isdbt_sb_mode == 1))) + time_intlv = c->layer[layer_index].interleaving; + else + time_intlv = 0; + + dib8000_write_word(state, 2 + layer_index, (constellation << 10) | ((c->layer[layer_index].segment_count & 0xf) << 6) | (cr << 3) | time_intlv); + if (c->layer[layer_index].segment_count > 0) { + switch (max_constellation) { + case DQPSK: + case QPSK: + if (c->layer[layer_index].modulation == QAM_16 || c->layer[layer_index].modulation == QAM_64) + max_constellation = c->layer[layer_index].modulation; break; - case QAM_16: - if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) - max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; + case QAM_16: + if (c->layer[layer_index].modulation == QAM_64) + max_constellation = c->layer[layer_index].modulation; break; - } } } - mode = fft_to_mode(state); + return max_constellation; +} - //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ +static const u16 adp_Q64[4] = {0x0148, 0xfff0, 0x00a4, 0xfff8}; /* P_adp_regul_cnt 0.04, P_adp_noise_cnt -0.002, P_adp_regul_ext 0.02, P_adp_noise_ext -0.001 */ +static const u16 adp_Q16[4] = {0x023d, 0xffdf, 0x00a4, 0xfff0}; /* P_adp_regul_cnt 0.07, P_adp_noise_cnt -0.004, P_adp_regul_ext 0.02, P_adp_noise_ext -0.002 */ +static const u16 adp_Qdefault[4] = {0x099a, 0xffae, 0x0333, 0xfff8}; /* P_adp_regul_cnt 0.3, P_adp_noise_cnt -0.01, P_adp_regul_ext 0.1, P_adp_noise_ext -0.002 */ +static u16 dib8000_adp_fine_tune(struct dib8000_state *state, u16 max_constellation) +{ + u16 i, ana_gain = 0; + const u16 *adp; - dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | - ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache. - isdbt_sb_mode & 1) << 4)); + /* channel estimation fine configuration */ + switch (max_constellation) { + case QAM_64: + ana_gain = 0x7; + adp = &adp_Q64[0]; + break; + case QAM_16: + ana_gain = 0x7; + adp = &adp_Q16[0]; + break; + default: + ana_gain = 0; + adp = &adp_Qdefault[0]; + break; + } - dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval); + for (i = 0; i < 4; i++) + dib8000_write_word(state, 215 + i, adp[i]); - /* signal optimization parameter */ + return ana_gain; +} - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { - seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; - for (i = 1; i < 3; i++) - nbseg_diff += - (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; - for (i = 0; i < nbseg_diff; i++) - seg_diff_mask |= 1 << permu_seg[i + 1]; - } else { - for (i = 0; i < 3; i++) - nbseg_diff += - (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; - for (i = 0; i < nbseg_diff; i++) - seg_diff_mask |= 1 << permu_seg[i]; +static void dib8000_update_ana_gain(struct dib8000_state *state, u16 ana_gain) +{ + u16 i; + + dib8000_write_word(state, 116, ana_gain); + + /* update ADC target depending on ana_gain */ + if (ana_gain) { /* set -16dB ADC target for ana_gain=-1 */ + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i]); + } else { /* set -22dB ADC target for ana_gain=0 */ + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); } - dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); +} - state->differential_constellation = (seg_diff_mask != 0); - if (state->revision != 0x8090) - dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); - else - dib8096p_set_diversity_in(state->fe[0], state->diversity_onoff); +static void dib8000_load_ana_fe_coefs(struct dib8000_state *state, const s16 *ana_fe) +{ + u16 mode = 0; - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) - seg_mask13 = 0x00E0; - else // 1-segment - seg_mask13 = 0x0040; - } else - seg_mask13 = 0x1fff; + if (state->isdbt_cfg_loaded == 0) + for (mode = 0; mode < 24; mode++) + dib8000_write_word(state, 117 + mode, ana_fe[mode]); +} - // WRITE: Mode & Diff mask - dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); +static const u16 lut_prbs_2k[14] = { + 0, 0x423, 0x009, 0x5C7, 0x7A6, 0x3D8, 0x527, 0x7FF, 0x79B, 0x3D6, 0x3A2, 0x53B, 0x2F4, 0x213 +}; +static const u16 lut_prbs_4k[14] = { + 0, 0x208, 0x0C3, 0x7B9, 0x423, 0x5C7, 0x3D8, 0x7FF, 0x3D6, 0x53B, 0x213, 0x029, 0x0D0, 0x48E +}; +static const u16 lut_prbs_8k[14] = { + 0, 0x740, 0x069, 0x7DD, 0x208, 0x7B9, 0x5C7, 0x7FF, 0x53B, 0x029, 0x48E, 0x4C4, 0x367, 0x684 +}; - if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode)) - dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); - else - dib8000_write_word(state, 268, (2 << 9) | 39); //init value +static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel) +{ + int sub_channel_prbs_group = 0; - // ---- SMALL ---- - // P_small_seg_diff - dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 + sub_channel_prbs_group = (subchannel / 3) + 1; + dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x", sub_channel_prbs_group, subchannel, lut_prbs_8k[sub_channel_prbs_group]); - dib8000_write_word(state, 353, seg_mask13); // ADDR 353 + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + return lut_prbs_2k[sub_channel_prbs_group]; + case TRANSMISSION_MODE_4K: + return lut_prbs_4k[sub_channel_prbs_group]; + default: + case TRANSMISSION_MODE_8K: + return lut_prbs_8k[sub_channel_prbs_group]; + } +} + +static void dib8000_set_13seg_channel(struct dib8000_state *state) +{ + u16 i; + u16 coff_pow = 0x2800; -/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ + state->seg_mask = 0x1fff; /* All 13 segments enabled */ - // ---- SMALL ---- - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - switch (state->fe[0]->dtv_property_cache.transmission_mode) { + /* ---- COFF ---- Carloff, the most robust --- */ + if (state->isdbt_cfg_loaded == 0) { /* if not Sound Broadcasting mode : put default values for 13 segments */ + dib8000_write_word(state, 180, (16 << 6) | 9); + dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); + coff_pow = 0x2800; + for (i = 0; i < 6; i++) + dib8000_write_word(state, 181+i, coff_pow); + + /* P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1 */ + /* P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + + /* P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 */ + dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); + /* P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 */ + dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + dib8000_write_word(state, 228, 0); /* default value */ + dib8000_write_word(state, 265, 31); /* default value */ + dib8000_write_word(state, 205, 0x200f); /* init value */ + } + + /* + * make the cpil_coff_lock more robust but slower p_coff_winlen + * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) + */ + + if (state->cfg.pll->ifreq == 0) + dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ + + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_13seg); +} + +static void dib8000_set_subchannel_prbs(struct dib8000_state *state, u16 init_prbs) +{ + u16 reg_1; + + reg_1 = dib8000_read_word(state, 1); + dib8000_write_word(state, 1, (init_prbs << 2) | (reg_1 & 0x3)); /* ADDR 1 */ +} + +static void dib8000_small_fine_tune(struct dib8000_state *state) +{ + u16 i; + const s16 *ncoeff; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + + dib8000_write_word(state, 352, state->seg_diff_mask); + dib8000_write_word(state, 353, state->seg_mask); + + /* P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 */ + dib8000_write_word(state, 351, (c->isdbt_sb_mode << 9) | (c->isdbt_sb_mode << 8) | (13 << 4) | 5); + + if (c->isdbt_sb_mode) { + /* ---- SMALL ---- */ + switch (c->transmission_mode) { case TRANSMISSION_MODE_2K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) - ncoeff = coeff_2k_sb_1seg_dqpsk; - else // QPSK or QAM - ncoeff = coeff_2k_sb_1seg; - } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) - ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; - else // QPSK or QAM on external segments - ncoeff = coeff_2k_sb_3seg_0dqpsk; - } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) - ncoeff = coeff_2k_sb_3seg_1dqpsk; - else // QPSK or QAM on external segments - ncoeff = coeff_2k_sb_3seg; + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_2k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_2k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_2k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_2k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_2k_sb_3seg; + } } - } - break; - + break; case TRANSMISSION_MODE_4K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) - ncoeff = coeff_4k_sb_1seg_dqpsk; - else // QPSK or QAM - ncoeff = coeff_4k_sb_1seg; - } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { - ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; - } else { // QPSK or QAM on external segments - ncoeff = coeff_4k_sb_3seg_0dqpsk; + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_4k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_4k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_4k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_4k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_4k_sb_3seg; } - } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { - ncoeff = coeff_4k_sb_3seg_1dqpsk; - } else // QPSK or QAM on external segments - ncoeff = coeff_4k_sb_3seg; } - } - break; - + break; case TRANSMISSION_MODE_AUTO: case TRANSMISSION_MODE_8K: default: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) - ncoeff = coeff_8k_sb_1seg_dqpsk; - else // QPSK or QAM - ncoeff = coeff_8k_sb_1seg; - } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { - ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; - } else { // QPSK or QAM on external segments - ncoeff = coeff_8k_sb_3seg_0dqpsk; + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_8k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_8k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_8k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_8k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_8k_sb_3seg; } - } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { - ncoeff = coeff_8k_sb_3seg_1dqpsk; - } else // QPSK or QAM on external segments - ncoeff = coeff_8k_sb_3seg; } - } - break; + break; } + for (i = 0; i < 8; i++) dib8000_write_word(state, 343 + i, ncoeff[i]); } +} - // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 - dib8000_write_word(state, 351, - (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); +static const u16 coff_thres_1seg[3] = {300, 150, 80}; +static const u16 coff_thres_3seg[3] = {350, 300, 250}; +static void dib8000_set_sb_channel(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + const u16 *coff; + u16 i; - // ---- COFF ---- - // Carloff, the most robust - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (c->transmission_mode == TRANSMISSION_MODE_2K || c->transmission_mode == TRANSMISSION_MODE_4K) { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); /* adp_pass =1 */ + dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); /* pha3_force_pha_shift = 1 */ + } else { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); /* adp_pass =0 */ + dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); /* pha3_force_pha_shift = 0 */ + } - // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 - // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 - dib8000_write_word(state, 187, - (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) - | 0x3); + if (c->isdbt_partial_reception == 1) /* 3-segments */ + state->seg_mask = 0x00E0; + else /* 1-segment */ + state->seg_mask = 0x0040; -/* // P_small_coef_ext_enable = 1 */ -/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + /* ---- COFF ---- Carloff, the most robust --- */ + /* P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64, P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 */ + dib8000_write_word(state, 187, (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~c->isdbt_partial_reception & 1) << 2) | 0x3); - // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) - if (mode == 3) - dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); - else - dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); - // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, - // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); - // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 - dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); - // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 - dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); - - // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k - dib8000_write_word(state, 181, 300); - dib8000_write_word(state, 182, 150); - dib8000_write_word(state, 183, 80); - dib8000_write_word(state, 184, 300); - dib8000_write_word(state, 185, 150); - dib8000_write_word(state, 186, 80); - } else { // Sound Broadcasting mode 3 seg - // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 - /* if (mode == 3) */ - /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ - /* else */ - /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ - dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); - - // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, - // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); - // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 - dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); - //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 - dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); - - // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k - dib8000_write_word(state, 181, 350); - dib8000_write_word(state, 182, 300); - dib8000_write_word(state, 183, 250); - dib8000_write_word(state, 184, 350); - dib8000_write_word(state, 185, 300); - dib8000_write_word(state, 186, 250); - } + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); /* P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 */ + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0));/* P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 */ - } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments - dib8000_write_word(state, 180, (16 << 6) | 9); - dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); - coff_pow = 0x2800; - for (i = 0; i < 6; i++) - dib8000_write_word(state, 181 + i, coff_pow); + /* Sound Broadcasting mode 1 seg */ + if (c->isdbt_partial_reception == 0) { + /* P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width = (P_mode == 3) , P_coff_one_seg_sym = (P_mode-1) */ + if (state->mode == 3) + dib8000_write_word(state, 180, 0x1fcf | ((state->mode - 1) << 14)); + else + dib8000_write_word(state, 180, 0x0fcf | ((state->mode - 1) << 14)); - // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, - // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + /* P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); + coff = &coff_thres_1seg[0]; + } else { /* Sound Broadcasting mode 3 seg */ + dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); + /* P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); + coff = &coff_thres_3seg[0]; + } - // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 - dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); - // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 - dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + dib8000_write_word(state, 228, 1); /* P_2d_mode_byp=1 */ + dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); /* P_cspu_win_cut = 0 */ + + if (c->isdbt_partial_reception == 0 && c->transmission_mode == TRANSMISSION_MODE_2K) + dib8000_write_word(state, 265, 15); /* P_equal_noise_sel = 15 */ + + /* Write COFF thres */ + for (i = 0 ; i < 3; i++) { + dib8000_write_word(state, 181+i, coff[i]); + dib8000_write_word(state, 184+i, coff[i]); } - // ---- FFT ---- - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) - dib8000_write_word(state, 178, 64); // P_fft_powrange=64 - else - dib8000_write_word(state, 178, 32); // P_fft_powrange=32 - /* make the cpil_coff_lock more robust but slower p_coff_winlen + /* + * make the cpil_coff_lock more robust but slower p_coff_winlen * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) */ - /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) - dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ - - dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ - dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ - dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ - if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) - dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ - else - dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ - dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ - //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ - if (!autosearching) - dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ + + dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask); /* P_equal_noise_seg_inh */ + + if (c->isdbt_partial_reception == 0) + dib8000_write_word(state, 178, 64); /* P_fft_powrange = 64 */ else - dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. - dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); - - dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ - - /* offset loop parameters */ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) - /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ - dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); - - else // Sound Broadcasting mode 3 seg - /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ - dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); - } else - // TODO in 13 seg, timf_alpha can always be the same or not ? - /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ - dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); - - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) - /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ - dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); - - else // Sound Broadcasting mode 3 seg - /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ - dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); - } else - /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ - dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); + dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ +} - /* P_dvsy_sync_wait - reuse mode */ - switch (state->fe[0]->dtv_property_cache.transmission_mode) { - case TRANSMISSION_MODE_8K: - mode = 256; - break; - case TRANSMISSION_MODE_4K: - mode = 128; - break; - default: - case TRANSMISSION_MODE_2K: - mode = 64; - break; +static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +{ + u16 p_cfr_left_edge = 0, p_cfr_right_edge = 0; + u16 tmcc_pow = 0, ana_gain = 0, tmp = 0, i = 0, nbseg_diff = 0 ; + u16 max_constellation = DQPSK; + int init_prbs; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + + /* P_mode */ + dib8000_write_word(state, 10, (seq << 4)); + + /* init mode */ + state->mode = fft_to_mode(state); + + /* set guard */ + tmp = dib8000_read_word(state, 1); + dib8000_write_word(state, 1, (tmp&0xfffc) | (c->guard_interval & 0x3)); + + dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | ((c->isdbt_partial_reception & 1) << 5) | ((c->isdbt_sb_mode & 1) << 4)); + + /* signal optimization parameter */ + if (c->isdbt_partial_reception) { + state->seg_diff_mask = (c->layer[0].modulation == DQPSK) << permu_seg[0]; + for (i = 1; i < 3; i++) + nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + state->seg_diff_mask |= 1 << permu_seg[i+1]; + } else { + for (i = 0; i < 3; i++) + nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + state->seg_diff_mask |= 1 << permu_seg[i]; } - if (state->cfg.diversity_delay == 0) - mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo + + if (state->seg_diff_mask) + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); else - mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo - mode <<= 4; - dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); + dib8000_write_word(state, 268, (2 << 9) | 39); /*init value */ - /* channel estimation fine configuration */ - switch (max_constellation) { - case QAM_64: - ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB - coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ - coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ - coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ - coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ - //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 - break; - case QAM_16: - ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB - coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ - coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ - coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ - coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ - //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) - break; - default: - ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level - coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ - coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ - coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ - coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ - break; + for (i = 0; i < 3; i++) + max_constellation = dib8000_set_layer(state, i, max_constellation); + if (autosearching == 0) { + state->layer_b_nb_seg = c->layer[1].segment_count; + state->layer_c_nb_seg = c->layer[2].segment_count; } - for (mode = 0; mode < 4; mode++) - dib8000_write_word(state, 215 + mode, coeff[mode]); - // update ana_gain depending on max constellation - dib8000_write_word(state, 116, ana_gain); - // update ADC target depending on ana_gain - if (ana_gain) { // set -16dB ADC target for ana_gain=-1 - for (i = 0; i < 10; i++) - dib8000_write_word(state, 80 + i, adc_target_16dB[i]); - } else { // set -22dB ADC target for ana_gain=0 - for (i = 0; i < 10; i++) - dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); + /* WRITE: Mode & Diff mask */ + dib8000_write_word(state, 0, (state->mode << 13) | state->seg_diff_mask); + + state->differential_constellation = (state->seg_diff_mask != 0); + + /* channel estimation fine configuration */ + ana_gain = dib8000_adp_fine_tune(state, max_constellation); + + /* update ana_gain depending on max constellation */ + dib8000_update_ana_gain(state, ana_gain); + + /* ---- ANA_FE ---- */ + if (c->isdbt_partial_reception) /* 3-segments */ + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_3seg); + else + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_1seg); /* 1-segment */ + + /* TSB or ISDBT ? apply it now */ + if (c->isdbt_sb_mode) { + dib8000_set_sb_channel(state); + if (c->isdbt_sb_subchannel < 14) + init_prbs = dib8000_get_init_prbs(state, c->isdbt_sb_subchannel); + else + init_prbs = 0; + } else { + dib8000_set_13seg_channel(state); + init_prbs = 0xfff; } - // ---- ANA_FE ---- - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) - ana_fe = ana_fe_coeff_3seg; - else // 1-segment - ana_fe = ana_fe_coeff_1seg; - } else - ana_fe = ana_fe_coeff_13seg; + /* SMALL */ + dib8000_small_fine_tune(state); - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) - for (mode = 0; mode < 24; mode++) - dib8000_write_word(state, 117 + mode, ana_fe[mode]); + dib8000_set_subchannel_prbs(state, init_prbs); - // ---- CHAN_BLK ---- + /* ---- CHAN_BLK ---- */ for (i = 0; i < 13; i++) { - if ((((~seg_diff_mask) >> i) & 1) == 1) { - P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); - P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); + if ((((~state->seg_diff_mask) >> i) & 1) == 1) { + p_cfr_left_edge += (1 << i) * ((i == 0) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i - 1)) & 1) == 0)); + p_cfr_right_edge += (1 << i) * ((i == 12) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i + 1)) & 1) == 0)); } } - dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge - dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge - // "P_cspu_left_edge" not used => do not care - // "P_cspu_right_edge" not used => do not care - - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 - dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 - && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { - //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 - dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 - } - } else if (state->isdbt_cfg_loaded == 0) { - dib8000_write_word(state, 228, 0); // default value - dib8000_write_word(state, 265, 31); // default value - dib8000_write_word(state, 205, 0x200f); // init value - } - // ---- TMCC ---- + dib8000_write_word(state, 222, p_cfr_left_edge); /* p_cfr_left_edge */ + dib8000_write_word(state, 223, p_cfr_right_edge); /* p_cfr_right_edge */ + /* "P_cspu_left_edge" & "P_cspu_right_edge" not used => do not care */ + + dib8000_write_word(state, 189, ~state->seg_mask | state->seg_diff_mask); /* P_lmod4_seg_inh */ + dib8000_write_word(state, 192, ~state->seg_mask | state->seg_diff_mask); /* P_pha3_seg_inh */ + dib8000_write_word(state, 225, ~state->seg_mask | state->seg_diff_mask); /* P_tac_seg_inh */ + + if (!autosearching) + dib8000_write_word(state, 288, (~state->seg_mask | state->seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ + else + dib8000_write_word(state, 288, 0x1fff); /*disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. */ + + dib8000_write_word(state, 211, state->seg_mask & (~state->seg_diff_mask)); /* P_des_seg_enabled */ + dib8000_write_word(state, 287, ~state->seg_mask | 0x1000); /* P_tmcc_seg_inh */ + + dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ + + /* ---- TMCC ---- */ for (i = 0; i < 3; i++) - tmcc_pow += - (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count); - // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); - // Threshold is set at 1/4 of max power. - tmcc_pow *= (1 << (9 - 2)); - - dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k - dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k - dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k - //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); - // ---- PHA3 ---- + tmcc_pow += (((c->layer[i].modulation == DQPSK) * 4 + 1) * c->layer[i].segment_count) ; + + /* Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); */ + /* Threshold is set at 1/4 of max power. */ + tmcc_pow *= (1 << (9-2)); + dib8000_write_word(state, 290, tmcc_pow); /* P_tmcc_dec_thres_2k */ + dib8000_write_word(state, 291, tmcc_pow); /* P_tmcc_dec_thres_4k */ + dib8000_write_word(state, 292, tmcc_pow); /* P_tmcc_dec_thres_8k */ + /*dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); */ + /* ---- PHA3 ---- */ if (state->isdbt_cfg_loaded == 0) - dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ + dib8000_write_word(state, 250, 3285); /* p_2d_hspeed_thr0 */ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) - state->isdbt_cfg_loaded = 0; - else - state->isdbt_cfg_loaded = 1; + state->isdbt_cfg_loaded = 0; +} +static u32 dib8000_wait_lock(struct dib8000_state *state, u32 internal, + u32 wait0_ms, u32 wait1_ms, u32 wait2_ms) +{ + u32 value = 0; /* P_search_end0 wait time */ + u16 reg = 11; /* P_search_end0 start addr */ + + for (reg = 11; reg < 16; reg += 2) { + if (reg == 11) { + if (state->revision == 0x8090) + value = internal * wait1_ms; + else + value = internal * wait0_ms; + } else if (reg == 13) + value = internal * wait1_ms; + else if (reg == 15) + value = internal * wait2_ms; + dib8000_write_word(state, reg, (u16)((value >> 16) & 0xffff)); + dib8000_write_word(state, (reg + 1), (u16)(value & 0xffff)); + } + return value; } static int dib8000_autosearch_start(struct dvb_frontend *fe) { - u8 factor; - u32 value; struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u8 slist = 0; + u32 value, internal = state->cfg.pll->internal; - int slist = 0; - - state->fe[0]->dtv_property_cache.inversion = 0; - if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) - state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; - state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; - state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; - state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; - - //choose the right list, in sb, always do everything - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; - slist = 7; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); - } else { - if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { - slist = 7; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 - } else - slist = 3; - } else { - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { - slist = 2; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 - } else - slist = 0; - } + if (state->revision == 0x8090) + internal = dib8000_read32(state, 23) / 1000; + + if (state->autosearch_state == AS_SEARCHING_FFT) { + dib8000_write_word(state, 37, 0x0065); /* P_ctrl_pha_off_max default values */ + dib8000_write_word(state, 116, 0x0000); /* P_ana_gain to 0 */ - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) - state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) - state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x1fff) | (0 << 13) | (1 << 15)); /* P_mode = 0, P_restart_search=1 */ + dib8000_write_word(state, 1, (dib8000_read_word(state, 1) & 0xfffc) | 0); /* P_guard = 0 */ + dib8000_write_word(state, 6, 0); /* P_lock0_mask = 0 */ + dib8000_write_word(state, 7, 0); /* P_lock1_mask = 0 */ + dib8000_write_word(state, 8, 0); /* P_lock2_mask = 0 */ + dib8000_write_word(state, 10, (dib8000_read_word(state, 10) & 0x200) | (16 << 4) | (0 << 0)); /* P_search_list=16, P_search_maxtrial=0 */ - dprintk("using list for autosearch : %d", slist); - dib8000_set_channel(state, (unsigned char)slist, 1); - //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + if (state->revision == 0x8090) + value = dib8000_wait_lock(state, internal, 10, 10, 10); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + value = dib8000_wait_lock(state, internal, 20, 20, 20); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - factor = 1; + dib8000_write_word(state, 17, 0); + dib8000_write_word(state, 18, 200); /* P_search_rstst = 200 */ + dib8000_write_word(state, 19, 0); + dib8000_write_word(state, 20, 400); /* P_search_rstend = 400 */ + dib8000_write_word(state, 21, (value >> 16) & 0xffff); /* P_search_checkst */ + dib8000_write_word(state, 22, value & 0xffff); - //set lock_mask values + if (state->revision == 0x8090) + dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (0 << 8)); /* P_corm_alpha = 0 */ + else + dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (9 << 8)); /* P_corm_alpha = 3 */ + dib8000_write_word(state, 355, 2); /* P_search_param_max = 2 */ + + /* P_search_param_select = (1 | 1<<4 | 1 << 8) */ + dib8000_write_word(state, 356, 0); + dib8000_write_word(state, 357, 0x111); + + dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (1 << 13)); /* P_restart_ccg = 1 */ + dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (0 << 13)); /* P_restart_ccg = 0 */ + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x7ff) | (0 << 15) | (1 << 13)); /* P_restart_search = 0; */ + } else if (state->autosearch_state == AS_SEARCHING_GUARD) { + c->transmission_mode = TRANSMISSION_MODE_8K; + c->guard_interval = GUARD_INTERVAL_1_8; + c->inversion = 0; + c->layer[0].modulation = QAM_64; + c->layer[0].fec = FEC_2_3; + c->layer[0].interleaving = 0; + c->layer[0].segment_count = 13; + + slist = 16; + c->transmission_mode = state->found_nfft; + + dib8000_set_isdbt_common_channel(state, slist, 1); + + /* set lock_mask values */ dib8000_write_word(state, 6, 0x4); - dib8000_write_word(state, 7, 0x8); + if (state->revision == 0x8090) + dib8000_write_word(state, 7, ((1 << 12) | (1 << 11) | (1 << 10)));/* tmcc_dec_lock, tmcc_sync_lock, tmcc_data_lock, tmcc_bch_uncor */ + else + dib8000_write_word(state, 7, 0x8); dib8000_write_word(state, 8, 0x1000); - //set lock_mask wait time values - value = 50 * state->cfg.pll->internal * factor; - dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time - dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time - value = 100 * state->cfg.pll->internal * factor; - dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time - dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time - value = 1000 * state->cfg.pll->internal * factor; - dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time - dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time + /* set lock_mask wait time values */ + if (state->revision == 0x8090) + dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + + dib8000_write_word(state, 355, 3); /* P_search_param_max = 3 */ + + /* P_search_param_select = 0xf; look for the 4 different guard intervals */ + dib8000_write_word(state, 356, 0); + dib8000_write_word(state, 357, 0xf); value = dib8000_read_word(state, 0); - dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); - dib8000_read_word(state, 1284); // reset the INT. n_irq_pending - dib8000_write_word(state, 0, (u16) value); + dib8000_write_word(state, 0, (u16)((1 << 15) | value)); + dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ + dib8000_write_word(state, 0, (u16)value); + } else { + c->inversion = 0; + c->layer[0].modulation = QAM_64; + c->layer[0].fec = FEC_2_3; + c->layer[0].interleaving = 0; + c->layer[0].segment_count = 13; + if (!c->isdbt_sb_mode) + c->layer[0].segment_count = 13; + + /* choose the right list, in sb, always do everything */ + if (c->isdbt_sb_mode) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); + } else { + if (c->guard_interval == GUARD_INTERVAL_AUTO) { + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + c->transmission_mode = TRANSMISSION_MODE_8K; + c->guard_interval = GUARD_INTERVAL_1_8; + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 to have autosearch start ok with mode2 */ + } else { + c->guard_interval = GUARD_INTERVAL_1_8; + slist = 3; + } + } else { + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + c->transmission_mode = TRANSMISSION_MODE_8K; + slist = 2; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 */ + } else + slist = 0; + } + } + dprintk("Using list for autosearch : %d", slist); - } + dib8000_set_isdbt_common_channel(state, slist, 1); + + /* set lock_mask values */ + dib8000_write_word(state, 6, 0x4); + if (state->revision == 0x8090) + dib8000_write_word(state, 7, (1 << 12) | (1 << 11) | (1 << 10)); + else + dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 8, 0x1000); + /* set lock_mask wait time values */ + if (state->revision == 0x8090) + dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + + value = dib8000_read_word(state, 0); + dib8000_write_word(state, 0, (u16)((1 << 15) | value)); + dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ + dib8000_write_word(state, 0, (u16)value); + } return 0; } @@ -2663,96 +2583,635 @@ static int dib8000_autosearch_irq(struct dvb_frontend *fe) struct dib8000_state *state = fe->demodulator_priv; u16 irq_pending = dib8000_read_word(state, 1284); - if (irq_pending & 0x1) { // failed - dprintk("dib8000_autosearch_irq failed"); - return 1; - } + if (state->autosearch_state == AS_SEARCHING_FFT) { + if (irq_pending & 0x1) { + dprintk("dib8000_autosearch_irq: max correlation result available"); + return 3; + } + } else { + if (irq_pending & 0x1) { /* failed */ + dprintk("dib8000_autosearch_irq failed"); + return 1; + } - if (irq_pending & 0x2) { // succeeded - dprintk("dib8000_autosearch_irq succeeded"); - return 2; + if (irq_pending & 0x2) { /* succeeded */ + dprintk("dib8000_autosearch_irq succeeded"); + return 2; + } } return 0; // still pending } -static int dib8000_tune(struct dvb_frontend *fe) +static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff) { - struct dib8000_state *state = fe->demodulator_priv; - int ret = 0; - u16 lock, value, mode; + u16 tmp; - // we are already tuned - just resuming from suspend - if (state == NULL) - return -EINVAL; + tmp = dib8000_read_word(state, 771); + if (onoff) /* start P_restart_chd : channel_decoder */ + dib8000_write_word(state, 771, tmp & 0xfffd); + else /* stop P_restart_chd : channel_decoder */ + dib8000_write_word(state, 771, tmp | (1<<1)); +} + +static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) +{ + s16 unit_khz_dds_val; + u32 abs_offset_khz = ABS(offset_khz); + u32 dds = state->cfg.pll->ifreq & 0x1ffffff; + u8 invert = !!(state->cfg.pll->ifreq & (1 << 25)); + u8 ratio; - mode = fft_to_mode(state); + if (state->revision == 0x8090) { + ratio = 4; + unit_khz_dds_val = (1<<26) / (dib8000_read32(state, 23) / 1000); + if (offset_khz < 0) + dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val); + else + dds = (abs_offset_khz * unit_khz_dds_val); - dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); - dib8000_set_channel(state, 0, 0); + if (invert) + dds = (1<<26) - dds; + } else { + ratio = 2; + unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal); - // restart demod - ret |= dib8000_write_word(state, 770, 0x4000); - ret |= dib8000_write_word(state, 770, 0x0000); - msleep(45); + if (offset_khz < 0) + unit_khz_dds_val *= -1; - /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ - /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ + /* IF tuner */ + if (invert) + dds -= abs_offset_khz * unit_khz_dds_val; + else + dds += abs_offset_khz * unit_khz_dds_val; + } - // never achieved a lock before - wait for timfreq to update - if (state->timf == 0) { - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) - msleep(300); - else // Sound Broadcasting mode 3 seg - msleep(500); - } else // 13 seg - msleep(200); + dprintk("setting a DDS frequency offset of %c%dkHz", invert ? '-' : ' ', dds / unit_khz_dds_val); + + if (abs_offset_khz <= (state->cfg.pll->internal / ratio)) { + /* Max dds offset is the half of the demod freq */ + dib8000_write_word(state, 26, invert); + dib8000_write_word(state, 27, (u16)(dds >> 16) & 0x1ff); + dib8000_write_word(state, 28, (u16)(dds & 0xffff)); } - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +} - /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ - dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); - //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); +static void dib8000_set_frequency_offset(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int i; + u32 current_rf; + int total_dds_offset_khz; - /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ - ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); + if (state->fe[0]->ops.tuner_ops.get_frequency) + state->fe[0]->ops.tuner_ops.get_frequency(state->fe[0], ¤t_rf); + else + current_rf = c->frequency; + current_rf /= 1000; + total_dds_offset_khz = (int)current_rf - (int)c->frequency / 1000; - } else { // Sound Broadcasting mode 3 seg + if (c->isdbt_sb_mode) { + state->subchannel = c->isdbt_sb_subchannel; - /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ - dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); + i = dib8000_read_word(state, 26) & 1; /* P_dds_invspec */ + dib8000_write_word(state, 26, c->inversion ^ i); - ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); + if (state->cfg.pll->ifreq == 0) { /* low if tuner */ + if ((c->inversion ^ i) == 0) + dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); + } else { + if ((c->inversion ^ i) == 0) + total_dds_offset_khz *= -1; } + } - } else { // 13 seg - /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ - dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); + dprintk("%dkhz tuner offset (frequency = %dHz & current_rf = %dHz) total_dds_offset_hz = %d", c->frequency - current_rf, c->frequency, current_rf, total_dds_offset_khz); - ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); + /* apply dds offset now */ + dib8000_set_dds(state, total_dds_offset_khz); +} + +static u16 LUT_isdbt_symbol_duration[4] = { 26, 101, 63 }; + +static u32 dib8000_get_symbol_duration(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 i; + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + i = 0; + break; + case TRANSMISSION_MODE_4K: + i = 2; + break; + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + i = 1; + break; } - // we achieved a coff_cpil_lock - it's time to update the timf - if (state->revision != 0x8090) - lock = dib8000_read_word(state, 568); + return (LUT_isdbt_symbol_duration[i] / (c->bandwidth_hz / 1000)) + 1; +} + +static void dib8000_set_isdbt_loop_params(struct dib8000_state *state, enum param_loop_step loop_step) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 reg_32 = 0, reg_37 = 0; + + switch (loop_step) { + case LOOP_TUNE_1: + if (c->isdbt_sb_mode) { + if (c->isdbt_partial_reception == 0) { + reg_32 = ((11 - state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x40 */ + reg_37 = (3 << 5) | (0 << 4) | (10 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ + } else { /* Sound Broadcasting mode 3 seg */ + reg_32 = ((10 - state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x60 */ + reg_37 = (3 << 5) | (0 << 4) | (9 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (9-P_mode) */ + } + } else { /* 13-seg start conf offset loop parameters */ + reg_32 = ((9 - state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ + reg_37 = (3 << 5) | (0 << 4) | (8 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ + } + break; + case LOOP_TUNE_2: + if (c->isdbt_sb_mode) { + if (c->isdbt_partial_reception == 0) { /* Sound Broadcasting mode 1 seg */ + reg_32 = ((13-state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40*/ + reg_37 = (12-state->mode) | ((5 + state->mode) << 5); + } else { /* Sound Broadcasting mode 3 seg */ + reg_32 = ((12-state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 */ + reg_37 = (11-state->mode) | ((5 + state->mode) << 5); + } + } else { /* 13 seg */ + reg_32 = ((11-state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 */ + reg_37 = ((5+state->mode) << 5) | (10 - state->mode); + } + break; + } + dib8000_write_word(state, 32, reg_32); + dib8000_write_word(state, 37, reg_37); +} + +static void dib8000_demod_restart(struct dib8000_state *state) +{ + dib8000_write_word(state, 770, 0x4000); + dib8000_write_word(state, 770, 0x0000); + return; +} + +static void dib8000_set_sync_wait(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 sync_wait = 64; + + /* P_dvsy_sync_wait - reuse mode */ + switch (c->transmission_mode) { + case TRANSMISSION_MODE_8K: + sync_wait = 256; + break; + case TRANSMISSION_MODE_4K: + sync_wait = 128; + break; + default: + case TRANSMISSION_MODE_2K: + sync_wait = 64; + break; + } + + if (state->cfg.diversity_delay == 0) + sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + 48; /* add 50% SFN margin + compensate for one DVSY-fifo */ else - lock = dib8000_read_word(state, 570); - if ((lock >> 11) & 0x1) - dib8000_update_timf(state); + sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + state->cfg.diversity_delay; /* add 50% SFN margin + compensate for DVSY-fifo */ - //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start - dib8000_write_word(state, 6, 0x200); + dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | (sync_wait << 4)); +} - if (state->revision == 0x8002) { - value = dib8000_read_word(state, 903); - dib8000_write_word(state, 903, value & ~(1 << 3)); - msleep(1); - dib8000_write_word(state, 903, value | (1 << 3)); +static u32 dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode) +{ + if (mode == SYMBOL_DEPENDENT_ON) + return systime() + (delay * state->symbol_duration); + else + return systime() + delay; +} + +static s32 dib8000_get_status(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + return state->status; +} + +enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + return state->tune_state; +} +EXPORT_SYMBOL(dib8000_get_tune_state); + +int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib8000_state *state = fe->demodulator_priv; + + state->tune_state = tune_state; + return 0; +} +EXPORT_SYMBOL(dib8000_set_tune_state); + +static int dib8000_tune_restart_from_demod(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + state->status = FE_STATUS_TUNE_PENDING; + state->tune_state = CT_DEMOD_START; + return 0; +} + +static u16 dib8000_read_lock(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if (state->revision == 0x8090) + return dib8000_read_word(state, 570); + return dib8000_read_word(state, 568); +} + +static int dib8090p_init_sdram(struct dib8000_state *state) +{ + u16 reg = 0; + dprintk("init sdram"); + + reg = dib8000_read_word(state, 274) & 0xfff0; + dib8000_write_word(state, 274, reg | 0x7); /* P_dintlv_delay_ram = 7 because of MobileSdram */ + + dib8000_write_word(state, 1803, (7 << 2)); + + reg = dib8000_read_word(state, 1280); + dib8000_write_word(state, 1280, reg | (1 << 2)); /* force restart P_restart_sdram */ + dib8000_write_word(state, 1280, reg); /* release restart P_restart_sdram */ + + return 0; +} + +static int dib8000_tune(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + enum frontend_tune_state *tune_state = &state->tune_state; + + u16 locks, deeper_interleaver = 0, i; + int ret = 1; /* 1 symbol duration (in 100us unit) delay most of the time */ + + u32 *timeout = &state->timeout; + u32 now = systime(); +#ifdef DIB8000_AGC_FREEZE + u16 agc1, agc2; +#endif + + u32 corm[4] = {0, 0, 0, 0}; + u8 find_index, max_value; + +#if 0 + if (*tune_state < CT_DEMOD_STOP) + dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u systime = %u", state->channel_parameters_set, *tune_state, state->autosearch_state, now); +#endif + + switch (*tune_state) { + case CT_DEMOD_START: /* 30 */ + if (state->revision == 0x8090) + dib8090p_init_sdram(state); + state->status = FE_STATUS_TUNE_PENDING; + if ((c->delivery_system != SYS_ISDBT) || + (c->inversion == INVERSION_AUTO) || + (c->transmission_mode == TRANSMISSION_MODE_AUTO) || + (c->guard_interval == GUARD_INTERVAL_AUTO) || + (((c->isdbt_layer_enabled & (1 << 0)) != 0) && + (c->layer[0].segment_count != 0xff) && + (c->layer[0].segment_count != 0) && + ((c->layer[0].modulation == QAM_AUTO) || + (c->layer[0].fec == FEC_AUTO))) || + (((c->isdbt_layer_enabled & (1 << 1)) != 0) && + (c->layer[1].segment_count != 0xff) && + (c->layer[1].segment_count != 0) && + ((c->layer[1].modulation == QAM_AUTO) || + (c->layer[1].fec == FEC_AUTO))) || + (((c->isdbt_layer_enabled & (1 << 2)) != 0) && + (c->layer[2].segment_count != 0xff) && + (c->layer[2].segment_count != 0) && + ((c->layer[2].modulation == QAM_AUTO) || + (c->layer[2].fec == FEC_AUTO))) || + (((c->layer[0].segment_count == 0) || + ((c->isdbt_layer_enabled & (1 << 0)) == 0)) && + ((c->layer[1].segment_count == 0) || + ((c->isdbt_layer_enabled & (2 << 0)) == 0)) && + ((c->layer[2].segment_count == 0) || ((c->isdbt_layer_enabled & (3 << 0)) == 0)))) + state->channel_parameters_set = 0; /* auto search */ + else + state->channel_parameters_set = 1; /* channel parameters are known */ + + dib8000_viterbi_state(state, 0); /* force chan dec in restart */ + + /* Layer monit */ + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + + dib8000_set_frequency_offset(state); + dib8000_set_bandwidth(fe, c->bandwidth_hz / 1000); + + if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */ +#ifdef DIB8000_AGC_FREEZE + if (state->revision != 0x8090) { + state->agc1_max = dib8000_read_word(state, 108); + state->agc1_min = dib8000_read_word(state, 109); + state->agc2_max = dib8000_read_word(state, 110); + state->agc2_min = dib8000_read_word(state, 111); + agc1 = dib8000_read_word(state, 388); + agc2 = dib8000_read_word(state, 389); + dib8000_write_word(state, 108, agc1); + dib8000_write_word(state, 109, agc1); + dib8000_write_word(state, 110, agc2); + dib8000_write_word(state, 111, agc2); + } +#endif + state->autosearch_state = AS_SEARCHING_FFT; + state->found_nfft = TRANSMISSION_MODE_AUTO; + state->found_guard = GUARD_INTERVAL_AUTO; + *tune_state = CT_DEMOD_SEARCH_NEXT; + } else { /* we already know the channel struct so TUNE only ! */ + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STEP_3; + } + state->symbol_duration = dib8000_get_symbol_duration(state); + break; + + case CT_DEMOD_SEARCH_NEXT: /* 51 */ + dib8000_autosearch_start(fe); + if (state->revision == 0x8090) + ret = 50; + else + ret = 15; + *tune_state = CT_DEMOD_STEP_1; + break; + + case CT_DEMOD_STEP_1: /* 31 */ + switch (dib8000_autosearch_irq(fe)) { + case 1: /* fail */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + case 2: /* Succes */ + state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */ + *tune_state = CT_DEMOD_STEP_3; + if (state->autosearch_state == AS_SEARCHING_GUARD) + *tune_state = CT_DEMOD_STEP_2; + else + state->autosearch_state = AS_DONE; + break; + case 3: /* Autosearch FFT max correlation endded */ + *tune_state = CT_DEMOD_STEP_2; + break; + } + break; + + case CT_DEMOD_STEP_2: + switch (state->autosearch_state) { + case AS_SEARCHING_FFT: + /* searching for the correct FFT */ + if (state->revision == 0x8090) { + corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601)); + } else { + corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595)); + corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + } + /* dprintk("corm fft: %u %u %u", corm[0], corm[1], corm[2]); */ + + max_value = 0; + for (find_index = 1 ; find_index < 3 ; find_index++) { + if (corm[max_value] < corm[find_index]) + max_value = find_index ; + } + + switch (max_value) { + case 0: + state->found_nfft = TRANSMISSION_MODE_2K; + break; + case 1: + state->found_nfft = TRANSMISSION_MODE_4K; + break; + case 2: + default: + state->found_nfft = TRANSMISSION_MODE_8K; + break; + } + /* dprintk("Autosearch FFT has found Mode %d", max_value + 1); */ + + *tune_state = CT_DEMOD_SEARCH_NEXT; + state->autosearch_state = AS_SEARCHING_GUARD; + if (state->revision == 0x8090) + ret = 50; + else + ret = 10; + break; + case AS_SEARCHING_GUARD: + /* searching for the correct guard interval */ + if (state->revision == 0x8090) + state->found_guard = dib8000_read_word(state, 572) & 0x3; + else + state->found_guard = dib8000_read_word(state, 570) & 0x3; + /* dprintk("guard interval found=%i", state->found_guard); */ + + *tune_state = CT_DEMOD_STEP_3; + break; + default: + /* the demod should never be in this state */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + } + break; + + case CT_DEMOD_STEP_3: /* 33 */ + state->symbol_duration = dib8000_get_symbol_duration(state); + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); + dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ + *tune_state = CT_DEMOD_STEP_4; + break; + + case CT_DEMOD_STEP_4: /* (34) */ + dib8000_demod_restart(state); + + dib8000_set_sync_wait(state); + dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); + + locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ + /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this lenght to lock */ + *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_5; + break; + + case CT_DEMOD_STEP_5: /* (35) */ + locks = dib8000_read_lock(fe); + if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */ + dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */ + if (!state->differential_constellation) { + /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */ + *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_7; + } else { + *tune_state = CT_DEMOD_STEP_8; + } + } else if (now > *timeout) { + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + } + break; + + case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */ + if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { + /* if there is a diversity fe in input and this fe is has not already failled : wait here until this this fe has succedeed or failled */ + if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ + *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ + else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failled also, break the current one */ + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + state->status = FE_STATUS_TUNE_FAILED; + } + } else { + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ + state->status = FE_STATUS_TUNE_FAILED; + } + break; + + case CT_DEMOD_STEP_7: /* 37 */ + locks = dib8000_read_lock(fe); + if (locks & (1<<10)) { /* lmod4_lock */ + ret = 14; /* wait for 14 symbols */ + *tune_state = CT_DEMOD_STEP_8; + } else if (now > *timeout) + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + break; + + case CT_DEMOD_STEP_8: /* 38 */ + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + + /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/ + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { + state->subchannel = 0; + *tune_state = CT_DEMOD_STEP_11; + } else { + *tune_state = CT_DEMOD_STEP_9; + state->status = FE_STATUS_LOCKED; + } + break; + + case CT_DEMOD_STEP_9: /* 39 */ + if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ + /* defines timeout for mpeg lock depending on interleaver lenght of longest layer */ + for (i = 0; i < 3; i++) { + if (c->layer[i].interleaving >= deeper_interleaver) { + dprintk("layer%i: time interleaver = %d ", i, c->layer[i].interleaving); + if (c->layer[i].segment_count > 0) { /* valid layer */ + deeper_interleaver = c->layer[0].interleaving; + state->longest_intlv_layer = i; + } + } + } + + if (deeper_interleaver == 0) + locks = 2; /* locks is the tmp local variable name */ + else if (deeper_interleaver == 3) + locks = 8; + else + locks = 2 * deeper_interleaver; + + if (state->diversity_onoff != 0) /* because of diversity sync */ + locks *= 2; + + *timeout = now + (2000 * locks); /* give the mpeg lock 800ms if sram is present */ + dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %d", deeper_interleaver, state->longest_intlv_layer, locks, *timeout); + + *tune_state = CT_DEMOD_STEP_10; + } else + *tune_state = CT_DEMOD_STOP; + break; + + case CT_DEMOD_STEP_10: /* 40 */ + locks = dib8000_read_lock(fe); + if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ + dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) + /* signal to the upper layer, that there was a channel found and the parameters can be read */ + state->status = FE_STATUS_DEMOD_SUCCESS; + else + state->status = FE_STATUS_DATA_LOCKED; + *tune_state = CT_DEMOD_STOP; + } else if (now > *timeout) { + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { /* continue to try init prbs autosearch */ + state->subchannel += 3; + *tune_state = CT_DEMOD_STEP_11; + } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ + if (locks & (0x7<<5)) { + dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); + state->status = FE_STATUS_DATA_LOCKED; + } else + state->status = FE_STATUS_TUNE_FAILED; + *tune_state = CT_DEMOD_STOP; + } + } + break; + + case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */ + if (state->subchannel <= 41) { + dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel)); + *tune_state = CT_DEMOD_STEP_9; + } else { + *tune_state = CT_DEMOD_STOP; + state->status = FE_STATUS_TUNE_FAILED; + } + break; + + default: + break; + } + + /* tuning is finished - cleanup the demod */ + switch (*tune_state) { + case CT_DEMOD_STOP: /* (42) */ +#ifdef DIB8000_AGC_FREEZE + if ((state->revision != 0x8090) && (state->agc1_max != 0)) { + dib8000_write_word(state, 108, state->agc1_max); + dib8000_write_word(state, 109, state->agc1_min); + dib8000_write_word(state, 110, state->agc2_max); + dib8000_write_word(state, 111, state->agc2_min); + state->agc1_max = 0; + state->agc1_min = 0; + state->agc2_max = 0; + state->agc2_min = 0; + } +#endif + ret = FE_CALLBACK_TIME_NEVER; + break; + default: + break; } + if ((ret > 0) && (*tune_state > CT_DEMOD_STEP_3)) + return ret * state->symbol_duration; + if ((ret > 0) && (ret < state->symbol_duration)) + return state->symbol_duration; /* at least one symbol */ return ret; } @@ -2767,7 +3226,7 @@ static int dib8000_wakeup(struct dvb_frontend *fe) if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) dprintk("could not start Slow ADC"); - if (state->revision != 0x8090) + if (state->revision == 0x8090) dib8000_sad_calib(state); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { @@ -2797,21 +3256,6 @@ static int dib8000_sleep(struct dvb_frontend *fe) return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); } -enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - return state->tune_state; -} -EXPORT_SYMBOL(dib8000_get_tune_state); - -int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) -{ - struct dib8000_state *state = fe->demodulator_priv; - state->tune_state = tune_state; - return 0; -} -EXPORT_SYMBOL(dib8000_set_tune_state); - static int dib8000_get_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; @@ -2961,19 +3405,19 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) static int dib8000_set_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; - u8 nbr_pending, exit_condition, index_frontend; - s8 index_frontend_success = -1; - int time, ret; - int time_slave = FE_CALLBACK_TIME_NEVER; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int l, i, active, time, ret, time_slave = FE_CALLBACK_TIME_NEVER; + u8 exit_condition, index_frontend; + u32 delay, callback_time; - if (state->fe[0]->dtv_property_cache.frequency == 0) { + if (c->frequency == 0) { dprintk("dib8000: must at least specify frequency "); return 0; } - if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { + if (c->bandwidth_hz == 0) { dprintk("dib8000: no bandwidth specified, set to default "); - state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000; + c->bandwidth_hz = 6000000; } for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { @@ -2981,18 +3425,36 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); - if (state->revision != 0x8090) - dib8000_set_output_mode(state->fe[index_frontend], - OUTMODE_HIGH_Z); - else - dib8096p_set_output_mode(state->fe[index_frontend], - OUTMODE_HIGH_Z); + /* set output mode and diversity input */ + if (state->revision != 0x8090) { + dib8000_set_diversity_in(state->fe[index_frontend], 1); + if (index_frontend != 0) + dib8000_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + else + dib8000_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); + } else { + dib8096p_set_diversity_in(state->fe[index_frontend], 1); + if (index_frontend != 0) + dib8096p_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + else + dib8096p_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); + } + + /* tune the tuner */ if (state->fe[index_frontend]->ops.tuner_ops.set_params) state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]); dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); } + /* turn off the diversity of the last chip */ + if (state->revision != 0x8090) + dib8000_set_diversity_in(state->fe[index_frontend - 1], 0); + else + dib8096p_set_diversity_in(state->fe[index_frontend - 1], 0); + /* start up the AGC */ do { time = dib8000_agc_startup(state->fe[0]); @@ -3019,139 +3481,88 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); - if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || - (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || - (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || - (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && - (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && - (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && - (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || - ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && - ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || - ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && - ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { - int i = 100; - u8 found = 0; - u8 tune_failed = 0; - + active = 1; + do { + callback_time = FE_CALLBACK_TIME_NEVER; for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); - dib8000_autosearch_start(state->fe[index_frontend]); - } - - do { - msleep(20); - nbr_pending = 0; - exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ - for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - if (((tune_failed >> index_frontend) & 0x1) == 0) { - found = dib8000_autosearch_irq(state->fe[index_frontend]); - switch (found) { - case 0: /* tune pending */ - nbr_pending++; - break; - case 2: - dprintk("autosearch succeed on the frontend%i", index_frontend); - exit_condition = 2; - index_frontend_success = index_frontend; - break; - default: - dprintk("unhandled autosearch result"); - case 1: - tune_failed |= (1 << index_frontend); - dprintk("autosearch failed for the frontend%i", index_frontend); - break; + delay = dib8000_tune(state->fe[index_frontend]); + if (delay != FE_CALLBACK_TIME_NEVER) + delay += systime(); + + /* we are in autosearch */ + if (state->channel_parameters_set == 0) { /* searching */ + if ((dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_DEMOD_SUCCESS) || (dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_FFT_SUCCESS)) { + dprintk("autosearch succeeded on fe%i", index_frontend); + dib8000_get_frontend(state->fe[index_frontend]); /* we read the channel parameters from the frontend which was successful */ + state->channel_parameters_set = 1; + + for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) { + if (l != index_frontend) { /* and for all frontend except the successful one */ + dib8000_tune_restart_from_demod(state->fe[l]); + + state->fe[l]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; + state->fe[l]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[l]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[l]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[l]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[l]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; + state->fe[l]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; + state->fe[l]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; + state->fe[l]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; + } + + } } } } - - /* if all tune are done and no success, exit: tune failed */ - if ((nbr_pending == 0) && (exit_condition == 0)) - exit_condition = 1; - } while ((exit_condition == 0) && i--); - - if (exit_condition == 1) { /* tune failed */ - dprintk("tune failed"); - return 0; + if (delay < callback_time) + callback_time = delay; + } + /* tuning is done when the master frontend is done (failed or success) */ + if (dib8000_get_status(state->fe[0]) == FE_STATUS_TUNE_FAILED || + dib8000_get_status(state->fe[0]) == FE_STATUS_LOCKED || + dib8000_get_status(state->fe[0]) == FE_STATUS_DATA_LOCKED) { + active = 0; + /* we need to wait for all frontends to be finished */ + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_DEMOD_STOP) + active = 1; + } + if (active == 0) + dprintk("tuning done with status %d", dib8000_get_status(state->fe[0])); } - dprintk("tune success on frontend%i", index_frontend_success); - - dib8000_get_frontend(fe); - } + if ((active == 1) && (callback_time == FE_CALLBACK_TIME_NEVER)) { + dprintk("strange callback time something went wrong"); + active = 0; + } - for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) - ret = dib8000_tune(state->fe[index_frontend]); + while ((active == 1) && (systime() < callback_time)) + msleep(100); + } while (active); - /* set output mode and diversity input */ - if (state->revision != 0x8090) { + /* set output mode */ + if (state->revision != 0x8090) dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); - for (index_frontend = 1; - (index_frontend < MAX_NUMBER_OF_FRONTENDS) && - (state->fe[index_frontend] != NULL); - index_frontend++) { - dib8000_set_output_mode(state->fe[index_frontend], - OUTMODE_DIVERSITY); - dib8000_set_diversity_in(state->fe[index_frontend-1], 1); - } - - /* turn off the diversity of the last chip */ - dib8000_set_diversity_in(state->fe[index_frontend-1], 0); - } else { + else { dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode); if (state->cfg.enMpegOutput == 0) { dib8096p_setDibTxMux(state, MPEG_ON_DIBTX); dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); } - for (index_frontend = 1; - (index_frontend < MAX_NUMBER_OF_FRONTENDS) && - (state->fe[index_frontend] != NULL); - index_frontend++) { - dib8096p_set_output_mode(state->fe[index_frontend], - OUTMODE_DIVERSITY); - dib8096p_set_diversity_in(state->fe[index_frontend-1], 1); - } - - /* turn off the diversity of the last chip */ - dib8096p_set_diversity_in(state->fe[index_frontend-1], 0); } return ret; } -static u16 dib8000_read_lock(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - - if (state->revision == 0x8090) - return dib8000_read_word(state, 570); - return dib8000_read_word(state, 568); -} - static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib8000_state *state = fe->demodulator_priv; u16 lock_slave = 0, lock; u8 index_frontend; - if (state->revision == 0x8090) - lock = dib8000_read_word(state, 570); - else - lock = dib8000_read_word(state, 568); - + lock = dib8000_read_lock(fe); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) lock_slave |= dib8000_read_lock(state->fe[index_frontend]); @@ -3545,10 +3956,11 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s dib8000_reset(fe); dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ + state->current_demod_bw = 6000; return fe; - error: +error: kfree(state); return NULL; } diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h index 9e7a2b1..b8c11e5 100644 --- a/drivers/media/dvb-frontends/dib8000.h +++ b/drivers/media/dvb-frontends/dib8000.h @@ -33,6 +33,8 @@ struct dib8000_config { u8 output_mode; u8 refclksel; u8 enMpegOutput:1; + + struct dibx000_bandwidth_config *plltable; }; #define DEFAULT_DIB8000_I2C_ADDRESS 18 @@ -58,7 +60,7 @@ extern int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ); extern u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf); extern int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll); + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio); extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); @@ -147,7 +149,7 @@ static inline u32 dib8000_ctrl_timf(struct dvb_frontend *fe, return 0; } static inline int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll) + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; diff --git a/drivers/media/dvb-frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h index 5f48488..b538e05 100644 --- a/drivers/media/dvb-frontends/dibx000_common.h +++ b/drivers/media/dvb-frontends/dibx000_common.h @@ -193,7 +193,8 @@ enum frontend_tune_state { CT_DEMOD_STEP_8, CT_DEMOD_STEP_9, CT_DEMOD_STEP_10, - CT_DEMOD_SEARCH_NEXT = 41, + CT_DEMOD_STEP_11, + CT_DEMOD_SEARCH_NEXT = 51, CT_DEMOD_STEP_LOCKED, CT_DEMOD_STOP, diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h index 216c8c3..5f1d6b5 100644 --- a/drivers/media/dvb-frontends/drxd.h +++ b/drivers/media/dvb-frontends/drxd.h @@ -24,6 +24,7 @@ #ifndef _DRXD_H_ #define _DRXD_H_ +#include <linux/kconfig.h> #include <linux/types.h> #include <linux/i2c.h> @@ -51,8 +52,7 @@ struct drxd_config { s16(*osc_deviation) (void *priv, s16 dev, int flag); }; -#if defined(CONFIG_DVB_DRXD) || \ - (defined(CONFIG_DVB_DRXD_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DRXD) extern struct dvb_frontend *drxd_attach(const struct drxd_config *config, void *priv, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h index 94fecfb..e666718 100644 --- a/drivers/media/dvb-frontends/drxk.h +++ b/drivers/media/dvb-frontends/drxk.h @@ -1,6 +1,7 @@ #ifndef _DRXK_H_ #define _DRXK_H_ +#include <linux/kconfig.h> #include <linux/types.h> #include <linux/i2c.h> @@ -52,8 +53,7 @@ struct drxk_config { int qam_demod_parameter_count; }; -#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DRXK) extern struct dvb_frontend *drxk_attach(const struct drxk_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index c2fc7da0d..ec24d71 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1947,8 +1947,7 @@ static int ShutDown(struct drxk_state *state) return 0; } -static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus, - u32 Time) +static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus) { int status = -EINVAL; @@ -2490,32 +2489,6 @@ error: return status; } -static int ReadIFAgc(struct drxk_state *state, u32 *pValue) -{ - u16 agcDacLvl; - int status; - u16 Level = 0; - - dprintk(1, "\n"); - - status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl); - if (status < 0) { - printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); - return status; - } - - *pValue = 0; - - if (agcDacLvl > DRXK_AGC_DAC_OFFSET) - Level = agcDacLvl - DRXK_AGC_DAC_OFFSET; - if (Level < 14000) - *pValue = (14000 - Level) / 4; - else - *pValue = 0; - - return status; -} - static int GetQAMSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise) { @@ -2654,12 +2627,7 @@ static int GetDVBTSignalToNoise(struct drxk_state *state, /* log(x) x = (16bits + 16bits) << 15 ->32 bits */ c = Log10Times100(SqrErrIQ); - iMER = a + b; - /* No negative MER, clip to zero */ - if (iMER > c) - iMER -= c; - else - iMER = 0; + iMER = a + b - c; } *pSignalToNoise = iMER; @@ -6380,46 +6348,257 @@ static int drxk_set_parameters(struct dvb_frontend *fe) fe->ops.tuner_ops.get_if_frequency(fe, &IF); Start(state, 0, IF); + /* After set_frontend, stats aren't avaliable */ + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */ return 0; } -static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int get_strength(struct drxk_state *state, u64 *strength) { + int status; + struct SCfgAgc rfAgc, ifAgc; + u32 totalGain = 0; + u32 atten = 0; + u32 agcRange = 0; + u16 scu_lvl = 0; + u16 scu_coc = 0; + /* FIXME: those are part of the tuner presets */ + u16 tunerRfGain = 50; /* Default value on az6007 driver */ + u16 tunerIfGain = 40; /* Default value on az6007 driver */ + + *strength = 0; + + if (IsDVBT(state)) { + rfAgc = state->m_dvbtRfAgcCfg; + ifAgc = state->m_dvbtIfAgcCfg; + } else if (IsQAM(state)) { + rfAgc = state->m_qamRfAgcCfg; + ifAgc = state->m_qamIfAgcCfg; + } else { + rfAgc = state->m_atvRfAgcCfg; + ifAgc = state->m_atvIfAgcCfg; + } + + if (rfAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) { + /* SCU outputLevel */ + status = read16(state, SCU_RAM_AGC_RF_IACCU_HI__A, &scu_lvl); + if (status < 0) + return status; + + /* SCU c.o.c. */ + read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc); + if (status < 0) + return status; + + if (((u32) scu_lvl + (u32) scu_coc) < 0xffff) + rfAgc.outputLevel = scu_lvl + scu_coc; + else + rfAgc.outputLevel = 0xffff; + + /* Take RF gain into account */ + totalGain += tunerRfGain; + + /* clip output value */ + if (rfAgc.outputLevel < rfAgc.minOutputLevel) + rfAgc.outputLevel = rfAgc.minOutputLevel; + if (rfAgc.outputLevel > rfAgc.maxOutputLevel) + rfAgc.outputLevel = rfAgc.maxOutputLevel; + + agcRange = (u32) (rfAgc.maxOutputLevel - rfAgc.minOutputLevel); + if (agcRange > 0) { + atten += 100UL * + ((u32)(tunerRfGain)) * + ((u32)(rfAgc.outputLevel - rfAgc.minOutputLevel)) + / agcRange; + } + } + + if (ifAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) { + status = read16(state, SCU_RAM_AGC_IF_IACCU_HI__A, + &ifAgc.outputLevel); + if (status < 0) + return status; + + status = read16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, + &ifAgc.top); + if (status < 0) + return status; + + /* Take IF gain into account */ + totalGain += (u32) tunerIfGain; + + /* clip output value */ + if (ifAgc.outputLevel < ifAgc.minOutputLevel) + ifAgc.outputLevel = ifAgc.minOutputLevel; + if (ifAgc.outputLevel > ifAgc.maxOutputLevel) + ifAgc.outputLevel = ifAgc.maxOutputLevel; + + agcRange = (u32) (ifAgc.maxOutputLevel - ifAgc.minOutputLevel); + if (agcRange > 0) { + atten += 100UL * + ((u32)(tunerIfGain)) * + ((u32)(ifAgc.outputLevel - ifAgc.minOutputLevel)) + / agcRange; + } + } + + /* + * Convert to 0..65535 scale. + * If it can't be measured (AGC is disabled), just show 100%. + */ + if (totalGain > 0) + *strength = (65535UL * atten / totalGain / 100); + else + *strength = 65535; + + return 0; +} + +static int drxk_get_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct drxk_state *state = fe->demodulator_priv; + int status; u32 stat; - - dprintk(1, "\n"); + u16 reg16; + u32 post_bit_count; + u32 post_bit_err_count; + u32 post_bit_error_scale; + u32 pre_bit_err_count; + u32 pre_bit_count; + u32 pkt_count; + u32 pkt_error_count; + s32 cnr; if (state->m_DrxkState == DRXK_NO_DEV) return -ENODEV; if (state->m_DrxkState == DRXK_UNINITIALIZED) return -EAGAIN; - *status = 0; - GetLockStatus(state, &stat, 0); + /* get status */ + state->fe_status = 0; + GetLockStatus(state, &stat); if (stat == MPEG_LOCK) - *status |= 0x1f; + state->fe_status |= 0x1f; if (stat == FEC_LOCK) - *status |= 0x0f; + state->fe_status |= 0x0f; if (stat == DEMOD_LOCK) - *status |= 0x07; - return 0; + state->fe_status |= 0x07; + + /* + * Estimate signal strength from AGC + */ + get_strength(state, &c->strength.stat[0].uvalue); + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + + + if (stat >= DEMOD_LOCK) { + GetSignalToNoise(state, &cnr); + c->cnr.stat[0].svalue = cnr * 100; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + if (stat < FEC_LOCK) { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Get post BER */ + + /* BER measurement is valid if at least FEC lock is achieved */ + + /* OFDM_EC_VD_REQ_SMB_CNT__A and/or OFDM_EC_VD_REQ_BIT_CNT can be written + to set nr of symbols or bits over which + to measure EC_VD_REG_ERR_BIT_CNT__A . See CtrlSetCfg(). */ + + /* Read registers for post/preViterbi BER calculation */ + status = read16(state, OFDM_EC_VD_ERR_BIT_CNT__A, ®16); + if (status < 0) + goto error; + pre_bit_err_count = reg16; + + status = read16(state, OFDM_EC_VD_IN_BIT_CNT__A , ®16); + if (status < 0) + goto error; + pre_bit_count = reg16; + + /* Number of bit-errors */ + status = read16(state, FEC_RS_NR_BIT_ERRORS__A, ®16); + if (status < 0) + goto error; + post_bit_err_count = reg16; + + status = read16(state, FEC_RS_MEASUREMENT_PRESCALE__A, ®16); + if (status < 0) + goto error; + post_bit_error_scale = reg16; + + status = read16(state, FEC_RS_MEASUREMENT_PERIOD__A, ®16); + if (status < 0) + goto error; + pkt_count = reg16; + + status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, ®16); + if (status < 0) + goto error; + pkt_error_count = reg16; + write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0); + + post_bit_err_count *= post_bit_error_scale; + + post_bit_count = pkt_count * 204 * 8; + + /* Store the results */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += pkt_error_count; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += pkt_count; + + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue += pre_bit_err_count; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue += pre_bit_count; + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += post_bit_err_count; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += post_bit_count; + +error: + return status; } -static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber) + +static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct drxk_state *state = fe->demodulator_priv; + int rc; dprintk(1, "\n"); - if (state->m_DrxkState == DRXK_NO_DEV) - return -ENODEV; - if (state->m_DrxkState == DRXK_UNINITIALIZED) - return -EAGAIN; + rc = drxk_get_stats(fe); + if (rc < 0) + return rc; + + *status = state->fe_status; - *ber = 0; return 0; } @@ -6427,7 +6606,7 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct drxk_state *state = fe->demodulator_priv; - u32 val = 0; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; dprintk(1, "\n"); @@ -6436,8 +6615,7 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe, if (state->m_DrxkState == DRXK_UNINITIALIZED) return -EAGAIN; - ReadIFAgc(state, &val); - *strength = val & 0xffff; + *strength = c->strength.stat[0].uvalue; return 0; } @@ -6454,6 +6632,10 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) return -EAGAIN; GetSignalToNoise(state, &snr2); + + /* No negative SNR, clip to zero */ + if (snr2 < 0) + snr2 = 0; *snr = snr2 & 0xffff; return 0; } @@ -6529,7 +6711,6 @@ static struct dvb_frontend_ops drxk_ops = { .get_tune_settings = drxk_get_tune_settings, .read_status = drxk_read_status, - .read_ber = drxk_read_ber, .read_signal_strength = drxk_read_signal_strength, .read_snr = drxk_read_snr, .read_ucblocks = drxk_read_ucblocks, @@ -6538,6 +6719,7 @@ static struct dvb_frontend_ops drxk_ops = { struct dvb_frontend *drxk_attach(const struct drxk_config *config, struct i2c_adapter *i2c) { + struct dtv_frontend_properties *p; struct drxk_state *state = NULL; u8 adr = config->adr; int status; @@ -6618,6 +6800,27 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, } else if (init_drxk(state) < 0) goto error; + + /* Initialize stats */ + p = &state->frontend.dtv_property_cache; + p->strength.len = 1; + p->cnr.len = 1; + p->block_error.len = 1; + p->block_count.len = 1; + p->pre_bit_error.len = 1; + p->pre_bit_count.len = 1; + p->post_bit_error.len = 1; + p->post_bit_count.len = 1; + + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + printk(KERN_INFO "drxk: frontend initialized.\n"); return &state->frontend; diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h index d18a896..b8424f1 100644 --- a/drivers/media/dvb-frontends/drxk_hard.h +++ b/drivers/media/dvb-frontends/drxk_hard.h @@ -345,6 +345,8 @@ struct drxk_state { bool antenna_dvbt; u16 antenna_gpio; + fe_status_t fe_status; + /* Firmware */ const char *microcode_name; struct completion fw_wait_load; diff --git a/drivers/media/dvb-frontends/drxk_map.h b/drivers/media/dvb-frontends/drxk_map.h index 23e16c1..761613f 100644 --- a/drivers/media/dvb-frontends/drxk_map.h +++ b/drivers/media/dvb-frontends/drxk_map.h @@ -10,6 +10,7 @@ #define FEC_RS_COMM_EXEC_STOP 0x0 #define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012 #define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013 +#define FEC_RS_NR_BIT_ERRORS__A 0x1C30014 #define FEC_OC_MODE__A 0x1C40011 #define FEC_OC_MODE_PARITY__M 0x1 #define FEC_OC_DTO_MODE__A 0x1C40014 @@ -129,6 +130,8 @@ #define OFDM_EC_SB_PRIOR__A 0x3410013 #define OFDM_EC_SB_PRIOR_HI 0x0 #define OFDM_EC_SB_PRIOR_LO 0x1 +#define OFDM_EC_VD_ERR_BIT_CNT__A 0x3420017 +#define OFDM_EC_VD_IN_BIT_CNT__A 0x3420018 #define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054 #define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3 #define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2 diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 478ad66..f9c21fb 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -22,6 +22,7 @@ #ifndef DS3000_H #define DS3000_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct ds3000_config { @@ -34,8 +35,7 @@ struct ds3000_config { void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; -#if defined(CONFIG_DVB_DS3000) || \ - (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DS3000) extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h index 1fcb987..0cbf961 100644 --- a/drivers/media/dvb-frontends/dvb_dummy_fe.h +++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h @@ -22,11 +22,11 @@ #ifndef DVB_DUMMY_FE_H #define DVB_DUMMY_FE_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" -#if defined(CONFIG_DVB_DUMMY_FE) || (defined(CONFIG_DVB_DUMMY_FE_MODULE) && \ -defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DUMMY_FE) extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void); extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void); extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void); diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h index b847971..3755840 100644 --- a/drivers/media/dvb-frontends/ec100.h +++ b/drivers/media/dvb-frontends/ec100.h @@ -22,6 +22,7 @@ #ifndef EC100_H #define EC100_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct ec100_config { @@ -30,8 +31,7 @@ struct ec100_config { }; -#if defined(CONFIG_DVB_EC100) || \ - (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_EC100) extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/hd29l2.h b/drivers/media/dvb-frontends/hd29l2.h index 4ad00d7..05cd130 100644 --- a/drivers/media/dvb-frontends/hd29l2.h +++ b/drivers/media/dvb-frontends/hd29l2.h @@ -23,6 +23,7 @@ #ifndef HD29L2_H #define HD29L2_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct hd29l2_config { @@ -50,8 +51,7 @@ struct hd29l2_config { }; -#if defined(CONFIG_DVB_HD29L2) || \ - (defined(CONFIG_DVB_HD29L2_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_HD29L2) extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c index 0cb3f0f..c77002f 100644 --- a/drivers/media/dvb-frontends/isl6421.c +++ b/drivers/media/dvb-frontends/isl6421.c @@ -89,6 +89,30 @@ static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; } +static int isl6421_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; + struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, + .buf = &isl6421->config, + .len = sizeof(isl6421->config) }; + + switch (tone) { + case SEC_TONE_ON: + isl6421->config |= ISL6421_ENT1; + break; + case SEC_TONE_OFF: + isl6421->config &= ~ISL6421_ENT1; + break; + default: + return -EINVAL; + } + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + static void isl6421_release(struct dvb_frontend *fe) { /* power off */ @@ -100,7 +124,7 @@ static void isl6421_release(struct dvb_frontend *fe) } struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, - u8 override_set, u8 override_clear) + u8 override_set, u8 override_clear, bool override_tone) { struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); if (!isl6421) @@ -131,6 +155,8 @@ struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter /* override frontend ops */ fe->ops.set_voltage = isl6421_set_voltage; fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; + if (override_tone) + fe->ops.set_tone = isl6421_set_tone; return fe; } diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h index e7ca7d1..630e7f8 100644 --- a/drivers/media/dvb-frontends/isl6421.h +++ b/drivers/media/dvb-frontends/isl6421.h @@ -42,10 +42,10 @@ #if IS_ENABLED(CONFIG_DVB_ISL6421) /* override_set and override_clear control which system register bits (above) to always set & clear */ extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, - u8 override_set, u8 override_clear); + u8 override_set, u8 override_clear, bool override_tone); #else static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, - u8 override_set, u8 override_clear) + u8 override_set, u8 override_clear, bool override_tone) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/dvb-frontends/it913x-fe.h b/drivers/media/dvb-frontends/it913x-fe.h index 07fa459..df0ad42 100644 --- a/drivers/media/dvb-frontends/it913x-fe.h +++ b/drivers/media/dvb-frontends/it913x-fe.h @@ -21,6 +21,7 @@ #ifndef IT913X_FE_H #define IT913X_FE_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -38,8 +39,7 @@ struct ite_config { u8 read_slevel; }; -#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \ -defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_IT913X_FE) extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct ite_config *config); #else diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h index 67e89d6..1a735a7 100644 --- a/drivers/media/dvb-frontends/ix2505v.h +++ b/drivers/media/dvb-frontends/ix2505v.h @@ -20,6 +20,7 @@ #ifndef DVB_IX2505V_H #define DVB_IX2505V_H +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -48,8 +49,7 @@ struct ix2505v_config { }; -#if defined(CONFIG_DVB_IX2505V) || \ - (defined(CONFIG_DVB_IX2505V_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_IX2505V) extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, const struct ix2505v_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h index 9e2c0f4..194a07a 100644 --- a/drivers/media/dvb-frontends/lg2160.h +++ b/drivers/media/dvb-frontends/lg2160.h @@ -22,6 +22,7 @@ #ifndef _LG2160_H_ #define _LG2160_H_ +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -56,18 +57,17 @@ struct lg2160_config { u16 if_khz; /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ - int deny_i2c_rptr:1; + unsigned int deny_i2c_rptr:1; /* spectral inversion - 0:disabled 1:enabled */ - int spectral_inversion:1; + unsigned int spectral_inversion:1; unsigned int output_if; enum lg2160_spi_clock spi_clock; enum lg_chip_type lg_chip; }; -#if defined(CONFIG_DVB_LG2160) || (defined(CONFIG_DVB_LG2160_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LG2160) extern struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, struct i2c_adapter *i2c_adap); diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h index 02172ec..d9ab556 100644 --- a/drivers/media/dvb-frontends/lgdt3305.h +++ b/drivers/media/dvb-frontends/lgdt3305.h @@ -22,6 +22,7 @@ #ifndef _LGDT3305_H_ #define _LGDT3305_H_ +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -73,8 +74,7 @@ struct lgdt3305_config { enum lgdt_demod_chip_type demod_chip; }; -#if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LGDT3305) extern struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, struct i2c_adapter *i2c_adap); diff --git a/drivers/media/dvb-frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h index d1417678..c2da596 100644 --- a/drivers/media/dvb-frontends/lgs8gl5.h +++ b/drivers/media/dvb-frontends/lgs8gl5.h @@ -23,6 +23,7 @@ #ifndef LGS8GL5_H #define LGS8GL5_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct lgs8gl5_config { @@ -30,8 +31,7 @@ struct lgs8gl5_config { u8 demod_address; }; -#if defined(CONFIG_DVB_LGS8GL5) || \ - (defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LGS8GL5) extern struct dvb_frontend *lgs8gl5_attach( const struct lgs8gl5_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h index 33c3c5e..dadb78b 100644 --- a/drivers/media/dvb-frontends/lgs8gxx.h +++ b/drivers/media/dvb-frontends/lgs8gxx.h @@ -26,6 +26,7 @@ #ifndef __LGS8GXX_H__ #define __LGS8GXX_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include <linux/i2c.h> @@ -79,8 +80,7 @@ struct lgs8gxx_config { u8 tuner_address; }; -#if defined(CONFIG_DVB_LGS8GXX) || \ - (defined(CONFIG_DVB_LGS8GXX_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LGS8GXX) extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h index c059b16..b327a4f 100644 --- a/drivers/media/dvb-frontends/lnbh24.h +++ b/drivers/media/dvb-frontends/lnbh24.h @@ -23,6 +23,8 @@ #ifndef _LNBH24_H #define _LNBH24_H +#include <linux/kconfig.h> + /* system register bits */ #define LNBH24_OLF 0x01 #define LNBH24_OTF 0x02 @@ -35,8 +37,7 @@ #include <linux/dvb/frontend.h> -#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LNBP21) /* override_set and override_clear control which system register bits (above) to always set & clear */ extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h index fcdf1c6..dbcbcc2 100644 --- a/drivers/media/dvb-frontends/lnbp21.h +++ b/drivers/media/dvb-frontends/lnbp21.h @@ -27,6 +27,8 @@ #ifndef _LNBP21_H #define _LNBP21_H +#include <linux/kconfig.h> + /* system register bits */ /* [RO] 0=OK; 1=over current limit flag */ #define LNBP21_OLF 0x01 @@ -55,8 +57,7 @@ #include <linux/dvb/frontend.h> -#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LNBP21) /* override_set and override_clear control which system register bits (above) to always set & clear */ extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h index 63e2dec..63861b3 100644 --- a/drivers/media/dvb-frontends/lnbp22.h +++ b/drivers/media/dvb-frontends/lnbp22.h @@ -28,6 +28,8 @@ #ifndef _LNBP22_H #define _LNBP22_H +#include <linux/kconfig.h> + /* Enable */ #define LNBP22_EN 0x10 /* Voltage selection */ @@ -37,8 +39,7 @@ #include <linux/dvb/frontend.h> -#if defined(CONFIG_DVB_LNBP22) || \ - (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LNBP22) /* * override_set and override_clear control which system register bits (above) * to always set & clear diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 5a8023e..14ce31e 100644 --- a/drivers/media/dvb-frontends/m88rs2000.h +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -20,6 +20,7 @@ #ifndef M88RS2000_H #define M88RS2000_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -40,8 +41,7 @@ enum { CALL_IS_READ, }; -#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_M88RS2000) extern struct dvb_frontend *m88rs2000_attach( const struct m88rs2000_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4faaf80..856374b 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -20,10 +20,24 @@ #include "dvb_frontend.h" #include "mb86a20s.h" +#define NUM_LAYERS 3 + static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +enum mb86a20s_bandwidth { + MB86A20S_13SEG = 0, + MB86A20S_13SEG_PARTIAL = 1, + MB86A20S_1SEG = 2, + MB86A20S_3SEG = 3, +}; + +u8 mb86a20s_subchannel[] = { + 0xb0, 0xc0, 0xd0, 0xe0, + 0xf0, 0x00, 0x10, 0x20, +}; + struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; @@ -31,7 +45,13 @@ struct mb86a20s_state { struct dvb_frontend frontend; - u32 estimated_rate[3]; + u32 if_freq; + enum mb86a20s_bandwidth bw; + bool inversion; + u32 subchannel; + + u32 estimated_rate[NUM_LAYERS]; + unsigned long get_strength_time; bool need_init; }; @@ -47,35 +67,33 @@ struct regdata { * Initialization sequence: Use whatevere default values that PV SBTVD * does on its initialisation, obtained via USB snoop */ -static struct regdata mb86a20s_init[] = { +static struct regdata mb86a20s_init1[] = { { 0x70, 0x0f }, { 0x70, 0xff }, { 0x08, 0x01 }, - { 0x09, 0x3e }, - { 0x50, 0xd1 }, { 0x51, 0x22 }, - { 0x39, 0x01 }, - { 0x71, 0x00 }, - { 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 }, - { 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 }, + { 0x50, 0xd1 }, { 0x51, 0x20 }, +}; + +static struct regdata mb86a20s_init2[] = { { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, { 0x3b, 0x21 }, - { 0x3c, 0x3a }, + { 0x3c, 0x38 }, { 0x01, 0x0d }, - { 0x04, 0x08 }, { 0x05, 0x05 }, + { 0x04, 0x08 }, { 0x05, 0x03 }, { 0x04, 0x0e }, { 0x05, 0x00 }, - { 0x04, 0x0f }, { 0x05, 0x14 }, - { 0x04, 0x0b }, { 0x05, 0x8c }, + { 0x04, 0x0f }, { 0x05, 0x37 }, + { 0x04, 0x0b }, { 0x05, 0x78 }, { 0x04, 0x00 }, { 0x05, 0x00 }, - { 0x04, 0x01 }, { 0x05, 0x07 }, - { 0x04, 0x02 }, { 0x05, 0x0f }, - { 0x04, 0x03 }, { 0x05, 0xa0 }, + { 0x04, 0x01 }, { 0x05, 0x1e }, + { 0x04, 0x02 }, { 0x05, 0x07 }, + { 0x04, 0x03 }, { 0x05, 0xd0 }, { 0x04, 0x09 }, { 0x05, 0x00 }, { 0x04, 0x0a }, { 0x05, 0xff }, - { 0x04, 0x27 }, { 0x05, 0x64 }, + { 0x04, 0x27 }, { 0x05, 0x00 }, { 0x04, 0x28 }, { 0x05, 0x00 }, - { 0x04, 0x1e }, { 0x05, 0xff }, - { 0x04, 0x29 }, { 0x05, 0x0a }, - { 0x04, 0x32 }, { 0x05, 0x0a }, + { 0x04, 0x1e }, { 0x05, 0x00 }, + { 0x04, 0x29 }, { 0x05, 0x64 }, + { 0x04, 0x32 }, { 0x05, 0x02 }, { 0x04, 0x14 }, { 0x05, 0x02 }, { 0x04, 0x04 }, { 0x05, 0x00 }, { 0x04, 0x05 }, { 0x05, 0x22 }, @@ -142,39 +160,39 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, - { 0x50, 0xd7 }, { 0x51, 0x3f }, - { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, - { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, + { 0x50, 0xd7 }, { 0x51, 0xbf }, + { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xff }, + { 0x28, 0x46 }, { 0x29, 0x00 }, { 0x2a, 0x1a }, { 0x2b, 0x0c }, { 0x04, 0x40 }, { 0x05, 0x00 }, - { 0x28, 0x00 }, { 0x29, 0x10 }, - { 0x28, 0x05 }, { 0x29, 0x02 }, + { 0x28, 0x00 }, { 0x2b, 0x08 }, + { 0x28, 0x05 }, { 0x2b, 0x00 }, { 0x1c, 0x01 }, - { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, - { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, - { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, - { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, - { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, - { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, - { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, - { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, - { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, - { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, - { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, - { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, - { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, - { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, - { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, - { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, - { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, - { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, - { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, - { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, - { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, - { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, - { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, - { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, - { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, + { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x1f }, + { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x18 }, + { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x12 }, + { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x30 }, + { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x37 }, + { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, + { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x09 }, + { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x06 }, + { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7b }, + { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x76 }, + { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7d }, + { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x08 }, + { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0b }, + { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, + { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf2 }, + { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf3 }, + { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x05 }, + { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, + { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, + { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xef }, + { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xd8 }, + { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xf1 }, + { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x3d }, + { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x94 }, + { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xba }, { 0x50, 0x1e }, { 0x51, 0x5d }, { 0x50, 0x22 }, { 0x51, 0x00 }, { 0x50, 0x23 }, { 0x51, 0xc8 }, @@ -183,6 +201,8 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0x26 }, { 0x51, 0x00 }, { 0x50, 0x27 }, { 0x51, 0xc3 }, { 0x50, 0x39 }, { 0x51, 0x02 }, + { 0xec, 0x0f }, + { 0xeb, 0x1f }, { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, { 0xd0, 0x00 }, }; @@ -308,15 +328,23 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", __func__, *status, val); - return 0; + return val; } static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int rc; unsigned rf_max, rf_min, rf; + if (state->get_strength_time && + (!time_after(jiffies, state->get_strength_time))) + return c->strength.stat[0].uvalue; + + /* Reset its value if an error happen */ + c->strength.stat[0].uvalue = 0; + /* Does a binary search to get RF strength */ rf_max = 0xfff; rf_min = 0; @@ -331,7 +359,7 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) rc = mb86a20s_writereg(state, 0x04, 0x20); if (rc < 0) return rc; - rc = mb86a20s_writereg(state, 0x04, rf); + rc = mb86a20s_writereg(state, 0x05, rf); if (rc < 0) return rc; @@ -346,15 +374,19 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) rf = (rf_max + rf_min) / 2; /* Rescale it from 2^12 (4096) to 2^16 */ - rf <<= (16 - 12); + rf = rf << (16 - 12); + if (rf) + rf |= (1 << 12) - 1; + dev_dbg(&state->i2c->dev, "%s: signal strength = %d (%d < RF=%d < %d)\n", __func__, rf, rf_min, rf >> 4, rf_max); - return rf; + c->strength.stat[0].uvalue = rf; + state->get_strength_time = jiffies + + msecs_to_jiffies(1000); + return 0; } } while (1); - - return 0; } static int mb86a20s_get_modulation(struct mb86a20s_state *state, @@ -534,12 +566,13 @@ static u32 isdbt_rate[3][5][4] = { }; static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, - u32 modulation, u32 fec, u32 interleaving, + u32 modulation, u32 forward_error_correction, + u32 interleaving, u32 segment) { struct mb86a20s_state *state = fe->demodulator_priv; u32 rate; - int m, f, i; + int mod, fec, guard; /* * If modulation/fec/interleaving is not detected, the default is @@ -550,54 +583,54 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, case DQPSK: case QPSK: default: - m = 0; + mod = 0; break; case QAM_16: - m = 1; + mod = 1; break; case QAM_64: - m = 2; + mod = 2; break; } - switch (fec) { + switch (forward_error_correction) { default: case FEC_1_2: case FEC_AUTO: - f = 0; + fec = 0; break; case FEC_2_3: - f = 1; + fec = 1; break; case FEC_3_4: - f = 2; + fec = 2; break; case FEC_5_6: - f = 3; + fec = 3; break; case FEC_7_8: - f = 4; + fec = 4; break; } switch (interleaving) { default: case GUARD_INTERVAL_1_4: - i = 0; + guard = 0; break; case GUARD_INTERVAL_1_8: - i = 1; + guard = 1; break; case GUARD_INTERVAL_1_16: - i = 2; + guard = 2; break; case GUARD_INTERVAL_1_32: - i = 3; + guard = 3; break; } /* Samples BER at BER_SAMPLING_RATE seconds */ - rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE; + rate = isdbt_rate[mod][fec][guard] * segment * BER_SAMPLING_RATE; /* Avoids sampling too quickly or to overflow the register */ if (rate < 256) @@ -607,18 +640,18 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, dev_dbg(&state->i2c->dev, "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", - __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000, + __func__, 'A' + layer, + segment * isdbt_rate[mod][fec][guard]/1000, rate, rate); state->estimated_rate[layer] = rate; } - static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int i, rc; + int layer, rc; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); @@ -636,43 +669,43 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) /* Get per-layer data */ - for (i = 0; i < 3; i++) { + for (layer = 0; layer < NUM_LAYERS; layer++) { dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n", - __func__, 'A' + i); + __func__, 'A' + layer); - rc = mb86a20s_get_segment_count(state, i); + rc = mb86a20s_get_segment_count(state, layer); if (rc < 0) goto noperlayer_error; if (rc >= 0 && rc < 14) { - c->layer[i].segment_count = rc; + c->layer[layer].segment_count = rc; } else { - c->layer[i].segment_count = 0; - state->estimated_rate[i] = 0; + c->layer[layer].segment_count = 0; + state->estimated_rate[layer] = 0; continue; } - c->isdbt_layer_enabled |= 1 << i; - rc = mb86a20s_get_modulation(state, i); + c->isdbt_layer_enabled |= 1 << layer; + rc = mb86a20s_get_modulation(state, layer); if (rc < 0) goto noperlayer_error; dev_dbg(&state->i2c->dev, "%s: modulation %d.\n", __func__, rc); - c->layer[i].modulation = rc; - rc = mb86a20s_get_fec(state, i); + c->layer[layer].modulation = rc; + rc = mb86a20s_get_fec(state, layer); if (rc < 0) goto noperlayer_error; dev_dbg(&state->i2c->dev, "%s: FEC %d.\n", __func__, rc); - c->layer[i].fec = rc; - rc = mb86a20s_get_interleaving(state, i); + c->layer[layer].fec = rc; + rc = mb86a20s_get_interleaving(state, layer); if (rc < 0) goto noperlayer_error; dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", __func__, rc); - c->layer[i].interleaving = rc; - mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation, - c->layer[i].fec, - c->layer[i].interleaving, - c->layer[i].segment_count); + c->layer[layer].interleaving = rc; + mb86a20s_layer_bitrate(fe, layer, c->layer[layer].modulation, + c->layer[layer].fec, + c->layer[layer].interleaving, + c->layer[layer].segment_count); } rc = mb86a20s_writereg(state, 0x6d, 0x84); @@ -735,7 +768,6 @@ static int mb86a20s_reset_counters(struct dvb_frontend *fe) /* Reset the counters, if the channel changed */ if (state->last_frequency != c->frequency) { - memset(&c->strength, 0, sizeof(c->strength)); memset(&c->cnr, 0, sizeof(c->cnr)); memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error)); memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count)); @@ -799,7 +831,7 @@ static int mb86a20s_get_pre_ber(struct dvb_frontend *fe, dev_dbg(&state->i2c->dev, "%s called.\n", __func__); - if (layer >= 3) + if (layer >= NUM_LAYERS) return -EINVAL; /* Check if the BER measures are already available */ @@ -933,7 +965,7 @@ static int mb86a20s_get_post_ber(struct dvb_frontend *fe, dev_dbg(&state->i2c->dev, "%s called.\n", __func__); - if (layer >= 3) + if (layer >= NUM_LAYERS) return -EINVAL; /* Check if the BER measures are already available */ @@ -1060,7 +1092,7 @@ static int mb86a20s_get_blk_error(struct dvb_frontend *fe, u32 collect_rate; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); - if (layer >= 3) + if (layer >= NUM_LAYERS) return -EINVAL; /* Check if the PER measures are already available */ @@ -1095,7 +1127,7 @@ static int mb86a20s_get_blk_error(struct dvb_frontend *fe, if (rc < 0) return rc; *error |= rc; - dev_err(&state->i2c->dev, "%s: block error for layer %c: %d.\n", + dev_dbg(&state->i2c->dev, "%s: block error for layer %c: %d.\n", __func__, 'A' + layer, *error); /* Read Bit Count */ @@ -1386,7 +1418,7 @@ static int mb86a20s_get_main_CNR(struct dvb_frontend *fe) return rc; if (!(rc & 0x40)) { - dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n", + dev_dbg(&state->i2c->dev, "%s: CNR is not available yet.\n", __func__); return -EBUSY; } @@ -1425,7 +1457,7 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; u32 mer, cnr; - int rc, val, i; + int rc, val, layer; struct linear_segments *segs; unsigned segs_len; @@ -1441,33 +1473,33 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) /* Check if data is available */ if (!(rc & 0x01)) { - dev_info(&state->i2c->dev, + dev_dbg(&state->i2c->dev, "%s: MER measures aren't available yet.\n", __func__); return -EBUSY; } /* Read all layers */ - for (i = 0; i < 3; i++) { - if (!(c->isdbt_layer_enabled & (1 << i))) { - c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + for (layer = 0; layer < NUM_LAYERS; layer++) { + if (!(c->isdbt_layer_enabled & (1 << layer))) { + c->cnr.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; continue; } - rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3); + rc = mb86a20s_writereg(state, 0x50, 0x52 + layer * 3); if (rc < 0) return rc; rc = mb86a20s_readreg(state, 0x51); if (rc < 0) return rc; mer = rc << 16; - rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3); + rc = mb86a20s_writereg(state, 0x50, 0x53 + layer * 3); if (rc < 0) return rc; rc = mb86a20s_readreg(state, 0x51); if (rc < 0) return rc; mer |= rc << 8; - rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3); + rc = mb86a20s_writereg(state, 0x50, 0x54 + layer * 3); if (rc < 0) return rc; rc = mb86a20s_readreg(state, 0x51); @@ -1475,7 +1507,7 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) return rc; mer |= rc; - switch (c->layer[i].modulation) { + switch (c->layer[layer].modulation) { case DQPSK: case QPSK: segs = cnr_qpsk_table; @@ -1493,12 +1525,12 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) } cnr = interpolate_value(mer, segs, segs_len); - c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL; - c->cnr.stat[1 + i].svalue = cnr; + c->cnr.stat[1 + layer].scale = FE_SCALE_DECIBEL; + c->cnr.stat[1 + layer].svalue = cnr; dev_dbg(&state->i2c->dev, "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n", - __func__, 'A' + i, cnr / 1000, cnr % 1000, mer); + __func__, 'A' + layer, cnr / 1000, cnr % 1000, mer); } @@ -1526,7 +1558,7 @@ static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int i; + int layer; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); @@ -1536,35 +1568,35 @@ static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) c->strength.len = 1; /* Per-layer stats - 3 layers + global */ - c->cnr.len = 4; - c->pre_bit_error.len = 4; - c->pre_bit_count.len = 4; - c->post_bit_error.len = 4; - c->post_bit_count.len = 4; - c->block_error.len = 4; - c->block_count.len = 4; + c->cnr.len = NUM_LAYERS + 1; + c->pre_bit_error.len = NUM_LAYERS + 1; + c->pre_bit_count.len = NUM_LAYERS + 1; + c->post_bit_error.len = NUM_LAYERS + 1; + c->post_bit_count.len = NUM_LAYERS + 1; + c->block_error.len = NUM_LAYERS + 1; + c->block_count.len = NUM_LAYERS + 1; /* Signal is always available */ c->strength.stat[0].scale = FE_SCALE_RELATIVE; c->strength.stat[0].uvalue = 0; /* Put all of them at FE_SCALE_NOT_AVAILABLE */ - for (i = 0; i < 4; i++) { - c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + for (layer = 0; layer < NUM_LAYERS + 1; layer++) { + c->cnr.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; } } -static int mb86a20s_get_stats(struct dvb_frontend *fe) +static int mb86a20s_get_stats(struct dvb_frontend *fe, int status_nr) { struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int rc = 0, i; + int rc = 0, layer; u32 bit_error = 0, bit_count = 0; u32 t_pre_bit_error = 0, t_pre_bit_count = 0; u32 t_post_bit_error = 0, t_post_bit_count = 0; @@ -1580,90 +1612,98 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe) /* Get per-layer stats */ mb86a20s_get_blk_error_layer_CNR(fe); - for (i = 0; i < 3; i++) { - if (c->isdbt_layer_enabled & (1 << i)) { + /* + * At state 7, only CNR is available + * For BER measures, state=9 is required + * FIXME: we may get MER measures with state=8 + */ + if (status_nr < 9) + return 0; + + for (layer = 0; layer < NUM_LAYERS; layer++) { + if (c->isdbt_layer_enabled & (1 << layer)) { /* Layer is active and has rc segments */ active_layers++; /* Handle BER before vterbi */ - rc = mb86a20s_get_pre_ber(fe, i, + rc = mb86a20s_get_pre_ber(fe, layer, &bit_error, &bit_count); if (rc >= 0) { - c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; - c->pre_bit_error.stat[1 + i].uvalue += bit_error; - c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; - c->pre_bit_count.stat[1 + i].uvalue += bit_count; + c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[1 + layer].uvalue += bit_error; + c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[1 + layer].uvalue += bit_count; } else if (rc != -EBUSY) { /* * If an I/O error happened, * measures are now unavailable */ - c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; - c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; dev_err(&state->i2c->dev, "%s: Can't get BER for layer %c (error %d).\n", - __func__, 'A' + i, rc); + __func__, 'A' + layer, rc); } - if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) pre_ber_layers++; /* Handle BER post vterbi */ - rc = mb86a20s_get_post_ber(fe, i, + rc = mb86a20s_get_post_ber(fe, layer, &bit_error, &bit_count); if (rc >= 0) { - c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[1 + i].uvalue += bit_error; - c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[1 + i].uvalue += bit_count; + c->post_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[1 + layer].uvalue += bit_error; + c->post_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[1 + layer].uvalue += bit_count; } else if (rc != -EBUSY) { /* * If an I/O error happened, * measures are now unavailable */ - c->post_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; dev_err(&state->i2c->dev, "%s: Can't get BER for layer %c (error %d).\n", - __func__, 'A' + i, rc); + __func__, 'A' + layer, rc); } - if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) post_ber_layers++; /* Handle Block errors for PER/UCB reports */ - rc = mb86a20s_get_blk_error(fe, i, + rc = mb86a20s_get_blk_error(fe, layer, &block_error, &block_count); if (rc >= 0) { - c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER; - c->block_error.stat[1 + i].uvalue += block_error; - c->block_count.stat[1 + i].scale = FE_SCALE_COUNTER; - c->block_count.stat[1 + i].uvalue += block_count; + c->block_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->block_error.stat[1 + layer].uvalue += block_error; + c->block_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->block_count.stat[1 + layer].uvalue += block_count; } else if (rc != -EBUSY) { /* * If an I/O error happened, * measures are now unavailable */ - c->block_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; - c->block_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; dev_err(&state->i2c->dev, "%s: Can't get PER for layer %c (error %d).\n", - __func__, 'A' + i, rc); + __func__, 'A' + layer, rc); } - if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) per_layers++; /* Update total preBER */ - t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue; - t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue; + t_pre_bit_error += c->pre_bit_error.stat[1 + layer].uvalue; + t_pre_bit_count += c->pre_bit_count.stat[1 + layer].uvalue; /* Update total postBER */ - t_post_bit_error += c->post_bit_error.stat[1 + i].uvalue; - t_post_bit_count += c->post_bit_count.stat[1 + i].uvalue; + t_post_bit_error += c->post_bit_error.stat[1 + layer].uvalue; + t_post_bit_count += c->post_bit_count.stat[1 + layer].uvalue; /* Update total PER */ - t_block_error += c->block_error.stat[1 + i].uvalue; - t_block_count += c->block_count.stat[1 + i].uvalue; + t_block_error += c->block_error.stat[1 + layer].uvalue; + t_block_count += c->block_count.stat[1 + layer].uvalue; } } @@ -1737,8 +1777,10 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe) static int mb86a20s_initfe(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; + u64 pll; + u32 fclk; int rc; - u8 regD5 = 1; + u8 regD5 = 1, reg71, reg09 = 0x3a; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); @@ -1746,10 +1788,78 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 0); /* Initialize the frontend */ - rc = mb86a20s_writeregdata(state, mb86a20s_init); + rc = mb86a20s_writeregdata(state, mb86a20s_init1); if (rc < 0) goto err; + if (!state->inversion) + reg09 |= 0x04; + rc = mb86a20s_writereg(state, 0x09, reg09); + if (rc < 0) + goto err; + if (!state->bw) + reg71 = 1; + else + reg71 = 0; + rc = mb86a20s_writereg(state, 0x39, reg71); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x71, state->bw); + if (rc < 0) + goto err; + if (state->subchannel) { + rc = mb86a20s_writereg(state, 0x44, state->subchannel); + if (rc < 0) + goto err; + } + + fclk = state->config->fclk; + if (!fclk) + fclk = 32571428; + + /* Adjust IF frequency to match tuner */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &state->if_freq); + + if (!state->if_freq) + state->if_freq = 3300000; + + pll = (((u64)1) << 34) * state->if_freq; + do_div(pll, 63 * fclk); + pll = (1 << 25) - pll; + rc = mb86a20s_writereg(state, 0x28, 0x2a); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2b, pll & 0xff); + if (rc < 0) + goto err; + dev_dbg(&state->i2c->dev, "%s: fclk=%d, IF=%d, clock reg=0x%06llx\n", + __func__, fclk, state->if_freq, (long long)pll); + + /* pll = freq[Hz] * 2^24/10^6 / 16.285714286 */ + pll = state->if_freq * 1677721600L; + do_div(pll, 1628571429L); + rc = mb86a20s_writereg(state, 0x28, 0x20); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2b, pll & 0xff); + if (rc < 0) + goto err; + dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n", + __func__, state->if_freq, (long long)pll); + if (!state->config->is_serial) { regD5 &= ~1; @@ -1761,6 +1871,11 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) goto err; } + rc = mb86a20s_writeregdata(state, mb86a20s_init2); + if (rc < 0) + goto err; + + err: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -1779,15 +1894,34 @@ err: static int mb86a20s_set_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - int rc; -#if 0 - /* - * FIXME: Properly implement the set frontend properties - */ struct dtv_frontend_properties *c = &fe->dtv_property_cache; -#endif + int rc, if_freq; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + if (!c->isdbt_layer_enabled) + c->isdbt_layer_enabled = 7; + + if (c->isdbt_layer_enabled == 1) + state->bw = MB86A20S_1SEG; + else if (c->isdbt_partial_reception) + state->bw = MB86A20S_13SEG_PARTIAL; + else + state->bw = MB86A20S_13SEG; + + if (c->inversion == INVERSION_ON) + state->inversion = true; + else + state->inversion = false; + + if (!c->isdbt_sb_mode) { + state->subchannel = 0; + } else { + if (c->isdbt_sb_subchannel >= ARRAY_SIZE(mb86a20s_subchannel)) + c->isdbt_sb_subchannel = 0; + + state->subchannel = mb86a20s_subchannel[c->isdbt_sb_subchannel]; + } + /* * Gate should already be opened, but it doesn't hurt to * double-check @@ -1796,6 +1930,9 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.set_params(fe); + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + /* * Make it more reliable: if, for some reason, the initial * device initialization doesn't happen, initialize it when @@ -1805,15 +1942,22 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) * the agc callback logic is not called during DVB attach time, * causing mb86a20s to not be initialized with Kworld SBTVD. * So, this hack is needed, in order to make Kworld SBTVD to work. + * + * It is also needed to change the IF after the initial init. + * + * HACK: Always init the frontend when set_frontend is called: + * it was noticed that, on some devices, it fails to lock on a + * different channel. So, it is better to reset everything, even + * wasting some time, than to loose channel lock. */ - if (state->need_init) - mb86a20s_initfe(fe); + mb86a20s_initfe(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); mb86a20s_reset_counters(fe); + mb86a20s_stats_not_ready(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -1825,8 +1969,7 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int rc; + int rc, status_nr; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); @@ -1834,12 +1977,12 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 0); /* Get lock */ - rc = mb86a20s_read_status(fe, status); - if (!(*status & FE_HAS_LOCK)) { + status_nr = mb86a20s_read_status(fe, status); + if (status_nr < 7) { mb86a20s_stats_not_ready(fe); mb86a20s_reset_frontend_cache(fe); } - if (rc < 0) { + if (status_nr < 0) { dev_err(&state->i2c->dev, "%s: Can't read frontend lock status\n", __func__); goto error; @@ -1856,10 +1999,8 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, rc = 0; /* Status is OK */ goto error; } - /* Fill signal strength */ - c->strength.stat[0].uvalue = rc; - if (*status & FE_HAS_LOCK) { + if (status_nr >= 7) { /* Get TMCC info*/ rc = mb86a20s_get_frontend(fe); if (rc < 0) { @@ -1870,7 +2011,7 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, } /* Get statistics */ - rc = mb86a20s_get_stats(fe); + rc = mb86a20s_get_stats(fe, status_nr); if (rc < 0 && rc != -EBUSY) { dev_err(&state->i2c->dev, "%s: Can't get FE statistics.\n", __func__); @@ -1994,7 +2135,7 @@ static struct dvb_frontend_ops mb86a20s_ops = { /* Use dib8000 values per default */ .info = { .name = "Fujitsu mb86A20s", - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER | + .caps = FE_CAN_RECOVER | 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 | diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h index bf22e77..6627a39 100644 --- a/drivers/media/dvb-frontends/mb86a20s.h +++ b/drivers/media/dvb-frontends/mb86a20s.h @@ -16,21 +16,25 @@ #ifndef MB86A20S_H #define MB86A20S_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> /** * struct mb86a20s_config - Define the per-device attributes of the frontend * + * @fclk: Clock frequency. If zero, assumes the default + * (32.57142 Mhz) * @demod_address: the demodulator's i2c address + * @is_serial: if true, TS is serial. Otherwise, TS is parallel */ struct mb86a20s_config { - u8 demod_address; - bool is_serial; + u32 fclk; + u8 demod_address; + bool is_serial; }; -#if defined(CONFIG_DVB_MB86A20S) || (defined(CONFIG_DVB_MB86A20S_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MB86A20S) extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct i2c_adapter *i2c); extern struct i2c_adapter *mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *); diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h index f4349a1..3313847 100644 --- a/drivers/media/dvb-frontends/rtl2830.h +++ b/drivers/media/dvb-frontends/rtl2830.h @@ -21,6 +21,7 @@ #ifndef RTL2830_H #define RTL2830_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct rtl2830_config { @@ -59,8 +60,7 @@ struct rtl2830_config { u8 agc_targ_val; }; -#if defined(CONFIG_DVB_RTL2830) || \ - (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_RTL2830) extern struct dvb_frontend *rtl2830_attach( const struct rtl2830_config *config, struct i2c_adapter *i2c diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 7388769..facb848 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -380,13 +380,41 @@ err: return ret; } -static int rtl2832_init(struct dvb_frontend *fe) + +static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) { struct rtl2832_priv *priv = fe->demodulator_priv; - int i, ret, len; - u8 en_bbin; + int ret; u64 pset_iffreq; + u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0); + + /* + * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) + * / CrystalFreqHz) + */ + + pset_iffreq = if_freq % priv->cfg.xtal; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = -pset_iffreq; + pset_iffreq = pset_iffreq & 0x3fffff; + dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d pset_iffreq=%08x\n", + __func__, if_freq, (unsigned)pset_iffreq); + + ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + if (ret) + return ret; + + ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + + return (ret); +} + +static int rtl2832_init(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; const struct rtl2832_reg_value *init; + int i, ret, len; /* initialization values for the demodulator registers */ struct rtl2832_reg_value rtl2832_initial_regs[] = { @@ -432,22 +460,10 @@ static int rtl2832_init(struct dvb_frontend *fe) {DVBT_TR_THD_SET2, 0x6}, {DVBT_TRK_KC_I2, 0x5}, {DVBT_CR_THD_SET2, 0x1}, - {DVBT_SPEC_INV, 0x0}, }; dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); - - /* - * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) - * / CrystalFreqHz) - */ - pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; - pset_iffreq *= 0x400000; - pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); - pset_iffreq = pset_iffreq & 0x3fffff; - for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); @@ -472,6 +488,10 @@ static int rtl2832_init(struct dvb_frontend *fe) len = ARRAY_SIZE(rtl2832_tuner_init_e4000); init = rtl2832_tuner_init_e4000; break; + case RTL2832_TUNER_R820T: + len = ARRAY_SIZE(rtl2832_tuner_init_r820t); + init = rtl2832_tuner_init_r820t; + break; default: ret = -EINVAL; goto err; @@ -483,14 +503,26 @@ static int rtl2832_init(struct dvb_frontend *fe) goto err; } - /* if frequency settings */ - ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + if (!fe->ops.tuner_ops.get_if_frequency) { + ret = rtl2832_set_if(fe, priv->cfg.if_dvbt); if (ret) goto err; + } - ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); - if (ret) - goto err; + /* + * r820t NIM code does a software reset here at the demod - + * may not be needed, as there's already a software reset at set_params() + */ +#if 1 + /* soft reset */ + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; +#endif priv->sleeping = false; @@ -564,6 +596,19 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); + /* If the frontend has get_if_frequency(), use it */ + if (fe->ops.tuner_ops.get_if_frequency) { + u32 if_freq; + + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + if (ret) + goto err; + + ret = rtl2832_set_if(fe, if_freq); + if (ret) + goto err; + } + switch (c->bandwidth_hz) { case 6000000: i = 0; diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index 785a466..91b2dcf 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -21,6 +21,7 @@ #ifndef RTL2832_H #define RTL2832_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct rtl2832_config { @@ -51,11 +52,11 @@ struct rtl2832_config { #define RTL2832_TUNER_FC0012 0x26 #define RTL2832_TUNER_E4000 0x27 #define RTL2832_TUNER_FC0013 0x29 +#define RTL2832_TUNER_R820T 0x2a u8 tuner; }; -#if defined(CONFIG_DVB_RTL2832) || \ - (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_RTL2832) extern struct dvb_frontend *rtl2832_attach( const struct rtl2832_config *cfg, struct i2c_adapter *i2c diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 7d97ce9..b5f2b80 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -267,6 +267,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = { {DVBT_OPT_ADC_IQ, 0x1}, {DVBT_AD_AVI, 0x0}, {DVBT_AD_AVQ, 0x0}, + {DVBT_SPEC_INV, 0x0}, }; static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { @@ -300,6 +301,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { {DVBT_GI_PGA_STATE, 0x0}, {DVBT_EN_AGC_PGA, 0x1}, {DVBT_IF_AGC_MAN, 0x0}, + {DVBT_SPEC_INV, 0x0}, }; static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { @@ -337,6 +339,32 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { {DVBT_REG_MONSEL, 0x1}, {DVBT_REG_MON, 0x1}, {DVBT_REG_4MSEL, 0x0}, + {DVBT_SPEC_INV, 0x0}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x40}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_SPEC_INV, 0x1}, }; #endif /* RTL2832_PRIV_H */ diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h index 91f2ebd..63b1e0a 100644 --- a/drivers/media/dvb-frontends/s5h1409.h +++ b/drivers/media/dvb-frontends/s5h1409.h @@ -22,6 +22,7 @@ #ifndef __S5H1409_H__ #define __S5H1409_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct s5h1409_config { @@ -66,8 +67,7 @@ struct s5h1409_config { u8 hvr1600_opt; }; -#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S5H1409) extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h index 45ec0f8..e4f5687 100644 --- a/drivers/media/dvb-frontends/s5h1411.h +++ b/drivers/media/dvb-frontends/s5h1411.h @@ -22,6 +22,7 @@ #ifndef __S5H1411_H__ #define __S5H1411_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #define S5H1411_I2C_TOP_ADDR (0x32 >> 1) @@ -68,8 +69,7 @@ struct s5h1411_config { u8 status_mode; }; -#if defined(CONFIG_DVB_S5H1411) || \ - (defined(CONFIG_DVB_S5H1411_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S5H1411) extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h index b57438c..70917dd 100644 --- a/drivers/media/dvb-frontends/s5h1432.h +++ b/drivers/media/dvb-frontends/s5h1432.h @@ -22,6 +22,7 @@ #ifndef __S5H1432_H__ #define __S5H1432_H__ +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #define S5H1432_I2C_TOP_ADDR (0x02 >> 1) @@ -74,8 +75,7 @@ struct s5h1432_config { u8 status_mode; }; -#if defined(CONFIG_DVB_S5H1432) || \ - (defined(CONFIG_DVB_S5H1432_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S5H1432) extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h index f220d82..8d5e2a6 100644 --- a/drivers/media/dvb-frontends/s921.h +++ b/drivers/media/dvb-frontends/s921.h @@ -17,6 +17,7 @@ #ifndef S921_H #define S921_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct s921_config { @@ -24,8 +25,7 @@ struct s921_config { u8 demod_address; }; -#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S921) extern struct dvb_frontend *s921_attach(const struct s921_config *config, struct i2c_adapter *i2c); extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *); diff --git a/drivers/media/dvb-frontends/si21xx.h b/drivers/media/dvb-frontends/si21xx.h index 141b5b8..1509fed 100644 --- a/drivers/media/dvb-frontends/si21xx.h +++ b/drivers/media/dvb-frontends/si21xx.h @@ -1,6 +1,7 @@ #ifndef SI21XX_H #define SI21XX_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -12,8 +13,7 @@ struct si21xx_config { int min_delay_ms; }; -#if defined(CONFIG_DVB_SI21XX) || \ - (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_SI21XX) extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h index 7be479c..a768189 100644 --- a/drivers/media/dvb-frontends/stb6000.h +++ b/drivers/media/dvb-frontends/stb6000.h @@ -23,6 +23,7 @@ #ifndef __DVB_STB6000_H__ #define __DVB_STB6000_H__ +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -34,8 +35,7 @@ * @param i2c i2c adapter to use. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STB6000) extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h index f2b53db..a0bd931 100644 --- a/drivers/media/dvb-frontends/stv0288.h +++ b/drivers/media/dvb-frontends/stv0288.h @@ -27,6 +27,7 @@ #ifndef STV0288_H #define STV0288_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -42,8 +43,7 @@ struct stv0288_config { int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; -#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \ - defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0288) extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h index 93cc4a5..ea80b34 100644 --- a/drivers/media/dvb-frontends/stv0367.h +++ b/drivers/media/dvb-frontends/stv0367.h @@ -26,6 +26,7 @@ #ifndef STV0367_H #define STV0367_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -38,8 +39,7 @@ struct stv0367_config { int clk_pol; }; -#if defined(CONFIG_DVB_STV0367) || (defined(CONFIG_DVB_STV0367_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0367) extern struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/stv0900.h b/drivers/media/dvb-frontends/stv0900.h index 91c7ee8..e2a6dc6 100644 --- a/drivers/media/dvb-frontends/stv0900.h +++ b/drivers/media/dvb-frontends/stv0900.h @@ -26,6 +26,7 @@ #ifndef STV0900_H #define STV0900_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include "dvb_frontend.h" @@ -57,8 +58,7 @@ struct stv0900_config { void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; -#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0900) extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, struct i2c_adapter *i2c, int demod); #else diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index f36eeef..56d470a 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -3906,12 +3906,12 @@ static int stv090x_sleep(struct dvb_frontend *fe) reg = stv090x_read_reg(state, STV090x_TSTTNR1); STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) - goto err; + goto err_unlock; /* power off DiSEqC 1 */ reg = stv090x_read_reg(state, STV090x_TSTTNR2); STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) - goto err; + goto err_unlock; /* check whether path 2 is already sleeping, that is when ADC2 is off */ @@ -3930,7 +3930,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (full_standby) STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) - goto err; + goto err_unlock; reg = stv090x_read_reg(state, STV090x_STOPCLK2); /* sampling 1 clock */ STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); @@ -3941,7 +3941,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (full_standby) STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) - goto err; + goto err_unlock; break; case STV090x_DEMODULATOR_1: @@ -3949,12 +3949,12 @@ static int stv090x_sleep(struct dvb_frontend *fe) reg = stv090x_read_reg(state, STV090x_TSTTNR3); STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) - goto err; + goto err_unlock; /* power off DiSEqC 2 */ reg = stv090x_read_reg(state, STV090x_TSTTNR4); STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) - goto err; + goto err_unlock; /* check whether path 1 is already sleeping, that is when ADC1 is off */ @@ -3973,7 +3973,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (full_standby) STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) - goto err; + goto err_unlock; reg = stv090x_read_reg(state, STV090x_STOPCLK2); /* sampling 2 clock */ STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); @@ -3984,7 +3984,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (full_standby) STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) - goto err; + goto err_unlock; break; default: @@ -3997,7 +3997,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) reg = stv090x_read_reg(state, STV090x_SYNTCTRL); STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) - goto err; + goto err_unlock; } mutex_unlock(&state->internal->demod_lock); @@ -4005,8 +4005,10 @@ static int stv090x_sleep(struct dvb_frontend *fe) err_gateoff: stv090x_i2c_gate_ctrl(state, 0); -err: + goto err; +err_unlock: mutex_unlock(&state->internal->demod_lock); +err: dprintk(FE_ERROR, 1, "I/O error"); return -1; } diff --git a/drivers/media/dvb-frontends/stv6110.h b/drivers/media/dvb-frontends/stv6110.h index fe71bba..8fa07e6 100644 --- a/drivers/media/dvb-frontends/stv6110.h +++ b/drivers/media/dvb-frontends/stv6110.h @@ -25,6 +25,7 @@ #ifndef __DVB_STV6110_H__ #define __DVB_STV6110_H__ +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -45,8 +46,7 @@ struct stv6110_config { u8 clk_div; /* divisor value for the output clock */ }; -#if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV6110) extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, const struct stv6110_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/tda10048.h b/drivers/media/dvb-frontends/tda10048.h index fb2ef5a..5e7bf4e 100644 --- a/drivers/media/dvb-frontends/tda10048.h +++ b/drivers/media/dvb-frontends/tda10048.h @@ -22,6 +22,7 @@ #ifndef TDA10048_H #define TDA10048_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> #include <linux/firmware.h> @@ -72,8 +73,7 @@ struct tda10048_config { u8 pll_n; }; -#if defined(CONFIG_DVB_TDA10048) || \ - (defined(CONFIG_DVB_TDA10048_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10048) extern struct dvb_frontend *tda10048_attach( const struct tda10048_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index bff1c38..f9542f6 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -21,6 +21,7 @@ #ifndef TDA10071_H #define TDA10071_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct tda10071_config { @@ -71,8 +72,7 @@ struct tda10071_config { }; -#if defined(CONFIG_DVB_TDA10071) || \ - (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10071) extern struct dvb_frontend *tda10071_attach( const struct tda10071_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/tda18271c2dd.h b/drivers/media/dvb-frontends/tda18271c2dd.h index 1389c74..dd84f7b 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.h +++ b/drivers/media/dvb-frontends/tda18271c2dd.h @@ -1,7 +1,9 @@ #ifndef _TDA18271C2DD_H_ #define _TDA18271C2DD_H_ -#if defined(CONFIG_DVB_TDA18271C2DD) || (defined(CONFIG_DVB_TDA18271C2DD_MODULE) \ - && defined(MODULE)) + +#include <linux/kconfig.h> + +#if IS_ENABLED(CONFIG_DVB_TDA18271C2DD) struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 adr); #else diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h index c7e64af..5bcb9a7 100644 --- a/drivers/media/dvb-frontends/ts2020.h +++ b/drivers/media/dvb-frontends/ts2020.h @@ -22,6 +22,7 @@ #ifndef TS2020_H #define TS2020_H +#include <linux/kconfig.h> #include <linux/dvb/frontend.h> struct ts2020_config { @@ -29,8 +30,7 @@ struct ts2020_config { u8 clk_out_div; }; -#if defined(CONFIG_DVB_TS2020) || \ - (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TS2020) extern struct dvb_frontend *ts2020_attach( struct dvb_frontend *fe, diff --git a/drivers/media/dvb-frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h index d84b8f8..5f1e821 100644 --- a/drivers/media/dvb-frontends/zl10036.h +++ b/drivers/media/dvb-frontends/zl10036.h @@ -21,6 +21,7 @@ #ifndef DVB_ZL10036_H #define DVB_ZL10036_H +#include <linux/kconfig.h> #include <linux/i2c.h> #include "dvb_frontend.h" @@ -37,8 +38,7 @@ struct zl10036_config { int rf_loop_enable; }; -#if defined(CONFIG_DVB_ZL10036) || \ - (defined(CONFIG_DVB_ZL10036_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ZL10036) extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, const struct zl10036_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/zl10039.h b/drivers/media/dvb-frontends/zl10039.h index 5eee7ea..750b9bc 100644 --- a/drivers/media/dvb-frontends/zl10039.h +++ b/drivers/media/dvb-frontends/zl10039.h @@ -22,8 +22,9 @@ #ifndef ZL10039_H #define ZL10039_H -#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \ - && defined(MODULE)) +#include <linux/kconfig.h> + +#if IS_ENABLED(CONFIG_DVB_ZL10039) struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, u8 i2c_addr, struct i2c_adapter *i2c); diff --git a/drivers/media/firewire/firedtv-dvb.c b/drivers/media/firewire/firedtv-dvb.c index eb7496e..f710e17 100644 --- a/drivers/media/firewire/firedtv-dvb.c +++ b/drivers/media/firewire/firedtv-dvb.c @@ -71,11 +71,11 @@ int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) if (dvbdmxfeed->type == DMX_TYPE_TS) { switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - case DMX_TS_PES_AUDIO: - case DMX_TS_PES_TELETEXT: - case DMX_TS_PES_PCR: - case DMX_TS_PES_OTHER: + case DMX_PES_VIDEO: + case DMX_PES_AUDIO: + case DMX_PES_TELETEXT: + case DMX_PES_PCR: + case DMX_PES_OTHER: c = alloc_channel(fdtv); break; default: @@ -132,7 +132,7 @@ int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) (demux->dmx.frontend->source != DMX_MEMORY_FE))) { if (dvbdmxfeed->ts_type & TS_DECODER) { - if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || + if (dvbdmxfeed->pes_type >= DMX_PES_OTHER || !demux->pesfilter[dvbdmxfeed->pes_type]) return -EINVAL; @@ -141,7 +141,7 @@ int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) } if (!(dvbdmxfeed->ts_type & TS_DECODER && - dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) + dvbdmxfeed->pes_type < DMX_PES_OTHER)) return 0; } diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7b771ba..f981d50 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -112,6 +112,15 @@ config VIDEO_TLV320AIC23B To compile this driver as a module, choose M here: the module will be called tlv320aic23b. +config VIDEO_UDA1342 + tristate "Philips UDA1342 audio codec" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips UDA1342 audio codec. + + To compile this driver as a module, choose M here: the + module will be called uda1342. + config VIDEO_WM8775 tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" depends on VIDEO_V4L2 && I2C @@ -133,7 +142,7 @@ config VIDEO_WM8739 module will be called wm8739. config VIDEO_VP27SMPX - tristate "Panasonic VP27s internal MPX" + tristate "Panasonic VP27's internal MPX" depends on VIDEO_V4L2 && I2C ---help--- Support for the internal MPX of the Panasonic VP27s tuner. @@ -141,6 +150,15 @@ config VIDEO_VP27SMPX To compile this driver as a module, choose M here: the module will be called vp27smpx. +config VIDEO_SONY_BTF_MPX + tristate "Sony BTF's internal MPX" + depends on VIDEO_V4L2 && I2C + help + Support for the internal MPX of the Sony BTF-PG472Z tuner. + + To compile this driver as a module, choose M here: the + module will be called sony-btf-mpx. + comment "RDS decoders" config VIDEO_SAA6588 @@ -283,6 +301,35 @@ config VIDEO_TVP7002 To compile this driver as a module, choose M here: the module will be called tvp7002. +config VIDEO_TW2804 + tristate "Techwell TW2804 multiple video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Techwell tw2804 multiple video decoder. + + To compile this driver as a module, choose M here: the + module will be called tw2804. + +config VIDEO_TW9903 + tristate "Techwell TW9903 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Techwell tw9903 multi-standard video decoder + with high quality down scaler. + + To compile this driver as a module, choose M here: the + module will be called tw9903. + +config VIDEO_TW9906 + tristate "Techwell TW9906 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Techwell tw9906 enhanced multi-standard comb filter + video decoder with YCbCr input support. + + To compile this driver as a module, choose M here: the + module will be called tw9906. + config VIDEO_VPX3220 tristate "vpx3220a, vpx3216b & vpx3214c video decoders" depends on VIDEO_V4L2 && I2C @@ -386,6 +433,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_OV7640 + tristate "OmniVision OV7640 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7640 camera. + + To compile this driver as a module, choose M here: the + module will be called ov7640. + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -501,8 +559,8 @@ config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for Samsung S5C73M3 - 8 Mpixel camera. + This is a V4L2 sensor-level driver for Samsung S5C73M3 + 8 Mpixel camera. comment "Flash devices" @@ -550,10 +608,10 @@ config VIDEO_UPD64083 comment "Miscelaneous helper chips" config VIDEO_THS7303 - tristate "THS7303 Video Amplifier" - depends on I2C + tristate "THS7303/53 Video Amplifier" + depends on VIDEO_V4L2 && I2C help - Support for TI THS7303 video amplifier + Support for TI THS7303/53 video amplifier To compile this driver as a module, choose M here: the module will be called ths7303. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index cfefd30..720f42d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -37,16 +37,22 @@ obj-$(CONFIG_VIDEO_THS7303) += ths7303.o obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +obj-$(CONFIG_VIDEO_TW2804) += tw2804.o +obj-$(CONFIG_VIDEO_TW9903) += tw9903.o +obj-$(CONFIG_VIDEO_TW9906) += tw9906.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o -obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_OV7640) += ov7640.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index c2886b6..58344b6 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -354,7 +354,7 @@ static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int ad9389b_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ad9389b_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index df16380..ef75abe 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -447,7 +447,7 @@ free_and_quit: return ret; } -static int __exit adp1653_remove(struct i2c_client *client) +static int adp1653_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct adp1653_flash *flash = to_adp1653_flash(subdev); @@ -476,7 +476,7 @@ static struct i2c_driver adp1653_i2c_driver = { .pm = &adp1653_pm_ops, }, .probe = adp1653_probe, - .remove = __exit_p(adp1653_remove), + .remove = adp1653_remove, .id_table = adp1653_id_table, }; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 34f39d3..afd561a 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -135,6 +135,10 @@ struct adv7180_state { static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { + /* in case V4L2_IN_ST_NO_SIGNAL */ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_STD_UNKNOWN; + switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { case ADV7180_STATUS1_AUTOD_NTSM_M_J: return V4L2_STD_NTSC; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 6fed5b7..56a1fa4 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -507,7 +507,7 @@ static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int adv7183_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index f47555b..31a63c9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -699,7 +699,7 @@ static int adv7604_g_register(struct v4l2_subdev *sd, } static int adv7604_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ba67465..fd47465 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -101,7 +101,7 @@ static int ak881x_g_register(struct v4l2_subdev *sd, } static int ak881x_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index c8581e2..1d2f7c8 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -110,7 +110,7 @@ static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r return 0; } -static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int cs5345_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index f4149eb..12fb9b2 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -1671,7 +1671,7 @@ static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int cx25840_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1835,7 +1835,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, return set_input(client, state->vid_input, input); } -static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int cx25840_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1881,7 +1881,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int cx25840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 08ae067..8e2f79c 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -230,7 +230,7 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, return 0; dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); - if (keygroup < 2 || keygroup > 3) { + if (keygroup < 2 || keygroup > 4) { /* Only a warning */ dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", keygroup, key); @@ -239,6 +239,10 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, *ir_key = key; *ir_raw = key; + if (!strcmp(ir->ir_codes, RC_MAP_AVERMEDIA_M733A_RM_K6)) { + *ir_key |= keygroup << 8; + *ir_raw |= keygroup << 8; + } return 1; } @@ -332,6 +336,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) rc_type = RC_BIT_OTHER; ir_codes = RC_MAP_AVERMEDIA_CARDBUS; break; + case 0x41: + name = "AVerMedia EM78P153"; + ir->get_key = get_key_avermedia_cardbus; + rc_type = RC_BIT_OTHER; + /* RM-KV remote, seems to be same as RM-K6 */ + ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6; + break; case 0x71: name = "Hauppauge/Zilog Z8"; ir->get_key = get_key_haup_xvr; @@ -423,6 +434,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) */ rc->map_name = ir->ir_codes; rc->allowed_protos = rc_type; + rc->enabled_protocols = rc_type; if (!rc->driver_name) rc->driver_name = MODULE_NAME; diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 0991576..39f50fd 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -96,7 +96,7 @@ static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r return 0; } -static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int m52790_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct m52790_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 766305f..54a9dd3 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -445,7 +445,7 @@ static int msp_s_radio(struct v4l2_subdev *sd) return 0; } -static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int msp_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -535,7 +535,7 @@ static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int msp_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index f80c1d7e..8edb3d8 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -87,9 +87,27 @@ #define MT9M032_RESTART 0x0b #define MT9M032_RESET 0x0d #define MT9M032_PLL_CONFIG1 0x11 -#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f +#define MT9M032_PLL_CONFIG1_PREDIV_MASK 0x3f #define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 #define MT9M032_READ_MODE1 0x1e +#define MT9M032_READ_MODE1_OUTPUT_BAD_FRAMES (1 << 13) +#define MT9M032_READ_MODE1_MAINTAIN_FRAME_RATE (1 << 12) +#define MT9M032_READ_MODE1_XOR_LINE_VALID (1 << 11) +#define MT9M032_READ_MODE1_CONT_LINE_VALID (1 << 10) +#define MT9M032_READ_MODE1_INVERT_TRIGGER (1 << 9) +#define MT9M032_READ_MODE1_SNAPSHOT (1 << 8) +#define MT9M032_READ_MODE1_GLOBAL_RESET (1 << 7) +#define MT9M032_READ_MODE1_BULB_EXPOSURE (1 << 6) +#define MT9M032_READ_MODE1_INVERT_STROBE (1 << 5) +#define MT9M032_READ_MODE1_STROBE_ENABLE (1 << 4) +#define MT9M032_READ_MODE1_STROBE_START_TRIG1 (0 << 2) +#define MT9M032_READ_MODE1_STROBE_START_EXP (1 << 2) +#define MT9M032_READ_MODE1_STROBE_START_SHUTTER (2 << 2) +#define MT9M032_READ_MODE1_STROBE_START_TRIG2 (3 << 2) +#define MT9M032_READ_MODE1_STROBE_END_TRIG1 (0 << 0) +#define MT9M032_READ_MODE1_STROBE_END_EXP (1 << 0) +#define MT9M032_READ_MODE1_STROBE_END_SHUTTER (2 << 0) +#define MT9M032_READ_MODE1_STROBE_END_TRIG2 (3 << 0) #define MT9M032_READ_MODE2 0x20 #define MT9M032_READ_MODE2_VFLIP_SHIFT 15 #define MT9M032_READ_MODE2_HFLIP_SHIFT 14 @@ -106,6 +124,8 @@ #define MT9M032_GAIN_AMUL_SHIFT 6 #define MT9M032_GAIN_ANALOG_MASK 0x3f #define MT9M032_FORMATTER1 0x9e +#define MT9M032_FORMATTER1_PLL_P1_6 (1 << 8) +#define MT9M032_FORMATTER1_PARALLEL (1 << 12) #define MT9M032_FORMATTER2 0x9f #define MT9M032_FORMATTER2_DOUT_EN 0x1000 #define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 @@ -121,8 +141,6 @@ #define MT9P031_PLL_CONTROL_PWROFF 0x0050 #define MT9P031_PLL_CONTROL_PWRON 0x0051 #define MT9P031_PLL_CONTROL_USEPLL 0x0052 -#define MT9P031_PLL_CONFIG2 0x11 -#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f struct mt9m032 { struct v4l2_subdev subdev; @@ -255,13 +273,14 @@ static int mt9m032_setup_pll(struct mt9m032 *sensor) .n_max = 64, .m_min = 16, .m_max = 255, - .p1_min = 1, - .p1_max = 128, + .p1_min = 6, + .p1_max = 7, }; struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); struct mt9m032_platform_data *pdata = sensor->pdata; struct aptina_pll pll; + u16 reg_val; int ret; pll.ext_clock = pdata->ext_clock; @@ -274,18 +293,21 @@ static int mt9m032_setup_pll(struct mt9m032 *sensor) sensor->pix_clock = pdata->pix_clock; ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, - (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) - | (pll.p1 - 1)); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); + (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) | + ((pll.n - 1) & MT9M032_PLL_CONFIG1_PREDIV_MASK)); if (!ret) ret = mt9m032_write(client, MT9P031_PLL_CONTROL, MT9P031_PLL_CONTROL_PWRON | MT9P031_PLL_CONTROL_USEPLL); if (!ret) /* more reserved, Continuous, Master Mode */ - ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); - if (!ret) /* Set 14-bit mode, select 7 divider */ - ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); + ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8000 | + MT9M032_READ_MODE1_STROBE_START_EXP | + MT9M032_READ_MODE1_STROBE_END_SHUTTER); + if (!ret) { + reg_val = (pll.p1 == 6 ? MT9M032_FORMATTER1_PLL_P1_6 : 0) + | MT9M032_FORMATTER1_PARALLEL | 0x001e; /* 14-bit */ + ret = mt9m032_write(client, MT9M032_FORMATTER1, reg_val); + } return ret; } @@ -548,7 +570,7 @@ static int mt9m032_g_register(struct v4l2_subdev *sd, } static int mt9m032_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct mt9m032 *sensor = to_mt9m032(sd); struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index e328332..28cf95b 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio.h> @@ -19,6 +20,7 @@ #include <linux/i2c.h> #include <linux/log2.h> #include <linux/pm.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -121,6 +123,11 @@ struct mt9p031 { struct mutex power_lock; /* lock to protect power_count */ int power_count; + struct clk *clk; + struct regulator *vaa; + struct regulator *vdd; + struct regulator *vdd_io; + enum mt9p031_model model; struct aptina_pll pll; int reset; @@ -195,7 +202,7 @@ static int mt9p031_reset(struct mt9p031 *mt9p031) 0); } -static int mt9p031_pll_setup(struct mt9p031 *mt9p031) +static int mt9p031_clk_setup(struct mt9p031 *mt9p031) { static const struct aptina_pll_limits limits = { .ext_clock_min = 6000000, @@ -216,6 +223,12 @@ static int mt9p031_pll_setup(struct mt9p031 *mt9p031) struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); struct mt9p031_platform_data *pdata = mt9p031->pdata; + mt9p031->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9p031->clk)) + return PTR_ERR(mt9p031->clk); + + clk_set_rate(mt9p031->clk, pdata->ext_freq); + mt9p031->pll.ext_clock = pdata->ext_freq; mt9p031->pll.pix_clock = pdata->target_freq; @@ -264,10 +277,14 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) usleep_range(1000, 2000); } + /* Bring up the supplies */ + regulator_enable(mt9p031->vdd); + regulator_enable(mt9p031->vdd_io); + regulator_enable(mt9p031->vaa); + /* Emable clock */ - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, - mt9p031->pdata->ext_freq); + if (mt9p031->clk) + clk_prepare_enable(mt9p031->clk); /* Now RESET_BAR must be high */ if (mt9p031->reset != -1) { @@ -285,8 +302,12 @@ static void mt9p031_power_off(struct mt9p031 *mt9p031) usleep_range(1000, 2000); } - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); + regulator_disable(mt9p031->vaa); + regulator_disable(mt9p031->vdd_io); + regulator_disable(mt9p031->vdd); + + if (mt9p031->clk) + clk_disable_unprepare(mt9p031->clk); } static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) @@ -927,7 +948,7 @@ static int mt9p031_probe(struct i2c_client *client, return -EIO; } - mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); + mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL); if (mt9p031 == NULL) return -ENOMEM; @@ -937,6 +958,16 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->model = did->driver_data; mt9p031->reset = -1; + mt9p031->vaa = devm_regulator_get(&client->dev, "vaa"); + mt9p031->vdd = devm_regulator_get(&client->dev, "vdd"); + mt9p031->vdd_io = devm_regulator_get(&client->dev, "vdd_io"); + + if (IS_ERR(mt9p031->vaa) || IS_ERR(mt9p031->vdd) || + IS_ERR(mt9p031->vdd_io)) { + dev_err(&client->dev, "Unable to get regulators\n"); + return -ENODEV; + } + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6); v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, @@ -1001,24 +1032,20 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; if (pdata->reset != -1) { - ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, - "mt9p031_rst"); + ret = devm_gpio_request_one(&client->dev, pdata->reset, + GPIOF_OUT_INIT_LOW, "mt9p031_rst"); if (ret < 0) goto done; mt9p031->reset = pdata->reset; } - ret = mt9p031_pll_setup(mt9p031); + ret = mt9p031_clk_setup(mt9p031); done: if (ret < 0) { - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - v4l2_ctrl_handler_free(&mt9p031->ctrls); media_entity_cleanup(&mt9p031->subdev.entity); - kfree(mt9p031); } return ret; @@ -1032,9 +1059,6 @@ static int mt9p031_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&mt9p031->ctrls); v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - kfree(mt9p031); return 0; } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 73b7688..3f415fd 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -421,7 +421,7 @@ static int mt9v011_g_register(struct v4l2_subdev *sd, } static int mt9v011_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c new file mode 100644 index 0000000..b0cc927 --- /dev/null +++ b/drivers/media/i2c/ov7640.c @@ -0,0 +1,106 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("OmniVision ov7640 sensor driver"); +MODULE_LICENSE("GPL v2"); + +static const u8 initial_registers[] = { + 0x12, 0x80, + 0x12, 0x54, + 0x14, 0x24, + 0x15, 0x01, + 0x28, 0x20, + 0x75, 0x82, + 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */ +}; + +static int write_regs(struct i2c_client *client, const u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0xFF; i += 2) + if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_ops ov7640_ops; + +static int ov7640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct v4l2_subdev *sd; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &ov7640_ops); + + client->flags = I2C_CLIENT_SCCB; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + if (write_regs(client, initial_registers) < 0) { + v4l_err(client, "error initializing OV7640\n"); + kfree(sd); + return -ENODEV; + } + + return 0; +} + + +static int ov7640_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id ov7640_id[] = { + { "ov7640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov7640_id); + +static struct i2c_driver ov7640_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov7640", + }, + .probe = ov7640_probe, + .remove = ov7640_remove, + .id_table = ov7640_id, +}; +module_i2c_driver(ov7640_driver); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 05ed5b8..617ad3f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1487,7 +1487,7 @@ static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r return ret; } -static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 5dbb65e..cb52438 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -357,7 +357,7 @@ static int s5c73m3_load_fw(struct v4l2_subdev *sd) return -EINVAL; } - v4l2_info(sd, "Loading firmware (%s, %d B)\n", fw_name, fw->size); + v4l2_info(sd, "Loading firmware (%s, %zu B)\n", fw_name, fw->size); ret = s5c73m3_spi_write(state, fw->data, fw->size, 64); @@ -1457,6 +1457,12 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) return ret; } +static void s5c73m3_oif_unregistered(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + v4l2_device_unregister_subdev(&state->sensor_sd); +} + static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = { .open = s5c73m3_open, }; @@ -1474,6 +1480,7 @@ static const struct v4l2_subdev_ops s5c73m3_subdev_ops = { static const struct v4l2_subdev_internal_ops oif_internal_ops = { .registered = s5c73m3_oif_registered, + .unregistered = s5c73m3_oif_unregistered, .open = s5c73m3_oif_open, }; @@ -1668,13 +1675,17 @@ out_err1: static int s5c73m3_remove(struct i2c_client *client) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + struct v4l2_subdev *oif_sd = i2c_get_clientdata(client); + struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); + struct v4l2_subdev *sensor_sd = &state->sensor_sd; - v4l2_device_unregister_subdev(sd); + v4l2_device_unregister_subdev(oif_sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(oif_sd->ctrl_handler); + media_entity_cleanup(&oif_sd->entity); + + v4l2_device_unregister_subdev(sensor_sd); + media_entity_cleanup(&sensor_sd->entity); s5c73m3_unregister_spi_driver(state); s5c73m3_free_gpios(state); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 0caac50..b4e1ccb 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -435,7 +435,7 @@ static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int saa6588_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct saa6588 *s = to_saa6588(sd); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 6b6788c..52c717d 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -83,9 +83,10 @@ struct saa711x_state { u32 ident; u32 audclk_freq; u32 crystal_freq; - u8 ucgc; + bool ucgc; u8 cgcdiv; - u8 apll; + bool apll; + bool double_asclk; }; static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) @@ -732,8 +733,12 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) if (state->apll) acc |= 0x08; + if (state->double_asclk) { + acpf <<= 1; + acni <<= 1; + } saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10 << state->double_asclk); saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); @@ -1259,6 +1264,12 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | (state->output & 0x01)); } + if (state->ident > V4L2_IDENT_SAA7111A) { + if (config & SAA7115_IDQ_IS_DEFAULT) + saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x20); + else + saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x21); + } return 0; } @@ -1296,9 +1307,10 @@ static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) return -EINVAL; state->crystal_freq = freq; + state->double_asclk = flags & SAA7115_FREQ_FL_DOUBLE_ASCLK; state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + state->ucgc = flags & SAA7115_FREQ_FL_UCGC; + state->apll = flags & SAA7115_FREQ_FL_APLL; saa711x_s_clock_freq(sd, state->audclk_freq); return 0; } @@ -1354,6 +1366,34 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) */ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + + if (state->ident == V4L2_IDENT_SAA7115) { + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + + v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); + + switch (reg1e & 0x03) { + case 1: + *std &= V4L2_STD_NTSC; + break; + case 2: + /* + * V4L2_STD_PAL just cover the european PAL standards. + * This is wrong, as the device could also be using an + * other PAL standard. + */ + *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | + V4L2_STD_PAL_M | V4L2_STD_PAL_60; + break; + case 3: + *std &= V4L2_STD_SECAM; + break; + default: + /* Can't detect anything */ + break; + } + } + v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); /* horizontal/vertical not locked */ @@ -1365,34 +1405,6 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) else *std &= V4L2_STD_625_50; - if (state->ident != V4L2_IDENT_SAA7115) - goto ret; - - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - - switch (reg1e & 0x03) { - case 1: - *std &= V4L2_STD_NTSC; - break; - case 2: - /* - * V4L2_STD_PAL just cover the european PAL standards. - * This is wrong, as the device could also be using an - * other PAL standard. - */ - *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | - V4L2_STD_PAL_M | V4L2_STD_PAL_60; - break; - case 3: - *std &= V4L2_STD_SECAM; - break; - default: - /* Can't detect anything */ - break; - } - - v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); - ret: v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); @@ -1428,7 +1440,7 @@ static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa711x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index b745f68..8a47ac1 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -672,7 +672,7 @@ static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa7127_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 1e84466..cf3a0aa 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -988,7 +988,7 @@ static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa717x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); u16 addr = reg->reg & 0xffff; @@ -1113,7 +1113,7 @@ static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) } /* change audio mode */ -static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int saa717x_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct saa717x_state *decoder = to_state(sd); int audio_mode; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 83c7ed7..cae4f46 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2833,7 +2833,7 @@ static int smiapp_probe(struct i2c_client *client, sensor->src->pads, 0); } -static int __exit smiapp_remove(struct i2c_client *client) +static int smiapp_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2881,7 +2881,7 @@ static struct i2c_driver smiapp_i2c_driver = { .pm = &smiapp_pm_ops, }, .probe = smiapp_probe, - .remove = __exit_p(smiapp_remove), + .remove = smiapp_remove, .id_table = smiapp_id_table, }; diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 6dff2b7..23d352f 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -9,7 +9,6 @@ config SOC_CAMERA_IMX074 config SOC_CAMERA_MT9M001 tristate "mt9m001 support" depends on SOC_CAMERA && I2C - select GPIO_PCA953X if MT9M001_PCA9536_SWITCH help This driver supports MT9M001 cameras from Micron, monochrome and colour models. @@ -36,7 +35,6 @@ config SOC_CAMERA_MT9T112 config SOC_CAMERA_MT9V022 tristate "mt9v022 and mt9v024 support" depends on SOC_CAMERA && I2C - select GPIO_PCA953X if MT9V022_PCA9536_SWITCH help This driver supports MT9V022 cameras from Micron diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index bcdc861..dd90898 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -360,7 +360,7 @@ static int mt9m001_g_register(struct v4l2_subdev *sd, } static int mt9m001_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index bbc4ff9..8bd4e0d 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -641,7 +641,7 @@ static int mt9m111_g_register(struct v4l2_subdev *sd, } static int mt9m111_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -701,11 +701,11 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain) return reg_write(GLOBAL_GAIN, val); } -static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on) +static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int val) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - if (on) + if (val == V4L2_EXPOSURE_AUTO) return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); } @@ -785,8 +785,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111) struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); int ret; - /* Default HIGHPOWER context */ - mt9m111->ctx = &context_b; ret = mt9m111_enable(mt9m111); if (!ret) ret = mt9m111_reset(mt9m111); @@ -975,6 +973,9 @@ static int mt9m111_probe(struct i2c_client *client, if (!mt9m111) return -ENOMEM; + /* Default HIGHPOWER context */ + mt9m111->ctx = &context_b; + v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); v4l2_ctrl_handler_init(&mt9m111->hdl, 5); v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index d80d044..26a15b8 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -430,7 +430,7 @@ static int mt9t031_g_register(struct v4l2_subdev *sd, } static int mt9t031_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 188e29b..a7256b7 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -766,7 +766,7 @@ static int mt9t112_g_register(struct v4l2_subdev *sd, } static int mt9t112_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index a5e65d6..a295e59 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -275,6 +275,7 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); struct v4l2_rect rect = a->c; + int min_row, min_blank; int ret; /* Bayer format - even size lengths */ @@ -310,13 +311,21 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ret = reg_write(client, MT9V022_COLUMN_START, rect.left); if (!ret) ret = reg_write(client, MT9V022_ROW_START, rect.top); + /* + * mt9v022: min total row time is 660 columns, min blanking is 43 + * mt9v024: min total row time is 690 columns, min blanking is 61 + */ + if (is_mt9v024(mt9v022->chip_version)) { + min_row = 690; + min_blank = 61; + } else { + min_row = 660; + min_blank = 43; + } if (!ret) - /* - * Default 94, Phytec driver says: - * "width + horizontal blank >= 660" - */ ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, - rect.width > 660 - 43 ? 43 : 660 - rect.width); + rect.width > min_row - min_blank ? + min_blank : min_row - rect.width); if (!ret) ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); if (!ret) @@ -488,7 +497,7 @@ static int mt9v022_g_register(struct v4l2_subdev *sd, } static int mt9v022_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 0f520f6..e316842 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -756,7 +756,7 @@ static int ov2640_g_register(struct v4l2_subdev *sd, } static int ov2640_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 9d53309..9aa56de 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -708,7 +708,7 @@ static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register return ret; } -static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ov5642_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index dbe4f56..991202d 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -421,7 +421,7 @@ static int ov6650_get_register(struct v4l2_subdev *sd, } static int ov6650_set_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index fbeb5b2..713d62e 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -652,7 +652,7 @@ static int ov772x_g_register(struct v4l2_subdev *sd, } static int ov772x_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 0599304..20ca62d 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -322,7 +322,7 @@ static int ov9640_get_register(struct v4l2_subdev *sd, } static int ov9640_set_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 2f236da..012bd62 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -835,7 +835,7 @@ static int ov9740_get_register(struct v4l2_subdev *sd, } static int ov9740_set_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 5c92679..1f9ec3b 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1161,7 +1161,7 @@ static int rj54n1_g_register(struct v4l2_subdev *sd, } static int rj54n1_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 7d20746..bad90b1 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -554,7 +554,7 @@ static int tw9910_g_register(struct v4l2_subdev *sd, } static int tw9910_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c new file mode 100644 index 0000000..38cbea9 --- /dev/null +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -0,0 +1,399 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/tuner.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("sony-btf-mpx driver"); +MODULE_LICENSE("GPL v2"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on\n"); + +/* #define MPX_DEBUG */ + +/* + * Note: + * + * AS(IF/MPX) pin: LOW HIGH/OPEN + * IF/MPX address: 0x42/0x40 0x43/0x44 + */ + + +static int force_mpx_mode = -1; +module_param(force_mpx_mode, int, 0644); + +struct sony_btf_mpx { + struct v4l2_subdev sd; + int mpxmode; + u32 audmode; +}; + +static inline struct sony_btf_mpx *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sony_btf_mpx, sd); +} + +static int mpx_write(struct i2c_client *client, int dev, int addr, int val) +{ + u8 buffer[5]; + struct i2c_msg msg; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + msg.addr = client->addr; + msg.flags = 0; + msg.len = 5; + msg.buf = buffer; + i2c_transfer(client->adapter, &msg, 1); + return 0; +} + +/* + * MPX register values for the BTF-PG472Z: + * + * FM_ NICAM_ SCART_ + * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME + * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 + * --------------------------------------------------------------- + * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500 + * + * B/G + * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500 + * A2 1003 0020 0100 2601 5000 XXXX 0003 7500 + * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500 + * + * I + * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500 + * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500 + * + * D/K + * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500 + * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500 + * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500 + * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500 + * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500 + * + * L/L' + * Mono 0003 0200 0100 7C03 5000 2200 0009 7500 + * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500 + * + * M + * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500 + * + * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. + * + * Bilingual selection in A2/NICAM: + * + * High byte of SOURCE Left chan Right chan + * 0x01 MAIN SUB + * 0x03 MAIN MAIN + * 0x04 SUB SUB + * + * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or + * 0x00 (all other bands). Force mono in A2 with FMONO_A2: + * + * FMONO_A2 + * 10/0022 + * -------- + * Forced mono ON 07F0 + * Forced mono OFF 0190 + */ + +static const struct { + enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; + u16 modus; + u16 source; + u16 acb; + u16 fm_prescale; + u16 nicam_prescale; + u16 scart_prescale; + u16 system; + u16 volume; +} mpx_audio_modes[] = { + /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0001, 0x7500 }, + /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0003, 0x7500 }, + /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0003, 0x7500 }, + /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0008, 0x7500 }, + /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x7900, 0x0000, 0x000A, 0x7500 }, + /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x7900, 0x0000, 0x000A, 0x7500 }, + /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0004, 0x7500 }, + /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0004, 0x7500 }, + /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0005, 0x7500 }, + /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0007, 0x7500 }, + /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x5000, 0x0000, 0x000B, 0x7500 }, + /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03, + 0x5000, 0x2200, 0x0009, 0x7500 }, + /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03, + 0x5000, 0x0000, 0x0009, 0x7500 }, +}; + +#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes) + +static int mpx_setup(struct sony_btf_mpx *t) +{ + struct i2c_client *client = v4l2_get_subdevdata(&t->sd); + u16 source = 0; + u8 buffer[3]; + struct i2c_msg msg; + int mode = t->mpxmode; + + /* reset MPX */ + buffer[0] = 0x00; + buffer[1] = 0x80; + buffer[2] = 0x00; + msg.addr = client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = buffer; + i2c_transfer(client->adapter, &msg, 1); + buffer[1] = 0x00; + i2c_transfer(client->adapter, &msg, 1); + + if (t->audmode != V4L2_TUNER_MODE_MONO) + mode++; + + if (mpx_audio_modes[mode].audio_mode != AUD_MONO) { + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + switch (mpx_audio_modes[mode].audio_mode) { + case AUD_A2: + source = mpx_audio_modes[mode].source; + break; + case AUD_NICAM: + source = 0x0000; + break; + case AUD_NICAM_L: + source = 0x0200; + break; + default: + break; + } + break; + case V4L2_TUNER_MODE_STEREO: + source = mpx_audio_modes[mode].source; + break; + case V4L2_TUNER_MODE_LANG1: + source = 0x0300; + break; + case V4L2_TUNER_MODE_LANG2: + source = 0x0400; + break; + } + source |= mpx_audio_modes[mode].source & 0x00ff; + } else + source = mpx_audio_modes[mode].source; + + mpx_write(client, 0x10, 0x0030, mpx_audio_modes[mode].modus); + mpx_write(client, 0x12, 0x0008, source); + mpx_write(client, 0x12, 0x0013, mpx_audio_modes[mode].acb); + mpx_write(client, 0x12, 0x000e, + mpx_audio_modes[mode].fm_prescale); + mpx_write(client, 0x12, 0x0010, + mpx_audio_modes[mode].nicam_prescale); + mpx_write(client, 0x12, 0x000d, + mpx_audio_modes[mode].scart_prescale); + mpx_write(client, 0x10, 0x0020, mpx_audio_modes[mode].system); + mpx_write(client, 0x12, 0x0000, mpx_audio_modes[mode].volume); + if (mpx_audio_modes[mode].audio_mode == AUD_A2) + mpx_write(client, 0x10, 0x0022, + t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); + +#ifdef MPX_DEBUG + { + u8 buf1[3], buf2[2]; + struct i2c_msg msgs[2]; + + v4l2_info(client, + "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[mode].modus, + source, + mpx_audio_modes[mode].acb, + mpx_audio_modes[mode].fm_prescale, + mpx_audio_modes[mode].nicam_prescale, + mpx_audio_modes[mode].scart_prescale, + mpx_audio_modes[mode].system, + mpx_audio_modes[mode].volume); + buf1[0] = 0x11; + buf1[1] = 0x00; + buf1[2] = 0x7e; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 3; + msgs[0].buf = buf1; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = buf2; + i2c_transfer(client->adapter, msgs, 2); + v4l2_info(client, "MPX system: %02x%02x\n", + buf2[0], buf2[1]); + buf1[0] = 0x11; + buf1[1] = 0x02; + buf1[2] = 0x00; + i2c_transfer(client->adapter, msgs, 2); + v4l2_info(client, "MPX status: %02x%02x\n", + buf2[0], buf2[1]); + } +#endif + return 0; +} + + +static int sony_btf_mpx_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct sony_btf_mpx *t = to_state(sd); + int default_mpx_mode = 0; + + if (std & V4L2_STD_PAL_BG) + default_mpx_mode = 1; + else if (std & V4L2_STD_PAL_I) + default_mpx_mode = 4; + else if (std & V4L2_STD_PAL_DK) + default_mpx_mode = 6; + else if (std & V4L2_STD_SECAM_L) + default_mpx_mode = 11; + + if (default_mpx_mode != t->mpxmode) { + t->mpxmode = default_mpx_mode; + mpx_setup(t); + } + return 0; +} + +static int sony_btf_mpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct sony_btf_mpx *t = to_state(sd); + + vt->capability = V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + vt->audmode = t->audmode; + return 0; +} + +static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) +{ + struct sony_btf_mpx *t = to_state(sd); + + if (vt->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + + if (vt->audmode != t->audmode) { + t->audmode = vt->audmode; + mpx_setup(t); + } + return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = { + .s_std = sony_btf_mpx_s_std, +}; + +static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = { + .s_tuner = sony_btf_mpx_s_tuner, + .g_tuner = sony_btf_mpx_g_tuner, +}; + +static const struct v4l2_subdev_ops sony_btf_mpx_ops = { + .core = &sony_btf_mpx_core_ops, + .tuner = &sony_btf_mpx_tuner_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int sony_btf_mpx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sony_btf_mpx *t; + struct v4l2_subdev *sd; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + t = kzalloc(sizeof(struct sony_btf_mpx), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &sony_btf_mpx_ops); + + /* Initialize sony_btf_mpx */ + t->mpxmode = 0; + t->audmode = V4L2_TUNER_MODE_STEREO; + + return 0; +} + +static int sony_btf_mpx_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id sony_btf_mpx_id[] = { + { "sony-btf-mpx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); + +static struct i2c_driver sony_btf_mpx_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "sony-btf-mpx", + }, + .probe = sony_btf_mpx_probe, + .remove = sony_btf_mpx_remove, + .id_table = sony_btf_mpx_id, +}; +module_i2c_driver(sony_btf_mpx_driver); diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index f7707e6..28b5121 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -35,6 +35,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> #include <media/i2c-addr.h> #ifndef VIDEO_AUDIO_BALANCE @@ -60,13 +61,17 @@ MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default struct tda7432 { struct v4l2_subdev sd; - int addr; - int input; - int volume; - int muted; - int bass, treble; - int lf, lr, rf, rr; - int loud; + struct v4l2_ctrl_handler hdl; + struct { + /* bass/treble cluster */ + struct v4l2_ctrl *bass; + struct v4l2_ctrl *treble; + }; + struct { + /* mute/balance cluster */ + struct v4l2_ctrl *mute; + struct v4l2_ctrl *balance; + }; }; static inline struct tda7432 *to_state(struct v4l2_subdev *sd) @@ -74,6 +79,11 @@ static inline struct tda7432 *to_state(struct v4l2_subdev *sd) return container_of(sd, struct tda7432, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tda7432, hdl)->sd; +} + /* The TDA7432 is made by STS-Thompson * http://www.st.com * http://us.st.com/stonline/books/pdf/docs/4056.pdf @@ -227,24 +237,22 @@ static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) static int tda7432_set(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tda7432 *t = to_state(sd); unsigned char buf[16]; - v4l2_dbg(1, debug, sd, - "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", - t->input, t->volume, t->bass, t->treble, t->lf, t->lr, - t->rf, t->rr, t->loud); buf[0] = TDA7432_IN; - buf[1] = t->input; - buf[2] = t->volume; - buf[3] = t->bass; - buf[4] = t->treble; - buf[5] = t->lf; - buf[6] = t->lr; - buf[7] = t->rf; - buf[8] = t->rr; - buf[9] = t->loud; - if (10 != i2c_master_send(client, buf, 10)) { + buf[1] = TDA7432_STEREO_IN | /* Main (stereo) input */ + TDA7432_BASS_SYM | /* Symmetric bass cut */ + TDA7432_BASS_NORM; /* Normal bass range */ + buf[2] = 0x3b; + if (loudness) /* Turn loudness on? */ + buf[2] |= TDA7432_LD_ON; + buf[3] = TDA7432_TREBLE_0DB | (TDA7432_BASS_0DB << 4); + buf[4] = TDA7432_ATTEN_0DB; + buf[5] = TDA7432_ATTEN_0DB; + buf[6] = TDA7432_ATTEN_0DB; + buf[7] = TDA7432_ATTEN_0DB; + buf[8] = loudness; + if (9 != i2c_master_send(client, buf, 9)) { v4l2_err(sd, "I/O error, trying tda7432_set\n"); return -1; } @@ -252,174 +260,86 @@ static int tda7432_set(struct v4l2_subdev *sd) return 0; } -static void do_tda7432_init(struct v4l2_subdev *sd) -{ - struct tda7432 *t = to_state(sd); - - v4l2_dbg(2, debug, sd, "In tda7432_init\n"); - - t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ - TDA7432_BASS_SYM | /* Symmetric bass cut */ - TDA7432_BASS_NORM; /* Normal bass range */ - t->volume = 0x3b ; /* -27dB Volume */ - if (loudness) /* Turn loudness on? */ - t->volume |= TDA7432_LD_ON; - t->muted = 1; - t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ - t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ - t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->loud = loudness; /* insmod parameter */ - - tda7432_set(sd); -} - -static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tda7432_log_status(struct v4l2_subdev *sd) { - struct tda7432 *t = to_state(sd); + struct tda7432 *state = to_state(sd); - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=t->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!maxvol){ /* max +20db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; - } else { /* max 0db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; - } - return 0; - case V4L2_CID_AUDIO_BALANCE: - { - if ( (t->lf) < (t->rf) ) - /* right is attenuated, balance shifted left */ - ctrl->value = (32768 - 1057*(t->rf)); - else - /* left is attenuated, balance shifted right */ - ctrl->value = (32768 + 1057*(t->lf)); - return 0; - } - case V4L2_CID_AUDIO_BASS: - { - /* Bass/treble 4 bits each */ - int bass=t->bass; - if(bass >= 0x8) - bass = ~(bass - 0x8) & 0xf; - ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); - return 0; - } - case V4L2_CID_AUDIO_TREBLE: - { - int treble=t->treble; - if(treble >= 0x8) - treble = ~(treble - 0x8) & 0xf; - ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); - return 0; - } - } - return -EINVAL; + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; } -static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tda7432_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct tda7432 *t = to_state(sd); + u8 bass, treble, volume; + u8 lf, lr, rf, rr; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - t->muted=ctrl->value; - break; - case V4L2_CID_AUDIO_VOLUME: - if(!maxvol){ /* max +20db */ - t->volume = 0x6f - ((ctrl->value)/630); - } else { /* max 0db */ - t->volume = 0x6f - ((ctrl->value)/829); - } - if (loudness) /* Turn on the loudness bit */ - t->volume |= TDA7432_LD_ON; - - tda7432_write(sd, TDA7432_VL, t->volume); - return 0; - case V4L2_CID_AUDIO_BALANCE: - if (ctrl->value < 32768) { + if (t->balance->val < 0) { /* shifted to left, attenuate right */ - t->rr = (32768 - ctrl->value)/1057; - t->rf = t->rr; - t->lr = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - } else if(ctrl->value > 32769) { + rr = rf = -t->balance->val; + lr = lf = TDA7432_ATTEN_0DB; + } else if (t->balance->val > 0) { /* shifted to right, attenuate left */ - t->lf = (ctrl->value - 32768)/1057; - t->lr = t->lf; - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; + rr = rf = TDA7432_ATTEN_0DB; + lr = lf = t->balance->val; } else { /* centered */ - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - t->lr = TDA7432_ATTEN_0DB; + rr = rf = TDA7432_ATTEN_0DB; + lr = lf = TDA7432_ATTEN_0DB; } - break; - case V4L2_CID_AUDIO_BASS: - t->bass = ctrl->value >> 12; - if(t->bass>= 0x8) - t->bass = (~t->bass & 0xf) + 0x8 ; - - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + if (t->mute->val) { + lf |= TDA7432_MUTE; + lr |= TDA7432_MUTE; + lf |= TDA7432_MUTE; + rr |= TDA7432_MUTE; + } + /* Mute & update balance*/ + tda7432_write(sd, TDA7432_LF, lf); + tda7432_write(sd, TDA7432_LR, lr); + tda7432_write(sd, TDA7432_RF, rf); + tda7432_write(sd, TDA7432_RR, rr); return 0; - case V4L2_CID_AUDIO_TREBLE: - t->treble= ctrl->value >> 12; - if(t->treble>= 0x8) - t->treble = (~t->treble & 0xf) + 0x8 ; + case V4L2_CID_AUDIO_VOLUME: + volume = 0x6f - ctrl->val; + if (loudness) /* Turn on the loudness bit */ + volume |= TDA7432_LD_ON; - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + tda7432_write(sd, TDA7432_VL, volume); return 0; - default: - return -EINVAL; - } - - /* Used for both mute and balance changes */ - if (t->muted) - { - /* Mute & update balance*/ - tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); - } else { - tda7432_write(sd, TDA7432_LF, t->lf); - tda7432_write(sd, TDA7432_LR, t->lr); - tda7432_write(sd, TDA7432_RF, t->rf); - tda7432_write(sd, TDA7432_RR, t->rr); - } - return 0; -} - -static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + bass = t->bass->val; + treble = t->treble->val; + if (bass >= 0x8) + bass = 14 - (bass - 8); + if (treble >= 0x8) + treble = 14 - (treble - 8); + + tda7432_write(sd, TDA7432_TN, 0x10 | (bass << 4) | treble); + return 0; } return -EINVAL; } /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops tda7432_core_ops = { - .queryctrl = tda7432_queryctrl, - .g_ctrl = tda7432_g_ctrl, +static const struct v4l2_ctrl_ops tda7432_ctrl_ops = { .s_ctrl = tda7432_s_ctrl, }; +static const struct v4l2_subdev_core_ops tda7432_core_ops = { + .log_status = tda7432_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + static const struct v4l2_subdev_ops tda7432_ops = { .core = &tda7432_core_ops, }; @@ -444,6 +364,28 @@ static int tda7432_probe(struct i2c_client *client, return -ENOMEM; sd = &t->sd; v4l2_i2c_subdev_init(sd, client, &tda7432_ops); + v4l2_ctrl_handler_init(&t->hdl, 5); + v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, maxvol ? 0x68 : 0x4f, 1, maxvol ? 0x5d : 0x47); + t->mute = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + t->balance = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, -31, 31, 1, 0); + t->bass = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, + V4L2_CID_AUDIO_BASS, 0, 14, 1, 7); + t->treble = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, 0, 14, 1, 7); + sd->ctrl_handler = &t->hdl; + if (t->hdl.error) { + int err = t->hdl.error; + + v4l2_ctrl_handler_free(&t->hdl); + kfree(t); + return err; + } + v4l2_ctrl_cluster(2, &t->bass); + v4l2_ctrl_cluster(2, &t->mute); + v4l2_ctrl_handler_setup(&t->hdl); if (loudness < 0 || loudness > 15) { v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); if (loudness < 0) @@ -452,17 +394,19 @@ static int tda7432_probe(struct i2c_client *client, loudness = 15; } - do_tda7432_init(sd); + tda7432_set(sd); return 0; } static int tda7432_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tda7432 *t = to_state(sd); - do_tda7432_init(sd); + tda7432_set(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&t->hdl); + kfree(t); return 0; } diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index 3d7ddd9..01441e3 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -87,7 +87,7 @@ static int tda9840_status(struct v4l2_subdev *sd) return byte & 0x60; } -static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +static int tda9840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *t) { int stat = tda9840_status(sd); int byte; diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index e747524..c433955 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -1,7 +1,15 @@ /* - * ths7303- THS7303 Video Amplifier driver + * ths7303/53- THS7303/53 Video Amplifier driver * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. + * + * Author: Chaithrika U S <chaithrika@ti.com> + * + * Contributors: + * Hans Verkuil <hans.verkuil@cisco.com> + * Lad, Prabhakar <prabhakar.lad@ti.com> + * Martin Bugge <marbugge@cisco.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,25 +21,27 @@ * GNU General Public License for more details. */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/ctype.h> -#include <linux/slab.h> #include <linux/i2c.h> -#include <linux/device.h> -#include <linux/delay.h> #include <linux/module.h> -#include <linux/uaccess.h> -#include <linux/videodev2.h> +#include <linux/slab.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> +#include <media/ths7303.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-device.h> #define THS7303_CHANNEL_1 1 #define THS7303_CHANNEL_2 2 #define THS7303_CHANNEL_3 3 +struct ths7303_state { + struct v4l2_subdev sd; + struct ths7303_platform_data pdata; + struct v4l2_bt_timings bt; + int std_id; + int stream_on; + int driver_data; +}; + enum ths7303_filter_mode { THS7303_FILTER_MODE_480I_576I, THS7303_FILTER_MODE_480P_576P, @@ -48,64 +58,84 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level 0-1"); +static inline struct ths7303_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ths7303_state, sd); +} + +static int ths7303_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + return 0; + } + return ret; +} + /* following function is used to set ths7303 */ int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) { - u8 input_bias_chroma = 3; - u8 input_bias_luma = 3; - int disable = 0; - int err = 0; - u8 val = 0; - u8 temp; - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ths7303_state *state = to_state(sd); + struct ths7303_platform_data *pdata = &state->pdata; + u8 val, sel = 0; + int err, disable = 0; if (!client) return -EINVAL; switch (mode) { case THS7303_FILTER_MODE_1080P: - val = (3 << 6); - val |= (3 << 3); + sel = 0x3; /*1080p and SXGA/UXGA */ break; case THS7303_FILTER_MODE_720P_1080I: - val = (2 << 6); - val |= (2 << 3); + sel = 0x2; /*720p, 1080i and SVGA/XGA */ break; case THS7303_FILTER_MODE_480P_576P: - val = (1 << 6); - val |= (1 << 3); + sel = 0x1; /* EDTV 480p/576p and VGA */ break; case THS7303_FILTER_MODE_480I_576I: + sel = 0x0; /* SDTV, S-Video, 480i/576i */ break; - case THS7303_FILTER_MODE_DISABLE: - pr_info("mode disabled\n"); - /* disable all channels */ - disable = 1; default: /* disable all channels */ disable = 1; } - /* Setup channel 2 - Luma - Green */ - temp = val; + + val = (sel << 6) | (sel << 3); if (!disable) - val |= input_bias_luma; - err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_2, val); + val |= (pdata->ch_1 & 0x27); + err = ths7303_write(sd, THS7303_CHANNEL_1, val); if (err) goto out; - /* setup two chroma channels */ + val = (sel << 6) | (sel << 3); if (!disable) - temp |= input_bias_chroma; - - err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_1, temp); + val |= (pdata->ch_2 & 0x27); + err = ths7303_write(sd, THS7303_CHANNEL_2, val); if (err) goto out; - err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_3, temp); + val = (sel << 6) | (sel << 3); + if (!disable) + val |= (pdata->ch_3 & 0x27); + err = ths7303_write(sd, THS7303_CHANNEL_3, val); if (err) goto out; - return err; + + return 0; out: pr_info("write byte data failed\n"); return err; @@ -113,49 +143,209 @@ out: static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) { - if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) + struct ths7303_state *state = to_state(sd); + + if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { + state->std_id = 1; + state->bt.pixelclock = 0; return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I); - else - return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); + } + + return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); } -/* for setting filter for HD output */ -static int ths7303_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *dv_timings) +static int ths7303_config(struct v4l2_subdev *sd) { - u32 height = dv_timings->bt.height; - int interlaced = dv_timings->bt.interlaced; - int res = 0; + struct ths7303_state *state = to_state(sd); + int res; + + if (!state->stream_on) { + ths7303_write(sd, THS7303_CHANNEL_1, + (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) | + 0x00); + ths7303_write(sd, THS7303_CHANNEL_2, + (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) | + 0x00); + ths7303_write(sd, THS7303_CHANNEL_3, + (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) | + 0x00); + return 0; + } - if (height == 1080 && !interlaced) + if (state->bt.pixelclock > 120000000) res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P); - else if ((height == 720 && !interlaced) || - (height == 1080 && interlaced)) + else if (state->bt.pixelclock > 70000000) res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I); - else if ((height == 480 || height == 576) && !interlaced) + else if (state->bt.pixelclock > 20000000) res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P); + else if (state->std_id) + res = ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I); else /* disable all channels */ res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); return res; + +} + +static int ths7303_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ths7303_state *state = to_state(sd); + + state->stream_on = enable; + + return ths7303_config(sd); +} + +/* for setting filter for HD output */ +static int ths7303_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct ths7303_state *state = to_state(sd); + + if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120) + return -EINVAL; + + state->bt = dv_timings->bt; + state->std_id = 0; + + return ths7303_config(sd); } static int ths7303_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ths7303_state *state = to_state(sd); - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); + return v4l2_chip_ident_i2c_client(client, chip, state->driver_data, 0); } static const struct v4l2_subdev_video_ops ths7303_video_ops = { + .s_stream = ths7303_s_stream, .s_std_output = ths7303_s_std_output, - .s_dv_timings = ths7303_s_dv_timings, + .s_dv_timings = ths7303_s_dv_timings, }; +#ifdef CONFIG_VIDEO_ADV_DEBUG + +static int ths7303_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + reg->size = 1; + reg->val = ths7303_read(sd, reg->reg); + return 0; +} + +static int ths7303_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ths7303_write(sd, reg->reg, reg->val); + return 0; +} +#endif + +static const char * const stc_lpf_sel_txt[4] = { + "500-kHz Filter", + "2.5-MHz Filter", + "5-MHz Filter", + "5-MHz Filter", +}; + +static const char * const in_mux_sel_txt[2] = { + "Input A Select", + "Input B Select", +}; + +static const char * const lpf_freq_sel_txt[4] = { + "9-MHz LPF", + "16-MHz LPF", + "35-MHz LPF", + "Bypass LPF", +}; + +static const char * const in_bias_sel_dis_cont_txt[8] = { + "Disable Channel", + "Mute Function - No Output", + "DC Bias Select", + "DC Bias + 250 mV Offset Select", + "AC Bias Select", + "Sync Tip Clamp with low bias", + "Sync Tip Clamp with mid bias", + "Sync Tip Clamp with high bias", +}; + +static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg) +{ + u8 val = ths7303_read(sd, reg); + + if ((val & 0x7) == 0) { + v4l2_info(sd, "Channel %d Off\n", reg); + return; + } + + v4l2_info(sd, "Channel %d On\n", reg); + v4l2_info(sd, " value 0x%x\n", val); + v4l2_info(sd, " %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]); + v4l2_info(sd, " %s\n", in_mux_sel_txt[(val >> 5) & 0x1]); + v4l2_info(sd, " %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]); + v4l2_info(sd, " %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]); +} + +static int ths7303_log_status(struct v4l2_subdev *sd) +{ + struct ths7303_state *state = to_state(sd); + + v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off"); + + if (state->bt.pixelclock) { + struct v4l2_bt_timings *bt = bt = &state->bt; + u32 frame_width, frame_height; + + frame_width = bt->width + bt->hfrontporch + + bt->hsync + bt->hbackporch; + frame_height = bt->height + bt->vfrontporch + + bt->vsync + bt->vbackporch; + v4l2_info(sd, + "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n", + bt->width, bt->height, bt->interlaced ? "i" : "p", + (frame_height * frame_width) > 0 ? + (int)bt->pixelclock / + (frame_height * frame_width) : 0, + frame_width, frame_height, + (int)bt->pixelclock, bt->polarities); + } else { + v4l2_info(sd, "no timings set\n"); + } + + ths7303_log_channel_status(sd, THS7303_CHANNEL_1); + ths7303_log_channel_status(sd, THS7303_CHANNEL_2); + ths7303_log_channel_status(sd, THS7303_CHANNEL_3); + + return 0; +} + static const struct v4l2_subdev_core_ops ths7303_core_ops = { .g_chip_ident = ths7303_g_chip_ident, + .log_status = ths7303_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ths7303_g_register, + .s_register = ths7303_s_register, +#endif }; static const struct v4l2_subdev_ops ths7303_ops = { @@ -163,11 +353,38 @@ static const struct v4l2_subdev_ops ths7303_ops = { .video = &ths7303_video_ops, }; +static int ths7303_setup(struct v4l2_subdev *sd) +{ + struct ths7303_state *state = to_state(sd); + struct ths7303_platform_data *pdata = &state->pdata; + int ret; + u8 mask; + + state->stream_on = pdata->init_enable; + + mask = state->stream_on ? 0xff : 0xf8; + + ret = ths7303_write(sd, THS7303_CHANNEL_1, pdata->ch_1 & mask); + if (ret) + return ret; + + ret = ths7303_write(sd, THS7303_CHANNEL_2, pdata->ch_2 & mask); + if (ret) + return ret; + + ret = ths7303_write(sd, THS7303_CHANNEL_3, pdata->ch_3 & mask); + if (ret) + return ret; + + return 0; +} + static int ths7303_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct ths7303_platform_data *pdata = client->dev.platform_data; + struct ths7303_state *state; struct v4l2_subdev *sd; - v4l2_std_id std_id = V4L2_STD_NTSC; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -175,13 +392,28 @@ static int ths7303_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = devm_kzalloc(&client->dev, sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) + state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state), + GFP_KERNEL); + if (!state) return -ENOMEM; + if (!pdata) + v4l_warn(client, "No platform data, using default data!\n"); + else + state->pdata = *pdata; + + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &ths7303_ops); - return ths7303_s_std_output(sd, std_id); + /* store the driver data to differntiate the chip */ + state->driver_data = (int)id->driver_data; + + if (ths7303_setup(sd) < 0) { + v4l_err(client, "init failed\n"); + return -EIO; + } + + return 0; } static int ths7303_remove(struct i2c_client *client) @@ -194,7 +426,8 @@ static int ths7303_remove(struct i2c_client *client) } static const struct i2c_device_id ths7303_id[] = { - {"ths7303", 0}, + {"ths7303", V4L2_IDENT_THS7303}, + {"ths7353", V4L2_IDENT_THS7353}, {}, }; @@ -203,7 +436,7 @@ MODULE_DEVICE_TABLE(i2c, ths7303_id); static struct i2c_driver ths7303_driver = { .driver = { .owner = THIS_MODULE, - .name = "ths7303", + .name = "ths73x3", }, .probe = ths7303_probe, .remove = ths7303_remove, diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index e3b33b7..b72a59d 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1761,7 +1761,7 @@ static int tvaudio_s_routing(struct v4l2_subdev *sd, return 0; } -static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int tvaudio_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; @@ -1803,7 +1803,7 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) vt->audmode = chip->audmode; vt->rxsubchans = desc->getrxsubchans(chip); - vt->capability = V4L2_TUNER_CAP_STEREO | + vt->capability |= V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; return 0; @@ -1817,7 +1817,7 @@ static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int tvaudio_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) { struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index aa94ebc..ab8f3fe 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -12,6 +12,7 @@ * Hardik Shah <hardik.shah@ti.com> * Manjunath Hadli <mrh@ti.com> * Karicheri Muralidharan <m-karicheri2@ti.com> + * Prabhakar Lad <prabhakar.lad@ti.com> * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -33,6 +34,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <linux/module.h> +#include <linux/v4l2-mediabus.h> #include <media/v4l2-device.h> #include <media/v4l2-common.h> @@ -40,12 +42,10 @@ #include <media/v4l2-chip-ident.h> #include <media/v4l2-ctrls.h> #include <media/tvp514x.h> +#include <media/media-entity.h> #include "tvp514x_regs.h" -/* Module Name */ -#define TVP514X_MODULE_NAME "tvp514x" - /* Private macros for TVP */ #define I2C_RETRY_COUNT (5) #define LOCK_RETRY_COUNT (5) @@ -91,6 +91,9 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); * @pdata: Board specific * @ver: Chip version * @streaming: TVP5146/47 decoder streaming - enabled or disabled. + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list * @current_std: Current standard * @num_stds: Number of standards * @std_list: Standards list @@ -106,12 +109,20 @@ struct tvp514x_decoder { int ver; int streaming; + struct v4l2_pix_format pix; + int num_fmts; + const struct v4l2_fmtdesc *fmt_list; + enum tvp514x_std current_std; int num_stds; const struct tvp514x_std_info *std_list; /* Input and Output Routing parameters */ u32 input; u32 output; + + /* mc related members */ + struct media_pad pad; + struct v4l2_mbus_framefmt format; }; /* TVP514x default register values */ @@ -200,6 +211,21 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = { }; /** + * List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +/** * Supported standards - * * Currently supports two standards only, need to add support for rest of the @@ -733,7 +759,7 @@ tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, } /** - * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * tvp514x_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to the mediabus format structure * @@ -751,12 +777,11 @@ tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) /* Calculate height and width based on current standard */ current_std = decoder->current_std; - f->code = V4L2_MBUS_FMT_YUYV10_2X10; + f->code = V4L2_MBUS_FMT_YUYV8_2X8; f->width = decoder->std_list[current_std].width; f->height = decoder->std_list[current_std].height; f->field = V4L2_FIELD_INTERLACED; f->colorspace = V4L2_COLORSPACE_SMPTE170M; - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", f->width, f->height); return 0; @@ -892,6 +917,88 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { .s_ctrl = tvp514x_s_ctrl, }; +/** + * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Enumertaes mbus codes supported + */ +static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + u32 pad = code->pad; + u32 index = code->index; + + memset(code, 0, sizeof(*code)); + code->index = index; + code->pad = pad; + + if (index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_YUYV8_2X8; + + return 0; +} + +/** + * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @format: pointer to v4l2_subdev_format structure + * + * Retrieves pad format which is active or tried based on requirement + */ +static int tvp514x_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + __u32 which = format->which; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + format->format = decoder->format; + return 0; + } + + format->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format->format.width = tvp514x_std_list[decoder->current_std].width; + format->format.height = tvp514x_std_list[decoder->current_std].height; + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->format.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +/** + * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @format: pointer to v4l2_subdev_format structure + * + * Set pad format for the output pad + */ +static int tvp514x_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + + if (fmt->format.field != V4L2_FIELD_INTERLACED || + fmt->format.code != V4L2_MBUS_FMT_YUYV8_2X8 || + fmt->format.colorspace != V4L2_COLORSPACE_SMPTE170M || + fmt->format.width != tvp514x_std_list[decoder->current_std].width || + fmt->format.height != tvp514x_std_list[decoder->current_std].height) + return -EINVAL; + + decoder->format = fmt->format; + + return 0; +} + static const struct v4l2_subdev_core_ops tvp514x_core_ops = { .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, @@ -915,13 +1022,33 @@ static const struct v4l2_subdev_video_ops tvp514x_video_ops = { .s_stream = tvp514x_s_stream, }; +static const struct v4l2_subdev_pad_ops tvp514x_pad_ops = { + .enum_mbus_code = tvp514x_enum_mbus_code, + .get_fmt = tvp514x_get_pad_format, + .set_fmt = tvp514x_set_pad_format, +}; + static const struct v4l2_subdev_ops tvp514x_ops = { .core = &tvp514x_core_ops, .video = &tvp514x_video_ops, + .pad = &tvp514x_pad_ops, }; static struct tvp514x_decoder tvp514x_dev = { .streaming = 0, + .fmt_list = tvp514x_fmt_list, + .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), + .pix = { + /* Default to NTSC 8-bit YUV 422 */ + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2, + .sizeimage = NTSC_NUM_ACTIVE_PIXELS * 2 * + NTSC_NUM_ACTIVE_LINES, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }, .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), @@ -941,6 +1068,7 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tvp514x_decoder *decoder; struct v4l2_subdev *sd; + int ret; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -981,7 +1109,21 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Register with V4L2 layer as slave device */ sd = &decoder->sd; v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); - + strlcpy(sd->name, TVP514X_MODULE_NAME, sizeof(sd->name)); + +#if defined(CONFIG_MEDIA_CONTROLLER) + decoder->pad.flags = MEDIA_PAD_FL_SOURCE; + decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + decoder->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + + ret = media_entity_init(&decoder->sd.entity, 1, &decoder->pad, 0); + if (ret < 0) { + v4l2_err(sd, "%s decoder driver failed to register !!\n", + sd->name); + kfree(decoder); + return ret; + } +#endif v4l2_ctrl_handler_init(&decoder->hdl, 5); v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); @@ -995,10 +1137,10 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) V4L2_CID_AUTOGAIN, 0, 1, 1, 1); sd->ctrl_handler = &decoder->hdl; if (decoder->hdl.error) { - int err = decoder->hdl.error; + ret = decoder->hdl.error; v4l2_ctrl_handler_free(&decoder->hdl); - return err; + return ret; } v4l2_ctrl_handler_setup(&decoder->hdl); @@ -1021,6 +1163,9 @@ static int tvp514x_remove(struct i2c_client *client) struct tvp514x_decoder *decoder = to_decoder(sd); v4l2_device_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&decoder->sd.entity); +#endif v4l2_ctrl_handler_free(&decoder->hdl); return 0; } diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 5967e1a0..485159a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1067,7 +1067,7 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 537f6b4..027809c 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -326,9 +326,8 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = { { TVP7002_EOR, 0xff, TVP7002_RESERVED } }; -/* Preset definition for handling device operation */ -struct tvp7002_preset_definition { - u32 preset; +/* Timings definition for handling device operation */ +struct tvp7002_timings_definition { struct v4l2_dv_timings timings; const struct i2c_reg_value *p_settings; enum v4l2_colorspace color_space; @@ -339,10 +338,9 @@ struct tvp7002_preset_definition { u16 cpl_max; }; -/* Struct list for digital video presets */ -static const struct tvp7002_preset_definition tvp7002_presets[] = { +/* Struct list for digital video timings */ +static const struct tvp7002_timings_definition tvp7002_timings[] = { { - V4L2_DV_720P60, V4L2_DV_BT_CEA_1280X720P60, tvp7002_parms_720P60, V4L2_COLORSPACE_REC709, @@ -353,7 +351,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 153 }, { - V4L2_DV_1080I60, V4L2_DV_BT_CEA_1920X1080I60, tvp7002_parms_1080I60, V4L2_COLORSPACE_REC709, @@ -364,7 +361,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 205 }, { - V4L2_DV_1080I50, V4L2_DV_BT_CEA_1920X1080I50, tvp7002_parms_1080I50, V4L2_COLORSPACE_REC709, @@ -375,7 +371,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 245 }, { - V4L2_DV_720P50, V4L2_DV_BT_CEA_1280X720P50, tvp7002_parms_720P50, V4L2_COLORSPACE_REC709, @@ -386,7 +381,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 183 }, { - V4L2_DV_1080P60, V4L2_DV_BT_CEA_1920X1080P60, tvp7002_parms_1080P60, V4L2_COLORSPACE_REC709, @@ -397,7 +391,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 102 }, { - V4L2_DV_480P59_94, V4L2_DV_BT_CEA_720X480P59_94, tvp7002_parms_480P, V4L2_COLORSPACE_SMPTE170M, @@ -408,7 +401,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { 0xffff }, { - V4L2_DV_576P50, V4L2_DV_BT_CEA_720X576P50, tvp7002_parms_576P, V4L2_COLORSPACE_SMPTE170M, @@ -420,7 +412,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { } }; -#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) +#define NUM_TIMINGS ARRAY_SIZE(tvp7002_timings) /* Device definition */ struct tvp7002 { @@ -431,7 +423,7 @@ struct tvp7002 { int ver; int streaming; - const struct tvp7002_preset_definition *current_preset; + const struct tvp7002_timings_definition *current_timings; }; /* @@ -588,32 +580,6 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd, return error; } -/* - * tvp7002_s_dv_preset() - Set digital video preset - * @sd: ptr to v4l2_subdev struct - * @dv_preset: ptr to v4l2_dv_preset struct - * - * Set the digital video preset for a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *dv_preset) -{ - struct tvp7002 *device = to_tvp7002(sd); - u32 preset; - int i; - - for (i = 0; i < NUM_PRESETS; i++) { - preset = tvp7002_presets[i].preset; - if (preset == dv_preset->preset) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); - } - } - - return -EINVAL; -} - static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *dv_timings) { @@ -623,12 +589,12 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, if (dv_timings->type != V4L2_DV_BT_656_1120) return -EINVAL; - for (i = 0; i < NUM_PRESETS; i++) { - const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; + for (i = 0; i < NUM_TIMINGS; i++) { + const struct v4l2_bt_timings *t = &tvp7002_timings[i].timings.bt; if (!memcmp(bt, t, &bt->standards - &bt->width)) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + device->current_timings = &tvp7002_timings[i]; + return tvp7002_write_inittab(sd, tvp7002_timings[i].p_settings); } } return -EINVAL; @@ -639,7 +605,7 @@ static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, { struct tvp7002 *device = to_tvp7002(sd); - *dv_timings = device->current_preset->timings; + *dv_timings = device->current_timings->timings; return 0; } @@ -677,19 +643,13 @@ static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) { struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - int error; + const struct v4l2_bt_timings *bt = &device->current_timings->timings.bt; - /* Calculate height and width based on current standard */ - error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); - if (error) - return error; - - f->width = e_preset.width; - f->height = e_preset.height; + f->width = bt->width; + f->height = bt->height; f->code = V4L2_MBUS_FMT_YUYV10_1X20; - f->field = device->current_preset->scanmode; - f->colorspace = device->current_preset->color_space; + f->field = device->current_timings->scanmode; + f->colorspace = device->current_timings->color_space; v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", f->width, f->height); @@ -697,16 +657,16 @@ static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f } /* - * tvp7002_query_dv_preset() - query DV preset + * tvp7002_query_dv() - query DV timings * @sd: pointer to standard V4L2 sub-device structure - * @qpreset: standard V4L2 v4l2_dv_preset structure + * @index: index into the tvp7002_timings array * - * Returns the current DV preset by TVP7002. If no active input is + * Returns the current DV timings detected by TVP7002. If no active input is * detected, returns -EINVAL */ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) { - const struct tvp7002_preset_definition *presets = tvp7002_presets; + const struct tvp7002_timings_definition *timings = tvp7002_timings; u8 progressive; u32 lpfr; u32 cpln; @@ -717,7 +677,7 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) u8 cpl_msb; /* Return invalid index if no active input is detected */ - *index = NUM_PRESETS; + *index = NUM_TIMINGS; /* Read standards from device registers */ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); @@ -738,39 +698,23 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; /* Do checking of video modes */ - for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) - if (lpfr == presets->lines_per_frame && - progressive == presets->progressive) { - if (presets->cpl_min == 0xffff) + for (*index = 0; *index < NUM_TIMINGS; (*index)++, timings++) + if (lpfr == timings->lines_per_frame && + progressive == timings->progressive) { + if (timings->cpl_min == 0xffff) break; - if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) + if (cpln >= timings->cpl_min && cpln <= timings->cpl_max) break; } - if (*index == NUM_PRESETS) { + if (*index == NUM_TIMINGS) { v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); return -ENOLINK; } /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); - return 0; -} - -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *qpreset) -{ - int index; - int err = tvp7002_query_dv(sd, &index); - - if (err || index == NUM_PRESETS) { - qpreset->preset = V4L2_DV_INVALID; - if (err == -ENOLINK) - err = 0; - return err; - } - qpreset->preset = tvp7002_presets[index].preset; + v4l2_dbg(1, debug, sd, "detected timings: %d\n", *index); return 0; } @@ -782,7 +726,7 @@ static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, if (err) return err; - *timings = tvp7002_presets[index].timings; + *timings = tvp7002_timings[index].timings; return 0; } @@ -824,7 +768,7 @@ static int tvp7002_g_register(struct v4l2_subdev *sd, * -EPERM if call not allowed. */ static int tvp7002_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -896,35 +840,21 @@ static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) */ static int tvp7002_log_status(struct v4l2_subdev *sd) { - const struct tvp7002_preset_definition *presets = tvp7002_presets; struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - struct v4l2_dv_preset detected; - int i; - - detected.preset = V4L2_DV_INVALID; - /* Find my current standard*/ - tvp7002_query_dv_preset(sd, &detected); + const struct v4l2_bt_timings *bt; + int detected; - /* Print standard related code values */ - for (i = 0; i < NUM_PRESETS; i++, presets++) - if (presets->preset == detected.preset) - break; + /* Find my current timings */ + tvp7002_query_dv(sd, &detected); - if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) - return -EINVAL; - - v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); - if (i == NUM_PRESETS) { - v4l2_info(sd, "Detected DV Preset: None\n"); + bt = &device->current_timings->timings.bt; + v4l2_info(sd, "Selected DV Timings: %ux%u\n", bt->width, bt->height); + if (detected == NUM_TIMINGS) { + v4l2_info(sd, "Detected DV Timings: None\n"); } else { - if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) - return -EINVAL; - v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + bt = &tvp7002_timings[detected].timings.bt; + v4l2_info(sd, "Detected DV Timings: %ux%u\n", + bt->width, bt->height); } v4l2_info(sd, "Streaming enabled: %s\n", device->streaming ? "yes" : "no"); @@ -935,31 +865,14 @@ static int tvp7002_log_status(struct v4l2_subdev *sd) return 0; } -/* - * tvp7002_enum_dv_presets() - Enum supported digital video formats - * @sd: pointer to standard V4L2 sub-device structure - * @preset: pointer to format struct - * - * Enumerate supported digital video formats. - */ -static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) -{ - /* Check requested format index is within range */ - if (preset->index >= NUM_PRESETS) - return -EINVAL; - - return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); -} - static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { /* Check requested format index is within range */ - if (timings->index >= NUM_PRESETS) + if (timings->index >= NUM_TIMINGS) return -EINVAL; - timings->timings = tvp7002_presets[timings->index].timings; + timings->timings = tvp7002_timings[timings->index].timings; return 0; } @@ -986,9 +899,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { /* Specific video subsystem operation handlers */ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { - .enum_dv_presets = tvp7002_enum_dv_presets, - .s_dv_preset = tvp7002_s_dv_preset, - .query_dv_preset = tvp7002_query_dv_preset, .g_dv_timings = tvp7002_g_dv_timings, .s_dv_timings = tvp7002_s_dv_timings, .enum_dv_timings = tvp7002_enum_dv_timings, @@ -1019,7 +929,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) { struct v4l2_subdev *sd; struct tvp7002 *device; - struct v4l2_dv_preset preset; + struct v4l2_dv_timings timings; int polarity_a; int polarity_b; u8 revision; @@ -1043,7 +953,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) sd = &device->sd; device->pdata = c->dev.platform_data; - device->current_preset = tvp7002_presets; + device->current_timings = tvp7002_timings; /* Tell v4l2 the device is ready */ v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); @@ -1080,8 +990,8 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) return error; /* Set registers according to default video mode */ - preset.preset = device->current_preset->preset; - error = tvp7002_s_dv_preset(sd, &preset); + timings = device->current_timings->timings; + error = tvp7002_s_dv_timings(sd, &timings); v4l2_ctrl_handler_init(&device->hdl, 1); v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c new file mode 100644 index 0000000..c5dc2c3 --- /dev/null +++ b/drivers/media/i2c/tw2804.c @@ -0,0 +1,453 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <linux/slab.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> + +#define TW2804_REG_AUTOGAIN 0x02 +#define TW2804_REG_HUE 0x0f +#define TW2804_REG_SATURATION 0x10 +#define TW2804_REG_CONTRAST 0x11 +#define TW2804_REG_BRIGHTNESS 0x12 +#define TW2804_REG_COLOR_KILLER 0x14 +#define TW2804_REG_GAIN 0x3c +#define TW2804_REG_CHROMA_GAIN 0x3d +#define TW2804_REG_BLUE_BALANCE 0x3e +#define TW2804_REG_RED_BALANCE 0x3f + +struct tw2804 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 channel:2; + u8 input:1; + int norm; +}; + +static const u8 global_registers[] = { + 0x39, 0x00, + 0x3a, 0xff, + 0x3b, 0x84, + 0x3c, 0x80, + 0x3d, 0x80, + 0x3e, 0x82, + 0x3f, 0x82, + 0x78, 0x00, + 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static const u8 channel_registers[] = { + 0x01, 0xc4, + 0x02, 0xa5, + 0x03, 0x20, + 0x04, 0xd0, + 0x05, 0x20, + 0x06, 0xd0, + 0x07, 0x88, + 0x08, 0x20, + 0x09, 0x07, + 0x0a, 0xf0, + 0x0b, 0x07, + 0x0c, 0xf0, + 0x0d, 0x40, + 0x0e, 0xd2, + 0x0f, 0x80, + 0x10, 0x80, + 0x11, 0x80, + 0x12, 0x80, + 0x13, 0x1f, + 0x14, 0x00, + 0x15, 0x00, + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0xff, + 0x19, 0xff, + 0x1a, 0xff, + 0x1b, 0xff, + 0x1c, 0xff, + 0x1d, 0xff, + 0x1e, 0xff, + 0x1f, 0xff, + 0x20, 0x07, + 0x21, 0x07, + 0x22, 0x00, + 0x23, 0x91, + 0x24, 0x51, + 0x25, 0x03, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x00, + 0x2a, 0x00, + 0x2b, 0x00, + 0x2c, 0x00, + 0x2d, 0x00, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0x00, + 0x31, 0x00, + 0x32, 0x00, + 0x33, 0x00, + 0x34, 0x00, + 0x35, 0x00, + 0x36, 0x00, + 0x37, 0x00, + 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel) +{ + return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); +} + +static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel) +{ + int ret; + int i; + + for (i = 0; regs[i] != 0xff; i += 2) { + ret = i2c_smbus_write_byte_data(client, + regs[i] | (channel << 6), regs[i + 1]); + if (ret < 0) + return ret; + } + return 0; +} + +static int read_reg(struct i2c_client *client, u8 reg, u8 channel) +{ + return i2c_smbus_read_byte_data(client, (reg) | (channel << 6)); +} + +static inline struct tw2804 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tw2804, sd); +} + +static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct tw2804, hdl); +} + +static int tw2804_log_status(struct v4l2_subdev *sd) +{ + struct tw2804 *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", + state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Channel: %d\n", state->channel); + v4l2_info(sd, "Input: %d\n", state->input); + return v4l2_ctrl_subdev_log_status(sd); +} + +/* + * These volatile controls are needed because all four channels share + * these controls. So a change made to them through one channel would + * require another channel to be updated. + * + * Normally this would have been done in a different way, but since the one + * board that uses this driver sees this single chip as if it was on four + * different i2c adapters (each adapter belonging to a separate instance of + * the same USB driver) there is no reliable method that I have found to let + * the instances know about each other. + * + * So implementing these global registers as volatile is the best we can do. + */ +static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw2804 *state = to_state_from_ctrl(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->val = read_reg(client, TW2804_REG_GAIN, 0); + return 0; + + case V4L2_CID_CHROMA_GAIN: + ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0); + return 0; + + case V4L2_CID_BLUE_BALANCE: + ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0); + return 0; + + case V4L2_CID_RED_BALANCE: + ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0); + return 0; + } + return 0; +} + +static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw2804 *state = to_state_from_ctrl(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + int addr; + int reg; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + addr = TW2804_REG_AUTOGAIN; + reg = read_reg(client, addr, state->channel); + if (reg < 0) + return reg; + if (ctrl->val == 0) + reg &= ~(1 << 7); + else + reg |= 1 << 7; + return write_reg(client, addr, reg, state->channel); + + case V4L2_CID_COLOR_KILLER: + addr = TW2804_REG_COLOR_KILLER; + reg = read_reg(client, addr, state->channel); + if (reg < 0) + return reg; + reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03); + return write_reg(client, addr, reg, state->channel); + + case V4L2_CID_GAIN: + return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0); + + case V4L2_CID_CHROMA_GAIN: + return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0); + + case V4L2_CID_BLUE_BALANCE: + return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0); + + case V4L2_CID_RED_BALANCE: + return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0); + + case V4L2_CID_BRIGHTNESS: + return write_reg(client, TW2804_REG_BRIGHTNESS, + ctrl->val, state->channel); + + case V4L2_CID_CONTRAST: + return write_reg(client, TW2804_REG_CONTRAST, + ctrl->val, state->channel); + + case V4L2_CID_SATURATION: + return write_reg(client, TW2804_REG_SATURATION, + ctrl->val, state->channel); + + case V4L2_CID_HUE: + return write_reg(client, TW2804_REG_HUE, + ctrl->val, state->channel); + + default: + break; + } + return -EINVAL; +} + +static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + bool is_60hz = norm & V4L2_STD_525_60; + u8 regs[] = { + 0x01, is_60hz ? 0xc4 : 0x84, + 0x09, is_60hz ? 0x07 : 0x04, + 0x0a, is_60hz ? 0xf0 : 0x20, + 0x0b, is_60hz ? 0x07 : 0x04, + 0x0c, is_60hz ? 0xf0 : 0x20, + 0x0d, is_60hz ? 0x40 : 0x4a, + 0x16, is_60hz ? 0x00 : 0x40, + 0x17, is_60hz ? 0x00 : 0x40, + 0x20, is_60hz ? 0x07 : 0x0f, + 0x21, is_60hz ? 0x07 : 0x0f, + 0xff, 0xff, + }; + + write_regs(client, regs, dec->channel); + dec->norm = norm; + return 0; +} + +static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int reg; + + if (config && config - 1 != dec->channel) { + if (config > 4) { + dev_err(&client->dev, + "channel %d is not between 1 and 4!\n", config); + return -EINVAL; + } + dec->channel = config - 1; + dev_dbg(&client->dev, "initializing TW2804 channel %d\n", + dec->channel); + if (dec->channel == 0 && + write_regs(client, global_registers, 0) < 0) { + dev_err(&client->dev, + "error initializing TW2804 global registers\n"); + return -EIO; + } + if (write_regs(client, channel_registers, dec->channel) < 0) { + dev_err(&client->dev, + "error initializing TW2804 channel %d\n", + dec->channel); + return -EIO; + } + } + + if (input > 1) + return -EINVAL; + + if (input == dec->input) + return 0; + + reg = read_reg(client, 0x22, dec->channel); + + if (reg >= 0) { + if (input == 0) + reg &= ~(1 << 2); + else + reg |= 1 << 2; + reg = write_reg(client, 0x22, reg, dec->channel); + } + + if (reg >= 0) + dec->input = input; + else + return reg; + return 0; +} + +static const struct v4l2_ctrl_ops tw2804_ctrl_ops = { + .g_volatile_ctrl = tw2804_g_volatile_ctrl, + .s_ctrl = tw2804_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops tw2804_video_ops = { + .s_routing = tw2804_s_video_routing, +}; + +static const struct v4l2_subdev_core_ops tw2804_core_ops = { + .log_status = tw2804_log_status, + .s_std = tw2804_s_std, +}; + +static const struct v4l2_subdev_ops tw2804_ops = { + .core = &tw2804_core_ops, + .video = &tw2804_video_ops, +}; + +static int tw2804_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct tw2804 *state; + struct v4l2_subdev *sd; + struct v4l2_ctrl *ctrl; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + state = kzalloc(sizeof(struct tw2804), GFP_KERNEL); + + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tw2804_ops); + state->channel = -1; + state->norm = V4L2_STD_NTSC; + + v4l2_ctrl_handler_init(&state->hdl, 10); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 0); + ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 255, 1, 122); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + sd->ctrl_handler = &state->hdl; + err = state->hdl.error; + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + return 0; +} + +static int tw2804_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tw2804 *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id tw2804_id[] = { + { "tw2804", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw2804_id); + +static struct i2c_driver tw2804_driver = { + .driver = { + .name = "tw2804", + }, + .probe = tw2804_probe, + .remove = tw2804_remove, + .id_table = tw2804_id, +}; + +module_i2c_driver(tw2804_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver"); +MODULE_AUTHOR("Micronas USA Inc"); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c new file mode 100644 index 0000000..87880b1 --- /dev/null +++ b/drivers/media/i2c/tw9903.c @@ -0,0 +1,279 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("TW9903 I2C subdev driver"); +MODULE_LICENSE("GPL v2"); + +/* + * This driver is based on the wis-tw9903.c source that was in + * drivers/staging/media/go7007. That source had commented out code for + * saturation and scaling (neither seemed to work). If anyone ever gets + * hardware to test this driver, then that code might be useful to look at. + * You need to get the kernel sources of, say, kernel 3.8 where that + * wis-tw9903 driver is still present. + */ + +struct tw9903 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + v4l2_std_id norm; +}; + +static inline struct tw9903 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tw9903, sd); +} + +static const u8 initial_registers[] = { + 0x02, 0x44, /* input 1, composite */ + 0x03, 0x92, /* correct digital format */ + 0x04, 0x00, + 0x05, 0x80, /* or 0x00 for PAL */ + 0x06, 0x40, /* second internal current reference */ + 0x07, 0x02, /* window */ + 0x08, 0x14, /* window */ + 0x09, 0xf0, /* window */ + 0x0a, 0x81, /* window */ + 0x0b, 0xd0, /* window */ + 0x0c, 0x8c, + 0x0d, 0x00, /* scaling */ + 0x0e, 0x11, /* scaling */ + 0x0f, 0x00, /* scaling */ + 0x10, 0x00, /* brightness */ + 0x11, 0x60, /* contrast */ + 0x12, 0x01, /* sharpness */ + 0x13, 0x7f, /* U gain */ + 0x14, 0x5a, /* V gain */ + 0x15, 0x00, /* hue */ + 0x16, 0xc3, /* sharpness */ + 0x18, 0x00, + 0x19, 0x58, /* vbi */ + 0x1a, 0x80, + 0x1c, 0x0f, /* video norm */ + 0x1d, 0x7f, /* video norm */ + 0x20, 0xa0, /* clamping gain (working 0x50) */ + 0x21, 0x22, + 0x22, 0xf0, + 0x23, 0xfe, + 0x24, 0x3c, + 0x25, 0x38, + 0x26, 0x44, + 0x27, 0x20, + 0x28, 0x00, + 0x29, 0x15, + 0x2a, 0xa0, + 0x2b, 0x44, + 0x2c, 0x37, + 0x2d, 0x00, + 0x2e, 0xa5, /* burst PLL control (working: a9) */ + 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */ + 0x31, 0x00, + 0x33, 0x22, + 0x34, 0x11, + 0x35, 0x35, + 0x3b, 0x05, + 0x06, 0xc0, /* reset device */ + 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct v4l2_subdev *sd, const u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0x00; i += 2) + if (write_reg(sd, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int tw9903_s_video_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + write_reg(sd, 0x02, 0x40 | (input << 1)); + return 0; +} + +static int tw9903_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct tw9903 *dec = to_state(sd); + bool is_60hz = norm & V4L2_STD_525_60; + static const u8 config_60hz[] = { + 0x05, 0x80, + 0x07, 0x02, + 0x08, 0x14, + 0x09, 0xf0, + 0, 0, + }; + static const u8 config_50hz[] = { + 0x05, 0x00, + 0x07, 0x12, + 0x08, 0x18, + 0x09, 0x20, + 0, 0, + }; + + write_regs(sd, is_60hz ? config_60hz : config_50hz); + dec->norm = norm; + return 0; +} + + +static int tw9903_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw9903 *dec = container_of(ctrl->handler, struct tw9903, hdl); + struct v4l2_subdev *sd = &dec->sd; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + write_reg(sd, 0x10, ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(sd, 0x11, ctrl->val); + break; + case V4L2_CID_HUE: + write_reg(sd, 0x15, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +static int tw9903_log_status(struct v4l2_subdev *sd) +{ + struct tw9903 *dec = to_state(sd); + bool is_60hz = dec->norm & V4L2_STD_525_60; + + v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50); + v4l2_ctrl_subdev_log_status(sd); + return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_ctrl_ops tw9903_ctrl_ops = { + .s_ctrl = tw9903_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tw9903_core_ops = { + .log_status = tw9903_log_status, + .s_std = tw9903_s_std, +}; + +static const struct v4l2_subdev_video_ops tw9903_video_ops = { + .s_routing = tw9903_s_video_routing, +}; + +static const struct v4l2_subdev_ops tw9903_ops = { + .core = &tw9903_core_ops, + .video = &tw9903_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int tw9903_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tw9903 *dec; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + dec = kzalloc(sizeof(struct tw9903), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + sd = &dec->sd; + v4l2_i2c_subdev_init(sd, client, &tw9903_ops); + hdl = &dec->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 0x60); + v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(dec); + return err; + } + + /* Initialize tw9903 */ + dec->norm = V4L2_STD_NTSC; + + if (write_regs(sd, initial_registers) < 0) { + v4l2_err(client, "error initializing TW9903\n"); + kfree(dec); + return -EINVAL; + } + + return 0; +} + +static int tw9903_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&to_state(sd)->hdl); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tw9903_id[] = { + { "tw9903", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9903_id); + +static struct i2c_driver tw9903_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tw9903", + }, + .probe = tw9903_probe, + .remove = tw9903_remove, + .id_table = tw9903_id, +}; +module_i2c_driver(tw9903_driver); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c new file mode 100644 index 0000000..accd79e --- /dev/null +++ b/drivers/media/i2c/tw9906.c @@ -0,0 +1,247 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <linux/slab.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> + +MODULE_DESCRIPTION("TW9906 I2C subdev driver"); +MODULE_LICENSE("GPL v2"); + +struct tw9906 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + v4l2_std_id norm; +}; + +static inline struct tw9906 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tw9906, sd); +} + +static const u8 initial_registers[] = { + 0x02, 0x40, /* input 0, composite */ + 0x03, 0xa2, /* correct digital format */ + 0x05, 0x81, /* or 0x01 for PAL */ + 0x07, 0x02, /* window */ + 0x08, 0x14, /* window */ + 0x09, 0xf0, /* window */ + 0x0a, 0x10, /* window */ + 0x0b, 0xd0, /* window */ + 0x0d, 0x00, /* scaling */ + 0x0e, 0x11, /* scaling */ + 0x0f, 0x00, /* scaling */ + 0x10, 0x00, /* brightness */ + 0x11, 0x60, /* contrast */ + 0x12, 0x11, /* sharpness */ + 0x13, 0x7e, /* U gain */ + 0x14, 0x7e, /* V gain */ + 0x15, 0x00, /* hue */ + 0x19, 0x57, /* vbi */ + 0x1a, 0x0f, + 0x1b, 0x40, + 0x29, 0x03, + 0x55, 0x00, + 0x6b, 0x26, + 0x6c, 0x36, + 0x6d, 0xf0, + 0x6e, 0x41, + 0x6f, 0x13, + 0xad, 0x70, + 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct v4l2_subdev *sd, const u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0x00; i += 2) + if (write_reg(sd, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int tw9906_s_video_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + write_reg(sd, 0x02, 0x40 | (input << 1)); + return 0; +} + +static int tw9906_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct tw9906 *dec = to_state(sd); + bool is_60hz = norm & V4L2_STD_525_60; + static const u8 config_60hz[] = { + 0x05, 0x81, + 0x07, 0x02, + 0x08, 0x14, + 0x09, 0xf0, + 0, 0, + }; + static const u8 config_50hz[] = { + 0x05, 0x01, + 0x07, 0x12, + 0x08, 0x18, + 0x09, 0x20, + 0, 0, + }; + + write_regs(sd, is_60hz ? config_60hz : config_50hz); + dec->norm = norm; + return 0; +} + +static int tw9906_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw9906 *dec = container_of(ctrl->handler, struct tw9906, hdl); + struct v4l2_subdev *sd = &dec->sd; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + write_reg(sd, 0x10, ctrl->val); + break; + case V4L2_CID_CONTRAST: + write_reg(sd, 0x11, ctrl->val); + break; + case V4L2_CID_HUE: + write_reg(sd, 0x15, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +static int tw9906_log_status(struct v4l2_subdev *sd) +{ + struct tw9906 *dec = to_state(sd); + bool is_60hz = dec->norm & V4L2_STD_525_60; + + v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50); + v4l2_ctrl_subdev_log_status(sd); + return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_ctrl_ops tw9906_ctrl_ops = { + .s_ctrl = tw9906_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tw9906_core_ops = { + .log_status = tw9906_log_status, + .s_std = tw9906_s_std, +}; + +static const struct v4l2_subdev_video_ops tw9906_video_ops = { + .s_routing = tw9906_s_video_routing, +}; + +static const struct v4l2_subdev_ops tw9906_ops = { + .core = &tw9906_core_ops, + .video = &tw9906_video_ops, +}; + +static int tw9906_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tw9906 *dec; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + dec = kzalloc(sizeof(struct tw9906), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + sd = &dec->sd; + v4l2_i2c_subdev_init(sd, client, &tw9906_ops); + hdl = &dec->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 0x60); + v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(dec); + return err; + } + + /* Initialize tw9906 */ + dec->norm = V4L2_STD_NTSC; + + if (write_regs(sd, initial_registers) < 0) { + v4l2_err(client, "error initializing TW9906\n"); + kfree(dec); + return -EINVAL; + } + + return 0; +} + +static int tw9906_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&to_state(sd)->hdl); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tw9906_id[] = { + { "tw9906", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9906_id); + +static struct i2c_driver tw9906_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tw9906", + }, + .probe = tw9906_probe, + .remove = tw9906_remove, + .id_table = tw9906_id, +}; +module_i2c_driver(tw9906_driver); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c new file mode 100644 index 0000000..3af4085 --- /dev/null +++ b/drivers/media/i2c/uda1342.c @@ -0,0 +1,113 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/uda1342.h> +#include <linux/slab.h> + +static int write_reg(struct i2c_client *client, int reg, int value) +{ + /* UDA1342 wants MSB first, but SMBus sends LSB first */ + i2c_smbus_write_word_data(client, reg, swab16(value)); + return 0; +} + +static int uda1342_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (input) { + case UDA1342_IN1: + write_reg(client, 0x00, 0x1241); /* select input 1 */ + break; + case UDA1342_IN2: + write_reg(client, 0x00, 0x1441); /* select input 2 */ + break; + default: + v4l2_err(sd, "input %d not supported\n", input); + break; + } + return 0; +} + +static const struct v4l2_subdev_audio_ops uda1342_audio_ops = { + .s_routing = uda1342_s_routing, +}; + +static const struct v4l2_subdev_ops uda1342_ops = { + .audio = &uda1342_audio_ops, +}; + +static int uda1342_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct v4l2_subdev *sd; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", + client->addr, adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(sd, client, &uda1342_ops); + + write_reg(client, 0x00, 0x8000); /* reset registers */ + write_reg(client, 0x00, 0x1241); /* select input 1 */ + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + return 0; +} + +static int uda1342_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id uda1342_id[] = { + { "uda1342", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, uda1342_id); + +static struct i2c_driver uda1342_driver = { + .driver = { + .name = "uda1342", + }, + .probe = uda1342_probe, + .remove = uda1342_remove, + .id_table = uda1342_id, +}; + +module_i2c_driver(uda1342_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index 1e74465..f0a0921 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -111,7 +111,7 @@ static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) /* ------------------------------------------------------------------------ */ /* The input changed due to new input or channel changed */ -static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int upd64031a_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) { struct upd64031a_state *state = to_state(sd); u8 reg = state->regs[R00]; @@ -175,7 +175,7 @@ static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register return 0; } -static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int upd64031a_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 75d6acc..343e021 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -133,7 +133,7 @@ static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register return 0; } -static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int upd64083_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 7cfbc9d..e71f139 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -90,7 +90,7 @@ static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) return 0; } -static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct vp27smpx_state *state = to_state(sd); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 9ac1b8c3..f366fad 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -748,7 +748,7 @@ static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r return 0; } -static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int vs6624_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index bee77ea..27c27b4 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -174,7 +174,7 @@ static int wm8775_log_status(struct v4l2_subdev *sd) return 0; } -static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int wm8775_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) { wm8775_set_audio(sd, 0); return 0; diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index d01fcb7..1957c0d 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -20,10 +20,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/types.h> +#include <linux/compat.h> +#include <linux/export.h> #include <linux/ioctl.h> #include <linux/media.h> -#include <linux/export.h> +#include <linux/types.h> #include <media/media-device.h> #include <media/media-devnode.h> @@ -101,9 +102,12 @@ static long media_device_enum_entities(struct media_device *mdev, return -EINVAL; u_ent.id = ent->id; - u_ent.name[0] = '\0'; - if (ent->name) - strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); + if (ent->name) { + strncpy(u_ent.name, ent->name, sizeof(u_ent.name)); + u_ent.name[sizeof(u_ent.name) - 1] = '\0'; + } else { + memset(u_ent.name, 0, sizeof(u_ent.name)); + } u_ent.type = ent->type; u_ent.revision = ent->revision; u_ent.flags = ent->flags; @@ -124,35 +128,31 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, upad->flags = kpad->flags; } -static long media_device_enum_links(struct media_device *mdev, - struct media_links_enum __user *ulinks) +static long __media_device_enum_links(struct media_device *mdev, + struct media_links_enum *links) { struct media_entity *entity; - struct media_links_enum links; - if (copy_from_user(&links, ulinks, sizeof(links))) - return -EFAULT; - - entity = find_entity(mdev, links.entity); + entity = find_entity(mdev, links->entity); if (entity == NULL) return -EINVAL; - if (links.pads) { + if (links->pads) { unsigned int p; for (p = 0; p < entity->num_pads; p++) { struct media_pad_desc pad; media_device_kpad_to_upad(&entity->pads[p], &pad); - if (copy_to_user(&links.pads[p], &pad, sizeof(pad))) + if (copy_to_user(&links->pads[p], &pad, sizeof(pad))) return -EFAULT; } } - if (links.links) { + if (links->links) { struct media_link_desc __user *ulink; unsigned int l; - for (l = 0, ulink = links.links; l < entity->num_links; l++) { + for (l = 0, ulink = links->links; l < entity->num_links; l++) { struct media_link_desc link; /* Ignore backlinks. */ @@ -169,8 +169,26 @@ static long media_device_enum_links(struct media_device *mdev, ulink++; } } + + return 0; +} + +static long media_device_enum_links(struct media_device *mdev, + struct media_links_enum __user *ulinks) +{ + struct media_links_enum links; + int rval; + + if (copy_from_user(&links, ulinks, sizeof(links))) + return -EFAULT; + + rval = __media_device_enum_links(mdev, &links); + if (rval < 0) + return rval; + if (copy_to_user(ulinks, &links, sizeof(*ulinks))) return -EFAULT; + return 0; } @@ -251,10 +269,71 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT + +struct media_links_enum32 { + __u32 entity; + compat_uptr_t pads; /* struct media_pad_desc * */ + compat_uptr_t links; /* struct media_link_desc * */ + __u32 reserved[4]; +}; + +static long media_device_enum_links32(struct media_device *mdev, + struct media_links_enum32 __user *ulinks) +{ + struct media_links_enum links; + compat_uptr_t pads_ptr, links_ptr; + + memset(&links, 0, sizeof(links)); + + if (get_user(links.entity, &ulinks->entity) + || get_user(pads_ptr, &ulinks->pads) + || get_user(links_ptr, &ulinks->links)) + return -EFAULT; + + links.pads = compat_ptr(pads_ptr); + links.links = compat_ptr(links_ptr); + + return __media_device_enum_links(mdev, &links); +} + +#define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) + +static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct media_devnode *devnode = media_devnode_data(filp); + struct media_device *dev = to_media_device(devnode); + long ret; + + switch (cmd) { + case MEDIA_IOC_DEVICE_INFO: + case MEDIA_IOC_ENUM_ENTITIES: + case MEDIA_IOC_SETUP_LINK: + return media_device_ioctl(filp, cmd, arg); + + case MEDIA_IOC_ENUM_LINKS32: + mutex_lock(&dev->graph_mutex); + ret = media_device_enum_links32(dev, + (struct media_links_enum32 __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} +#endif /* CONFIG_COMPAT */ + static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, .open = media_device_open, .ioctl = media_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = media_device_compat_ioctl, +#endif /* CONFIG_COMPAT */ .release = media_device_close, }; diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 023b2a1..fb0f046 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -116,19 +116,41 @@ static unsigned int media_poll(struct file *filp, return mdev->fops->poll(filp, poll); } -static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long +__media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, + long (*ioctl_func)(struct file *filp, unsigned int cmd, + unsigned long arg)) { struct media_devnode *mdev = media_devnode_data(filp); - if (!mdev->fops->ioctl) + if (!ioctl_func) return -ENOTTY; if (!media_devnode_is_registered(mdev)) return -EIO; - return mdev->fops->ioctl(filp, cmd, arg); + return ioctl_func(filp, cmd, arg); +} + +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); } +#ifdef CONFIG_COMPAT + +static long media_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); +} + +#endif /* CONFIG_COMPAT */ + /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { @@ -188,6 +210,9 @@ static const struct file_operations media_devnode_fops = { .write = media_write, .open = media_open, .unlocked_ioctl = media_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = media_compat_ioctl, +#endif /* CONFIG_COMPAT */ .release = media_release, .poll = media_poll, .llseek = no_llseek, diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c index 15d3493..912c281 100644 --- a/drivers/media/mmc/siano/smssdio.c +++ b/drivers/media/mmc/siano/smssdio.c @@ -43,6 +43,7 @@ #include "smscoreapi.h" #include "sms-cards.h" +#include "smsendian.h" /* Registers */ @@ -61,6 +62,16 @@ static const struct sdio_device_id smssdio_ids[] = { .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302), + .driver_data = SMS1XXX_BOARD_SIANO_MING}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500), + .driver_data = SMS1XXX_BOARD_SIANO_PELE}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600), + .driver_data = SMS1XXX_BOARD_SIANO_RIO}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700), + .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800), + .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530}, { /* end: all zeroes */ }, }; @@ -87,6 +98,7 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size) sdio_claim_host(smsdev->func); + smsendian_handle_tx_message((struct sms_msg_data *) buffer); while (size >= smsdev->func->cur_blksize) { ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, buffer, smsdev->func->cur_blksize); @@ -118,7 +130,7 @@ static void smssdio_interrupt(struct sdio_func *func) struct smssdio_device *smsdev; struct smscore_buffer_t *cb; - struct SmsMsgHdr_ST *hdr; + struct sms_msg_hdr *hdr; size_t size; smsdev = sdio_get_drvdata(func); @@ -151,20 +163,20 @@ static void smssdio_interrupt(struct sdio_func *func) hdr = cb->p; - if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { + if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) { smsdev->split_cb = cb; return; } - if (hdr->msgLength > smsdev->func->cur_blksize) - size = hdr->msgLength - smsdev->func->cur_blksize; + if (hdr->msg_length > smsdev->func->cur_blksize) + size = hdr->msg_length - smsdev->func->cur_blksize; else size = 0; } else { cb = smsdev->split_cb; hdr = cb->p; - size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); + size = hdr->msg_length - sizeof(struct sms_msg_hdr); smsdev->split_cb = NULL; } @@ -172,7 +184,7 @@ static void smssdio_interrupt(struct sdio_func *func) if (size) { void *buffer; - buffer = cb->p + (hdr->msgLength - size); + buffer = cb->p + (hdr->msg_length - size); size = ALIGN(size, SMSSDIO_BLOCK_SIZE); BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); @@ -218,9 +230,10 @@ static void smssdio_interrupt(struct sdio_func *func) } } - cb->size = hdr->msgLength; + cb->size = hdr->msg_length; cb->offset = 0; + smsendian_handle_rx_message((struct sms_msg_data *) cb->p); smscore_onresponse(smsdev->coredev, cb); } diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c index 77f9c92..66c957a 100644 --- a/drivers/media/parport/pms.c +++ b/drivers/media/parport/pms.c @@ -735,12 +735,12 @@ static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) return 0; } -static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) +static int pms_s_std(struct file *file, void *fh, v4l2_std_id std) { struct pms *dev = video_drvdata(file); int ret = 0; - dev->std = *std; + dev->std = std; if (dev->std & V4L2_STD_NTSC) { pms_framerate(dev, 30); pms_secamcross(dev, 0); diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index c4c5917..b7dc921 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -3547,6 +3547,16 @@ void bttv_init_card2(struct bttv *btv) if (btv->sd_msp34xx) return; + /* Now see if we can find one of the tvaudio devices. */ + btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs()); + if (btv->sd_tvaudio) { + /* There may be two tvaudio chips on the card, so try to + find another. */ + v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs()); + } + /* it might also be a tda7432. */ if (!bttv_tvcards[btv->c.type].no_tda7432) { static const unsigned short addrs[] = { @@ -3554,14 +3564,11 @@ void bttv_init_card2(struct bttv *btv) I2C_CLIENT_END }; - if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tda7432", 0, addrs)) + btv->sd_tda7432 = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", 0, addrs); + if (btv->sd_tda7432) return; } - - /* Now see if we can find one of the tvaudio devices. */ - btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs()); if (btv->sd_tvaudio) return; @@ -3940,7 +3947,7 @@ static void avermedia_eeprom(struct bttv *btv) u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits) { - if (btv->audio == TVAUDIO_INPUT_TUNER) { + if (btv->audio_input == TVAUDIO_INPUT_TUNER) { if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN) gpiobits |= 0x10000; else diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 54579e4..e7d0884 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -49,6 +49,8 @@ #include "bttvp.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-chip-ident.h> #include <media/tvaudio.h> #include <media/msp3400.h> @@ -93,7 +95,7 @@ static unsigned int combfilter; static unsigned int lumafilter; static unsigned int automute = 1; static unsigned int chroma_agc; -static unsigned int adc_crush = 1; +static unsigned int agc_crush = 1; static unsigned int whitecrush_upper = 0xCF; static unsigned int whitecrush_lower = 0x7F; static unsigned int vcr_hack; @@ -125,7 +127,7 @@ module_param(combfilter, int, 0444); module_param(lumafilter, int, 0444); module_param(automute, int, 0444); module_param(chroma_agc, int, 0444); -module_param(adc_crush, int, 0444); +module_param(agc_crush, int, 0444); module_param(whitecrush_upper, int, 0444); module_param(whitecrush_lower, int, 0444); module_param(vcr_hack, int, 0444); @@ -138,27 +140,27 @@ module_param_array(video_nr, int, NULL, 0444); module_param_array(radio_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); -MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); -MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); -MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)"); -MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)"); -MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); -MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); +MODULE_PARM_DESC(radio, "The TV card supports radio, default is 0 (no)"); +MODULE_PARM_DESC(bigendian, "byte order of the framebuffer, default is native endian"); +MODULE_PARM_DESC(bttv_verbose, "verbose startup messages, default is 1 (yes)"); +MODULE_PARM_DESC(bttv_gpio, "log gpio changes, default is 0 (no)"); +MODULE_PARM_DESC(bttv_debug, "debug messages, default is 0 (no)"); +MODULE_PARM_DESC(irq_debug, "irq handler debug messages, default is 0 (no)"); MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); -MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); -MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); -MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " +MODULE_PARM_DESC(gbuffers, "number of capture buffers. range 2-32, default 8"); +MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 0x208000"); +MODULE_PARM_DESC(reset_crop, "reset cropping parameters at open(), default " "is 1 (yes) for compatibility with older applications"); -MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); -MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); -MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); -MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207"); -MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127"); -MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); -MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); -MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50"); -MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)"); -MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)"); +MODULE_PARM_DESC(automute, "mute audio on bad/missing video signal, default is 1 (yes)"); +MODULE_PARM_DESC(chroma_agc, "enables the AGC of chroma signal, default is 0 (no)"); +MODULE_PARM_DESC(agc_crush, "enables the luminance AGC crush, default is 1 (yes)"); +MODULE_PARM_DESC(whitecrush_upper, "sets the white crush upper value, default is 207"); +MODULE_PARM_DESC(whitecrush_lower, "sets the white crush lower value, default is 127"); +MODULE_PARM_DESC(vcr_hack, "enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); +MODULE_PARM_DESC(irq_iswitch, "switch inputs in irq handler"); +MODULE_PARM_DESC(uv_ratio, "ratio between u and v gains, default is 50"); +MODULE_PARM_DESC(full_luma_range, "use the full luma range, default is 0 (no)"); +MODULE_PARM_DESC(coring, "set the luma coring level, default is 0 (no)"); MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); @@ -168,6 +170,17 @@ MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); MODULE_LICENSE("GPL"); MODULE_VERSION(BTTV_VERSION); +#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_USER_BTTV_BASE + 0) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_USER_BTTV_BASE + 1) +#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_USER_BTTV_BASE + 2) +#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_USER_BTTV_BASE + 3) +#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_USER_BTTV_BASE + 4) +#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_USER_BTTV_BASE + 5) +#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_USER_BTTV_BASE + 6) +#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_USER_BTTV_BASE + 7) +#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_USER_BTTV_BASE + 8) +#define V4L2_CID_PRIVATE_CORING (V4L2_CID_USER_BTTV_BASE + 9) + /* ----------------------------------------------------------------------- */ /* sysfs */ @@ -632,198 +645,6 @@ static const struct bttv_format formats[] = { static const unsigned int FORMATS = ARRAY_SIZE(formats); /* ----------------------------------------------------------------------- */ - -#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) -#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) -#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3) -#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4) -#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5) -#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6) -#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7) -#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8) -#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9) -#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10) -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11) - -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; -static const struct v4l2_queryctrl bttv_ctls[] = { - /* --- video --- */ - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 65535, - .step = 256, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 65535, - .step = 128, - .default_value = 27648, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 65535, - .step = 128, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_COLOR_KILLER, - .name = "Color killer", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = 0, - .maximum = 65535, - .step = 256, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 65535, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_BALANCE, - .name = "Balance", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_BASS, - .name = "Bass", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_TREBLE, - .name = "Treble", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- private --- */ - { - .id = V4L2_CID_PRIVATE_CHROMA_AGC, - .name = "chroma agc", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_COMBFILTER, - .name = "combfilter", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_AUTOMUTE, - .name = "automute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_LUMAFILTER, - .name = "luma decimation filter", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_AGC_CRUSH, - .name = "agc crush", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_VCR_HACK, - .name = "vcr hack", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, - .name = "whitecrush upper", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0xCF, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, - .name = "whitecrush lower", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0x7F, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_UV_RATIO, - .name = "uv ratio", - .minimum = 0, - .maximum = 100, - .step = 1, - .default_value = 50, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE, - .name = "full luma range", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_CORING, - .name = "coring", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } - - - -}; - -static const struct v4l2_queryctrl *ctrl_by_id(int id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++) - if (bttv_ctls[i].id == id) - return bttv_ctls+i; - - return NULL; -} - -/* ----------------------------------------------------------------------- */ /* resource management */ /* @@ -1178,23 +999,20 @@ static char *audio_modes[] = { "audio: intern", "audio: mute" }; -static int -audio_mux(struct bttv *btv, int input, int mute) +static void +audio_mux_gpio(struct bttv *btv, int input, int mute) { - int gpio_val, signal; - struct v4l2_control ctrl; + int gpio_val, signal, mute_gpio; gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; - btv->mute = mute; - btv->audio = input; - /* automute */ - mute = mute || (btv->opt_automute && !signal && !btv->radio_user); + mute_gpio = mute || (btv->opt_automute && (!signal || !btv->users) + && !btv->has_radio_tuner); - if (mute) + if (mute_gpio) gpio_val = bttv_tvcards[btv->c.type].gpiomute; else gpio_val = bttv_tvcards[btv->c.type].gpiomux[input]; @@ -1210,13 +1028,39 @@ audio_mux(struct bttv *btv, int input, int mute) } if (bttv_gpio) - bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]); - if (in_interrupt()) - return 0; + bttv_gpio_tracking(btv, audio_modes[mute_gpio ? 4 : input]); +} + +static int +audio_mute(struct bttv *btv, int mute) +{ + struct v4l2_ctrl *ctrl; + + audio_mux_gpio(btv, btv->audio_input, mute); + + if (btv->sd_msp34xx) { + ctrl = v4l2_ctrl_find(btv->sd_msp34xx->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, mute); + } + if (btv->sd_tvaudio) { + ctrl = v4l2_ctrl_find(btv->sd_tvaudio->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, mute); + } + if (btv->sd_tda7432) { + ctrl = v4l2_ctrl_find(btv->sd_tda7432->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, mute); + } + return 0; +} + +static int +audio_input(struct bttv *btv, int input) +{ + audio_mux_gpio(btv, input, btv->mute); - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = btv->mute; - bttv_call_all(btv, core, s_ctrl, &ctrl); if (btv->sd_msp34xx) { u32 in; @@ -1265,23 +1109,11 @@ audio_mux(struct bttv *btv, int input, int mute) } if (btv->sd_tvaudio) { v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, - input, 0, 0); + input, 0, 0); } return 0; } -static inline int -audio_mute(struct bttv *btv, int mute) -{ - return audio_mux(btv, btv->audio, mute); -} - -static inline int -audio_input(struct bttv *btv, int input) -{ - return audio_mux(btv, input, btv->mute); -} - static void bttv_crop_calc_limits(struct bttv_crop *c) { @@ -1375,8 +1207,9 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm) } else { video_mux(btv,input); } - audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ? - TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN); + btv->audio_input = (btv->tuner_type != TUNER_ABSENT && input == 0) ? + TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN; + audio_input(btv, btv->audio_input); set_tvnorm(btv, norm); } @@ -1404,8 +1237,6 @@ static void init_irqreg(struct bttv *btv) static void init_bt848(struct bttv *btv) { - int val; - if (bttv_tvcards[btv->c.type].no_video) { /* very basic init only */ init_irqreg(btv); @@ -1425,30 +1256,10 @@ static void init_bt848(struct bttv *btv) BT848_GPIO_DMA_CTL_GPINTI, BT848_GPIO_DMA_CTL); - val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; - btwrite(val, BT848_E_SCLOOP); - btwrite(val, BT848_O_SCLOOP); - btwrite(0x20, BT848_E_VSCALE_HI); btwrite(0x20, BT848_O_VSCALE_HI); - btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), - BT848_ADC); - - btwrite(whitecrush_upper, BT848_WC_UP); - btwrite(whitecrush_lower, BT848_WC_DOWN); - - if (btv->opt_lumafilter) { - btwrite(0, BT848_E_CONTROL); - btwrite(0, BT848_O_CONTROL); - } else { - btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); - btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); - } - bt848_bright(btv, btv->bright); - bt848_hue(btv, btv->hue); - bt848_contrast(btv, btv->contrast); - bt848_sat(btv, btv->saturation); + v4l2_ctrl_handler_setup(&btv->ctrl_handler); /* interrupt */ init_irqreg(btv); @@ -1470,103 +1281,26 @@ static void bttv_reinit_bt848(struct bttv *btv) set_input(btv, btv->input, btv->tvnorm); } -static int bttv_g_ctrl(struct file *file, void *priv, - struct v4l2_control *c) +static int bttv_s_ctrl(struct v4l2_ctrl *c) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = btv->bright; - break; - case V4L2_CID_HUE: - c->value = btv->hue; - break; - case V4L2_CID_CONTRAST: - c->value = btv->contrast; - break; - case V4L2_CID_SATURATION: - c->value = btv->saturation; - break; - case V4L2_CID_COLOR_KILLER: - c->value = btv->opt_color_killer; - break; - - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - bttv_call_all(btv, core, g_ctrl, c); - break; - - case V4L2_CID_PRIVATE_CHROMA_AGC: - c->value = btv->opt_chroma_agc; - break; - case V4L2_CID_PRIVATE_COMBFILTER: - c->value = btv->opt_combfilter; - break; - case V4L2_CID_PRIVATE_LUMAFILTER: - c->value = btv->opt_lumafilter; - break; - case V4L2_CID_PRIVATE_AUTOMUTE: - c->value = btv->opt_automute; - break; - case V4L2_CID_PRIVATE_AGC_CRUSH: - c->value = btv->opt_adc_crush; - break; - case V4L2_CID_PRIVATE_VCR_HACK: - c->value = btv->opt_vcr_hack; - break; - case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: - c->value = btv->opt_whitecrush_upper; - break; - case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: - c->value = btv->opt_whitecrush_lower; - break; - case V4L2_CID_PRIVATE_UV_RATIO: - c->value = btv->opt_uv_ratio; - break; - case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: - c->value = btv->opt_full_luma_range; - break; - case V4L2_CID_PRIVATE_CORING: - c->value = btv->opt_coring; - break; - default: - return -EINVAL; - } - return 0; -} - -static int bttv_s_ctrl(struct file *file, void *f, - struct v4l2_control *c) -{ - int err; - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; - - err = v4l2_prio_check(&btv->prio, fh->prio); - if (0 != err) - return err; + struct bttv *btv = container_of(c->handler, struct bttv, ctrl_handler); + int val; switch (c->id) { case V4L2_CID_BRIGHTNESS: - bt848_bright(btv, c->value); + bt848_bright(btv, c->val); break; case V4L2_CID_HUE: - bt848_hue(btv, c->value); + bt848_hue(btv, c->val); break; case V4L2_CID_CONTRAST: - bt848_contrast(btv, c->value); + bt848_contrast(btv, c->val); break; case V4L2_CID_SATURATION: - bt848_sat(btv, c->value); + bt848_sat(btv, c->val); break; case V4L2_CID_COLOR_KILLER: - btv->opt_color_killer = c->value; - if (btv->opt_color_killer) { + if (c->val) { btor(BT848_SCLOOP_CKILL, BT848_E_SCLOOP); btor(BT848_SCLOOP_CKILL, BT848_O_SCLOOP); } else { @@ -1575,36 +1309,23 @@ static int bttv_s_ctrl(struct file *file, void *f, } break; case V4L2_CID_AUDIO_MUTE: - audio_mute(btv, c->value); - /* fall through */ - case V4L2_CID_AUDIO_VOLUME: - if (btv->volume_gpio) - btv->volume_gpio(btv, c->value); - - bttv_call_all(btv, core, s_ctrl, c); + audio_mute(btv, c->val); + btv->mute = c->val; break; - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - bttv_call_all(btv, core, s_ctrl, c); + case V4L2_CID_AUDIO_VOLUME: + btv->volume_gpio(btv, c->val); break; - case V4L2_CID_PRIVATE_CHROMA_AGC: - btv->opt_chroma_agc = c->value; - if (btv->opt_chroma_agc) { - btor(BT848_SCLOOP_CAGC, BT848_E_SCLOOP); - btor(BT848_SCLOOP_CAGC, BT848_O_SCLOOP); - } else { - btand(~BT848_SCLOOP_CAGC, BT848_E_SCLOOP); - btand(~BT848_SCLOOP_CAGC, BT848_O_SCLOOP); - } + case V4L2_CID_CHROMA_AGC: + val = c->val ? BT848_SCLOOP_CAGC : 0; + btwrite(val, BT848_E_SCLOOP); + btwrite(val, BT848_O_SCLOOP); break; case V4L2_CID_PRIVATE_COMBFILTER: - btv->opt_combfilter = c->value; + btv->opt_combfilter = c->val; break; case V4L2_CID_PRIVATE_LUMAFILTER: - btv->opt_lumafilter = c->value; - if (btv->opt_lumafilter) { + if (c->val) { btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL); btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL); } else { @@ -1613,36 +1334,31 @@ static int bttv_s_ctrl(struct file *file, void *f, } break; case V4L2_CID_PRIVATE_AUTOMUTE: - btv->opt_automute = c->value; + btv->opt_automute = c->val; break; case V4L2_CID_PRIVATE_AGC_CRUSH: - btv->opt_adc_crush = c->value; btwrite(BT848_ADC_RESERVED | - (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), + (c->val ? BT848_ADC_CRUSH : 0), BT848_ADC); break; case V4L2_CID_PRIVATE_VCR_HACK: - btv->opt_vcr_hack = c->value; + btv->opt_vcr_hack = c->val; break; case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: - btv->opt_whitecrush_upper = c->value; - btwrite(c->value, BT848_WC_UP); + btwrite(c->val, BT848_WC_UP); break; case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: - btv->opt_whitecrush_lower = c->value; - btwrite(c->value, BT848_WC_DOWN); + btwrite(c->val, BT848_WC_DOWN); break; case V4L2_CID_PRIVATE_UV_RATIO: - btv->opt_uv_ratio = c->value; + btv->opt_uv_ratio = c->val; bt848_sat(btv, btv->saturation); break; case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: - btv->opt_full_luma_range = c->value; - btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM); + btaor((c->val << 7), ~BT848_OFORM_RANGE, BT848_OFORM); break; case V4L2_CID_PRIVATE_CORING: - btv->opt_coring = c->value; - btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM); + btaor((c->val << 5), ~BT848_OFORM_CORE32, BT848_OFORM); break; default: return -EINVAL; @@ -1652,6 +1368,121 @@ static int bttv_s_ctrl(struct file *file, void *f, /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops bttv_ctrl_ops = { + .s_ctrl = bttv_s_ctrl, +}; + +static struct v4l2_ctrl_config bttv_ctrl_combfilter = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_COMBFILTER, + .name = "Comb Filter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_automute = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "Auto Mute", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_lumafilter = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_LUMAFILTER, + .name = "Luma Decimation Filter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_agc_crush = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_AGC_CRUSH, + .name = "AGC Crush", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_vcr_hack = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_VCR_HACK, + .name = "VCR Hack", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_whitecrush_lower = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, + .name = "Whitecrush Lower", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 255, + .step = 1, + .def = 0x7f, +}; + +static struct v4l2_ctrl_config bttv_ctrl_whitecrush_upper = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, + .name = "Whitecrush Upper", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 255, + .step = 1, + .def = 0xcf, +}; + +static struct v4l2_ctrl_config bttv_ctrl_uv_ratio = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_UV_RATIO, + .name = "UV Ratio", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = 50, +}; + +static struct v4l2_ctrl_config bttv_ctrl_full_luma = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE, + .name = "Full Luma Range", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_coring = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_CORING, + .name = "Coring", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 3, + .step = 1, +}; + + +/* ----------------------------------------------------------------------- */ + void bttv_gpio_tracking(struct bttv *btv, char *comment) { unsigned int outbits, data; @@ -1881,25 +1712,33 @@ static struct videobuf_queue_ops bttv_video_qops = { .buf_release = buffer_release, }; -static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id) +static void radio_enable(struct bttv *btv) +{ + /* Switch to the radio tuner */ + if (!btv->has_radio_tuner) { + btv->has_radio_tuner = 1; + bttv_call_all(btv, tuner, s_radio); + btv->audio_input = TVAUDIO_INPUT_RADIO; + audio_input(btv, btv->audio_input); + } +} + +static int bttv_s_std(struct file *file, void *priv, v4l2_std_id id) { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; unsigned int i; - int err; - - err = v4l2_prio_check(&btv->prio, fh->prio); - if (err) - goto err; + int err = 0; for (i = 0; i < BTTV_TVNORMS; i++) - if (*id & bttv_tvnorms[i].v4l2_id) + if (id & bttv_tvnorms[i].v4l2_id) break; if (i == BTTV_TVNORMS) { err = -EINVAL; goto err; } + btv->std = id; set_tvnorm(btv, i); err: @@ -1907,6 +1746,15 @@ err: return err; } +static int bttv_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct bttv_fh *fh = priv; + struct bttv *btv = fh->btv; + + *id = btv->std; + return 0; +} + static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id) { struct bttv_fh *fh = f; @@ -1932,7 +1780,7 @@ static int bttv_enum_input(struct file *file, void *priv, } i->type = V4L2_INPUT_TYPE_CAMERA; - i->audioset = 1; + i->audioset = 0; if (btv->tuner_type != TUNER_ABSENT && i->index == 0) { sprintf(i->name, "Television"); @@ -1974,49 +1822,29 @@ static int bttv_s_input(struct file *file, void *priv, unsigned int i) struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - int err; - - err = v4l2_prio_check(&btv->prio, fh->prio); - if (unlikely(err)) - goto err; - - if (i > bttv_tvcards[btv->c.type].video_inputs) { - err = -EINVAL; - goto err; - } + if (i >= bttv_tvcards[btv->c.type].video_inputs) + return -EINVAL; set_input(btv, i, btv->tvnorm); - -err: return 0; } static int bttv_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - int err; - if (unlikely(0 != t->index)) + if (t->index) return -EINVAL; - if (unlikely(btv->tuner_type == TUNER_ABSENT)) { - err = -EINVAL; - goto err; - } - - err = v4l2_prio_check(&btv->prio, fh->prio); - if (unlikely(err)) - goto err; - bttv_call_all(btv, tuner, s_tuner, t); - if (btv->audio_mode_gpio) - btv->audio_mode_gpio(btv, t, 1); - -err: + if (btv->audio_mode_gpio) { + struct v4l2_tuner copy = *t; + btv->audio_mode_gpio(btv, ©, 1); + } return 0; } @@ -2026,49 +1854,81 @@ static int bttv_g_frequency(struct file *file, void *priv, struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = btv->freq; + if (f->tuner) + return -EINVAL; + + if (f->type == V4L2_TUNER_RADIO) + radio_enable(btv); + f->frequency = f->type == V4L2_TUNER_RADIO ? + btv->radio_freq : btv->tv_freq; return 0; } +static void bttv_set_frequency(struct bttv *btv, const struct v4l2_frequency *f) +{ + struct v4l2_frequency new_freq = *f; + + bttv_call_all(btv, tuner, s_frequency, f); + /* s_frequency may clamp the frequency, so get the actual + frequency before assigning radio/tv_freq. */ + bttv_call_all(btv, tuner, g_frequency, &new_freq); + if (new_freq.type == V4L2_TUNER_RADIO) { + radio_enable(btv); + btv->radio_freq = new_freq.frequency; + if (btv->has_matchbox) + tea5757_set_freq(btv, btv->radio_freq); + } else { + btv->tv_freq = new_freq.frequency; + } +} + static int bttv_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - int err; - if (unlikely(f->tuner != 0)) + if (f->tuner) return -EINVAL; - err = v4l2_prio_check(&btv->prio, fh->prio); - if (unlikely(err)) - goto err; - - if (unlikely(f->type != (btv->radio_user - ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) { - err = -EINVAL; - goto err; - } - btv->freq = f->frequency; - bttv_call_all(btv, tuner, s_frequency, f); - if (btv->has_matchbox && btv->radio_user) - tea5757_set_freq(btv, btv->freq); -err: - + bttv_set_frequency(btv, f); return 0; } static int bttv_log_status(struct file *file, void *f) { + struct video_device *vdev = video_devdata(file); struct bttv_fh *fh = f; struct bttv *btv = fh->btv; + v4l2_ctrl_handler_log_status(vdev->ctrl_handler, btv->c.v4l2_dev.name); bttv_call_all(btv, core, log_status); return 0; } +static int bttv_g_chip_ident(struct file *file, void *f, struct v4l2_dbg_chip_ident *chip) +{ + struct bttv_fh *fh = f; + struct bttv *btv = fh->btv; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = btv->id; + if (chip->ident == PCI_DEVICE_ID_FUSION879) + chip->ident = V4L2_IDENT_BT879; + } + return 0; + } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + /* TODO: is this correct? */ + return bttv_call_all_err(btv, core, g_chip_ident, chip); +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int bttv_g_register(struct file *file, void *f, struct v4l2_dbg_register *reg) @@ -2079,8 +1939,12 @@ static int bttv_g_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + if (!v4l2_chip_match_host(®->match)) { + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + bttv_call_all(btv, core, g_register, reg); + return 0; + } /* bt848 has a 12-bit register space */ reg->reg &= 0xfff; @@ -2091,7 +1955,7 @@ static int bttv_g_register(struct file *file, void *f, } static int bttv_s_register(struct file *file, void *f, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -2099,12 +1963,15 @@ static int bttv_s_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + if (!v4l2_chip_match_host(®->match)) { + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + bttv_call_all(btv, core, s_register, reg); + return 0; + } /* bt848 has a 12-bit register space */ - reg->reg &= 0xfff; - btwrite(reg->val, reg->reg); + btwrite(reg->val, reg->reg & 0xfff); return 0; } @@ -2273,22 +2140,33 @@ limit_scaled_size_lock (struct bttv_fh * fh, may also adjust the current cropping parameters to get closer to the desired window size. */ static int -verify_window_lock (struct bttv_fh * fh, - struct v4l2_window * win, - int adjust_size, - int adjust_crop) +verify_window_lock(struct bttv_fh *fh, struct v4l2_window *win, + int adjust_size, int adjust_crop) { enum v4l2_field field; unsigned int width_mask; int rc; - if (win->w.width < 48 || win->w.height < 32) - return -EINVAL; + if (win->w.width < 48) + win->w.width = 48; + if (win->w.height < 32) + win->w.height = 32; if (win->clipcount > 2048) - return -EINVAL; + win->clipcount = 2048; + win->chromakey = 0; + win->global_alpha = 0; field = win->field; + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED: + break; + default: + field = V4L2_FIELD_ANY; + break; + } if (V4L2_FIELD_ANY == field) { __s32 height2; @@ -2297,18 +2175,11 @@ verify_window_lock (struct bttv_fh * fh, ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; } - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } + win->field = field; - /* 4-byte alignment. */ if (NULL == fh->ovfmt) return -EINVAL; + /* 4-byte alignment. */ width_mask = ~0; switch (fh->ovfmt->depth) { case 8: @@ -2333,8 +2204,6 @@ verify_window_lock (struct bttv_fh * fh, adjust_size, adjust_crop); if (0 != rc) return rc; - - win->field = field; return 0; } @@ -2491,6 +2360,7 @@ static int bttv_g_fmt_vid_cap(struct file *file, void *priv, fh->width, fh->height); f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -2514,6 +2384,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, struct bttv *btv = fh->btv; enum v4l2_field field; __s32 width, height; + __s32 height2; int rc; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -2522,30 +2393,25 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, field = f->fmt.pix.field; - if (V4L2_FIELD_ANY == field) { - __s32 height2; - - height2 = btv->crop[!!fh->do_crop].rect.height >> 1; - field = (f->fmt.pix.height > height2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_BOTTOM; - } - - if (V4L2_FIELD_SEQ_BT == field) - field = V4L2_FIELD_SEQ_TB; - switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_ALTERNATE: case V4L2_FIELD_INTERLACED: break; + case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_SEQ_TB: - if (fmt->flags & FORMAT_FLAGS_PLANAR) - return -EINVAL; + if (!(fmt->flags & FORMAT_FLAGS_PLANAR)) { + field = V4L2_FIELD_SEQ_TB; + break; + } + /* fall through */ + default: /* FIELD_ANY case */ + height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + field = (f->fmt.pix.height > height2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; break; - default: - return -EINVAL; } width = f->fmt.pix.width; @@ -2562,6 +2428,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, /* update data for the application */ f->fmt.pix.field = field; pix_format_set_size(&f->fmt.pix, fmt, width, height); + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -2571,9 +2438,10 @@ static int bttv_try_fmt_vid_overlay(struct file *file, void *priv, { struct bttv_fh *fh = priv; - return verify_window_lock(fh, &f->fmt.win, + verify_window_lock(fh, &f->fmt.win, /* adjust_size */ 1, /* adjust_crop */ 0); + return 0; } static int bttv_s_fmt_vid_cap(struct file *file, void *priv, @@ -2640,6 +2508,7 @@ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv, static int bttv_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; @@ -2652,11 +2521,15 @@ static int bttv_querycap(struct file *file, void *priv, "PCI:%s", pci_name(btv->c.pci)); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; + V4L2_CAP_STREAMING | + V4L2_CAP_DEVICE_CAPS; if (no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; + if (btv->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (btv->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; /* * No need to lock here: those vars are initialized during board @@ -2666,6 +2539,25 @@ static int bttv_querycap(struct file *file, void *priv, cap->capabilities |= V4L2_CAP_RDS_CAPTURE; if (btv->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps = cap->capabilities & + (V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_TUNER); + else if (vdev->vfl_type == VFL_TYPE_VBI) + cap->device_caps = cap->capabilities & + (V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER); + else { + cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + if (btv->has_saa6588) + cap->device_caps |= V4L2_CAP_READWRITE | + V4L2_CAP_RDS_CAPTURE; + } return 0; } @@ -2728,6 +2620,7 @@ static int bttv_g_fbuf(struct file *file, void *f, *fb = btv->fbuf; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; if (fh->ovfmt) fb->fmt.pixelformat = fh->ovfmt->fourcc; return 0; @@ -2901,36 +2794,15 @@ static int bttv_streamoff(struct file *file, void *priv, return 0; } -static int bttv_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - const struct v4l2_queryctrl *ctrl; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - - if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME)) - *c = no_ctl; - else { - ctrl = ctrl_by_id(c->id); - - *c = (NULL != ctrl) ? *ctrl : no_ctl; - } - - return 0; -} - static int bttv_g_parm(struct file *file, void *f, struct v4l2_streamparm *parm) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + parm->parm.capture.readbuffers = gbuffers; v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id, &parm->parm.capture.timeperframe); @@ -2943,15 +2815,13 @@ static int bttv_g_tuner(struct file *file, void *priv, struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - if (btv->tuner_type == TUNER_ABSENT) - return -EINVAL; if (0 != t->index) return -EINVAL; t->rxsubchans = V4L2_TUNER_SUB_MONO; + t->capability = V4L2_TUNER_CAP_NORM; bttv_call_all(btv, tuner, g_tuner, t); strcpy(t->name, "Television"); - t->capability = V4L2_TUNER_CAP_NORM; t->type = V4L2_TUNER_ANALOG_TV; if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) t->signal = 0xffff; @@ -2962,28 +2832,6 @@ static int bttv_g_tuner(struct file *file, void *priv, return 0; } -static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p) -{ - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; - - *p = v4l2_prio_max(&btv->prio); - - return 0; -} - -static int bttv_s_priority(struct file *file, void *f, - enum v4l2_priority prio) -{ - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; - int rc; - - rc = v4l2_prio_change(&btv->prio, &fh->prio, prio); - - return rc; -} - static int bttv_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) { @@ -3036,11 +2884,6 @@ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) /* Make sure tvnorm, vbi_end and the current cropping parameters remain consistent until we're done. Note read() may change vbi_end in check_alloc_btres_lock(). */ - retval = v4l2_prio_check(&btv->prio, fh->prio); - if (0 != retval) { - return retval; - } - retval = -EBUSY; if (locked_btres(fh->btv, VIDEO_RESOURCES)) { @@ -3098,23 +2941,6 @@ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) return 0; } -static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "audio"); - return 0; -} - -static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - return 0; -} - static ssize_t bttv_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -3154,34 +2980,43 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) struct bttv_fh *fh = file->private_data; struct bttv_buffer *buf; enum v4l2_field field; - unsigned int rc = POLLERR; + unsigned int rc = 0; + unsigned long req_events = poll_requested_events(wait); + + if (v4l2_event_pending(&fh->fh)) + rc = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); + + if (!(req_events & (POLLIN | POLLRDNORM))) + return rc; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(file, &fh->vbi, wait); + return rc | POLLERR; + return rc | videobuf_poll_stream(file, &fh->vbi, wait); } if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { /* streaming capture */ if (list_empty(&fh->cap.stream)) - goto err; + return rc | POLLERR; buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); } else { /* read() capture */ if (NULL == fh->cap.read_buf) { /* need to capture a new frame */ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) - goto err; + return rc | POLLERR; fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize); if (NULL == fh->cap.read_buf) - goto err; + return rc | POLLERR; fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; field = videobuf_next_field(&fh->cap); if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) { kfree (fh->cap.read_buf); fh->cap.read_buf = NULL; - goto err; + return rc | POLLERR; } fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); fh->cap.read_off = 0; @@ -3192,10 +3027,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; - else - rc = 0; -err: + rc = rc | POLLIN|POLLRDNORM; return rc; } @@ -3224,15 +3056,15 @@ static int bttv_open(struct file *file) fh = kmalloc(sizeof(*fh), GFP_KERNEL); if (unlikely(!fh)) return -ENOMEM; + btv->users++; file->private_data = fh; *fh = btv->init; + v4l2_fh_init(&fh->fh, vdev); fh->type = type; fh->ov.setup_ok = 0; - v4l2_prio_open(&btv->prio, &fh->prio); - videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, &btv->c.pci->dev, &btv->s_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, @@ -3247,8 +3079,7 @@ static int bttv_open(struct file *file) fh, &btv->lock); set_tvnorm(btv,btv->tvnorm); set_input(btv, btv->input, btv->tvnorm); - - btv->users++; + audio_mute(btv, btv->mute); /* The V4L2 spec requires one global set of cropping parameters which only change on request. These are stored in btv->crop[1]. @@ -3267,6 +3098,7 @@ static int bttv_open(struct file *file) bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm); bttv_field_count(btv); + v4l2_fh_add(&fh->fh); return 0; } @@ -3302,16 +3134,17 @@ static int bttv_release(struct file *file) videobuf_mmap_free(&fh->cap); videobuf_mmap_free(&fh->vbi); - v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; - kfree(fh); btv->users--; bttv_field_count(btv); if (!btv->users) - audio_mute(btv, 1); + audio_mute(btv, btv->mute); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + kfree(fh); return 0; } @@ -3350,20 +3183,16 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap, - .vidioc_g_audio = bttv_g_audio, - .vidioc_s_audio = bttv_s_audio, .vidioc_cropcap = bttv_cropcap, .vidioc_reqbufs = bttv_reqbufs, .vidioc_querybuf = bttv_querybuf, .vidioc_qbuf = bttv_qbuf, .vidioc_dqbuf = bttv_dqbuf, .vidioc_s_std = bttv_s_std, + .vidioc_g_std = bttv_g_std, .vidioc_enum_input = bttv_enum_input, .vidioc_g_input = bttv_g_input, .vidioc_s_input = bttv_s_input, - .vidioc_queryctrl = bttv_queryctrl, - .vidioc_g_ctrl = bttv_g_ctrl, - .vidioc_s_ctrl = bttv_s_ctrl, .vidioc_streamon = bttv_streamon, .vidioc_streamoff = bttv_streamoff, .vidioc_g_tuner = bttv_g_tuner, @@ -3373,13 +3202,14 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_g_fbuf = bttv_g_fbuf, .vidioc_s_fbuf = bttv_s_fbuf, .vidioc_overlay = bttv_overlay, - .vidioc_g_priority = bttv_g_priority, - .vidioc_s_priority = bttv_s_priority, .vidioc_g_parm = bttv_g_parm, .vidioc_g_frequency = bttv_g_frequency, .vidioc_s_frequency = bttv_s_frequency, .vidioc_log_status = bttv_log_status, .vidioc_querystd = bttv_querystd, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = bttv_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = bttv_g_register, .vidioc_s_register = bttv_s_register, @@ -3390,7 +3220,6 @@ static struct video_device bttv_video_template = { .fops = &bttv_fops, .ioctl_ops = &bttv_ioctl_ops, .tvnorms = BTTV_NORMS, - .current_norm = V4L2_STD_PAL, }; /* ----------------------------------------------------------------------- */ @@ -3412,13 +3241,12 @@ static int radio_open(struct file *file) return -ENOMEM; file->private_data = fh; *fh = btv->init; - - v4l2_prio_open(&btv->prio, &fh->prio); + v4l2_fh_init(&fh->fh, vdev); btv->radio_user++; + audio_mute(btv, btv->mute); - bttv_call_all(btv, tuner, s_radio); - audio_input(btv,TVAUDIO_INPUT_RADIO); + v4l2_fh_add(&fh->fh); return 0; } @@ -3429,28 +3257,17 @@ static int radio_release(struct file *file) struct bttv *btv = fh->btv; struct saa6588_command cmd; - v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); btv->radio_user--; bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd); - return 0; -} - -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - - strcpy(cap->driver, "bttv"); - strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card)); - sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci)); - cap->capabilities = V4L2_CAP_TUNER; - + if (btv->radio_user == 0) + btv->has_radio_tuner = 0; return 0; } @@ -3459,12 +3276,11 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - if (btv->tuner_type == TUNER_ABSENT) - return -EINVAL; if (0 != t->index) return -EINVAL; strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; + radio_enable(btv); bttv_call_all(btv, tuner, g_tuner, t); @@ -3474,31 +3290,8 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - - return 0; -} - static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; @@ -3506,56 +3299,11 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; + radio_enable(btv); bttv_call_all(btv, tuner, s_tuner, t); return 0; } -static int radio_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - return 0; -} - -static int radio_s_input(struct file *filp, void *priv, unsigned int i) -{ - if (unlikely(i)) - return -EINVAL; - - return 0; -} - -static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctl; - - return 0; -} - -static int radio_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -3566,6 +3314,7 @@ static ssize_t radio_read(struct file *file, char __user *data, cmd.buffer = data; cmd.instance = file; cmd.result = -ENODEV; + radio_enable(btv); bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd); @@ -3576,10 +3325,18 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) { struct bttv_fh *fh = file->private_data; struct bttv *btv = fh->btv; + unsigned long req_events = poll_requested_events(wait); struct saa6588_command cmd; + unsigned int res = 0; + + if (v4l2_event_pending(&fh->fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); + radio_enable(btv); cmd.instance = file; cmd.event_list = wait; - cmd.result = -ENODEV; + cmd.result = res; bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd); return cmd.result; @@ -3596,20 +3353,14 @@ static const struct v4l2_file_operations radio_fops = }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = bttv_querycap, + .vidioc_log_status = bttv_log_status, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_s_std = radio_s_std, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_input = radio_g_input, - .vidioc_g_ctrl = bttv_g_ctrl, - .vidioc_s_ctrl = bttv_s_ctrl, .vidioc_g_frequency = bttv_g_frequency, .vidioc_s_frequency = bttv_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device radio_template = { @@ -4112,7 +3863,8 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) bttv_irq_switch_video(btv); if ((astat & BT848_INT_HLOCK) && btv->opt_automute) - audio_mute(btv, btv->mute); /* trigger automute */ + /* trigger automute */ + audio_mux_gpio(btv, btv->audio_input, btv->mute); if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { pr_info("%d: %s%s @ %08x,", @@ -4161,7 +3913,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) /* ----------------------------------------------------------------------- */ -/* initialitation */ +/* initialization */ static struct video_device *vdev_init(struct bttv *btv, const struct video_device *template, @@ -4176,10 +3928,17 @@ static struct video_device *vdev_init(struct bttv *btv, vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; vfd->debug = bttv_debug; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); video_set_drvdata(vfd, btv); snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", type_name, bttv_tvcards[btv->c.type].name); + if (btv->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); + v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); + } return vfd; } @@ -4247,6 +4006,7 @@ static int bttv_register_video(struct bttv *btv) btv->radio_dev = vdev_init(btv, &radio_template, "radio"); if (NULL == btv->radio_dev) goto err; + btv->radio_dev->ctrl_handler = &btv->radio_ctrl_handler; if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, radio_nr[btv->c.nr]) < 0) goto err; @@ -4277,9 +4037,15 @@ static void pci_set_command(struct pci_dev *dev) static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { + struct v4l2_frequency init_freq = { + .tuner = 0, + .type = V4L2_TUNER_ANALOG_TV, + .frequency = 980, + }; int result; unsigned char lat; struct bttv *btv; + struct v4l2_ctrl_handler *hdl; if (bttv_num == BTTV_MAX) return -ENOMEM; @@ -4301,7 +4067,6 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) INIT_LIST_HEAD(&btv->c.subs); INIT_LIST_HEAD(&btv->capture); INIT_LIST_HEAD(&btv->vcapture); - v4l2_prio_init(&btv->prio); init_timer(&btv->timeout); btv->timeout.function = bttv_irq_timeout; @@ -4339,6 +4104,10 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr); goto fail0; } + hdl = &btv->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 20); + btv->c.v4l2_dev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(&btv->radio_ctrl_handler, 6); btv->revision = dev->revision; pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); @@ -4375,16 +4144,19 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) /* init options from insmod args */ btv->opt_combfilter = combfilter; - btv->opt_lumafilter = lumafilter; + bttv_ctrl_combfilter.def = combfilter; + bttv_ctrl_lumafilter.def = lumafilter; btv->opt_automute = automute; - btv->opt_chroma_agc = chroma_agc; - btv->opt_adc_crush = adc_crush; + bttv_ctrl_automute.def = automute; + bttv_ctrl_agc_crush.def = agc_crush; btv->opt_vcr_hack = vcr_hack; - btv->opt_whitecrush_upper = whitecrush_upper; - btv->opt_whitecrush_lower = whitecrush_lower; + bttv_ctrl_vcr_hack.def = vcr_hack; + bttv_ctrl_whitecrush_upper.def = whitecrush_upper; + bttv_ctrl_whitecrush_lower.def = whitecrush_lower; btv->opt_uv_ratio = uv_ratio; - btv->opt_full_luma_range = full_luma_range; - btv->opt_coring = coring; + bttv_ctrl_uv_ratio.def = uv_ratio; + bttv_ctrl_full_luma.def = full_luma_range; + bttv_ctrl_coring.def = coring; /* fill struct bttv with some useful defaults */ btv->init.btv = btv; @@ -4393,8 +4165,39 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); btv->init.width = 320; btv->init.height = 240; + btv->init.ov.w.width = 320; + btv->init.ov.w.height = 240; + btv->init.ov.field = V4L2_FIELD_INTERLACED; btv->input = 0; + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xff00, 0x100, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xff80, 0x80, 0x6c00); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xff80, 0x80, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_HUE, 0, 0xff00, 0x100, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, !!chroma_agc); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + if (btv->volume_gpio) + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0xff00, 0x100, 0xff00); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_combfilter, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_automute, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_lumafilter, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_agc_crush, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_vcr_hack, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_lower, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_upper, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_uv_ratio, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_full_luma, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_coring, NULL); + /* initialize hardware */ if (bttv_gpio) bttv_gpio_tracking(btv,"pre-init"); @@ -4417,21 +4220,34 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) /* some card-specific stuff (needs working i2c) */ bttv_init_card2(btv); bttv_init_tuner(btv); + if (btv->tuner_type != TUNER_ABSENT) { + bttv_set_frequency(btv, &init_freq); + btv->radio_freq = 90500 * 16; /* 90.5Mhz default */ + } + btv->std = V4L2_STD_PAL; init_irqreg(btv); + v4l2_ctrl_handler_setup(hdl); + if (hdl->error) { + result = hdl->error; + goto fail2; + } + /* mute device */ + audio_mute(btv, 1); /* register video4linux + input */ if (!bttv_tvcards[btv->c.type].no_video) { - bttv_register_video(btv); - bt848_bright(btv,32768); - bt848_contrast(btv, 27648); - bt848_hue(btv,32768); - bt848_sat(btv,32768); - audio_mute(btv, 1); + v4l2_ctrl_add_handler(&btv->radio_ctrl_handler, hdl, + v4l2_ctrl_radio_filter); + if (btv->radio_ctrl_handler.error) { + result = btv->radio_ctrl_handler.error; + goto fail2; + } set_input(btv, 0, btv->tvnorm); bttv_crop_reset(&btv->crop[0], btv->tvnorm); btv->crop[1] = btv->crop[0]; /* current = default */ disclaim_vbi_lines(btv); disclaim_video_lines(btv); + bttv_register_video(btv); } /* add subdevices and autoload dvb-bt8xx if needed */ @@ -4453,6 +4269,8 @@ fail2: free_irq(btv->c.pci->irq,btv); fail1: + v4l2_ctrl_handler_free(&btv->ctrl_handler); + v4l2_ctrl_handler_free(&btv->radio_ctrl_handler); v4l2_device_unregister(&btv->c.v4l2_dev); fail0: @@ -4494,9 +4312,11 @@ static void bttv_remove(struct pci_dev *pci_dev) bttv_unregister_video(btv); /* free allocated memory */ + v4l2_ctrl_handler_free(&btv->ctrl_handler); + v4l2_ctrl_handler_free(&btv->radio_ctrl_handler); btcx_riscmem_free(btv->c.pci,&btv->main); - /* free ressources */ + /* free resources */ free_irq(btv->c.pci->irq,btv); iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index c63c643..b7c52dc 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -394,3 +394,11 @@ int init_bttv_i2c(struct bttv *btv) return btv->i2c_rc; } + +int fini_bttv_i2c(struct bttv *btv) +{ + if (0 != btv->i2c_rc) + return 0; + + return i2c_del_adapter(&btv->c.i2c_adap); +} diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index 04207a7..f368213 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -375,6 +375,7 @@ void init_bttv_i2c_ir(struct bttv *btv) I2C_CLIENT_END }; struct i2c_board_info info; + struct i2c_client *i2c_dev; if (0 != btv->i2c_rc) return; @@ -390,7 +391,12 @@ void init_bttv_i2c_ir(struct bttv *btv) btv->init_data.ir_codes = RC_MAP_PV951; info.addr = 0x4b; break; - default: + } + + if (btv->init_data.name) { + info.platform_data = &btv->init_data; + i2c_dev = i2c_new_device(&btv->c.i2c_adap, &info); + } else { /* * The external IR receiver is at i2c address 0x34 (0x35 for * reads). Future Hauppauge cards will have an internal @@ -399,24 +405,14 @@ void init_bttv_i2c_ir(struct bttv *btv) * internal. * That's why we probe 0x1a (~0x34) first. CB */ - - i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL); - return; + i2c_dev = i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL); } + if (NULL == i2c_dev) + return; - if (btv->init_data.name) - info.platform_data = &btv->init_data; - i2c_new_device(&btv->c.i2c_adap, &info); - - return; -} - -int fini_bttv_i2c(struct bttv *btv) -{ - if (0 != btv->i2c_rc) - return 0; - - return i2c_del_adapter(&btv->c.i2c_adap); +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("ir-kbd-i2c"); +#endif } int bttv_input_init(struct bttv *btv) diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index 79a1124..6139ce2 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -359,6 +359,9 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits); #define bttv_call_all(btv, o, f, args...) \ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args) +#define bttv_call_all_err(btv, o, f, args...) \ + v4l2_device_call_until_err(&btv->c.v4l2_dev, 0, o, f, ##args) + extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both); diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 9ec0adb..9c1cc2c 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -33,9 +33,11 @@ #include <linux/input.h> #include <linux/mutex.h> #include <linux/scatterlist.h> +#include <linux/device.h> #include <asm/io.h> #include <media/v4l2-common.h> -#include <linux/device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> #include <media/videobuf-dma-sg.h> #include <media/tveeprom.h> #include <media/rc-core.h> @@ -214,11 +216,11 @@ struct bttv_crop { }; struct bttv_fh { + /* This must be the first field in this struct */ + struct v4l2_fh fh; + struct bttv *btv; int resources; -#ifdef VIDIOC_G_PRIORITY - enum v4l2_priority prio; -#endif enum v4l2_buf_type type; /* video capture */ @@ -298,6 +300,10 @@ extern int no_overlay; /* bttv-input.c */ extern void init_bttv_i2c_ir(struct bttv *btv); + +/* ---------------------------------------------------------- */ +/* bttv-i2c.c */ +extern int init_bttv_i2c(struct bttv *btv); extern int fini_bttv_i2c(struct bttv *btv); /* ---------------------------------------------------------- */ @@ -308,7 +314,6 @@ extern unsigned int bttv_verbose; extern unsigned int bttv_debug; extern unsigned int bttv_gpio; extern void bttv_gpio_tracking(struct bttv *btv, char *comment); -extern int init_bttv_i2c(struct bttv *btv); #define dprintk(fmt, ...) \ do { \ @@ -393,12 +398,17 @@ struct bttv { wait_queue_head_t i2c_queue; struct v4l2_subdev *sd_msp34xx; struct v4l2_subdev *sd_tvaudio; + struct v4l2_subdev *sd_tda7432; /* video4linux (1) */ struct video_device *video_dev; struct video_device *radio_dev; struct video_device *vbi_dev; + /* controls */ + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; + /* infrared remote */ int has_remote; struct bttv_ir *remote; @@ -410,38 +420,30 @@ struct bttv { spinlock_t s_lock; struct mutex lock; int resources; -#ifdef VIDIOC_G_PRIORITY - struct v4l2_prio_state prio; -#endif /* video state */ unsigned int input; - unsigned int audio; + unsigned int audio_input; unsigned int mute; - unsigned long freq; + unsigned long tv_freq; unsigned int tvnorm; + v4l2_std_id std; int hue, contrast, bright, saturation; struct v4l2_framebuffer fbuf; unsigned int field_count; /* various options */ int opt_combfilter; - int opt_lumafilter; int opt_automute; - int opt_chroma_agc; - int opt_color_killer; - int opt_adc_crush; int opt_vcr_hack; - int opt_whitecrush_upper; - int opt_whitecrush_lower; int opt_uv_ratio; - int opt_full_luma_range; - int opt_coring; /* radio data/state */ int has_radio; + int has_radio_tuner; int radio_user; int radio_uses_msp_demodulator; + unsigned long radio_freq; /* miro/pinnacle + Aimslab VHX philips matchbox (tea5757 radio tuner) support */ diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c index f164b7f..38b1d64 100644 --- a/drivers/media/pci/cx18/cx18-av-core.c +++ b/drivers/media/pci/cx18/cx18-av-core.c @@ -576,7 +576,7 @@ static void input_change(struct cx18 *cx) } static int cx18_av_s_frequency(struct v4l2_subdev *sd, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct cx18 *cx = v4l2_get_subdevdata(sd); input_change(cx); @@ -809,7 +809,7 @@ static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int cx18_av_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct cx18_av_state *state = to_cx18_av_state(sd); struct cx18 *cx = v4l2_get_subdevdata(sd); @@ -1266,7 +1266,7 @@ static int cx18_av_g_register(struct v4l2_subdev *sd, } static int cx18_av_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct cx18 *cx = v4l2_get_subdevdata(sd); diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 613e5ae..67b61cf 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -1243,7 +1243,7 @@ int cx18_init_on_first_open(struct cx18 *cx) in one place. */ cx->std++; /* Force full standard initialization */ std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std; - cx18_s_std(NULL, &fh, &std); + cx18_s_std(NULL, &fh, std); cx18_s_frequency(NULL, &fh, &vf); return 0; } diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index cd8d2c2..aee7b6d 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -415,42 +415,34 @@ static int cx18_g_chip_ident(struct file *file, void *fh, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) -{ - struct v4l2_dbg_register *regs = arg; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) - return -EINVAL; - - regs->size = 4; - if (cmd == VIDIOC_DBG_S_REGISTER) - cx18_write_enc(cx, regs->val, regs->reg); - else - regs->val = cx18_read_enc(cx, regs->reg); - return 0; -} - static int cx18_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct cx18 *cx = fh2id(fh)->cx; - if (v4l2_chip_match_host(®->match)) - return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); + if (v4l2_chip_match_host(®->match)) { + if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) + return -EINVAL; + reg->size = 4; + reg->val = cx18_read_enc(cx, reg->reg); + return 0; + } /* FIXME - errors shouldn't be ignored */ cx18_call_all(cx, core, g_register, reg); return 0; } static int cx18_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct cx18 *cx = fh2id(fh)->cx; - if (v4l2_chip_match_host(®->match)) - return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); + if (v4l2_chip_match_host(®->match)) { + if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) + return -EINVAL; + cx18_write_enc(cx, reg->val, reg->reg); + return 0; + } /* FIXME - errors shouldn't be ignored */ cx18_call_all(cx, core, s_register, reg); return 0; @@ -614,7 +606,7 @@ static int cx18_g_frequency(struct file *file, void *fh, return 0; } -int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; @@ -637,15 +629,15 @@ static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) return 0; } -int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) +int cx18_s_std(struct file *file, void *fh, v4l2_std_id std) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - if ((*std & V4L2_STD_ALL) == 0) + if ((std & V4L2_STD_ALL) == 0) return -EINVAL; - if (*std == cx->std) + if (std == cx->std) return 0; if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || @@ -656,8 +648,8 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) return -EBUSY; } - cx->std = *std; - cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; + cx->std = std; + cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; cx->is_50hz = !cx->is_60hz; cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz); cx->cxhdl.width = 720; @@ -673,7 +665,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) return 0; } -static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +static int cx18_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; @@ -1118,7 +1110,7 @@ static int cx18_log_status(struct file *file, void *fh) } static long cx18_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) + unsigned int cmd, void *arg) { struct cx18 *cx = fh2id(fh)->cx; diff --git a/drivers/media/pci/cx18/cx18-ioctl.h b/drivers/media/pci/cx18/cx18-ioctl.h index 2f9dd59..4343396 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.h +++ b/drivers/media/pci/cx18/cx18-ioctl.h @@ -26,6 +26,6 @@ u16 cx18_service2vbi(int type); void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt); void cx18_set_funcs(struct video_device *vdev); -int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std); -int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int cx18_s_std(struct file *file, void *fh, v4l2_std_id std); +int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); int cx18_s_input(struct file *file, void *fh, unsigned int inp); diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h index 70e4fd6..4998c96 100644 --- a/drivers/media/pci/cx23885/altera-ci.h +++ b/drivers/media/pci/cx23885/altera-ci.h @@ -24,6 +24,8 @@ #ifndef __ALTERA_CI_H #define __ALTERA_CI_H +#include <linux/kconfig.h> + #define ALT_DATA 0x000000ff #define ALT_TDI 0x00008000 #define ALT_TDO 0x00004000 @@ -41,8 +43,7 @@ struct altera_ci_config { int (*fpga_rw) (void *dev, int ad_rg, int val, int rw); }; -#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \ - && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_ALTERA_CI) extern int altera_ci_init(struct altera_ci_config *config, int ci_nr); extern void altera_ci_release(void *dev, int ci_nr); diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 5d5052d..6dea11a 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1222,14 +1222,14 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } -static int vidioc_s_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; unsigned int i; for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) - if (*id & cx23885_tvnorms[i].id) + if (id & cx23885_tvnorms[i].id) break; if (i == ARRAY_SIZE(cx23885_tvnorms)) return -EINVAL; @@ -1237,7 +1237,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) /* Have the drier core notify the subdevices */ mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, *id); + cx23885_set_tvnorm(dev, id); mutex_unlock(&dev->lock); return 0; @@ -1280,7 +1280,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; @@ -1311,7 +1311,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { return cx23885_set_frequency(file, priv, f); } diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c index ea9a614..acdb6d5 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.c +++ b/drivers/media/pci/cx23885/cx23885-ioctl.c @@ -158,18 +158,17 @@ int cx23885_g_register(struct file *file, void *fh, } static int cx23885_s_host_register(struct cx23885_dev *dev, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) return -EINVAL; - reg->size = 4; cx_write(reg->reg, reg->val); return 0; } static int cx23417_s_register(struct cx23885_dev *dev, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { if (dev->v4l_device == NULL) return -EINVAL; @@ -179,13 +178,11 @@ static int cx23417_s_register(struct cx23885_dev *dev, if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val)) return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ - - reg->size = 4; return 0; } int cx23885_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h index 315be0c..a608096 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.h +++ b/drivers/media/pci/cx23885/cx23885-ioctl.h @@ -33,7 +33,7 @@ int cx23885_g_register(struct file *file, void *fh, int cx23885_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg); + const struct v4l2_dbg_register *reg); #endif #endif diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 5991bc8..ed08c89 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1259,13 +1259,13 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; dprintk(1, "%s()\n", __func__); mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, *tvnorms); + cx23885_set_tvnorm(dev, tvnorms); mutex_unlock(&dev->lock); return 0; @@ -1486,7 +1486,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; @@ -1518,7 +1518,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } -static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) +static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency *f) { struct v4l2_control ctrl; @@ -1550,7 +1550,7 @@ static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) } static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct v4l2_control ctrl; struct videobuf_dvb_frontend *vfe; @@ -1608,7 +1608,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, } int cx23885_set_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct cx23885_fh *fh = priv; struct cx23885_dev *dev = fh->dev; @@ -1628,7 +1628,7 @@ int cx23885_set_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { return cx23885_set_frequency(file, priv, f); } diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 59c322d..5687d3f 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -587,7 +587,7 @@ extern void cx23885_video_wakeup(struct cx23885_dev *dev, 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, struct v4l2_frequency *f); +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); diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index d51eed0..fa672fe 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -1124,7 +1124,7 @@ static int cx23888_ir_g_register(struct v4l2_subdev *sd, } static int cx23888_ir_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct cx23888_ir_state *state = to_state(sd); u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig index 4017c94..6439a84 100644 --- a/drivers/media/pci/cx25821/Kconfig +++ b/drivers/media/pci/cx25821/Kconfig @@ -1,14 +1,9 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" - depends on DVB_CORE && VIDEO_DEV && PCI && I2C + depends on VIDEO_DEV && PCI && I2C select I2C_ALGOBIT select VIDEO_BTCX - select VIDEO_TVEEPROM - depends on RC_CORE - select VIDEOBUF_DVB select VIDEOBUF_DMA_SG - select VIDEO_CX25840 - select VIDEO_CX2341X ---help--- This is a video4linux driver for Conexant 25821 based TV cards. diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index caa32b7b..fb76c3d 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -1,14 +1,9 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ cx25821-gpio.o cx25821-medusa-video.o \ - cx25821-video.o cx25821-video-upstream.o \ - cx25821-video-upstream-ch2.o \ - cx25821-audio-upstream.o + cx25821-video.o cx25821-video-upstream.o obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.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/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 1858a45..6e91e84 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -59,7 +59,6 @@ do { \ Data type declarations - Can be moded to a header file later ****************************************************************************/ -static struct snd_card *snd_cx25821_cards[SNDRV_CARDS]; static int devno; struct cx25821_audio_buffer { @@ -151,7 +150,7 @@ static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) { struct cx25821_audio_buffer *buf = chip->buf; struct cx25821_dev *dev = chip->dev; - struct sram_channel *audio_ch = + const struct sram_channel *audio_ch = &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; u32 tmp = 0; @@ -627,34 +626,6 @@ static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = { MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); /* - * Not used in the function snd_cx25821_dev_free so removing - * from the file. - */ -/* -static int snd_cx25821_free(struct cx25821_audio_dev *chip) -{ - if (chip->irq >= 0) - free_irq(chip->irq, chip); - - cx25821_dev_unregister(chip->dev); - pci_disable_device(chip->pci); - - return 0; -} -*/ - -/* - * Component Destructor - */ -static void snd_cx25821_dev_free(struct snd_card *card) -{ - struct cx25821_audio_dev *chip = card->private_data; - - /* snd_cx25821_free(chip); */ - snd_card_free(chip->card); -} - -/* * Alsa Constructor - Component probe */ static int cx25821_audio_initdev(struct cx25821_dev *dev) @@ -685,7 +656,6 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) strcpy(card->driver, "cx25821"); /* Card "creation" */ - card->private_free = snd_cx25821_dev_free; chip = card->private_data; spin_lock_init(&chip->reg_lock); @@ -729,8 +699,7 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) goto error; } - snd_cx25821_cards[devno] = card; - + dev->card = card; devno++; return 0; @@ -742,9 +711,33 @@ error: /**************************************************************************** LINUX MODULE INIT ****************************************************************************/ + +static int cx25821_alsa_exit_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct cx25821_dev *cxdev = get_cx25821(v4l2_dev); + + snd_card_free(cxdev->card); + return 0; +} + static void cx25821_audio_fini(void) { - snd_card_free(snd_cx25821_cards[0]); + struct device_driver *drv = driver_find("cx25821", &pci_bus_type); + int ret; + + ret = driver_for_each_device(drv, NULL, NULL, cx25821_alsa_exit_callback); + if (ret) + pr_err("%s failed to find a cx25821 driver.\n", __func__); +} + +static int cx25821_alsa_init_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct cx25821_dev *cxdev = get_cx25821(v4l2_dev); + + cx25821_audio_initdev(cxdev); + return 0; } /* @@ -756,29 +749,11 @@ static void cx25821_audio_fini(void) */ static int cx25821_alsa_init(void) { - struct cx25821_dev *dev = NULL; - struct list_head *list; + struct device_driver *drv = driver_find("cx25821", &pci_bus_type); - mutex_lock(&cx25821_devlist_mutex); - list_for_each(list, &cx25821_devlist) { - dev = list_entry(list, struct cx25821_dev, devlist); - cx25821_audio_initdev(dev); - } - mutex_unlock(&cx25821_devlist_mutex); - - if (dev == NULL) - pr_info("ERROR ALSA: no cx25821 cards found\n"); - - return 0; + return driver_for_each_device(drv, NULL, NULL, cx25821_alsa_init_callback); } late_initcall(cx25821_alsa_init); module_exit(cx25821_audio_fini); - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c index 87491ca..b9be535 100644 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.c +++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c @@ -45,7 +45,7 @@ static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc) { unsigned int i, lines; @@ -106,7 +106,7 @@ static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, int fifo_enable) { unsigned int line; - struct sram_channel *sram_ch = + const struct sram_channel *sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; int offset = 0; @@ -215,7 +215,7 @@ static void cx25821_free_memory_audio(struct cx25821_dev *dev) void cx25821_stop_upstream_audio(struct cx25821_dev *dev) { - struct sram_channel *sram_ch = + const struct sram_channel *sram_ch = dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; u32 tmp = 0; @@ -257,7 +257,7 @@ void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) } static int cx25821_get_audio_data(struct cx25821_dev *dev, - struct sram_channel *sram_ch) + const struct sram_channel *sram_ch) { struct file *myfile; int frame_index_temp = dev->_audioframe_index; @@ -352,7 +352,7 @@ static void cx25821_audioups_handler(struct work_struct *work) } static int cx25821_openfile_audio(struct cx25821_dev *dev, - struct sram_channel *sram_ch) + const struct sram_channel *sram_ch) { struct file *myfile; int i = 0, j = 0; @@ -433,7 +433,7 @@ static int cx25821_openfile_audio(struct cx25821_dev *dev, } static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, - struct sram_channel *sram_ch, + const struct sram_channel *sram_ch, int bpl) { int ret = 0; @@ -495,7 +495,7 @@ static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, { int i = 0; u32 int_msk_tmp; - struct sram_channel *channel = dev->channels[chan_num].sram_channels; + const struct sram_channel *channel = dev->channels[chan_num].sram_channels; dma_addr_t risc_phys_jump_addr; __le32 *rp; @@ -587,7 +587,7 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) struct cx25821_dev *dev = dev_id; u32 audio_status; int handled = 0; - struct sram_channel *sram_ch; + const struct sram_channel *sram_ch; if (!dev) return -1; @@ -611,7 +611,7 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) } static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, - struct sram_channel *sram_ch) + const struct sram_channel *sram_ch) { int count = 0; u32 tmp; @@ -635,7 +635,7 @@ static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, } static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, - struct sram_channel *sram_ch) + const struct sram_channel *sram_ch) { u32 tmp = 0; int err = 0; @@ -699,7 +699,7 @@ fail_irq: int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) { - struct sram_channel *sram_ch; + const struct sram_channel *sram_ch; int err = 0; if (dev->_audio_is_running) { @@ -728,26 +728,17 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; _line_size = AUDIO_LINE_SIZE; - if (dev->input_audiofilename) { + if ((dev->input_audiofilename) && + (strcmp(dev->input_audiofilename, "") != 0)) dev->_audiofilename = kstrdup(dev->input_audiofilename, GFP_KERNEL); - - if (!dev->_audiofilename) { - err = -ENOMEM; - goto error; - } - - /* Default if filename is empty string */ - if (strcmp(dev->input_audiofilename, "") == 0) - dev->_audiofilename = "/root/audioGOOD.wav"; - } else { + else dev->_audiofilename = kstrdup(_defaultAudioName, GFP_KERNEL); - if (!dev->_audiofilename) { - err = -ENOMEM; - goto error; - } + if (!dev->_audiofilename) { + err = -ENOMEM; + goto error; } cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, diff --git a/drivers/media/pci/cx25821/cx25821-cards.c b/drivers/media/pci/cx25821/cx25821-cards.c index 99988c9..3b409fe 100644 --- a/drivers/media/pci/cx25821/cx25821-cards.c +++ b/drivers/media/pci/cx25821/cx25821-cards.c @@ -26,11 +26,8 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/delay.h> -#include <media/cx25840.h> #include "cx25821.h" -#include "tuner-xc2028.h" /* board config info */ @@ -45,28 +42,8 @@ struct cx25821_board cx25821_boards[] = { .name = "CX25821", .portb = CX25821_RAW, .portc = CX25821_264, - .input[0].type = CX25821_VMUX_COMPOSITE, }, }; const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards); - -struct cx25821_subid cx25821_subids[] = { - { - .subvendor = 0x14f1, - .subdevice = 0x0920, - .card = CX25821_BOARD, - }, -}; - -void cx25821_card_setup(struct cx25821_dev *dev) -{ - static u8 eeprom[256]; - - if (dev->i2c_bus[0].i2c_rc == 0) { - dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, - sizeof(eeprom)); - } -} diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 1884e2c..b762c5b 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -41,14 +41,7 @@ static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -static unsigned int cx25821_devcount; - -DEFINE_MUTEX(cx25821_devlist_mutex); -EXPORT_SYMBOL(cx25821_devlist_mutex); -LIST_HEAD(cx25821_devlist); -EXPORT_SYMBOL(cx25821_devlist); - -struct sram_channel cx25821_sram_channels[] = { +const struct sram_channel cx25821_sram_channels[] = { [SRAM_CH00] = { .i = SRAM_CH00, .name = "VID A", @@ -317,20 +310,6 @@ struct sram_channel cx25821_sram_channels[] = { }; EXPORT_SYMBOL(cx25821_sram_channels); -struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00]; -struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01]; -struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02]; -struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03]; -struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04]; -struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05]; -struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06]; -struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07]; -struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09]; -struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10]; -struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11]; - -struct cx25821_dmaqueue mpegq; - static int cx25821_risc_decode(u32 risc) { static const char * const instr[16] = { @@ -457,7 +436,7 @@ static void cx25821_registers_init(struct cx25821_dev *dev) } int cx25821_sram_channel_setup(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc) { unsigned int i, lines; @@ -523,10 +502,9 @@ int cx25821_sram_channel_setup(struct cx25821_dev *dev, return 0; } -EXPORT_SYMBOL(cx25821_sram_channel_setup); int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc) { unsigned int i, lines; @@ -592,7 +570,7 @@ int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, } EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); -void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) +void cx25821_sram_channel_dump(struct cx25821_dev *dev, const struct sram_channel *ch) { static char *name[] = { "init risc lo", @@ -652,10 +630,9 @@ void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) pr_warn(" : cnt2_reg: 0x%08x\n", cx_read(ch->cnt2_reg)); } -EXPORT_SYMBOL(cx25821_sram_channel_dump); void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, - struct sram_channel *ch) + const struct sram_channel *ch) { static const char * const name[] = { "init risc lo", @@ -798,12 +775,12 @@ void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select, if (channel_select <= 7 && channel_select >= 0) { cx_write(dev->channels[channel_select].sram_channels->pix_frmt, format); - dev->channels[channel_select].pixel_formats = format; } + dev->channels[channel_select].pixel_formats = format; } static void cx25821_set_vip_mode(struct cx25821_dev *dev, - struct sram_channel *ch) + const struct sram_channel *ch) { cx_write(ch->pix_frmt, PIXEL_FRMT_422); cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1); @@ -837,12 +814,13 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels, 1440, 0); dev->channels[i].pixel_formats = PIXEL_FRMT_422; - dev->channels[i].use_cif_resolution = FALSE; + dev->channels[i].use_cif_resolution = 0; } /* Probably only affect Downstream */ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { + dev->channels[i].pixel_formats = PIXEL_FRMT_422; cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); } @@ -868,8 +846,7 @@ static void cx25821_dev_checkrevision(struct cx25821_dev *dev) { dev->hwrevision = cx_read(RDR_CFG2) & 0xff; - pr_info("%s(): Hardware revision = 0x%02x\n", - __func__, dev->hwrevision); + pr_info("Hardware revision = 0x%02x\n", dev->hwrevision); } static void cx25821_iounmap(struct cx25821_dev *dev) @@ -879,7 +856,6 @@ static void cx25821_iounmap(struct cx25821_dev *dev) /* Releasing IO memory */ if (dev->lmmio != NULL) { - CX25821_INFO("Releasing lmmio.\n"); iounmap(dev->lmmio); dev->lmmio = NULL; } @@ -887,23 +863,14 @@ static void cx25821_iounmap(struct cx25821_dev *dev) static int cx25821_dev_setup(struct cx25821_dev *dev) { + static unsigned int cx25821_devcount; int i; - pr_info("\n***********************************\n"); - pr_info("cx25821 set up\n"); - pr_info("***********************************\n\n"); - mutex_init(&dev->lock); - atomic_inc(&dev->refcount); - dev->nr = ++cx25821_devcount; sprintf(dev->name, "cx25821[%d]", dev->nr); - mutex_lock(&cx25821_devlist_mutex); - list_add_tail(&dev->devlist, &cx25821_devlist); - mutex_unlock(&cx25821_devlist_mutex); - if (dev->pci->device != 0x8210) { pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", __func__, dev->pci->device); @@ -914,8 +881,11 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 28000000; - for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) + for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { + dev->channels[i].dev = dev; + dev->channels[i].id = i; dev->channels[i].sram_channels = &cx25821_sram_channels[i]; + } if (dev->nr > 1) CX25821_INFO("dev->nr > 1!"); @@ -978,63 +948,15 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* cx25821_i2c_register(&dev->i2c_bus[1]); * cx25821_i2c_register(&dev->i2c_bus[2]); */ - CX25821_INFO("i2c register! bus->i2c_rc = %d\n", - dev->i2c_bus[0].i2c_rc); - - cx25821_card_setup(dev); - if (medusa_video_init(dev) < 0) CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__); cx25821_video_register(dev); - /* register IOCTL device */ - dev->ioctl_dev = cx25821_vdev_init(dev, dev->pci, - &cx25821_videoioctl_template, "video"); - - if (video_register_device - (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) { - cx25821_videoioctl_unregister(dev); - pr_err("%s(): Failed to register video adapter for IOCTL, so unregistering videoioctl device\n", - __func__); - } - cx25821_dev_checkrevision(dev); - CX25821_INFO("setup done!\n"); - return 0; } -void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, - struct upstream_user_struct *up_data) -{ - dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0; - - dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; - medusa_set_videostandard(dev); - - cx25821_vidupstream_init_ch1(dev, dev->channel_select, - dev->pixel_format); -} - -void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, - struct upstream_user_struct *up_data) -{ - dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0; - - dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; - medusa_set_videostandard(dev); - - cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2, - dev->pixel_format_ch2); -} - -void cx25821_start_upstream_audio(struct cx25821_dev *dev, - struct upstream_user_struct *up_data) -{ - cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B); -} - void cx25821_dev_unregister(struct cx25821_dev *dev) { int i; @@ -1042,25 +964,16 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) if (!dev->base_io_addr) return; - cx25821_free_mem_upstream_ch1(dev); - cx25821_free_mem_upstream_ch2(dev); - cx25821_free_mem_upstream_audio(dev); - release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); - if (!atomic_dec_and_test(&dev->refcount)) - return; - - for (i = 0; i < VID_CHANNEL_NUM; i++) - cx25821_video_unregister(dev, i); - - for (i = VID_UPSTREAM_SRAM_CHANNEL_I; - i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { + for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; i++) { + if (i == SRAM_CH08) /* audio channel */ + continue; + if (i == SRAM_CH09 || i == SRAM_CH10) + cx25821_free_mem_upstream(&dev->channels[i]); cx25821_video_unregister(dev, i); } - cx25821_videoioctl_unregister(dev); - cx25821_i2c_unregister(&dev->i2c_bus[0]); cx25821_iounmap(dev); } @@ -1385,8 +1298,6 @@ static int cx25821_initdev(struct pci_dev *pci_dev, goto fail_unregister_device; } - pr_info("Athena pci enable !\n"); - err = cx25821_dev_setup(dev); if (err) { if (err == -EBUSY) @@ -1445,10 +1356,6 @@ static void cx25821_finidev(struct pci_dev *pci_dev) if (pci_dev->irq) free_irq(pci_dev->irq, dev); - mutex_lock(&cx25821_devlist_mutex); - list_del(&dev->devlist); - mutex_unlock(&cx25821_devlist_mutex); - cx25821_dev_unregister(dev); v4l2_device_unregister(v4l2_dev); kfree(dev); diff --git a/drivers/media/pci/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c index 29e43b0..95e8ddf 100644 --- a/drivers/media/pci/cx25821/cx25821-gpio.c +++ b/drivers/media/pci/cx25821/cx25821-gpio.c @@ -20,6 +20,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/module.h> #include "cx25821.h" /********************* GPIO stuffs *********************/ diff --git a/drivers/media/pci/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c index a8dc945..dca37c7 100644 --- a/drivers/media/pci/cx25821/cx25821-i2c.c +++ b/drivers/media/pci/cx25821/cx25821-i2c.c @@ -23,8 +23,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "cx25821.h" +#include <linux/module.h> #include <linux/i2c.h> +#include "cx25821.h" static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); diff --git a/drivers/media/pci/cx25821/cx25821-medusa-video.c b/drivers/media/pci/cx25821/cx25821-medusa-video.c index 6a92e5c..22fa044 100644 --- a/drivers/media/pci/cx25821/cx25821-medusa-video.c +++ b/drivers/media/pci/cx25821/cx25821-medusa-video.c @@ -94,8 +94,6 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) u32 value = 0; u32 tmp = 0; - mutex_lock(&dev->lock); - for (i = 0; i < MAX_DECODERS; i++) { /* set video format NTSC-M */ value = cx25821_i2c_read(&dev->i2c_bus[0], @@ -222,8 +220,6 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) value |= 0x00080200; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); - mutex_unlock(&dev->lock); - return ret_val; } @@ -265,8 +261,6 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) u32 value = 0; u32 tmp = 0; - mutex_lock(&dev->lock); - for (i = 0; i < MAX_DECODERS; i++) { /* set video format PAL-BDGHI */ value = cx25821_i2c_read(&dev->i2c_bus[0], @@ -397,14 +391,12 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) value &= 0xFFF7FDFF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); - mutex_unlock(&dev->lock); - return ret_val; } int medusa_set_videostandard(struct cx25821_dev *dev) { - int status = STATUS_SUCCESS; + int status = 0; u32 value = 0, tmp = 0; if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) @@ -434,8 +426,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, u32 vscale = 0x0; const int MAX_WIDTH = 720; - mutex_lock(&dev->lock); - /* validate the width */ if (width > MAX_WIDTH) { pr_info("%s(): width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n", @@ -485,8 +475,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL + (0x200 * decoder), vscale); } - - mutex_unlock(&dev->lock); } static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, @@ -496,11 +484,8 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, u32 tmp = 0; u32 disp_cnt_reg = DISP_AB_CNT; - mutex_lock(&dev->lock); - /* no support */ if (decoder < VDEC_A || decoder > VDEC_H) { - mutex_unlock(&dev->lock); return; } @@ -535,8 +520,6 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, } cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); - - mutex_unlock(&dev->lock); } /* Map to Medusa register setting */ @@ -587,10 +570,8 @@ int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) int value = 0; u32 val = 0, tmp = 0; - mutex_lock(&dev->lock); if ((brightness > VIDEO_PROCAMP_MAX) || (brightness < VIDEO_PROCAMP_MIN)) { - mutex_unlock(&dev->lock); return -1; } ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness, @@ -601,7 +582,6 @@ int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) val &= 0xFFFFFF00; ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_BRITE_CTRL + (0x200 * decoder), val | value); - mutex_unlock(&dev->lock); return ret_val; } @@ -611,10 +591,7 @@ int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) int value = 0; u32 val = 0, tmp = 0; - mutex_lock(&dev->lock); - if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) { - mutex_unlock(&dev->lock); return -1; } @@ -626,7 +603,6 @@ int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_CNTRST_CTRL + (0x200 * decoder), val | value); - mutex_unlock(&dev->lock); return ret_val; } @@ -636,10 +612,7 @@ int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) int value = 0; u32 val = 0, tmp = 0; - mutex_lock(&dev->lock); - if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) { - mutex_unlock(&dev->lock); return -1; } @@ -654,7 +627,6 @@ int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_HUE_CTRL + (0x200 * decoder), val | value); - mutex_unlock(&dev->lock); return ret_val; } @@ -664,11 +636,8 @@ int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) int value = 0; u32 val = 0, tmp = 0; - mutex_lock(&dev->lock); - if ((saturation > VIDEO_PROCAMP_MAX) || (saturation < VIDEO_PROCAMP_MIN)) { - mutex_unlock(&dev->lock); return -1; } @@ -687,7 +656,6 @@ int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], VDEC_A_VSAT_CTRL + (0x200 * decoder), val | value); - mutex_unlock(&dev->lock); return ret_val; } @@ -699,8 +667,6 @@ int medusa_video_init(struct cx25821_dev *dev) int ret_val = 0; int i = 0; - mutex_lock(&dev->lock); - _num_decoders = dev->_max_num_decoders; /* disable Auto source selection on all video decoders */ @@ -719,13 +685,9 @@ int medusa_video_init(struct cx25821_dev *dev) if (ret_val < 0) goto error; - mutex_unlock(&dev->lock); - for (i = 0; i < _num_decoders; i++) medusa_set_decoderduration(dev, i, _display_field_cnt[i]); - mutex_lock(&dev->lock); - /* Select monitor as DENC A input, power up the DAC */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp); value &= 0xFF70FF70; @@ -774,14 +736,8 @@ int medusa_video_init(struct cx25821_dev *dev) if (ret_val < 0) goto error; - - mutex_unlock(&dev->lock); - ret_val = medusa_set_videostandard(dev); - return ret_val; - error: - mutex_unlock(&dev->lock); return ret_val; } diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c deleted file mode 100644 index cf2723c..0000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "cx25821-video.h" -#include "cx25821-video-upstream-ch2.h" - -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/syscalls.h> -#include <linux/file.h> -#include <linux/fcntl.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); -MODULE_LICENSE("GPL"); - -static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | - FLD_VID_SRC_OPC_ERR; - -static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev, - __le32 *rp, unsigned int offset, - unsigned int bpl, u32 sync_line, - unsigned int lines, - int fifo_enable, int field_type) -{ - unsigned int line, i; - int dist_betwn_starts = bpl * 2; - - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) || - (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) { - offset += dist_betwn_starts; - } - } - - return rp; -} - -static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, - __le32 *rp, - dma_addr_t databuf_phys_addr, - unsigned int offset, - u32 sync_line, unsigned int bpl, - unsigned int lines, - int fifo_enable, int field_type) -{ - unsigned int line, i; - struct sram_channel *sram_ch = - dev->channels[dev->_channel2_upstream_select].sram_channels; - int dist_betwn_starts = bpl * 2; - - /* sync instruction */ - if (sync_line != NO_SYNC_LINE) - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(databuf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) || - (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) { - offset += dist_betwn_starts; - } - - /* - check if we need to enable the FIFO after the first 4 lines - For the upstream video channel, the risc engine will enable - the FIFO. - */ - if (fifo_enable && line == 3) { - *(rp++) = RISC_WRITECR; - *(rp++) = sram_ch->dma_ctl; - *(rp++) = FLD_VID_FIFO_EN; - *(rp++) = 0x00000001; - } - } - - return rp; -} - -static int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, - struct pci_dev *pci, - unsigned int top_offset, - unsigned int bpl, - unsigned int lines) -{ - __le32 *rp; - int fifo_enable = 0; - int singlefield_lines = lines >> 1; /*get line count for single field */ - int odd_num_lines = singlefield_lines; - int frame = 0; - int frame_size = 0; - int databuf_offset = 0; - int risc_program_size = 0; - int risc_flag = RISC_CNT_RESET; - unsigned int bottom_offset = bpl; - dma_addr_t risc_phys_jump_addr; - - if (dev->_isNTSC_ch2) { - odd_num_lines = singlefield_lines + 1; - risc_program_size = FRAME1_VID_PROG_SIZE; - if (bpl == Y411_LINE_SZ) - frame_size = FRAME_SIZE_NTSC_Y411; - else - frame_size = FRAME_SIZE_NTSC_Y422; - } else { - risc_program_size = PAL_VID_PROG_SIZE; - if (bpl == Y411_LINE_SZ) - frame_size = FRAME_SIZE_PAL_Y411; - else - frame_size = FRAME_SIZE_PAL_Y422; - } - - /* Virtual address of Risc buffer program */ - rp = dev->_dma_virt_addr_ch2; - - for (frame = 0; frame < NUM_FRAMES; frame++) { - databuf_offset = frame_size * frame; - - if (UNSET != top_offset) { - fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; - rp = cx25821_risc_field_upstream_ch2(dev, rp, - dev->_data_buf_phys_addr_ch2 + databuf_offset, - top_offset, 0, bpl, odd_num_lines, fifo_enable, - ODD_FIELD); - } - - fifo_enable = FIFO_DISABLE; - - /* Even field */ - rp = cx25821_risc_field_upstream_ch2(dev, rp, - dev->_data_buf_phys_addr_ch2 + databuf_offset, - bottom_offset, 0x200, bpl, singlefield_lines, - fifo_enable, EVEN_FIELD); - - if (frame == 0) { - risc_flag = RISC_CNT_RESET; - risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 + - risc_program_size; - } else { - risc_flag = RISC_CNT_INC; - risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2; - } - - /* - * Loop to 2ndFrameRISC or to Start of - * Risc program & generate IRQ - */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - - return 0; -} - -void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) -{ - struct sram_channel *sram_ch = - dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels; - u32 tmp = 0; - - if (!dev->_is_running_ch2) { - pr_info("No video file is currently running so return!\n"); - return; - } - /* Disable RISC interrupts */ - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp & ~_intr_msk); - - /* Turn OFF risc and fifo */ - tmp = cx_read(sram_ch->dma_ctl); - cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - - /* Clear data buffer memory */ - if (dev->_data_buf_virt_addr_ch2) - memset(dev->_data_buf_virt_addr_ch2, 0, - dev->_data_buf_size_ch2); - - dev->_is_running_ch2 = 0; - dev->_is_first_frame_ch2 = 0; - dev->_frame_count_ch2 = 0; - dev->_file_status_ch2 = END_OF_FILE; - - kfree(dev->_irq_queues_ch2); - dev->_irq_queues_ch2 = NULL; - - kfree(dev->_filename_ch2); - - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); -} - -void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev) -{ - if (dev->_is_running_ch2) - cx25821_stop_upstream_video_ch2(dev); - - if (dev->_dma_virt_addr_ch2) { - pci_free_consistent(dev->pci, dev->_risc_size_ch2, - dev->_dma_virt_addr_ch2, - dev->_dma_phys_addr_ch2); - dev->_dma_virt_addr_ch2 = NULL; - } - - if (dev->_data_buf_virt_addr_ch2) { - pci_free_consistent(dev->pci, dev->_data_buf_size_ch2, - dev->_data_buf_virt_addr_ch2, - dev->_data_buf_phys_addr_ch2); - dev->_data_buf_virt_addr_ch2 = NULL; - } -} - -static int cx25821_get_frame_ch2(struct cx25821_dev *dev, - struct sram_channel *sram_ch) -{ - struct file *myfile; - int frame_index_temp = dev->_frame_index_ch2; - int i = 0; - int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? - Y411_LINE_SZ : Y422_LINE_SZ; - int frame_size = 0; - int frame_offset = 0; - ssize_t vfs_read_retval = 0; - char mybuf[line_size]; - loff_t file_offset; - loff_t pos; - mm_segment_t old_fs; - - if (dev->_file_status_ch2 == END_OF_FILE) - return 0; - - if (dev->_isNTSC_ch2) { - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; - } else { - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - } - - frame_offset = (frame_index_temp > 0) ? frame_size : 0; - file_offset = dev->_frame_count_ch2 * frame_size; - - myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(myfile)) { - const int open_errno = -PTR_ERR(myfile); - pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", - __func__, dev->_filename_ch2, open_errno); - return PTR_ERR(myfile); - } else { - if (!(myfile->f_op)) { - pr_err("%s(): File has no file operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - if (!myfile->f_op->read) { - pr_err("%s(): File has no READ operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - pos = myfile->f_pos; - old_fs = get_fs(); - set_fs(KERNEL_DS); - - for (i = 0; i < dev->_lines_count_ch2; i++) { - pos = file_offset; - - vfs_read_retval = vfs_read(myfile, mybuf, line_size, - &pos); - - if (vfs_read_retval > 0 && vfs_read_retval == line_size - && dev->_data_buf_virt_addr_ch2 != NULL) { - memcpy((void *)(dev->_data_buf_virt_addr_ch2 + - frame_offset / 4), mybuf, - vfs_read_retval); - } - - file_offset += vfs_read_retval; - frame_offset += vfs_read_retval; - - if (vfs_read_retval < line_size) { - pr_info("Done: exit %s() since no more bytes to read from Video file\n", - __func__); - break; - } - } - - if (i > 0) - dev->_frame_count_ch2++; - - dev->_file_status_ch2 = (vfs_read_retval == line_size) ? - IN_PROGRESS : END_OF_FILE; - - set_fs(old_fs); - filp_close(myfile, NULL); - } - - return 0; -} - -static void cx25821_vidups_handler_ch2(struct work_struct *work) -{ - struct cx25821_dev *dev = container_of(work, struct cx25821_dev, - _irq_work_entry_ch2); - - if (!dev) { - pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", - __func__); - return; - } - - cx25821_get_frame_ch2(dev, dev->channels[dev-> - _channel2_upstream_select].sram_channels); -} - -static int cx25821_openfile_ch2(struct cx25821_dev *dev, - struct sram_channel *sram_ch) -{ - struct file *myfile; - int i = 0, j = 0; - int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? - Y411_LINE_SZ : Y422_LINE_SZ; - ssize_t vfs_read_retval = 0; - char mybuf[line_size]; - loff_t pos; - loff_t offset = (unsigned long)0; - mm_segment_t old_fs; - - myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); - - if (IS_ERR(myfile)) { - const int open_errno = -PTR_ERR(myfile); - pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", - __func__, dev->_filename_ch2, open_errno); - return PTR_ERR(myfile); - } else { - if (!(myfile->f_op)) { - pr_err("%s(): File has no file operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - if (!myfile->f_op->read) { - pr_err("%s(): File has no READ operations registered! Returning\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - pos = myfile->f_pos; - old_fs = get_fs(); - set_fs(KERNEL_DS); - - for (j = 0; j < NUM_FRAMES; j++) { - for (i = 0; i < dev->_lines_count_ch2; i++) { - pos = offset; - - vfs_read_retval = vfs_read(myfile, mybuf, - line_size, &pos); - - if (vfs_read_retval > 0 && - vfs_read_retval == line_size && - dev->_data_buf_virt_addr_ch2 != NULL) { - memcpy((void *)(dev-> - _data_buf_virt_addr_ch2 - + offset / 4), mybuf, - vfs_read_retval); - } - - offset += vfs_read_retval; - - if (vfs_read_retval < line_size) { - pr_info("Done: exit %s() since no more bytes to read from Video file\n", - __func__); - break; - } - } - - if (i > 0) - dev->_frame_count_ch2++; - - if (vfs_read_retval < line_size) - break; - } - - dev->_file_status_ch2 = (vfs_read_retval == line_size) ? - IN_PROGRESS : END_OF_FILE; - - set_fs(old_fs); - myfile->f_pos = 0; - filp_close(myfile, NULL); - } - - return 0; -} - -static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, - struct sram_channel *sram_ch, - int bpl) -{ - int ret = 0; - dma_addr_t dma_addr; - dma_addr_t data_dma_addr; - - if (dev->_dma_virt_addr_ch2 != NULL) { - pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, - dev->_dma_virt_addr_ch2, - dev->_dma_phys_addr_ch2); - } - - dev->_dma_virt_addr_ch2 = pci_alloc_consistent(dev->pci, - dev->upstream_riscbuf_size_ch2, &dma_addr); - dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2; - dev->_dma_phys_start_addr_ch2 = dma_addr; - dev->_dma_phys_addr_ch2 = dma_addr; - dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2; - - if (!dev->_dma_virt_addr_ch2) { - pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); - return -ENOMEM; - } - - /* Iniitize at this address until n bytes to 0 */ - memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2); - - if (dev->_data_buf_virt_addr_ch2 != NULL) { - pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2, - dev->_data_buf_virt_addr_ch2, - dev->_data_buf_phys_addr_ch2); - } - /* For Video Data buffer allocation */ - dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci, - dev->upstream_databuf_size_ch2, &data_dma_addr); - dev->_data_buf_phys_addr_ch2 = data_dma_addr; - dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2; - - if (!dev->_data_buf_virt_addr_ch2) { - pr_err("FAILED to allocate memory for data buffer! Returning\n"); - return -ENOMEM; - } - - /* Initialize at this address until n bytes to 0 */ - memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); - - ret = cx25821_openfile_ch2(dev, sram_ch); - if (ret < 0) - return ret; - - /* Creating RISC programs */ - ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, - dev->_lines_count_ch2); - if (ret < 0) { - pr_info("Failed creating Video Upstream Risc programs!\n"); - goto error; - } - - return 0; - -error: - return ret; -} - -static int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, - int chan_num, - u32 status) -{ - u32 int_msk_tmp; - struct sram_channel *channel = dev->channels[chan_num].sram_channels; - int singlefield_lines = NTSC_FIELD_HEIGHT; - int line_size_in_bytes = Y422_LINE_SZ; - int odd_risc_prog_size = 0; - dma_addr_t risc_phys_jump_addr; - __le32 *rp; - - if (status & FLD_VID_SRC_RISC1) { - /* We should only process one program per call */ - u32 prog_cnt = cx_read(channel->gpcnt); - - /* - * Since we've identified our IRQ, clear our bits from the - * interrupt mask and interrupt status registers - */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); - cx_write(channel->int_stat, _intr_msk); - - spin_lock(&dev->slock); - - dev->_frame_index_ch2 = prog_cnt; - - queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2); - - if (dev->_is_first_frame_ch2) { - dev->_is_first_frame_ch2 = 0; - - if (dev->_isNTSC_ch2) { - singlefield_lines += 1; - odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; - } else { - singlefield_lines = PAL_FIELD_HEIGHT; - odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; - } - - if (dev->_dma_virt_start_addr_ch2 != NULL) { - if (dev->_pixel_format_ch2 == PIXEL_FRMT_411) - line_size_in_bytes = Y411_LINE_SZ; - else - line_size_in_bytes = Y422_LINE_SZ; - risc_phys_jump_addr = - dev->_dma_phys_start_addr_ch2 + - odd_risc_prog_size; - - rp = cx25821_update_riscprogram_ch2(dev, - dev->_dma_virt_start_addr_ch2, - TOP_OFFSET, line_size_in_bytes, - 0x0, singlefield_lines, - FIFO_DISABLE, ODD_FIELD); - - /* Jump to Even Risc program of 1st Frame */ - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - } - - spin_unlock(&dev->slock); - } - - if (dev->_file_status_ch2 == END_OF_FILE) { - pr_info("EOF Channel 2 Framecount = %d\n", - dev->_frame_count_ch2); - return -1; - } - /* ElSE, set the interrupt mask register, re-enable irq. */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); - - return 0; -} - -static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) -{ - struct cx25821_dev *dev = dev_id; - u32 vid_status; - int handled = 0; - int channel_num = 0; - struct sram_channel *sram_ch; - - if (!dev) - return -1; - - channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; - sram_ch = dev->channels[channel_num].sram_channels; - - vid_status = cx_read(sram_ch->int_stat); - - /* Only deal with our interrupt */ - if (vid_status) - handled = cx25821_video_upstream_irq_ch2(dev, channel_num, - vid_status); - - if (handled < 0) - cx25821_stop_upstream_video_ch2(dev); - else - handled += handled; - - return IRQ_RETVAL(handled); -} - -static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, - struct sram_channel *ch, int pix_format) -{ - int width = WIDTH_D1; - int height = dev->_lines_count_ch2; - int num_lines, odd_num_lines; - u32 value; - int vip_mode = PIXEL_ENGINE_VIP1; - - value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); - value &= 0xFFFFFFEF; - value |= dev->_isNTSC_ch2 ? 0 : 0x10; - cx_write(ch->vid_fmt_ctl, value); - - /* - * set number of active pixels in each line. Default is 720 - * pixels in both NTSC and PAL format - */ - cx_write(ch->vid_active_ctl1, width); - - num_lines = (height / 2) & 0x3FF; - odd_num_lines = num_lines; - - if (dev->_isNTSC_ch2) - odd_num_lines += 1; - - value = (num_lines << 16) | odd_num_lines; - - /* set number of active lines in field 0 (top) and field 1 (bottom) */ - cx_write(ch->vid_active_ctl2, value); - - cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); -} - -static int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, - struct sram_channel *sram_ch) -{ - u32 tmp = 0; - int err = 0; - - /* - * 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface - * for channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - /* - * Set the physical start address of the RISC program in the initial - * program counter(IPC) member of the cmds. - */ - cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2); - cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ - - /* reset counter */ - cx_write(sram_ch->gpcnt_ctl, 3); - - /* Clear our bits from the interrupt status register. */ - cx_write(sram_ch->int_stat, _intr_msk); - - /* Set the interrupt mask register, enable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp |= _intr_msk); - - err = request_irq(dev->pci->irq, cx25821_upstream_irq_ch2, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - pr_err("%s: can't get upstream IRQ %d\n", - dev->name, dev->pci->irq); - goto fail_irq; - } - /* Start the DMA engine */ - tmp = cx_read(sram_ch->dma_ctl); - cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); - - dev->_is_running_ch2 = 1; - dev->_is_first_frame_ch2 = 1; - - return 0; - -fail_irq: - cx25821_dev_unregister(dev); - return err; -} - -int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, - int pixel_format) -{ - struct sram_channel *sram_ch; - u32 tmp; - int err = 0; - int data_frame_size = 0; - int risc_buffer_size = 0; - - if (dev->_is_running_ch2) { - pr_info("Video Channel is still running so return!\n"); - return 0; - } - - dev->_channel2_upstream_select = channel_select; - sram_ch = dev->channels[channel_select].sram_channels; - - INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2); - dev->_irq_queues_ch2 = - create_singlethread_workqueue("cx25821_workqueue2"); - - if (!dev->_irq_queues_ch2) { - pr_err("create_singlethread_workqueue() for Video FAILED!\n"); - return -ENOMEM; - } - /* - * 656/VIP SRC Upstream Channel I & J and 7 - - * Host Bus Interface for channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - dev->_is_running_ch2 = 0; - dev->_frame_count_ch2 = 0; - dev->_file_status_ch2 = RESET_STATUS; - dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576; - dev->_pixel_format_ch2 = pixel_format; - dev->_line_size_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_422) ? - (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; - risc_buffer_size = dev->_isNTSC_ch2 ? - NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; - - if (dev->input_filename_ch2) - dev->_filename_ch2 = kstrdup(dev->input_filename_ch2, - GFP_KERNEL); - else - dev->_filename_ch2 = kstrdup(dev->_defaultname_ch2, - GFP_KERNEL); - - if (!dev->_filename_ch2) { - err = -ENOENT; - goto error; - } - - /* Default if filename is empty string */ - if (strcmp(dev->_filename_ch2, "") == 0) { - if (dev->_isNTSC_ch2) { - dev->_filename_ch2 = (dev->_pixel_format_ch2 == - PIXEL_FRMT_411) ? "/root/vid411.yuv" : - "/root/vidtest.yuv"; - } else { - dev->_filename_ch2 = (dev->_pixel_format_ch2 == - PIXEL_FRMT_411) ? "/root/pal411.yuv" : - "/root/pal422.yuv"; - } - } - - err = cx25821_sram_channel_setup_upstream(dev, sram_ch, - dev->_line_size_ch2, 0); - - /* setup fifo + format */ - cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2); - - dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2; - dev->upstream_databuf_size_ch2 = data_frame_size * 2; - - /* Allocating buffers and prepare RISC program */ - err = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, - dev->_line_size_ch2); - if (err < 0) { - pr_err("%s: Failed to set up Video upstream buffers!\n", - dev->name); - goto error; - } - - cx25821_start_video_dma_upstream_ch2(dev, sram_ch); - - return 0; - -error: - cx25821_dev_unregister(dev); - - return err; -} diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h deleted file mode 100644 index d42dab5..0000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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 <linux/mutex.h> -#include <linux/workqueue.h> - -#define OPEN_FILE_1 0 -#define NUM_PROGS 8 -#define NUM_FRAMES 2 -#define ODD_FIELD 0 -#define EVEN_FIELD 1 -#define TOP_OFFSET 0 -#define FIFO_DISABLE 0 -#define FIFO_ENABLE 1 -#define TEST_FRAMES 5 -#define END_OF_FILE 0 -#define IN_PROGRESS 1 -#define RESET_STATUS -1 -#define NUM_NO_OPS 5 - -/* PAL and NTSC line sizes and number of lines. */ -#define WIDTH_D1 720 -#define NTSC_LINES_PER_FRAME 480 -#define PAL_LINES_PER_FRAME 576 -#define PAL_LINE_SZ 1440 -#define Y422_LINE_SZ 1440 -#define Y411_LINE_SZ 1080 -#define NTSC_FIELD_HEIGHT 240 -#define NTSC_ODD_FLD_LINES 241 -#define PAL_FIELD_HEIGHT 288 - -#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) -#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) - -#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) -#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) - -#define RISC_WRITECR_INSTRUCTION_SIZE 16 -#define RISC_SYNC_INSTRUCTION_SIZE 4 -#define JUMP_INSTRUCTION_SIZE 12 -#define MAXSIZE_NO_OPS 36 -#define DWORD_SIZE 4 - -#define USE_RISC_NOOP_VIDEO 1 - -#ifdef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ - 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ - 2 * NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) -#endif - -#ifndef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE) - -#define PAL_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE)) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ - 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) - -#endif diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c index 7fc9711..88ffef4 100644 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.c +++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c @@ -25,16 +25,11 @@ #include "cx25821-video.h" #include "cx25821-video-upstream.h" -#include <linux/fs.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/syscalls.h> -#include <linux/file.h> -#include <linux/fcntl.h> #include <linux/slab.h> -#include <linux/uaccess.h> MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); @@ -44,7 +39,7 @@ static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR; int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc) { unsigned int i, lines; @@ -97,12 +92,13 @@ int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, return 0; } -static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, +static __le32 *cx25821_update_riscprogram(struct cx25821_channel *chan, __le32 *rp, unsigned int offset, unsigned int bpl, u32 sync_line, unsigned int lines, int fifo_enable, int field_type) { + struct cx25821_video_out_data *out = chan->out; unsigned int line, i; int dist_betwn_starts = bpl * 2; @@ -116,11 +112,11 @@ static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, /* scan lines */ for (line = 0; line < lines; line++) { *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset); + *(rp++) = cpu_to_le32(out->_data_buf_phys_addr + offset); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) { offset += dist_betwn_starts; } } @@ -128,15 +124,15 @@ static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, return rp; } -static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, +static __le32 *cx25821_risc_field_upstream(struct cx25821_channel *chan, __le32 *rp, dma_addr_t databuf_phys_addr, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int lines, int fifo_enable, int field_type) { + struct cx25821_video_out_data *out = chan->out; unsigned int line, i; - struct sram_channel *sram_ch = - dev->channels[dev->_channel_upstream_select].sram_channels; + const struct sram_channel *sram_ch = chan->sram_channels; int dist_betwn_starts = bpl * 2; /* sync instruction */ @@ -155,7 +151,7 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, *(rp++) = cpu_to_le32(0); /* bits 63-32 */ if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) /* to skip the other field line */ offset += dist_betwn_starts; @@ -173,11 +169,12 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, return rp; } -static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, +static int cx25821_risc_buffer_upstream(struct cx25821_channel *chan, struct pci_dev *pci, unsigned int top_offset, unsigned int bpl, unsigned int lines) { + struct cx25821_video_out_data *out = chan->out; __le32 *rp; int fifo_enable = 0; /* get line count for single field */ @@ -191,7 +188,7 @@ static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, unsigned int bottom_offset = bpl; dma_addr_t risc_phys_jump_addr; - if (dev->_isNTSC) { + if (out->is_60hz) { odd_num_lines = singlefield_lines + 1; risc_program_size = FRAME1_VID_PROG_SIZE; frame_size = (bpl == Y411_LINE_SZ) ? @@ -203,15 +200,15 @@ static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, } /* Virtual address of Risc buffer program */ - rp = dev->_dma_virt_addr; + rp = out->_dma_virt_addr; for (frame = 0; frame < NUM_FRAMES; frame++) { databuf_offset = frame_size * frame; if (UNSET != top_offset) { fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; - rp = cx25821_risc_field_upstream(dev, rp, - dev->_data_buf_phys_addr + + rp = cx25821_risc_field_upstream(chan, rp, + out->_data_buf_phys_addr + databuf_offset, top_offset, 0, bpl, odd_num_lines, fifo_enable, ODD_FIELD); } @@ -219,18 +216,18 @@ static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, fifo_enable = FIFO_DISABLE; /* Even Field */ - rp = cx25821_risc_field_upstream(dev, rp, - dev->_data_buf_phys_addr + + rp = cx25821_risc_field_upstream(chan, rp, + out->_data_buf_phys_addr + databuf_offset, bottom_offset, 0x200, bpl, singlefield_lines, fifo_enable, EVEN_FIELD); if (frame == 0) { risc_flag = RISC_CNT_RESET; - risc_phys_jump_addr = dev->_dma_phys_start_addr + + risc_phys_jump_addr = out->_dma_phys_start_addr + risc_program_size; } else { - risc_phys_jump_addr = dev->_dma_phys_start_addr; + risc_phys_jump_addr = out->_dma_phys_start_addr; risc_flag = RISC_CNT_INC; } @@ -245,16 +242,21 @@ static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, return 0; } -void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) +void cx25821_stop_upstream_video(struct cx25821_channel *chan) { - struct sram_channel *sram_ch = - dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels; + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; + const struct sram_channel *sram_ch = chan->sram_channels; u32 tmp = 0; - if (!dev->_is_running) { + if (!out->_is_running) { pr_info("No video file is currently running so return!\n"); return; } + + /* Set the interrupt mask register, disable irq. */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) & ~(1 << sram_ch->irq_bit)); + /* Disable RISC interrupts */ tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp & ~_intr_msk); @@ -263,283 +265,133 @@ void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) tmp = cx_read(sram_ch->dma_ctl); cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - /* Clear data buffer memory */ - if (dev->_data_buf_virt_addr) - memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); + free_irq(dev->pci->irq, chan); - dev->_is_running = 0; - dev->_is_first_frame = 0; - dev->_frame_count = 0; - dev->_file_status = END_OF_FILE; - - kfree(dev->_irq_queues); - dev->_irq_queues = NULL; + /* Clear data buffer memory */ + if (out->_data_buf_virt_addr) + memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); - kfree(dev->_filename); + out->_is_running = 0; + out->_is_first_frame = 0; + out->_frame_count = 0; + out->_file_status = END_OF_FILE; tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); } -void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev) +void cx25821_free_mem_upstream(struct cx25821_channel *chan) { - if (dev->_is_running) - cx25821_stop_upstream_video_ch1(dev); + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; - if (dev->_dma_virt_addr) { - pci_free_consistent(dev->pci, dev->_risc_size, - dev->_dma_virt_addr, dev->_dma_phys_addr); - dev->_dma_virt_addr = NULL; + if (out->_is_running) + cx25821_stop_upstream_video(chan); + + if (out->_dma_virt_addr) { + pci_free_consistent(dev->pci, out->_risc_size, + out->_dma_virt_addr, out->_dma_phys_addr); + out->_dma_virt_addr = NULL; } - if (dev->_data_buf_virt_addr) { - pci_free_consistent(dev->pci, dev->_data_buf_size, - dev->_data_buf_virt_addr, - dev->_data_buf_phys_addr); - dev->_data_buf_virt_addr = NULL; + if (out->_data_buf_virt_addr) { + pci_free_consistent(dev->pci, out->_data_buf_size, + out->_data_buf_virt_addr, + out->_data_buf_phys_addr); + out->_data_buf_virt_addr = NULL; } } -static int cx25821_get_frame(struct cx25821_dev *dev, - struct sram_channel *sram_ch) +int cx25821_write_frame(struct cx25821_channel *chan, + const char __user *data, size_t count) { - struct file *myfile; - int frame_index_temp = dev->_frame_index; - int i = 0; - int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? + struct cx25821_video_out_data *out = chan->out; + int line_size = (out->_pixel_format == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; int frame_size = 0; int frame_offset = 0; - ssize_t vfs_read_retval = 0; - char mybuf[line_size]; - loff_t file_offset; - loff_t pos; - mm_segment_t old_fs; - - if (dev->_file_status == END_OF_FILE) - return 0; + int curpos = out->curpos; - if (dev->_isNTSC) + if (out->is_60hz) frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; else frame_size = (line_size == Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - frame_offset = (frame_index_temp > 0) ? frame_size : 0; - file_offset = dev->_frame_count * frame_size; - - myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); - - if (IS_ERR(myfile)) { - const int open_errno = -PTR_ERR(myfile); - pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", - __func__, dev->_filename, open_errno); - return PTR_ERR(myfile); - } else { - if (!(myfile->f_op)) { - pr_err("%s(): File has no file operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - if (!myfile->f_op->read) { - pr_err("%s(): File has no READ operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - pos = myfile->f_pos; - old_fs = get_fs(); - set_fs(KERNEL_DS); - - for (i = 0; i < dev->_lines_count; i++) { - pos = file_offset; - - vfs_read_retval = vfs_read(myfile, mybuf, line_size, - &pos); - - if (vfs_read_retval > 0 && vfs_read_retval == line_size - && dev->_data_buf_virt_addr != NULL) { - memcpy((void *)(dev->_data_buf_virt_addr + - frame_offset / 4), mybuf, - vfs_read_retval); - } - - file_offset += vfs_read_retval; - frame_offset += vfs_read_retval; - - if (vfs_read_retval < line_size) { - pr_info("Done: exit %s() since no more bytes to read from Video file\n", - __func__); - break; - } - } - - if (i > 0) - dev->_frame_count++; - - dev->_file_status = (vfs_read_retval == line_size) ? - IN_PROGRESS : END_OF_FILE; - - set_fs(old_fs); - filp_close(myfile, NULL); - } - - return 0; -} - -static void cx25821_vidups_handler(struct work_struct *work) -{ - struct cx25821_dev *dev = container_of(work, struct cx25821_dev, - _irq_work_entry); - - if (!dev) { - pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", - __func__); - return; + if (curpos == 0) { + out->cur_frame_index = out->_frame_index; + if (wait_event_interruptible(out->waitq, out->cur_frame_index != out->_frame_index)) + return -EINTR; + out->cur_frame_index = out->_frame_index; } - cx25821_get_frame(dev, dev->channels[dev->_channel_upstream_select]. - sram_channels); -} - -static int cx25821_openfile(struct cx25821_dev *dev, - struct sram_channel *sram_ch) -{ - struct file *myfile; - int i = 0, j = 0; - int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? - Y411_LINE_SZ : Y422_LINE_SZ; - ssize_t vfs_read_retval = 0; - char mybuf[line_size]; - loff_t pos; - loff_t offset = (unsigned long)0; - mm_segment_t old_fs; - - myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); - - if (IS_ERR(myfile)) { - const int open_errno = -PTR_ERR(myfile); - pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", - __func__, dev->_filename, open_errno); - return PTR_ERR(myfile); - } else { - if (!(myfile->f_op)) { - pr_err("%s(): File has no file operations registered!\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - if (!myfile->f_op->read) { - pr_err("%s(): File has no READ operations registered! Returning\n", - __func__); - filp_close(myfile, NULL); - return -EIO; - } - - pos = myfile->f_pos; - old_fs = get_fs(); - set_fs(KERNEL_DS); - - for (j = 0; j < NUM_FRAMES; j++) { - for (i = 0; i < dev->_lines_count; i++) { - pos = offset; - - vfs_read_retval = vfs_read(myfile, mybuf, - line_size, &pos); - - if (vfs_read_retval > 0 - && vfs_read_retval == line_size - && dev->_data_buf_virt_addr != NULL) { - memcpy((void *)(dev-> - _data_buf_virt_addr + - offset / 4), mybuf, - vfs_read_retval); - } - - offset += vfs_read_retval; - - if (vfs_read_retval < line_size) { - pr_info("Done: exit %s() since no more bytes to read from Video file\n", - __func__); - break; - } - } + frame_offset = out->cur_frame_index ? frame_size : 0; - if (i > 0) - dev->_frame_count++; - - if (vfs_read_retval < line_size) - break; - } - - dev->_file_status = (vfs_read_retval == line_size) ? - IN_PROGRESS : END_OF_FILE; - - set_fs(old_fs); - myfile->f_pos = 0; - filp_close(myfile, NULL); + if (frame_size - curpos < count) + count = frame_size - curpos; + memcpy((char *)out->_data_buf_virt_addr + frame_offset + curpos, + data, count); + curpos += count; + if (curpos == frame_size) { + out->_frame_count++; + curpos = 0; } + out->curpos = curpos; - return 0; + return count; } -static int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, - struct sram_channel *sram_ch, +static int cx25821_upstream_buffer_prepare(struct cx25821_channel *chan, + const struct sram_channel *sram_ch, int bpl) { + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; int ret = 0; dma_addr_t dma_addr; dma_addr_t data_dma_addr; - if (dev->_dma_virt_addr != NULL) - pci_free_consistent(dev->pci, dev->upstream_riscbuf_size, - dev->_dma_virt_addr, dev->_dma_phys_addr); + if (out->_dma_virt_addr != NULL) + pci_free_consistent(dev->pci, out->upstream_riscbuf_size, + out->_dma_virt_addr, out->_dma_phys_addr); - dev->_dma_virt_addr = pci_alloc_consistent(dev->pci, - dev->upstream_riscbuf_size, &dma_addr); - dev->_dma_virt_start_addr = dev->_dma_virt_addr; - dev->_dma_phys_start_addr = dma_addr; - dev->_dma_phys_addr = dma_addr; - dev->_risc_size = dev->upstream_riscbuf_size; + out->_dma_virt_addr = pci_alloc_consistent(dev->pci, + out->upstream_riscbuf_size, &dma_addr); + out->_dma_virt_start_addr = out->_dma_virt_addr; + out->_dma_phys_start_addr = dma_addr; + out->_dma_phys_addr = dma_addr; + out->_risc_size = out->upstream_riscbuf_size; - if (!dev->_dma_virt_addr) { + if (!out->_dma_virt_addr) { pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); return -ENOMEM; } /* Clear memory at address */ - memset(dev->_dma_virt_addr, 0, dev->_risc_size); + memset(out->_dma_virt_addr, 0, out->_risc_size); - if (dev->_data_buf_virt_addr != NULL) - pci_free_consistent(dev->pci, dev->upstream_databuf_size, - dev->_data_buf_virt_addr, - dev->_data_buf_phys_addr); + if (out->_data_buf_virt_addr != NULL) + pci_free_consistent(dev->pci, out->upstream_databuf_size, + out->_data_buf_virt_addr, + out->_data_buf_phys_addr); /* For Video Data buffer allocation */ - dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, - dev->upstream_databuf_size, &data_dma_addr); - dev->_data_buf_phys_addr = data_dma_addr; - dev->_data_buf_size = dev->upstream_databuf_size; + out->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, + out->upstream_databuf_size, &data_dma_addr); + out->_data_buf_phys_addr = data_dma_addr; + out->_data_buf_size = out->upstream_databuf_size; - if (!dev->_data_buf_virt_addr) { + if (!out->_data_buf_virt_addr) { pr_err("FAILED to allocate memory for data buffer! Returning\n"); return -ENOMEM; } /* Clear memory at address */ - memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); - - ret = cx25821_openfile(dev, sram_ch); - if (ret < 0) - return ret; + memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); /* Create RISC programs */ - ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, - dev->_lines_count); + ret = cx25821_risc_buffer_upstream(chan, dev->pci, 0, bpl, + out->_lines_count); if (ret < 0) { pr_info("Failed creating Video Upstream Risc programs!\n"); goto error; @@ -551,11 +403,12 @@ error: return ret; } -static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, - u32 status) +static int cx25821_video_upstream_irq(struct cx25821_channel *chan, u32 status) { + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; u32 int_msk_tmp; - struct sram_channel *channel = dev->channels[chan_num].sram_channels; + const struct sram_channel *channel = chan->sram_channels; int singlefield_lines = NTSC_FIELD_HEIGHT; int line_size_in_bytes = Y422_LINE_SZ; int odd_risc_prog_size = 0; @@ -572,16 +425,16 @@ static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); cx_write(channel->int_stat, _intr_msk); - spin_lock(&dev->slock); + wake_up(&out->waitq); - dev->_frame_index = prog_cnt; + spin_lock(&dev->slock); - queue_work(dev->_irq_queues, &dev->_irq_work_entry); + out->_frame_index = prog_cnt; - if (dev->_is_first_frame) { - dev->_is_first_frame = 0; + if (out->_is_first_frame) { + out->_is_first_frame = 0; - if (dev->_isNTSC) { + if (out->is_60hz) { singlefield_lines += 1; odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; } else { @@ -589,17 +442,17 @@ static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; } - if (dev->_dma_virt_start_addr != NULL) { + if (out->_dma_virt_start_addr != NULL) { line_size_in_bytes = - (dev->_pixel_format == + (out->_pixel_format == PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; risc_phys_jump_addr = - dev->_dma_phys_start_addr + + out->_dma_phys_start_addr + odd_risc_prog_size; - rp = cx25821_update_riscprogram(dev, - dev->_dma_virt_start_addr, TOP_OFFSET, + rp = cx25821_update_riscprogram(chan, + out->_dma_virt_start_addr, TOP_OFFSET, line_size_in_bytes, 0x0, singlefield_lines, FIFO_DISABLE, ODD_FIELD); @@ -626,8 +479,8 @@ static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, __func__); } - if (dev->_file_status == END_OF_FILE) { - pr_err("EOF Channel 1 Framecount = %d\n", dev->_frame_count); + if (out->_file_status == END_OF_FILE) { + pr_err("EOF Channel 1 Framecount = %d\n", out->_frame_count); return -1; } /* ElSE, set the interrupt mask register, re-enable irq. */ @@ -639,47 +492,41 @@ static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) { - struct cx25821_dev *dev = dev_id; + struct cx25821_channel *chan = dev_id; + struct cx25821_dev *dev = chan->dev; u32 vid_status; int handled = 0; - int channel_num = 0; - struct sram_channel *sram_ch; + const struct sram_channel *sram_ch; if (!dev) return -1; - channel_num = VID_UPSTREAM_SRAM_CHANNEL_I; - - sram_ch = dev->channels[channel_num].sram_channels; + sram_ch = chan->sram_channels; vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ if (vid_status) - handled = cx25821_video_upstream_irq(dev, channel_num, - vid_status); - - if (handled < 0) - cx25821_stop_upstream_video_ch1(dev); - else - handled += handled; + handled = cx25821_video_upstream_irq(chan, vid_status); return IRQ_RETVAL(handled); } -static void cx25821_set_pixelengine(struct cx25821_dev *dev, - struct sram_channel *ch, +static void cx25821_set_pixelengine(struct cx25821_channel *chan, + const struct sram_channel *ch, int pix_format) { + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; int width = WIDTH_D1; - int height = dev->_lines_count; + int height = out->_lines_count; int num_lines, odd_num_lines; u32 value; int vip_mode = OUTPUT_FRMT_656; value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); value &= 0xFFFFFFEF; - value |= dev->_isNTSC ? 0 : 0x10; + value |= out->is_60hz ? 0 : 0x10; cx_write(ch->vid_fmt_ctl, value); /* set number of active pixels in each line. @@ -689,7 +536,7 @@ static void cx25821_set_pixelengine(struct cx25821_dev *dev, num_lines = (height / 2) & 0x3FF; odd_num_lines = num_lines; - if (dev->_isNTSC) + if (out->is_60hz) odd_num_lines += 1; value = (num_lines << 16) | odd_num_lines; @@ -700,9 +547,11 @@ static void cx25821_set_pixelengine(struct cx25821_dev *dev, cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); } -static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, - struct sram_channel *sram_ch) +static int cx25821_start_video_dma_upstream(struct cx25821_channel *chan, + const struct sram_channel *sram_ch) { + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; u32 tmp = 0; int err = 0; @@ -715,7 +564,7 @@ static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, /* Set the physical start address of the RISC program in the initial * program counter(IPC) member of the cmds. */ - cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr); + cx_write(sram_ch->cmds_start + 0, out->_dma_phys_addr); /* Risc IPC High 64 bits 63-32 */ cx_write(sram_ch->cmds_start + 4, 0); @@ -731,7 +580,7 @@ static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, cx_write(sram_ch->int_msk, tmp |= _intr_msk); err = request_irq(dev->pci->irq, cx25821_upstream_irq, - IRQF_SHARED, dev->name, dev); + IRQF_SHARED, dev->name, chan); if (err < 0) { pr_err("%s: can't get upstream IRQ %d\n", dev->name, dev->pci->irq); @@ -742,8 +591,8 @@ static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, tmp = cx_read(sram_ch->dma_ctl); cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); - dev->_is_running = 1; - dev->_is_first_frame = 1; + out->_is_running = 1; + out->_is_first_frame = 1; return 0; @@ -752,107 +601,71 @@ fail_irq: return err; } -int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, +int cx25821_vidupstream_init(struct cx25821_channel *chan, int pixel_format) { - struct sram_channel *sram_ch; + struct cx25821_video_out_data *out = chan->out; + struct cx25821_dev *dev = chan->dev; + const struct sram_channel *sram_ch; u32 tmp; int err = 0; int data_frame_size = 0; int risc_buffer_size = 0; - int str_length = 0; - if (dev->_is_running) { + if (out->_is_running) { pr_info("Video Channel is still running so return!\n"); return 0; } - dev->_channel_upstream_select = channel_select; - sram_ch = dev->channels[channel_select].sram_channels; + sram_ch = chan->sram_channels; - INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler); - dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue"); + out->is_60hz = dev->tvnorm & V4L2_STD_525_60; - if (!dev->_irq_queues) { - pr_err("create_singlethread_workqueue() for Video FAILED!\n"); - return -ENOMEM; - } /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for * channel A-C */ tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - dev->_is_running = 0; - dev->_frame_count = 0; - dev->_file_status = RESET_STATUS; - dev->_lines_count = dev->_isNTSC ? 480 : 576; - dev->_pixel_format = pixel_format; - dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? + out->_is_running = 0; + out->_frame_count = 0; + out->_file_status = RESET_STATUS; + out->_lines_count = out->is_60hz ? 480 : 576; + out->_pixel_format = pixel_format; + out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; - risc_buffer_size = dev->_isNTSC ? + data_frame_size = out->is_60hz ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; + risc_buffer_size = out->is_60hz ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; - if (dev->input_filename) { - str_length = strlen(dev->input_filename); - dev->_filename = kmemdup(dev->input_filename, str_length + 1, - GFP_KERNEL); - - if (!dev->_filename) { - err = -ENOENT; - goto error; - } - } else { - str_length = strlen(dev->_defaultname); - dev->_filename = kmemdup(dev->_defaultname, str_length + 1, - GFP_KERNEL); - - if (!dev->_filename) { - err = -ENOENT; - goto error; - } - } - - /* Default if filename is empty string */ - if (strcmp(dev->_filename, "") == 0) { - if (dev->_isNTSC) { - dev->_filename = - (dev->_pixel_format == PIXEL_FRMT_411) ? - "/root/vid411.yuv" : "/root/vidtest.yuv"; - } else { - dev->_filename = - (dev->_pixel_format == PIXEL_FRMT_411) ? - "/root/pal411.yuv" : "/root/pal422.yuv"; - } - } - - dev->_is_running = 0; - dev->_frame_count = 0; - dev->_file_status = RESET_STATUS; - dev->_lines_count = dev->_isNTSC ? 480 : 576; - dev->_pixel_format = pixel_format; - dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? + out->_is_running = 0; + out->_frame_count = 0; + out->_file_status = RESET_STATUS; + out->_lines_count = out->is_60hz ? 480 : 576; + out->_pixel_format = pixel_format; + out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + out->curpos = 0; + init_waitqueue_head(&out->waitq); err = cx25821_sram_channel_setup_upstream(dev, sram_ch, - dev->_line_size, 0); + out->_line_size, 0); /* setup fifo + format */ - cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format); + cx25821_set_pixelengine(chan, sram_ch, out->_pixel_format); - dev->upstream_riscbuf_size = risc_buffer_size * 2; - dev->upstream_databuf_size = data_frame_size * 2; + out->upstream_riscbuf_size = risc_buffer_size * 2; + out->upstream_databuf_size = data_frame_size * 2; /* Allocating buffers and prepare RISC program */ - err = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size); + err = cx25821_upstream_buffer_prepare(chan, sram_ch, out->_line_size); if (err < 0) { pr_err("%s: Failed to set up Video upstream buffers!\n", dev->name); goto error; } - cx25821_start_video_dma_upstream(dev, sram_ch); + cx25821_start_video_dma_upstream(chan, sram_ch); return 0; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 31ce769..d270819 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -33,13 +33,10 @@ MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); MODULE_LICENSE("GPL"); static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; module_param_array(video_nr, int, NULL, 0444); -module_param_array(radio_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video device numbers"); -MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug = VIDEO_DEBUG; module_param(video_debug, int, 0644); @@ -49,24 +46,14 @@ static unsigned int irq_debug; module_param(irq_debug, int, 0644); MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); -unsigned int vid_limit = 16; +static unsigned int vid_limit = 16; module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); -static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num); - -static const struct v4l2_file_operations video_fops; -static const struct v4l2_ioctl_ops video_ioctl_ops; - #define FORMAT_FLAGS_PACKED 0x01 -struct cx25821_fmt formats[] = { +static const struct cx25821_fmt formats[] = { { - .name = "8 bpp, gray", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8, - .flags = FORMAT_FLAGS_PACKED, - }, { .name = "4:1:1, packed, Y41P", .fourcc = V4L2_PIX_FMT_Y41P, .depth = 12, @@ -76,36 +63,16 @@ struct cx25821_fmt formats[] = { .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, - }, { - .name = "4:2:0, YUV", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .flags = FORMAT_FLAGS_PACKED, }, }; -int cx25821_get_format_size(void) -{ - return ARRAY_SIZE(formats); -} - -struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) +static const struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) { unsigned int i; - if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) - return formats + 1; - for (i = 0; i < ARRAY_SIZE(formats); i++) if (formats[i].fourcc == fourcc) return formats + i; - - pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc); return NULL; } @@ -144,129 +111,10 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); } -#ifdef TUNER_FLAG -int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm) -{ - dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", - __func__, (unsigned int)norm, v4l2_norm_to_name(norm)); - - dev->tvnorm = norm; - - /* Tell the internal A/V decoder */ - cx25821_call_all(dev, core, s_std, norm); - - return 0; -} -#endif - -struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, - struct pci_dev *pci, - struct video_device *template, - char *type) -{ - struct video_device *vfd; - dprintk(1, "%s()\n", __func__); - - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; - snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, - cx25821_boards[dev->board].name); - video_set_drvdata(vfd, dev); - return vfd; -} - -/* -static int cx25821_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 < CX25821_CTLS; i++) - if (cx25821_ctls[i].v.id == qctrl->id) - break; - if (i == CX25821_CTLS) { - *qctrl = no_ctl; - return 0; - } - *qctrl = cx25821_ctls[i].v; - return 0; -} -*/ - -/* resource management */ -int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_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->channels[fh->channel_id].resources & bit) { - /* no, someone else uses it */ - mutex_unlock(&dev->lock); - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->channels[fh->channel_id].resources |= bit; - dprintk(1, "res: get %d\n", bit); - mutex_unlock(&dev->lock); - return 1; -} - -int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit) -{ - return fh->dev->channels[fh->channel_id].resources & bit; -} - -void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, - unsigned int bits) -{ - BUG_ON((fh->resources & bits) != bits); - dprintk(1, "%s()\n", __func__); - - mutex_lock(&dev->lock); - fh->resources &= ~bits; - dev->channels[fh->channel_id].resources &= ~bits; - dprintk(1, "res: put %d\n", bits); - mutex_unlock(&dev->lock); -} - -int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input) -{ - struct v4l2_routing route; - memset(&route, 0, sizeof(route)); - - dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", - __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, - INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); - dev->input = input; - - route.input = INPUT(input)->vmux; - - /* Tell the internal A/V decoder */ - cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0); - - return 0; -} - int cx25821_start_video_dma(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, struct cx25821_buffer *buf, - struct sram_channel *channel) + const struct sram_channel *channel) { int tmp = 0; @@ -293,7 +141,7 @@ int cx25821_start_video_dma(struct cx25821_dev *dev, static int cx25821_restart_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, - struct sram_channel *channel) + const struct sram_channel *channel) { struct cx25821_buffer *buf, *prev; struct list_head *item; @@ -346,8 +194,8 @@ static void cx25821_vid_timeout(unsigned long data) { struct cx25821_data *timeout_data = (struct cx25821_data *)data; struct cx25821_dev *dev = timeout_data->dev; - struct sram_channel *channel = timeout_data->channel; - struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq; + const struct sram_channel *channel = timeout_data->channel; + struct cx25821_dmaqueue *q = &dev->channels[channel->i].dma_vidq; struct cx25821_buffer *buf; unsigned long flags; @@ -373,7 +221,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) u32 count = 0; int handled = 0; u32 mask; - struct sram_channel *channel = dev->channels[chan_num].sram_channels; + const struct sram_channel *channel = dev->channels[chan_num].sram_channels; mask = cx_read(channel->int_msk); if (0 == (status & mask)) @@ -393,7 +241,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) if (status & FLD_VID_DST_RISC1) { spin_lock(&dev->slock); count = cx_read(channel->gpcnt); - cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq, + cx25821_video_wakeup(dev, &dev->channels[channel->i].dma_vidq, count); spin_unlock(&dev->slock); handled++; @@ -404,122 +252,19 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) dprintk(2, "stopper video\n"); spin_lock(&dev->slock); cx25821_restart_video_queue(dev, - &dev->channels[channel->i].vidq, channel); + &dev->channels[channel->i].dma_vidq, channel); spin_unlock(&dev->slock); handled++; } return handled; } -void cx25821_videoioctl_unregister(struct cx25821_dev *dev) -{ - if (dev->ioctl_dev) { - if (video_is_registered(dev->ioctl_dev)) - video_unregister_device(dev->ioctl_dev); - else - video_device_release(dev->ioctl_dev); - - dev->ioctl_dev = NULL; - } -} - -void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) -{ - cx_clear(PCI_INT_MSK, 1); - - if (dev->channels[chan_num].video_dev) { - if (video_is_registered(dev->channels[chan_num].video_dev)) - video_unregister_device( - dev->channels[chan_num].video_dev); - else - video_device_release( - dev->channels[chan_num].video_dev); - - dev->channels[chan_num].video_dev = NULL; - - btcx_riscmem_free(dev->pci, - &dev->channels[chan_num].vidq.stopper); - - pr_warn("device %d released!\n", chan_num); - } - -} - -int cx25821_video_register(struct cx25821_dev *dev) -{ - int err; - int i; - - struct video_device cx25821_video_device = { - .name = "cx25821-video", - .fops = &video_fops, - .minor = -1, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, - }; - - spin_lock_init(&dev->slock); - - for (i = 0; i < VID_CHANNEL_NUM; ++i) { - cx25821_init_controls(dev, i); - - cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper, - dev->channels[i].sram_channels->dma_ctl, 0x11, 0); - - dev->channels[i].sram_channels = &cx25821_sram_channels[i]; - dev->channels[i].video_dev = NULL; - dev->channels[i].resources = 0; - - cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); - - INIT_LIST_HEAD(&dev->channels[i].vidq.active); - INIT_LIST_HEAD(&dev->channels[i].vidq.queued); - - dev->channels[i].timeout_data.dev = dev; - dev->channels[i].timeout_data.channel = - &cx25821_sram_channels[i]; - dev->channels[i].vidq.timeout.function = cx25821_vid_timeout; - dev->channels[i].vidq.timeout.data = - (unsigned long)&dev->channels[i].timeout_data; - init_timer(&dev->channels[i].vidq.timeout); - - /* register v4l devices */ - dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci, - &cx25821_video_device, "video"); - - err = video_register_device(dev->channels[i].video_dev, - VFL_TYPE_GRABBER, video_nr[dev->nr]); - - if (err < 0) - goto fail_unreg; - - } - - /* set PCI interrupt */ - cx_set(PCI_INT_MSK, 0xff); - - /* initial device configuration */ - mutex_lock(&dev->lock); -#ifdef TUNER_FLAG - dev->tvnorm = cx25821_video_device.current_norm; - cx25821_set_tvnorm(dev, dev->tvnorm); -#endif - mutex_unlock(&dev->lock); - - return 0; - -fail_unreg: - cx25821_video_unregister(dev, i); - return err; -} - -int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, +static int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { - struct cx25821_fh *fh = q->priv_data; + struct cx25821_channel *chan = q->priv_data; - *size = fh->fmt->depth * fh->width * fh->height >> 3; + *size = chan->fmt->depth * chan->width * chan->height >> 3; if (0 == *count) *count = 32; @@ -530,35 +275,34 @@ int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, return 0; } -int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, +static int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { - struct cx25821_fh *fh = q->priv_data; - struct cx25821_dev *dev = fh->dev; + struct cx25821_channel *chan = q->priv_data; + struct cx25821_dev *dev = chan->dev; struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); int rc, init_buffer = 0; u32 line0_offset; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; - int channel_opened = fh->channel_id; - BUG_ON(NULL == fh->fmt); - if (fh->width < 48 || fh->width > 720 || - fh->height < 32 || fh->height > 576) + BUG_ON(NULL == chan->fmt); + if (chan->width < 48 || chan->width > 720 || + chan->height < 32 || chan->height > 576) return -EINVAL; - buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + buf->vb.size = (chan->width * chan->height * chan->fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; - 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; + if (buf->fmt != chan->fmt || + buf->vb.width != chan->width || + buf->vb.height != chan->height || buf->vb.field != field) { + buf->fmt = chan->fmt; + buf->vb.width = chan->width; + buf->vb.height = chan->height; buf->vb.field = field; init_buffer = 1; } @@ -575,34 +319,21 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, dprintk(1, "init_buffer=%d\n", init_buffer); if (init_buffer) { - - channel_opened = dev->channel_opened; - if (channel_opened < 0 || channel_opened > 7) - channel_opened = 7; - - if (dev->channels[channel_opened].pixel_formats == - PIXEL_FRMT_411) + if (chan->pixel_formats == PIXEL_FRMT_411) buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; else buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); - if (dev->channels[channel_opened].pixel_formats == - PIXEL_FRMT_411) { + if (chan->pixel_formats == PIXEL_FRMT_411) { bpl_local = buf->bpl; } else { bpl_local = buf->bpl; /* Default */ - if (channel_opened >= 0 && channel_opened <= 7) { - if (dev->channels[channel_opened] - .use_cif_resolution) { - if (dev->tvnorm & V4L2_STD_PAL_BG || - dev->tvnorm & V4L2_STD_PAL_DK) - bpl_local = 352 << 1; - else - bpl_local = dev->channels[ - channel_opened]. - cif_width << 1; - } + if (chan->use_cif_resolution) { + if (dev->tvnorm & V4L2_STD_625_50) + bpl_local = 352 << 1; + else + bpl_local = chan->cif_width << 1; } } @@ -645,8 +376,8 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, } 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, (unsigned long)buf->risc.dma); + buf, buf->vb.i, chan->width, chan->height, chan->fmt->depth, + chan->fmt->name, (unsigned long)buf->risc.dma); buf->vb.state = VIDEOBUF_PREPARED; @@ -657,7 +388,7 @@ fail: return rc; } -void cx25821_buffer_release(struct videobuf_queue *q, +static void cx25821_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct cx25821_buffer *buf = @@ -666,33 +397,11 @@ void cx25821_buffer_release(struct videobuf_queue *q, cx25821_free_buffer(q, buf); } -struct videobuf_queue *get_queue(struct cx25821_fh *fh) +static int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) { - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &fh->vidq; - default: - BUG(); - return NULL; - } -} + struct cx25821_channel *chan = video_drvdata(file); -int cx25821_get_resource(struct cx25821_fh *fh, int resource) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return resource; - default: - BUG(); - return 0; - } -} - -int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx25821_fh *fh = file->private_data; - - return videobuf_mmap_mapper(get_queue(fh), vma); + return videobuf_mmap_mapper(&chan->vidq, vma); } @@ -701,9 +410,9 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq; + struct cx25821_channel *chan = vq->priv_data; + struct cx25821_dev *dev = chan->dev; + struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq; /* add jump to stopper */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); @@ -720,8 +429,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } else if (list_empty(&q->active)) { list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - dev->channels[fh->channel_id].sram_channels); + cx25821_start_video_dma(dev, q, buf, chan->sram_channels); buf->vb.state = VIDEOBUF_ACTIVE; buf->count = q->count++; mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); @@ -762,1183 +470,487 @@ static struct videobuf_queue_ops cx25821_video_qops = { .buf_release = cx25821_buffer_release, }; -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *h, *dev = video_drvdata(file); - struct cx25821_fh *fh; - struct list_head *list; - int minor = video_devdata(file)->minor; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - int ch_id = 0; - int i; - - dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - mutex_lock(&cx25821_devlist_mutex); - - list_for_each(list, &cx25821_devlist) - { - h = list_entry(list, struct cx25821_dev, devlist); - - for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { - if (h->channels[i].video_dev && - h->channels[i].video_dev->minor == minor) { - dev = h; - ch_id = i; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - } - } - - if (NULL == dev) { - mutex_unlock(&cx25821_devlist_mutex); - kfree(fh); - return -ENODEV; - } - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - fh->channel_id = ch_id; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = fh->channel_id; - if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411) - pix_format = V4L2_PIX_FMT_Y41P; - else - pix_format = V4L2_PIX_FMT_YUYV; - fh->fmt = cx25821_format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev, - &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), - fh, NULL); - - dprintk(1, "post videobuf_queue_init()\n"); - mutex_unlock(&cx25821_devlist_mutex); - - return 0; -} - static ssize_t video_read(struct file *file, char __user * data, size_t count, loff_t *ppos) { - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh, RESOURCE_VIDEO0)) - return -EBUSY; + struct v4l2_fh *fh = file->private_data; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + int err = 0; - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (chan->streaming_fh && chan->streaming_fh != fh) { + err = -EBUSY; + goto unlock; } + chan->streaming_fh = fh; + + err = videobuf_read_one(&chan->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); +unlock: + mutex_unlock(&dev->lock); + return err; } static unsigned int video_poll(struct file *file, struct poll_table_struct *wait) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->channels[fh->channel_id] - .use_cif_resolution) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } + struct cx25821_channel *chan = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); + unsigned int res = v4l2_ctrl_poll(file, wait); + + if (req_events & (POLLIN | POLLRDNORM)) + res |= videobuf_poll_stream(file, &chan->vidq, wait); + return res; + + /* This doesn't belong in poll(). This can be done + * much better with vb2. We keep this code here as a + * reminder. + if ((res & POLLIN) && buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = chan->dev; + + if (dev && chan->use_cif_resolution) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (chan->width * 2), + (chan->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; } - - return POLLIN | POLLRDNORM; } - - return 0; + */ } static int video_release(struct file *file) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; + struct cx25821_channel *chan = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct cx25821_dev *dev = chan->dev; + const struct sram_channel *sram_ch = + dev->channels[0].sram_channels; + mutex_lock(&dev->lock); /* stop the risc engine and fifo */ - cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ + cx_write(sram_ch->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO0); + if (chan->streaming_fh == fh) { + videobuf_queue_cancel(&chan->vidq); + chan->streaming_fh = NULL; } - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); + if (chan->vidq.read_buf) { + cx25821_buffer_release(&chan->vidq, chan->vidq.read_buf); + kfree(chan->vidq.read_buf); } - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio); - file->private_data = NULL; - kfree(fh); + videobuf_mmap_free(&chan->vidq); + mutex_unlock(&dev->lock); - return 0; + return v4l2_fh_release(file); } -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; +/* VIDEO IOCTLS */ - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) +static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(formats))) return -EINVAL; - if (unlikely(i != fh->type)) - return -EINVAL; + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, - RESOURCE_VIDEO0)))) - return -EBUSY; + return 0; +} + +static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_channel *chan = video_drvdata(file); + + f->fmt.pix.width = chan->width; + f->fmt.pix.height = chan->height; + f->fmt.pix.field = chan->vidq.field; + f->fmt.pix.pixelformat = chan->fmt->fourcc; + f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; + f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; - return videobuf_streamon(get_queue(fh)); + return 0; } -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +static int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) { - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + const struct cx25821_fmt *fmt; + enum v4l2_field field = f->fmt.pix.field; + unsigned int maxh; + unsigned w; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) + fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) return -EINVAL; + maxh = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; + + w = f->fmt.pix.width; + if (field != V4L2_FIELD_BOTTOM) + field = V4L2_FIELD_TOP; + if (w < 352) { + w = 176; + f->fmt.pix.height = maxh / 4; + } else if (w < 720) { + w = 352; + f->fmt.pix.height = maxh / 2; + } else { + w = 720; + f->fmt.pix.height = maxh; + field = V4L2_FIELD_INTERLACED; + } + f->fmt.pix.field = field; + f->fmt.pix.width = w; + 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; + f->fmt.pix.priv = 0; - res = cx25821_get_resource(fh, RESOURCE_VIDEO0); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); return 0; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct v4l2_mbus_framefmt mbus_fmt; - int err; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; int pix_format = PIXEL_FRMT_422; + int err; - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; - fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - /* check if width and height is valid based on set standard */ - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) - fh->width = f->fmt.pix.width; - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) - fh->height = f->fmt.pix.height; + chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + chan->vidq.field = f->fmt.pix.field; + chan->width = f->fmt.pix.width; + chan->height = f->fmt.pix.height; if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; else - return -EINVAL; + pix_format = PIXEL_FRMT_422; cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); /* check if cif resolution */ - if (fh->width == 320 || fh->width == 352) - dev->channels[fh->channel_id].use_cif_resolution = 1; + if (chan->width == 320 || chan->width == 352) + chan->use_cif_resolution = 1; else - dev->channels[fh->channel_id].use_cif_resolution = 0; - - dev->channels[fh->channel_id].cif_width = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH00); - - dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); - cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt); + chan->use_cif_resolution = 0; + chan->cif_width = chan->width; + medusa_set_resolution(dev, chan->width, SRAM_CH00); return 0; } -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_channel *chan = video_drvdata(file); - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - p->sequence = dev->channels[fh->channel_id].vidq.count; + if (chan->streaming_fh && chan->streaming_fh != priv) + return -EBUSY; + chan->streaming_fh = priv; - return ret_val; + return videobuf_streamon(&chan->vidq); } -static int vidioc_log_status(struct file *file, void *priv) +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct cx25821_fh *fh = priv; - char name[32 + 2]; + struct cx25821_channel *chan = video_drvdata(file); - struct sram_channel *sram_ch = dev->channels[fh->channel_id] - .sram_channels; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - pr_info("%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - tmp = cx_read(sram_ch->dma_ctl); - pr_info("Video input 0 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - pr_info("%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } + if (chan->streaming_fh && chan->streaming_fh != priv) + return -EBUSY; + if (chan->streaming_fh == NULL) + return 0; - return cx25821_set_control(dev, ctl, fh->channel_id); + chan->streaming_fh = NULL; + return videobuf_streamoff(&chan->vidq); } -/* VIDEO IOCTLS */ -int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx25821_fh *fh = priv; + int ret_val = 0; + struct cx25821_channel *chan = 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.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK); + p->sequence = chan->dma_vidq.count; - return 0; + return ret_val; } -int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int vidioc_log_status(struct file *file, void *priv) { - struct cx25821_fmt *fmt; - enum v4l2_field field; - unsigned int maxw, maxh; - - fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; - - field = f->fmt.pix.field; - maxw = 720; - maxh = 576; - - if (V4L2_FIELD_ANY == field) { - if (f->fmt.pix.height > maxh / 2) - field = V4L2_FIELD_INTERLACED; - else - field = V4L2_FIELD_TOP; - } - - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } - - f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - 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; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + const struct sram_channel *sram_ch = chan->sram_channels; + u32 tmp = 0; + tmp = cx_read(sram_ch->dma_ctl); + pr_info("Video input 0 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); return 0; } -int cx25821_vidioc_querycap(struct file *file, void *priv, + +static int cx25821_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + const u32 cap_input = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + const u32 cap_output = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE; strcpy(cap->driver, "cx25821"); strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); - cap->version = CX25821_VERSION_CODE; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - if (UNSET != dev->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; - return 0; -} - -int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (unlikely(f->index >= ARRAY_SIZE(formats))) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].fourcc; - + if (chan->id >= VID_CHANNEL_NUM) + cap->device_caps = cap_output; + else + cap->device_caps = cap_input; + cap->capabilities = cap_input | cap_output | V4L2_CAP_DEVICE_CAPS; return 0; } -int cx25821_vidioc_reqbufs(struct file *file, void *priv, +static int cx25821_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct cx25821_fh *fh = priv; - return videobuf_reqbufs(get_queue(fh), p); + struct cx25821_channel *chan = video_drvdata(file); + + return videobuf_reqbufs(&chan->vidq, p); } -int cx25821_vidioc_querybuf(struct file *file, void *priv, +static int cx25821_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx25821_fh *fh = priv; - return videobuf_querybuf(get_queue(fh), p); -} + struct cx25821_channel *chan = video_drvdata(file); -int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_fh *fh = priv; - return videobuf_qbuf(get_queue(fh), p); + return videobuf_querybuf(&chan->vidq, p); } -int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; - struct cx25821_fh *fh = f; + struct cx25821_channel *chan = video_drvdata(file); - *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio); - - return 0; + return videobuf_qbuf(&chan->vidq, p); } -int cx25821_vidioc_s_priority(struct file *file, void *f, - enum v4l2_priority prio) +static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms) { - struct cx25821_fh *fh = f; - struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + struct cx25821_channel *chan = video_drvdata(file); - return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio, - prio); + *tvnorms = chan->dev->tvnorm; + return 0; } -#ifdef TUNER_FLAG -int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) +static int cx25821_vidioc_s_std(struct file *file, void *priv, + v4l2_std_id tvnorms) { - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - dprintk(1, "%s()\n", __func__); - - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; - if (dev->tvnorm == *tvnorms) + if (dev->tvnorm == tvnorms) return 0; - mutex_lock(&dev->lock); - cx25821_set_tvnorm(dev, *tvnorms); - mutex_unlock(&dev->lock); + dev->tvnorm = tvnorms; + chan->width = 720; + chan->height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; medusa_set_videostandard(dev); return 0; } -#endif -int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) +static int cx25821_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) { - static const char * const iname[] = { - [CX25821_VMUX_COMPOSITE] = "Composite", - [CX25821_VMUX_SVIDEO] = "S-Video", - [CX25821_VMUX_DEBUG] = "for debug only", - }; - unsigned int n; - dprintk(1, "%s()\n", __func__); - - n = i->index; - if (n >= 2) - return -EINVAL; - - if (0 == INPUT(n)->type) + if (i->index) return -EINVAL; i->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(i->name, iname[INPUT(n)->type]); - i->std = CX25821_NORMS; + strcpy(i->name, "Composite"); return 0; } -int cx25821_vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - dprintk(1, "%s()\n", __func__); - return cx25821_enum_input(dev, i); -} - -int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - *i = dev->input; - dprintk(1, "%s(): returns %d\n", __func__, *i); - return 0; -} - -int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - dprintk(1, "%s(%d)\n", __func__, i); - - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } - - if (i >= CX25821_NR_INPUT) { - dprintk(1, "%s(): -EINVAL\n", __func__); - return -EINVAL; - } - - mutex_lock(&dev->lock); - cx25821_video_mux(dev, i); - mutex_unlock(&dev->lock); - return 0; -} - -#ifdef TUNER_FLAG -int cx25821_vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - f->frequency = dev->freq; - - cx25821_call_all(dev, tuner, g_frequency, f); - - return 0; -} - -int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) -{ - mutex_lock(&dev->lock); - dev->freq = f->frequency; - - cx25821_call_all(dev, tuner, s_frequency, f); - - /* When changing channels it is required to reset TVAUDIO */ - msleep(10); - - mutex_unlock(&dev->lock); - - return 0; -} - -int cx25821_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev; - int err; - - if (fh) { - dev = fh->dev; - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } else { - pr_err("Invalid fh pointer!\n"); - return -EINVAL; - } - - return cx25821_set_freq(dev, f); -} -#endif - -#ifdef CONFIG_VIDEO_ADV_DEBUG -int cx25821_vidioc_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - cx25821_call_all(dev, core, g_register, reg); - - return 0; -} - -int cx25821_vidioc_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) +static int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - cx25821_call_all(dev, core, s_register, reg); - + *i = 0; return 0; } -#endif - -#ifdef TUNER_FLAG -int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +static int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - - t->signal = 0xffff; /* LOCKED */ - return 0; + return i ? -EINVAL : 0; } -int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +static int cx25821_s_ctrl(struct v4l2_ctrl *ctrl) { - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct cx25821_fh *fh = priv; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; - } + struct cx25821_channel *chan = + container_of(ctrl->handler, struct cx25821_channel, hdl); + struct cx25821_dev *dev = chan->dev; - dprintk(1, "%s()\n", __func__); - if (UNSET == dev->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - return 0; -} - -#endif -/*****************************************************************************/ -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static struct v4l2_queryctrl cx25821_ctls[] = { - /* --- video --- */ - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 10000, - .step = 1, - .default_value = 6200, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 10000, - .step = 1, - .default_value = 5000, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 10000, - .step = 1, - .default_value = 5000, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = 0, - .maximum = 10000, - .step = 1, - .default_value = 5000, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; -static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls); - -static int cx25821_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 < CX25821_CTLS; i++) - if (cx25821_ctls[i].id == qctrl->id) - break; - if (i == CX25821_CTLS) { - *qctrl = no_ctl; - return 0; - } - *qctrl = cx25821_ctls[i]; - return 0; -} - -int cx25821_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - return cx25821_ctrl_query(qctrl); -} - -/* ------------------------------------------------------------------ */ -/* VIDEO CTRL IOCTLS */ - -static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) -{ - unsigned int i; - - for (i = 0; i < CX25821_CTLS; i++) - if (cx25821_ctls[i].id == id) - return cx25821_ctls + i; - return NULL; -} - -int cx25821_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct cx25821_fh *fh = priv; - - const struct v4l2_queryctrl *ctrl; - - ctrl = ctrl_by_id(ctl->id); - - if (NULL == ctrl) - return -EINVAL; - switch (ctl->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctl->value = dev->channels[fh->channel_id].ctl_bright; + medusa_set_brightness(dev, ctrl->val, chan->id); break; case V4L2_CID_HUE: - ctl->value = dev->channels[fh->channel_id].ctl_hue; + medusa_set_hue(dev, ctrl->val, chan->id); break; case V4L2_CID_CONTRAST: - ctl->value = dev->channels[fh->channel_id].ctl_contrast; + medusa_set_contrast(dev, ctrl->val, chan->id); break; case V4L2_CID_SATURATION: - ctl->value = dev->channels[fh->channel_id].ctl_saturation; - break; - } - return 0; -} - -int cx25821_set_control(struct cx25821_dev *dev, - struct v4l2_control *ctl, int chan_num) -{ - int err; - const struct v4l2_queryctrl *ctrl; - - err = -EINVAL; - - ctrl = ctrl_by_id(ctl->id); - - if (NULL == ctrl) - return err; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER: - if (ctl->value < ctrl->minimum) - ctl->value = ctrl->minimum; - if (ctl->value > ctrl->maximum) - ctl->value = ctrl->maximum; + medusa_set_saturation(dev, ctrl->val, chan->id); break; default: - /* nothing */ ; - } - - switch (ctl->id) { - case V4L2_CID_BRIGHTNESS: - dev->channels[chan_num].ctl_bright = ctl->value; - medusa_set_brightness(dev, ctl->value, chan_num); - break; - case V4L2_CID_HUE: - dev->channels[chan_num].ctl_hue = ctl->value; - medusa_set_hue(dev, ctl->value, chan_num); - break; - case V4L2_CID_CONTRAST: - dev->channels[chan_num].ctl_contrast = ctl->value; - medusa_set_contrast(dev, ctl->value, chan_num); - break; - case V4L2_CID_SATURATION: - dev->channels[chan_num].ctl_saturation = ctl->value; - medusa_set_saturation(dev, ctl->value, chan_num); - break; - } - - err = 0; - - return err; -} - -static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num) -{ - struct v4l2_control ctrl; - int i; - for (i = 0; i < CX25821_CTLS; i++) { - ctrl.id = cx25821_ctls[i].id; - ctrl.value = cx25821_ctls[i].default_value; - - cx25821_set_control(dev, &ctrl, chan_num); - } -} - -int cx25821_vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cropcap) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480; - cropcap->pixelaspect.numerator = - dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10; - cropcap->pixelaspect.denominator = - dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11; - cropcap->defrect = cropcap->bounds; - return 0; -} - -int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct cx25821_fh *fh = priv; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, - fh->prio); - if (0 != err) - return err; } - /* cx25821_vidioc_s_crop not supported */ - return -EINVAL; -} - -int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - /* cx25821_vidioc_g_crop not supported */ - return -EINVAL; + return 0; } -int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) +static int cx25821_vidioc_enum_output(struct file *file, void *priv, + struct v4l2_output *o) { - /* medusa does not support video standard sensing of current input */ - *norm = CX25821_NORMS; + if (o->index) + return -EINVAL; + o->type = V4L2_INPUT_TYPE_CAMERA; + o->std = CX25821_NORMS; + strcpy(o->name, "Composite"); return 0; } -int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm) +static int cx25821_vidioc_g_output(struct file *file, void *priv, unsigned int *o) { - if (tvnorm == V4L2_STD_PAL_BG) { - if (width == 352 || width == 720) - return 1; - else - return 0; - } - - if (tvnorm == V4L2_STD_NTSC_M) { - if (width == 320 || width == 352 || width == 720) - return 1; - else - return 0; - } + *o = 0; return 0; } -int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) +static int cx25821_vidioc_s_output(struct file *file, void *priv, unsigned int o) { - if (tvnorm == V4L2_STD_PAL_BG) { - if (height == 576 || height == 288) - return 1; - else - return 0; - } - - if (tvnorm == V4L2_STD_NTSC_M) { - if (height == 480 || height == 240) - return 1; - else - return 0; - } - - return 0; + return o ? -EINVAL : 0; } -static long video_ioctl_upstream9(struct file *file, unsigned int cmd, - unsigned long arg) +static int cx25821_vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; - - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); - return 0; - } - - command = data_from_user->command; - - if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) - return 0; - - dev->input_filename = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname = data_from_user->vid_stdname; - dev->pixel_format = data_from_user->pixel_format; - dev->channel_select = data_from_user->channel_select; - dev->command = data_from_user->command; - - switch (command) { - case UPSTREAM_START_VIDEO: - cx25821_start_upstream_video_ch1(dev, data_from_user); - break; - - case UPSTREAM_STOP_VIDEO: - cx25821_stop_upstream_video_ch1(dev); - break; - } + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + const struct cx25821_fmt *fmt; + fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + f->fmt.pix.width = 720; + f->fmt.pix.height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + 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; + f->fmt.pix.priv = 0; return 0; } -static long video_ioctl_upstream10(struct file *file, unsigned int cmd, - unsigned long arg) +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; - - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); - return 0; - } - - command = data_from_user->command; - - if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) - return 0; - - dev->input_filename_ch2 = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname_ch2 = data_from_user->vid_stdname; - dev->pixel_format_ch2 = data_from_user->pixel_format; - dev->channel_select_ch2 = data_from_user->channel_select; - dev->command_ch2 = data_from_user->command; + struct cx25821_channel *chan = video_drvdata(file); + int err; - switch (command) { - case UPSTREAM_START_VIDEO: - cx25821_start_upstream_video_ch2(dev, data_from_user); - break; + err = cx25821_vidioc_try_fmt_vid_out(file, priv, f); - case UPSTREAM_STOP_VIDEO: - cx25821_stop_upstream_video_ch2(dev); - break; - } + if (0 != err) + return err; + chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + chan->vidq.field = f->fmt.pix.field; + chan->width = f->fmt.pix.width; + chan->height = f->fmt.pix.height; + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + chan->pixel_formats = PIXEL_FRMT_411; + else + chan->pixel_formats = PIXEL_FRMT_422; return 0; } -static long video_ioctl_upstream11(struct file *file, unsigned int cmd, - unsigned long arg) +static ssize_t video_write(struct file *file, const char __user *data, size_t count, + loff_t *ppos) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + struct v4l2_fh *fh = file->private_data; + int err = 0; - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); - return 0; + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (chan->streaming_fh && chan->streaming_fh != fh) { + err = -EBUSY; + goto unlock; } - - command = data_from_user->command; - - if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO) - return 0; - - dev->input_filename = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname = data_from_user->vid_stdname; - dev->pixel_format = data_from_user->pixel_format; - dev->channel_select = data_from_user->channel_select; - dev->command = data_from_user->command; - - switch (command) { - case UPSTREAM_START_AUDIO: - cx25821_start_upstream_audio(dev, data_from_user); - break; - - case UPSTREAM_STOP_AUDIO: - cx25821_stop_upstream_audio(dev); - break; + if (!chan->streaming_fh) { + err = cx25821_vidupstream_init(chan, chan->pixel_formats); + if (err) + goto unlock; + chan->streaming_fh = fh; } - return 0; + err = cx25821_write_frame(chan, data, count); + count -= err; + *ppos += err; + +unlock: + mutex_unlock(&dev->lock); + return err; } -static long video_ioctl_set(struct file *file, unsigned int cmd, - unsigned long arg) +static int video_out_release(struct file *file) { - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - struct downstream_user_struct *data_from_user; - int command; - int width = 720; - int selected_channel = 0; - int pix_format = 0; - int i = 0; - int cif_enable = 0; - int cif_width = 0; - - data_from_user = (struct downstream_user_struct *)arg; - - if (!data_from_user) { - pr_err("%s(): User data is INVALID. Returning\n", __func__); - return 0; - } - - command = data_from_user->command; - - if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT - && command != ENABLE_CIF_RESOLUTION && command != REG_READ - && command != REG_WRITE && command != MEDUSA_READ - && command != MEDUSA_WRITE) { - return 0; - } - - switch (command) { - case SET_VIDEO_STD: - if (!strcmp(data_from_user->vid_stdname, "PAL")) - dev->tvnorm = V4L2_STD_PAL_BG; - else - dev->tvnorm = V4L2_STD_NTSC_M; - medusa_set_videostandard(dev); - break; - - case SET_PIXEL_FORMAT: - selected_channel = data_from_user->decoder_select; - pix_format = data_from_user->pixel_format; - - if (!(selected_channel <= 7 && selected_channel >= 0)) { - selected_channel -= 4; - selected_channel = selected_channel % 8; - } - - if (selected_channel >= 0) - cx25821_set_pixel_format(dev, selected_channel, - pix_format); - - break; - - case ENABLE_CIF_RESOLUTION: - selected_channel = data_from_user->decoder_select; - cif_enable = data_from_user->cif_resolution_enable; - cif_width = data_from_user->cif_width; - - if (cif_enable) { - if (dev->tvnorm & V4L2_STD_PAL_BG - || dev->tvnorm & V4L2_STD_PAL_DK) { - width = 352; - } else { - width = cif_width; - if (cif_width != 320 && cif_width != 352) - width = 320; - } - } - - if (!(selected_channel <= 7 && selected_channel >= 0)) { - selected_channel -= 4; - selected_channel = selected_channel % 8; - } - - if (selected_channel <= 7 && selected_channel >= 0) { - dev->channels[selected_channel].use_cif_resolution = - cif_enable; - dev->channels[selected_channel].cif_width = width; - } else { - for (i = 0; i < VID_CHANNEL_NUM; i++) { - dev->channels[i].use_cif_resolution = - cif_enable; - dev->channels[i].cif_width = width; - } - } + struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_dev *dev = chan->dev; + struct v4l2_fh *fh = file->private_data; - medusa_set_resolution(dev, width, selected_channel); - break; - case REG_READ: - data_from_user->reg_data = cx_read(data_from_user->reg_address); - break; - case REG_WRITE: - cx_write(data_from_user->reg_address, data_from_user->reg_data); - break; - case MEDUSA_READ: - cx25821_i2c_read(&dev->i2c_bus[0], - (u16) data_from_user->reg_address, - &data_from_user->reg_data); - break; - case MEDUSA_WRITE: - cx25821_i2c_write(&dev->i2c_bus[0], - (u16) data_from_user->reg_address, - data_from_user->reg_data); - break; + mutex_lock(&dev->lock); + if (chan->streaming_fh == fh) { + cx25821_stop_upstream_video(chan); + chan->streaming_fh = NULL; } + mutex_unlock(&dev->lock); - return 0; + return v4l2_fh_release(file); } -static long cx25821_video_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = 0; - - struct cx25821_fh *fh = file->private_data; - - /* check to see if it's the video upstream */ - if (fh->channel_id == SRAM_CH09) { - ret = video_ioctl_upstream9(file, cmd, arg); - return ret; - } else if (fh->channel_id == SRAM_CH10) { - ret = video_ioctl_upstream10(file, cmd, arg); - return ret; - } else if (fh->channel_id == SRAM_CH11) { - ret = video_ioctl_upstream11(file, cmd, arg); - ret = video_ioctl_set(file, cmd, arg); - return ret; - } - - return video_ioctl2(file, cmd, arg); -} +static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { + .s_ctrl = cx25821_s_ctrl, +}; -/* exported stuff */ static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, - .open = video_open, + .open = v4l2_fh_open, .release = video_release, .read = video_read, .poll = video_poll, .mmap = cx25821_video_mmap, - .ioctl = cx25821_video_ioctl, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -1951,40 +963,170 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = cx25821_vidioc_querybuf, .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG + .vidioc_g_std = cx25821_vidioc_g_std, .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, .vidioc_enum_input = cx25821_vidioc_enum_input, .vidioc_g_input = cx25821_vidioc_g_input, .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -struct video_device cx25821_videoioctl_template = { - .name = "cx25821-videoioctl", +static const struct video_device cx25821_video_device = { + .name = "cx25821-video", .fops = &video_fops, + .release = video_device_release_empty, + .minor = -1, .ioctl_ops = &video_ioctl_ops, .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, }; + +static const struct v4l2_file_operations video_out_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .write = video_write, + .release = video_out_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_out_ioctl_ops = { + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_out = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_out = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_out = cx25821_vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_g_std = cx25821_vidioc_g_std, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_enum_output = cx25821_vidioc_enum_output, + .vidioc_g_output = cx25821_vidioc_g_output, + .vidioc_s_output = cx25821_vidioc_s_output, + .vidioc_log_status = vidioc_log_status, +}; + +static const struct video_device cx25821_video_out_device = { + .name = "cx25821-video", + .fops = &video_out_fops, + .release = video_device_release_empty, + .minor = -1, + .ioctl_ops = &video_out_ioctl_ops, + .tvnorms = CX25821_NORMS, +}; + +void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) +{ + cx_clear(PCI_INT_MSK, 1); + + if (video_is_registered(&dev->channels[chan_num].vdev)) { + video_unregister_device(&dev->channels[chan_num].vdev); + v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); + + btcx_riscmem_free(dev->pci, + &dev->channels[chan_num].dma_vidq.stopper); + } +} + +int cx25821_video_register(struct cx25821_dev *dev) +{ + int err; + int i; + + /* initial device configuration */ + dev->tvnorm = V4L2_STD_NTSC_M; + + spin_lock_init(&dev->slock); + + for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + struct cx25821_channel *chan = &dev->channels[i]; + struct video_device *vdev = &chan->vdev; + struct v4l2_ctrl_handler *hdl = &chan->hdl; + bool is_output = i > SRAM_CH08; + + if (i == SRAM_CH08) /* audio channel */ + continue; + + if (!is_output) { + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 10000, 1, 6200); + v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, + V4L2_CID_CONTRAST, 0, 10000, 1, 5000); + v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, + V4L2_CID_SATURATION, 0, 10000, 1, 5000); + v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, + V4L2_CID_HUE, 0, 10000, 1, 5000); + if (hdl->error) { + err = hdl->error; + goto fail_unreg; + } + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto fail_unreg; + } else { + chan->out = &dev->vid_out_data[i - SRAM_CH09]; + chan->out->chan = chan; + } + + cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper, + chan->sram_channels->dma_ctl, 0x11, 0); + + chan->sram_channels = &cx25821_sram_channels[i]; + chan->width = 720; + if (dev->tvnorm & V4L2_STD_625_50) + chan->height = 576; + else + chan->height = 480; + + if (chan->pixel_formats == PIXEL_FRMT_411) + chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_Y41P); + else + chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_YUYV); + + cx_write(chan->sram_channels->int_stat, 0xffffffff); + + INIT_LIST_HEAD(&chan->dma_vidq.active); + INIT_LIST_HEAD(&chan->dma_vidq.queued); + + chan->timeout_data.dev = dev; + chan->timeout_data.channel = &cx25821_sram_channels[i]; + chan->dma_vidq.timeout.function = cx25821_vid_timeout; + chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data; + init_timer(&chan->dma_vidq.timeout); + + if (!is_output) + videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev, + &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), + chan, &dev->lock); + + /* register v4l devices */ + *vdev = is_output ? cx25821_video_out_device : cx25821_video_device; + vdev->v4l2_dev = &dev->v4l2_dev; + if (!is_output) + vdev->ctrl_handler = hdl; + else + vdev->vfl_dir = VFL_DIR_TX; + vdev->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); + snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); + video_set_drvdata(vdev, chan); + + err = video_register_device(vdev, VFL_TYPE_GRABBER, + video_nr[dev->nr]); + + if (err < 0) + goto fail_unreg; + } + + /* set PCI interrupt */ + cx_set(PCI_INT_MSK, 0xff); + + return 0; + +fail_unreg: + while (i >= 0) + cx25821_video_unregister(dev, i--); + return err; +} diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h index c265e35..ab63b38 100644 --- a/drivers/media/pci/cx25821/cx25821-video.h +++ b/drivers/media/pci/cx25821/cx25821-video.h @@ -39,8 +39,7 @@ #include "cx25821.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> - -#define TUNER_FLAG +#include <media/v4l2-event.h> #define VIDEO_DEBUG 0 @@ -50,137 +49,17 @@ do { \ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \ } while (0) -/* For IOCTL to identify running upstream */ -#define UPSTREAM_START_VIDEO 700 -#define UPSTREAM_STOP_VIDEO 701 -#define UPSTREAM_START_AUDIO 702 -#define UPSTREAM_STOP_AUDIO 703 -#define UPSTREAM_DUMP_REGISTERS 702 -#define SET_VIDEO_STD 800 -#define SET_PIXEL_FORMAT 1000 -#define ENABLE_CIF_RESOLUTION 1001 - -#define REG_READ 900 -#define REG_WRITE 901 -#define MEDUSA_READ 910 -#define MEDUSA_WRITE 911 - -extern struct sram_channel *channel0; -extern struct sram_channel *channel1; -extern struct sram_channel *channel2; -extern struct sram_channel *channel3; -extern struct sram_channel *channel4; -extern struct sram_channel *channel5; -extern struct sram_channel *channel6; -extern struct sram_channel *channel7; -extern struct sram_channel *channel9; -extern struct sram_channel *channel10; -extern struct sram_channel *channel11; -extern struct video_device cx25821_videoioctl_template; -/* extern const u32 *ctrl_classes[]; */ - -extern unsigned int vid_limit; - #define FORMAT_FLAGS_PACKED 0x01 -extern struct cx25821_fmt formats[]; -extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); -extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; - extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count); -#ifdef TUNER_FLAG -extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); -#endif - -extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, - unsigned int bit); -extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit); -extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit); -extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, - unsigned int bits); -extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); extern int cx25821_start_video_dma(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, struct cx25821_buffer *buf, - struct sram_channel *channel); + const struct sram_channel *channel); -extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, - unsigned int height, enum v4l2_field field); extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); extern int cx25821_video_register(struct cx25821_dev *dev); -extern int cx25821_get_format_size(void); - -extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size); -extern int cx25821_buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, - enum v4l2_field field); -extern void cx25821_buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb); -extern struct videobuf_queue *get_queue(struct cx25821_fh *fh); -extern int cx25821_get_resource(struct cx25821_fh *fh, int resource); -extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma); -extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f); -extern int cx25821_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap); -extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f); -extern int cx25821_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p); -extern int cx25821_vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p); -extern int cx25821_vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p); -extern int cx25821_vidioc_s_std(struct file *file, void *priv, - v4l2_std_id *tvnorms); -extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i); -extern int cx25821_vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i); -extern int cx25821_vidioc_g_input(struct file *file, void *priv, - unsigned int *i); -extern int cx25821_vidioc_s_input(struct file *file, void *priv, - unsigned int i); -extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl); -extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f); -extern int cx25821_vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f); -extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f); -extern int cx25821_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f); -extern int cx25821_vidioc_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg); -extern int cx25821_vidioc_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg); -extern int cx25821_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t); -extern int cx25821_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t); - -extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm); -extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm); - -extern int cx25821_vidioc_g_priority(struct file *file, void *f, - enum v4l2_priority *p); -extern int cx25821_vidioc_s_priority(struct file *file, void *f, - enum v4l2_priority prio); - -extern int cx25821_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl); -extern int cx25821_set_control(struct cx25821_dev *dev, - struct v4l2_control *ctrl, int chan_num); - -extern int cx25821_vidioc_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cropcap); -extern int cx25821_vidioc_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop); -extern int cx25821_vidioc_g_crop(struct file *file, void *priv, - struct v4l2_crop *crop); -extern int cx25821_vidioc_querystd(struct file *file, void *priv, - v4l2_std_id *norm); #endif diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 8a9c0c8..90bdc19 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -33,17 +33,14 @@ #include <media/v4l2-common.h> #include <media/v4l2-device.h> -#include <media/tuner.h> -#include <media/tveeprom.h> +#include <media/v4l2-ctrls.h> #include <media/videobuf-dma-sg.h> -#include <media/videobuf-dvb.h> #include "btcx-risc.h" #include "cx25821-reg.h" #include "cx25821-medusa-reg.h" #include "cx25821-sram.h" #include "cx25821-audio.h" -#include "media/cx2341x.h" #include <linux/version.h> #include <linux/mutex.h> @@ -55,8 +52,6 @@ #define CX25821_MAXBOARDS 2 -#define TRUE 1 -#define FALSE 0 #define LINE_SIZE_D1 1440 /* Number of decoders and encoders */ @@ -67,7 +62,6 @@ /* Max number of inputs by card */ #define MAX_CX25821_INPUT 8 -#define INPUT(nr) (&cx25821_boards[dev->board].input[nr]) #define RESOURCE_VIDEO0 1 #define RESOURCE_VIDEO1 2 #define RESOURCE_VIDEO2 4 @@ -80,7 +74,6 @@ #define RESOURCE_VIDEO9 512 #define RESOURCE_VIDEO10 1024 #define RESOURCE_VIDEO11 2048 -#define RESOURCE_VIDEO_IOCTL 4096 #define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ @@ -97,7 +90,6 @@ #define CX25821_BOARD_CONEXANT_ATHENA10 1 #define MAX_VID_CHANNEL_NUM 12 #define VID_CHANNEL_NUM 8 -#define CX25821_NR_INPUT 2 struct cx25821_fmt { char *name; @@ -107,14 +99,6 @@ struct cx25821_fmt { u32 cxformat; }; -struct cx25821_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 mask; - u32 shift; -}; - struct cx25821_tvnorm { char *name; v4l2_std_id id; @@ -122,40 +106,6 @@ struct cx25821_tvnorm { u32 cxoformat; }; -struct cx25821_fh { - struct cx25821_dev *dev; - enum v4l2_buf_type type; - int radio; - u32 resources; - - enum v4l2_priority prio; - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - - /* video capture */ - struct cx25821_fmt *fmt; - unsigned int width, height; - int channel_id; - - /* vbi capture */ - struct videobuf_queue vidq; - struct videobuf_queue vbiq; - - /* H264 Encoder specifics ONLY */ - struct videobuf_queue mpegq; - atomic_t v4l_reading; -}; - -enum cx25821_itype { - CX25821_VMUX_COMPOSITE = 1, - CX25821_VMUX_SVIDEO, - CX25821_VMUX_DEBUG, - CX25821_RADIO, -}; - enum cx25821_src_sel_type { CX25821_SRC_SEL_EXT_656_VIDEO = 0, CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO @@ -169,16 +119,10 @@ struct cx25821_buffer { /* cx25821 specific */ unsigned int bpl; struct btcx_riscmem risc; - struct cx25821_fmt *fmt; + const struct cx25821_fmt *fmt; u32 count; }; -struct cx25821_input { - enum cx25821_itype type; - unsigned int vmux; - u32 gpio0, gpio1, gpio2, gpio3; -}; - enum port { CX25821_UNDEFINED = 0, CX25821_RAW, @@ -190,19 +134,8 @@ struct cx25821_board { enum port porta; enum port portb; enum port portc; - unsigned int tuner_type; - unsigned int radio_type; - unsigned char tuner_addr; - unsigned char radio_addr; u32 clk_freq; - struct cx25821_input input[CX25821_NR_INPUT]; -}; - -struct cx25821_subid { - u16 subvendor; - u16 subdevice; - u32 card; }; struct cx25821_i2c { @@ -234,34 +167,70 @@ struct cx25821_dmaqueue { struct cx25821_data { struct cx25821_dev *dev; - struct sram_channel *channel; + const struct sram_channel *channel; +}; + +struct cx25821_dev; + +struct cx25821_channel; + +struct cx25821_video_out_data { + struct cx25821_channel *chan; + int _line_size; + int _prog_cnt; + int _pixel_format; + int _is_first_frame; + int _is_running; + int _file_status; + int _lines_count; + int _frame_count; + unsigned int _risc_size; + + __le32 *_dma_virt_start_addr; + __le32 *_dma_virt_addr; + dma_addr_t _dma_phys_addr; + dma_addr_t _dma_phys_start_addr; + + unsigned int _data_buf_size; + __le32 *_data_buf_virt_addr; + dma_addr_t _data_buf_phys_addr; + + u32 upstream_riscbuf_size; + u32 upstream_databuf_size; + int is_60hz; + int _frame_index; + int cur_frame_index; + int curpos; + wait_queue_head_t waitq; }; struct cx25821_channel { - struct v4l2_prio_state prio; + unsigned id; + struct cx25821_dev *dev; + struct v4l2_fh *streaming_fh; - int ctl_bright; - int ctl_contrast; - int ctl_hue; - int ctl_saturation; + struct v4l2_ctrl_handler hdl; struct cx25821_data timeout_data; - struct video_device *video_dev; - struct cx25821_dmaqueue vidq; - - struct sram_channel *sram_channels; + struct video_device vdev; + struct cx25821_dmaqueue dma_vidq; + struct videobuf_queue vidq; - struct mutex lock; - int resources; + const struct sram_channel *sram_channels; + const struct cx25821_fmt *fmt; + unsigned int width, height; int pixel_formats; int use_cif_resolution; int cif_width; + + /* video output data for the video output channel */ + struct cx25821_video_out_data *out; }; +struct snd_card; + struct cx25821_dev { - struct list_head devlist; - atomic_t refcount; struct v4l2_device v4l2_dev; /* pci stuff */ @@ -273,6 +242,8 @@ struct cx25821_dev { u8 __iomem *bmmio; int pci_irqmask; int hwrevision; + /* used by cx25821-alsa */ + struct snd_card *card; u32 clk_freq; @@ -289,17 +260,8 @@ struct cx25821_dev { char name[32]; /* Analog video */ - u32 resources; unsigned int input; - u32 tvaudio; v4l2_std_id tvnorm; - unsigned int tuner_type; - unsigned char tuner_addr; - unsigned int radio_type; - unsigned char radio_addr; - unsigned int has_radio; - unsigned int videc_type; - unsigned char videc_addr; unsigned short _max_num_decoders; /* Analog Audio Upstream */ @@ -323,132 +285,26 @@ struct cx25821_dev { __le32 *_audiodata_buf_virt_addr; dma_addr_t _audiodata_buf_phys_addr; char *_audiofilename; - - /* V4l */ - u32 freq; - struct video_device *vbi_dev; - struct video_device *radio_dev; - struct video_device *ioctl_dev; - - spinlock_t slock; - - /* Video Upstream */ - int _line_size; - int _prog_cnt; - int _pixel_format; - int _is_first_frame; - int _is_running; - int _file_status; - int _lines_count; - int _frame_count; - int _channel_upstream_select; - unsigned int _risc_size; - - __le32 *_dma_virt_start_addr; - __le32 *_dma_virt_addr; - dma_addr_t _dma_phys_addr; - dma_addr_t _dma_phys_start_addr; - - unsigned int _data_buf_size; - __le32 *_data_buf_virt_addr; - dma_addr_t _data_buf_phys_addr; - char *_filename; - char *_defaultname; - - int _line_size_ch2; - int _prog_cnt_ch2; - int _pixel_format_ch2; - int _is_first_frame_ch2; - int _is_running_ch2; - int _file_status_ch2; - int _lines_count_ch2; - int _frame_count_ch2; - int _channel2_upstream_select; - unsigned int _risc_size_ch2; - - __le32 *_dma_virt_start_addr_ch2; - __le32 *_dma_virt_addr_ch2; - dma_addr_t _dma_phys_addr_ch2; - dma_addr_t _dma_phys_start_addr_ch2; - - unsigned int _data_buf_size_ch2; - __le32 *_data_buf_virt_addr_ch2; - dma_addr_t _data_buf_phys_addr_ch2; - char *_filename_ch2; - char *_defaultname_ch2; - - /* MPEG Encoder ONLY settings */ - u32 cx23417_mailbox; - struct cx2341x_mpeg_params mpeg_params; - struct video_device *v4l_device; - atomic_t v4l_reader_count; - struct cx25821_tvnorm encodernorm; - - u32 upstream_riscbuf_size; - u32 upstream_databuf_size; - u32 upstream_riscbuf_size_ch2; - u32 upstream_databuf_size_ch2; u32 audio_upstream_riscbuf_size; u32 audio_upstream_databuf_size; - int _isNTSC; - int _frame_index; int _audioframe_index; - struct workqueue_struct *_irq_queues; - struct work_struct _irq_work_entry; - struct workqueue_struct *_irq_queues_ch2; - struct work_struct _irq_work_entry_ch2; struct workqueue_struct *_irq_audio_queues; struct work_struct _audio_work_entry; - char *input_filename; - char *input_filename_ch2; - int _frame_index_ch2; - int _isNTSC_ch2; - char *vid_stdname_ch2; - int pixel_format_ch2; - int channel_select_ch2; - int command_ch2; char *input_audiofilename; - char *vid_stdname; - int pixel_format; - int channel_select; - int command; - int channel_opened; -}; -struct upstream_user_struct { - char *input_filename; - char *vid_stdname; - int pixel_format; - int channel_select; - int command; -}; + /* V4l */ + spinlock_t slock; -struct downstream_user_struct { - char *vid_stdname; - int pixel_format; - int cif_resolution_enable; - int cif_width; - int decoder_select; - int command; - int reg_address; - int reg_data; + /* Video Upstream */ + struct cx25821_video_out_data vid_out_data[2]; }; -extern struct upstream_user_struct *up_data; - static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) { return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev); } -#define cx25821_call_all(dev, o, f, args...) \ - v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) - -extern struct list_head cx25821_devlist; -extern struct mutex cx25821_devlist_mutex; - extern struct cx25821_board cx25821_boards[]; -extern struct cx25821_subid cx25821_subids[]; #define SRAM_CH00 0 /* Video A */ #define SRAM_CH01 1 /* Video B */ @@ -467,7 +323,6 @@ extern struct cx25821_subid cx25821_subids[]; #define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09 #define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10 #define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11 -#define VIDEO_IOCTL_CH 11 struct sram_channel { char *name; @@ -503,10 +358,8 @@ struct sram_channel { u32 jumponly; u32 irq_bit; }; -extern struct sram_channel cx25821_sram_channels[]; -#define STATUS_SUCCESS 0 -#define STATUS_UNSUCCESSFUL -1 +extern const struct sram_channel cx25821_sram_channels[]; #define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) #define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2)) @@ -529,8 +382,6 @@ extern struct sram_channel cx25821_sram_channels[]; pr_info("(%d): " fmt, dev->board, ##args) extern int cx25821_i2c_register(struct cx25821_i2c *bus); -extern void cx25821_card_setup(struct cx25821_dev *dev); -extern int cx25821_ir_init(struct cx25821_dev *dev); extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value); extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value); extern int cx25821_i2c_unregister(struct cx25821_i2c *bus); @@ -551,7 +402,7 @@ extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder); extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, - struct sram_channel *ch, unsigned int bpl, + const struct sram_channel *ch, unsigned int bpl, u32 risc); extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, @@ -570,46 +421,31 @@ extern void cx25821_free_buffer(struct videobuf_queue *q, extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value); extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, - struct sram_channel *ch); + const struct sram_channel *ch); extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, - struct sram_channel *ch); + const struct sram_channel *ch); extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci); extern void cx25821_print_irqbits(char *name, char *tag, char **strings, int len, u32 bits, u32 mask); extern void cx25821_dev_unregister(struct cx25821_dev *dev); extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc); -extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, - int channel_select, int pixel_format); -extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, - int channel_select, int pixel_format); +extern int cx25821_vidupstream_init(struct cx25821_channel *chan, int pixel_format); extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select); -extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev); -extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev); +extern int cx25821_write_frame(struct cx25821_channel *chan, + const char __user *data, size_t count); +extern void cx25821_free_mem_upstream(struct cx25821_channel *chan); extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); -extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, - struct upstream_user_struct - *up_data); -extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, - struct upstream_user_struct - *up_data); -extern void cx25821_start_upstream_audio(struct cx25821_dev *dev, - struct upstream_user_struct *up_data); -extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev); -extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev); +extern void cx25821_stop_upstream_video(struct cx25821_channel *chan); extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - struct sram_channel *ch, + const struct sram_channel *ch, unsigned int bpl, u32 risc); extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, u32 format); -extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev); -extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, - struct pci_dev *pci, - struct video_device *template, - char *type); + #endif diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index a6ff8a6..150bb76 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -815,7 +815,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) } static int vidioc_s_frequency (struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct cx8802_fh *fh = priv; struct cx8802_dev *dev = fh->dev; @@ -918,7 +918,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, } static int vidioc_s_tuner (struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; @@ -939,12 +939,12 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) return 0; } -static int vidioc_s_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 cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; mutex_lock(&core->lock); - cx88_set_tvnorm(core,*id); + cx88_set_tvnorm(core, id); mutex_unlock(&core->lock); return 0; } diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index e2e0b8f..a87a0e1 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -59,6 +59,11 @@ MODULE_PARM_DESC(disable_ir, "Disable IR support"); #define err_printk(core, fmt, arg...) \ printk(KERN_ERR "%s: " fmt, core->name , ## arg) +#define dprintk(level,fmt, arg...) do { \ + if (cx88_core_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, core->name , ## arg); \ + } while(0) + /* ------------------------------------------------------------------ */ /* board config info */ @@ -2855,6 +2860,7 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) core->board.tuner_type = tv.tuner_type; core->tuner_formats = tv.tuner_formats; core->board.radio.type = tv.has_radio ? CX88_RADIO : 0; + core->model = tv.model; /* Make sure we support the board model */ switch (tv.model) @@ -3133,7 +3139,7 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, case XC2028_TUNER_RESET: switch (INPUT(core->input).type) { case CX88_RADIO: - info_printk(core, "setting GPIO to radio!\n"); + dprintk(1, "setting GPIO to radio!\n"); cx_write(MO_GP0_IO, 0x4ff); mdelay(250); cx_write(MO_GP2_IO, 0xff); @@ -3141,7 +3147,7 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, break; case CX88_VMUX_DVB: /* Digital TV*/ default: /* Analog TV */ - info_printk(core, "setting GPIO to TV!\n"); + dprintk(1, "setting GPIO to TV!\n"); break; } cx_write(MO_GP1_IO, 0x101010); @@ -3199,8 +3205,7 @@ static int cx88_xc5000_tuner_callback(struct cx88_core *core, not having any tuning at all. */ return 0; } else { - err_printk(core, "xc5000: unknown tuner " - "callback command.\n"); + dprintk(1, "xc5000: unknown tuner callback command.\n"); return -EINVAL; } break; @@ -3211,8 +3216,7 @@ static int cx88_xc5000_tuner_callback(struct cx88_core *core, cx_set(MO_GP0_IO, 0x00000010); return 0; } else { - printk(KERN_ERR - "xc5000: unknown tuner callback command.\n"); + dprintk(1, "xc5000: unknown tuner callback command.\n"); return -EINVAL; } break; @@ -3242,13 +3246,13 @@ int cx88_tuner_callback(void *priv, int component, int command, int arg) switch (core->board.tuner_type) { case TUNER_XC2028: - info_printk(core, "Calling XC2028/3028 callback\n"); + dprintk(1, "Calling XC2028/3028 callback\n"); return cx88_xc2028_tuner_callback(core, command, arg); case TUNER_XC4000: - info_printk(core, "Calling XC4000 callback\n"); + dprintk(1, "Calling XC4000 callback\n"); return cx88_xc4000_tuner_callback(core, command, arg); case TUNER_XC5000: - info_printk(core, "Calling XC5000 callback\n"); + dprintk(1, "Calling XC5000 callback\n"); return cx88_xc5000_tuner_callback(core, command, arg); } err_printk(core, "Error: Calling callback for tuner %d\n", @@ -3589,8 +3593,8 @@ static void cx88_card_setup(struct cx88_core *core) memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; - info_printk(core, "Asking xc2028/3028 to load firmware %s\n", - ctl.fname); + dprintk(1, "Asking xc2028/3028 to load firmware %s\n", + ctl.fname); call_all(core, tuner, s_config, &xc2028_cfg); } call_all(core, core, s_power, 0); @@ -3759,8 +3763,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) if (radio[core->nr] != UNSET) core->board.radio_type = radio[core->nr]; - info_printk(core, "TV tuner type %d, Radio tuner type %d\n", - core->board.tuner_type, core->board.radio_type); + dprintk(1, "TV tuner type %d, Radio tuner type %d\n", + core->board.tuner_type, core->board.radio_type); /* init hardware */ cx88_reset(core); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 39f095c..c8f3dcc 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -48,9 +48,9 @@ MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ -static unsigned int core_debug; -module_param(core_debug,int,0644); -MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); +unsigned int cx88_core_debug; +module_param_named(core_debug, cx88_core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); static unsigned int nicam; module_param(nicam,int,0644); @@ -60,8 +60,10 @@ static unsigned int nocomb; module_param(nocomb,int,0644); MODULE_PARM_DESC(nocomb,"disable comb filter"); -#define dprintk(level,fmt, arg...) if (core_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) +#define dprintk(level,fmt, arg...) do { \ + if (cx88_core_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, core->name , ## arg); \ + } while(0) static unsigned int cx88_devcount; static LIST_HEAD(cx88_devlist); diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 672b267..053ed1b 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1042,7 +1042,7 @@ static int dvb_register(struct cx8802_dev *dev) if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->core->i2c_adap, - 0x08, ISL6421_DCL, 0x00)) + 0x08, ISL6421_DCL, 0x00, false)) goto frontend_detach; } /* MFE frontend 2 */ @@ -1279,8 +1279,16 @@ static int dvb_register(struct cx8802_dev *dev) &hauppauge_novas_config, &core->i2c_adap); if (fe0->dvb.frontend) { + bool override_tone; + + if (core->model == 92001) + override_tone = true; + else + override_tone = false; + if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, - &core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) + &core->i2c_adap, 0x08, ISL6421_DCL, 0x00, + override_tone)) goto frontend_detach; } break; @@ -1403,7 +1411,7 @@ static int dvb_register(struct cx8802_dev *dev) if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->core->i2c_adap, - 0x08, ISL6421_DCL, 0x00)) + 0x08, ISL6421_DCL, 0x00, false)) goto frontend_detach; } /* MFE frontend 2 */ @@ -1431,7 +1439,7 @@ static int dvb_register(struct cx8802_dev *dev) if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->core->i2c_adap, - 0x08, ISL6421_DCL, 0x00)) + 0x08, ISL6421_DCL, 0x00, false)) goto frontend_detach; } break; diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index c9d3182..2d3507e 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -532,16 +532,17 @@ static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) { struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; + unsigned long flags; /* stop mpeg dma */ - spin_lock(&dev->slock); + spin_lock_irqsave(&dev->slock, flags); if (!list_empty(&dev->mpegq.active)) { dprintk( 2, "suspend\n" ); printk("%s: suspend mpeg\n", core->name); cx8802_stop_dma(dev); del_timer(&dev->mpegq.timeout); } - spin_unlock(&dev->slock); + spin_unlock_irqrestore(&dev->slock, flags); /* FIXME -- shutdown device */ cx88_shutdown(dev->core); @@ -558,6 +559,7 @@ static int cx8802_resume_common(struct pci_dev *pci_dev) { struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; + unsigned long flags; int err; if (dev->state.disabled) { @@ -584,12 +586,12 @@ static int cx8802_resume_common(struct pci_dev *pci_dev) cx88_reset(dev->core); /* restart video+vbi capture */ - spin_lock(&dev->slock); + spin_lock_irqsave(&dev->slock, flags); if (!list_empty(&dev->mpegq.active)) { printk("%s: resume mpeg\n", core->name); cx8802_restart_queue(dev,&dev->mpegq); } - spin_unlock(&dev->slock); + spin_unlock_irqrestore(&dev->slock, flags); return 0; } diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index bc78354..1b00615 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1193,12 +1193,12 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) return 0; } -static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms) { struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; mutex_lock(&core->lock); - cx88_set_tvnorm(core,*tvnorms); + cx88_set_tvnorm(core, tvnorms); mutex_unlock(&core->lock); return 0; @@ -1289,7 +1289,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, } static int vidioc_s_tuner (struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; @@ -1321,8 +1321,10 @@ static int vidioc_g_frequency (struct file *file, void *priv, } int cx88_set_freq (struct cx88_core *core, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { + struct v4l2_frequency new_freq = *f; + if (unlikely(UNSET == core->board.tuner_type)) return -EINVAL; if (unlikely(f->tuner != 0)) @@ -1331,8 +1333,8 @@ int cx88_set_freq (struct cx88_core *core, mutex_lock(&core->lock); cx88_newstation(core); call_all(core, tuner, s_frequency, f); - call_all(core, tuner, g_frequency, f); - core->freq = f->frequency; + call_all(core, tuner, g_frequency, &new_freq); + core->freq = new_freq.frequency; /* When changing channels it is required to reset TVAUDIO */ msleep (10); @@ -1345,7 +1347,7 @@ int cx88_set_freq (struct cx88_core *core, EXPORT_SYMBOL(cx88_set_freq); static int vidioc_s_frequency (struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct cx8800_fh *fh = priv; struct cx88_core *core = fh->dev->core; @@ -1378,7 +1380,7 @@ static int vidioc_g_register (struct file *file, void *fh, } static int vidioc_s_register (struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; @@ -1407,20 +1409,15 @@ static int radio_g_tuner (struct file *file, void *priv, return 0; } -/* FIXME: Should add a standard for radio */ - static int radio_s_tuner (struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; if (0 != t->index) return -EINVAL; - if (t->audmode > V4L2_TUNER_MODE_STEREO) - t->audmode = V4L2_TUNER_MODE_STEREO; call_all(core, tuner, s_tuner, t); - return 0; } @@ -1957,9 +1954,10 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; + unsigned long flags; /* stop video+vbi capture */ - spin_lock(&dev->slock); + spin_lock_irqsave(&dev->slock, flags); if (!list_empty(&dev->vidq.active)) { printk("%s/0: suspend video\n", core->name); stop_video_dma(dev); @@ -1970,7 +1968,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) cx8800_stop_vbi_dma(dev); del_timer(&dev->vbiq.timeout); } - spin_unlock(&dev->slock); + spin_unlock_irqrestore(&dev->slock, flags); if (core->ir) cx88_ir_stop(core); @@ -1989,6 +1987,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; + unsigned long flags; int err; if (dev->state.disabled) { @@ -2019,7 +2018,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) cx_set(MO_PCI_INTMSK, core->pci_irqmask); /* restart video+vbi capture */ - spin_lock(&dev->slock); + spin_lock_irqsave(&dev->slock, flags); if (!list_empty(&dev->vidq.active)) { printk("%s/0: resume video\n", core->name); restart_video_queue(dev,&dev->vidq); @@ -2028,7 +2027,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) printk("%s/0: resume vbi\n", core->name); cx8800_restart_vbi_queue(dev,&dev->vbiq); } - spin_unlock(&dev->slock); + spin_unlock_irqrestore(&dev->slock, flags); return 0; } diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index feff53c..51ce2c0 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -334,6 +334,7 @@ struct cx88_core { /* board name */ int nr; char name[32]; + u32 model; /* pci stuff */ int pci_bus; @@ -617,6 +618,8 @@ struct cx8802_dev { /* ----------------------------------------------------------- */ /* cx88-core.c */ +extern unsigned int cx88_core_debug; + extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[], int len, u32 bits, u32 mask); @@ -740,7 +743,7 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev); /* ----------------------------------------------------------- */ /* cx88-video.c*/ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); -int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); +int cx88_set_freq(struct cx88_core *core, const struct v4l2_frequency *f); int cx88_video_mux(struct cx88_core *core, unsigned int input); void cx88_querycap(struct file *file, struct cx88_core *core, struct v4l2_capability *cap); diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 2928e72..07b8460 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -1387,7 +1387,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (!itv->has_cx23415) write_reg_sync(0x03, IVTV_REG_DMACONTROL); - ivtv_s_std_enc(itv, &itv->tuner_std); + ivtv_s_std_enc(itv, itv->tuner_std); /* Default interrupts enabled. For the PVR350 this includes the decoder VSYNC interrupt, which is always on. It is not only used @@ -1397,7 +1397,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); ivtv_set_osd_alpha(itv); - ivtv_s_std_dec(itv, &itv->tuner_std); + ivtv_s_std_dec(itv, itv->tuner_std); } else { ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); } diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c index 68387d4..ed73edd 100644 --- a/drivers/media/pci/ivtv/ivtv-firmware.c +++ b/drivers/media/pci/ivtv/ivtv-firmware.c @@ -302,7 +302,7 @@ static int ivtv_firmware_restart(struct ivtv *itv) /* Restore encoder video standard */ std = itv->std; itv->std = 0; - ivtv_s_std_enc(itv, &std); + ivtv_s_std_enc(itv, std); if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ivtv_init_mpeg_decoder(itv); @@ -310,7 +310,7 @@ static int ivtv_firmware_restart(struct ivtv *itv) /* Restore decoder video standard */ std = itv->std_out; itv->std_out = 0; - ivtv_s_std_dec(itv, &std); + ivtv_s_std_dec(itv, std); /* Restore framebuffer if active */ if (itv->ivtvfb_restore) diff --git a/drivers/media/pci/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c index 8f0d077..af52def 100644 --- a/drivers/media/pci/ivtv/ivtv-gpio.c +++ b/drivers/media/pci/ivtv/ivtv-gpio.c @@ -192,7 +192,7 @@ static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int subdev_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 7a8b0d0..9cbbce0 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -711,28 +711,26 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_i } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +static int ivtv_itvc(struct ivtv *itv, bool get, u64 reg, u64 *val) { - struct v4l2_dbg_register *regs = arg; volatile u8 __iomem *reg_start; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) + if (reg >= IVTV_REG_OFFSET && reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) reg_start = itv->reg_mem - IVTV_REG_OFFSET; - else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && - regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) + else if (itv->has_cx23415 && reg >= IVTV_DECODER_OFFSET && + reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; - else if (regs->reg < IVTV_ENCODER_SIZE) + else if (reg < IVTV_ENCODER_SIZE) reg_start = itv->enc_mem; else return -EINVAL; - regs->size = 4; - if (cmd == VIDIOC_DBG_G_REGISTER) - regs->val = readl(regs->reg + reg_start); + if (get) + *val = readl(reg + reg_start); else - writel(regs->val, regs->reg + reg_start); + writel(*val, reg + reg_start); return 0; } @@ -740,20 +738,25 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register { struct ivtv *itv = fh2id(fh)->itv; - if (v4l2_chip_match_host(®->match)) - return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); + if (v4l2_chip_match_host(®->match)) { + reg->size = 4; + return ivtv_itvc(itv, true, reg->reg, ®->val); + } /* TODO: subdev errors should not be ignored, this should become a subdev helper function. */ ivtv_call_all(itv, core, g_register, reg); return 0; } -static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +static int ivtv_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) { struct ivtv *itv = fh2id(fh)->itv; - if (v4l2_chip_match_host(®->match)) - return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); + if (v4l2_chip_match_host(®->match)) { + u64 val = reg->val; + + return ivtv_itvc(itv, false, reg->reg, &val); + } /* TODO: subdev errors should not be ignored, this should become a subdev helper function. */ ivtv_call_all(itv, core, s_register, reg); @@ -1078,7 +1081,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency * return 0; } -int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +int ivtv_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) { struct ivtv *itv = fh2id(fh)->itv; struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; @@ -1103,10 +1106,10 @@ static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) return 0; } -void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std) +void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std) { - itv->std = *std; - itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; + itv->std = std; + itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; itv->is_50hz = !itv->is_60hz; cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz); itv->cxhdl.width = 720; @@ -1122,15 +1125,15 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std) ivtv_call_all(itv, core, s_std, itv->std); } -void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std) +void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std) { struct yuv_playback_info *yi = &itv->yuv_info; DEFINE_WAIT(wait); int f; /* set display standard */ - itv->std_out = *std; - itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; + itv->std_out = std; + itv->is_out_60hz = (std & V4L2_STD_525_60) ? 1 : 0; itv->is_out_50hz = !itv->is_out_60hz; ivtv_call_all(itv, video, s_std_output, itv->std_out); @@ -1168,14 +1171,14 @@ void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std) } } -static int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) +static int ivtv_s_std(struct file *file, void *fh, v4l2_std_id std) { struct ivtv *itv = fh2id(fh)->itv; - if ((*std & V4L2_STD_ALL) == 0) + if ((std & V4L2_STD_ALL) == 0) return -EINVAL; - if (*std == itv->std) + if (std == itv->std) return 0; if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || @@ -1196,7 +1199,7 @@ static int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) return 0; } -static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +static int ivtv_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) { struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; @@ -1804,7 +1807,7 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) } static long ivtv_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) + unsigned int cmd, void *arg) { struct ivtv *itv = fh2id(fh)->itv; diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.h b/drivers/media/pci/ivtv/ivtv-ioctl.h index 7c553d1..75c3977 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.h +++ b/drivers/media/pci/ivtv/ivtv-ioctl.h @@ -27,9 +27,9 @@ u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt); void ivtv_set_osd_alpha(struct ivtv *itv); int ivtv_set_speed(struct ivtv *itv, int speed); void ivtv_set_funcs(struct video_device *vdev); -void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std); -void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std); -int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std); +void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std); +int ivtv_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); int ivtv_s_input(struct file *file, void *fh, unsigned int inp); #endif diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 05b94aa..9ff1230 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -1171,8 +1171,7 @@ static void ivtvfb_release_buffers (struct ivtv *itv) fb_dealloc_cmap(&oi->ivtvfb_info.cmap); /* Release pseudo palette */ - if (oi->ivtvfb_info.pseudo_palette) - kfree(oi->ivtvfb_info.pseudo_palette); + kfree(oi->ivtvfb_info.pseudo_palette); #ifdef CONFIG_MTRR if (oi->fb_end_aligned_physaddr) { diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 7859c43..2381b05 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1410,7 +1410,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) } static long vidioc_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) + unsigned int cmd, void *arg) { switch (cmd) { case MEYEIOC_G_PARAMS: diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index dc68cf1..d45e7f6 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -50,6 +50,11 @@ static char name_svideo[] = "S-Video"; /* ------------------------------------------------------------------ */ /* board config info */ +static struct tda18271_std_map aver_a706_std_map = { + .fm_radio = { .if_freq = 5500, .fm_rfn = 0, .agc_mode = 3, .std = 0, + .if_lvl = 0, .rfagc_top = 0x2c, }, +}; + /* If radio_type !=UNSET, radio_addr should be specified */ @@ -2760,7 +2765,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = {{ @@ -3291,7 +3296,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 1, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_ON }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x000200000, .inputs = {{ @@ -3395,7 +3400,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 1, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_ON }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200100, .inputs = {{ @@ -3426,7 +3431,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 3, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_ON_BRIDGE }, .mpeg = SAA7134_MPEG_DVB, .ts_type = SAA7134_MPEG_TS_SERIAL, .ts_force_val = 1, @@ -3459,7 +3464,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 3, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_ON_BRIDGE }, .mpeg = SAA7134_MPEG_DVB, .ts_type = SAA7134_MPEG_TS_SERIAL, .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ @@ -3683,7 +3688,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = {{ @@ -3736,7 +3741,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = {{ @@ -3754,7 +3759,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .gpiomask = 1 << 21, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ @@ -3887,7 +3892,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, /* FIXME: analog tv untested */ @@ -3903,7 +3908,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .gpiomask = 0x020200000, .inputs = {{ .name = name_tv, @@ -3937,7 +3942,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .gpiomask = 0x020200000, .inputs = {{ .name = name_tv, @@ -4737,7 +4742,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = {{ @@ -4823,7 +4828,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, @@ -4847,7 +4852,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = { { @@ -5057,7 +5062,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .gpiomask = 1 << 21, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ @@ -5087,7 +5092,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 2, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_GP0_HIGH_OFF }, .gpiomask = 1 << 21, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ @@ -5176,7 +5181,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x0200000, .inputs = { { @@ -5406,7 +5411,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tuner_config = 0, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF }, .mpeg = SAA7134_MPEG_DVB, .ts_type = SAA7134_MPEG_TS_PARALLEL, .inputs = {{ @@ -5629,7 +5634,7 @@ struct saa7134_board saa7134_boards[] = { .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, - .tuner_config = 3, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_ON_BRIDGE }, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .gpiomask = 0x02050000, @@ -5790,6 +5795,38 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x6010000, } }, }, + [SAA7134_BOARD_AVERMEDIA_A706] = { + .name = "AverMedia AverTV Satellite Hybrid+FM A706", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda829x_conf = { .lna_cfg = TDA8290_LNA_OFF, + .no_i2c_gate = 1, + .tda18271_std_map = &aver_a706_std_map }, + .gpiomask = 1 << 11, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 4, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0000800, + }, + }, }; @@ -7037,6 +7074,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x0911, .driver_data = SAA7134_BOARD_SENSORAY811_911, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x2055, /* AverTV Satellite Hybrid+FM A706 */ + .driver_data = SAA7134_BOARD_AVERMEDIA_A706, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7585,6 +7628,17 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); break; + case SAA7134_BOARD_AVERMEDIA_A706: + /* radio antenna select: tristate both as in Windows driver */ + saa7134_set_gpio(dev, 12, 3); /* TV antenna */ + saa7134_set_gpio(dev, 13, 3); /* FM antenna */ + dev->has_remote = SAA7134_REMOTE_I2C; + /* + * Disable CE5039 DVB-S tuner now (SLEEP pin high) to prevent + * it from interfering with analog tuner detection + */ + saa7134_set_gpio(dev, 23, 1); + break; case SAA7134_BOARD_VIDEOMATE_S350: dev->has_remote = SAA7134_REMOTE_GPIO; saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000); @@ -7633,7 +7687,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) { tun_setup.type = dev->tuner_type; tun_setup.addr = dev->tuner_addr; - tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.config = &saa7134_boards[dev->board].tda829x_conf; tun_setup.tuner_callback = saa7134_tuner_callback; tun_setup.mode_mask = mode_mask; diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 8fd24e7..45f0aca 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -805,6 +805,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, saa7134_boards[dev->board].name); + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); video_set_drvdata(vfd, dev); return vfd; } @@ -1028,8 +1029,6 @@ static int saa7134_initdev(struct pci_dev *pci_dev, } } - v4l2_prio_init(&dev->prio); - mutex_lock(&saa7134_devlist_lock); list_for_each_entry(mops, &mops_list, next) mpeg_ops_attach(mops, dev); diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index 27915e5..4a08ae3 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -1073,6 +1073,10 @@ static struct mt312_config zl10313_compro_s350_config = { .demod_address = 0x0e, }; +static struct mt312_config zl10313_avermedia_a706_config = { + .demod_address = 0x0e, +}; + static struct lgdt3305_config hcw_lgdt3305_config = { .i2c_addr = 0x0e, .mpeg_mode = LGDT3305_MPEG_SERIAL, @@ -1391,8 +1395,9 @@ static int dvb_init(struct saa7134_dev *dev) wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); goto detach_frontend; } - if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, - 0x08, 0, 0) == NULL) { + if (dvb_attach(isl6421_attach, fe0->dvb.frontend, + &dev->i2c_adap, + 0x08, 0, 0, false) == NULL) { wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); goto detach_frontend; } @@ -1509,7 +1514,8 @@ static int dvb_init(struct saa7134_dev *dev) goto detach_frontend; } if (dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->i2c_adap, 0x08, 0, 0) == NULL) { + &dev->i2c_adap, + 0x08, 0, 0, false) == NULL) { wprintk("%s: No ISL6421 found!\n", __func__); goto detach_frontend; } @@ -1820,6 +1826,25 @@ static int dvb_init(struct saa7134_dev *dev) &prohdtv_pro2_tda18271_config); } break; + case SAA7134_BOARD_AVERMEDIA_A706: + /* Enable all DVB-S devices now */ + /* CE5039 DVB-S tuner SLEEP pin low */ + saa7134_set_gpio(dev, 23, 0); + /* CE6313 DVB-S demod SLEEP pin low */ + saa7134_set_gpio(dev, 9, 0); + /* CE6313 DVB-S demod RESET# pin high */ + saa7134_set_gpio(dev, 25, 1); + msleep(1); + fe0->dvb.frontend = dvb_attach(mt312_attach, + &zl10313_avermedia_a706_config, &dev->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (dvb_attach(zl10039_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap) == NULL) + wprintk("%s: No zl10039 found!\n", + __func__); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 4df79c6..66a7081 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -428,7 +428,7 @@ static int empress_g_chip_ident(struct file *file, void *fh, return -EINVAL; } -static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id) +static int empress_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7134_dev *dev = file->private_data; diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c index a176ec3..c68169d 100644 --- a/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -256,6 +256,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, addr |= 1; if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40 && + msgs[i].addr != 0x41 && msgs[i].addr != 0x19) { /* workaround for a saa7134 i2c bug * needed to talk to the mt352 demux diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index e761262..6f43126 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -997,6 +997,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: info.addr = 0x40; break; + case SAA7134_BOARD_AVERMEDIA_A706: + info.addr = 0x41; + break; case SAA7134_BOARD_FLYDVB_TRIO: dev->init_data.name = "FlyDVB Trio"; dev->init_data.get_key = get_key_flydvb_trio; diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c index b7a99be..0f34e09 100644 --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -796,6 +796,7 @@ static int tvaudio_thread_ddep(void *data) dprintk("FM Radio\n"); if (dev->tuner_type == TUNER_PHILIPS_TDA8290) { norms = (0x11 << 2) | 0x01; + /* set IF frequency to 5.5 MHz */ saa_dsp_writel(dev, 0x42c >> 2, 0x729555); } else { norms = (0x0f << 2) | 0x01; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 7c503fb..cc40938 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1176,14 +1176,6 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str int restart_overlay = 0; int err; - /* When called from the empress code fh == NULL. - That needs to be fixed somehow, but for now this is - good enough. */ - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } err = -EINVAL; mutex_lock(&dev->lock); @@ -1352,6 +1344,7 @@ static int video_open(struct file *file) if (NULL == fh) return -ENOMEM; + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -1359,7 +1352,6 @@ static int video_open(struct file *file) fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); fh->width = 720; fh->height = 576; - v4l2_prio_open(&dev->prio, &fh->prio); videobuf_queue_sg_init(&fh->cap, &video_qops, &dev->pci->dev, &dev->slock, @@ -1384,6 +1376,8 @@ static int video_open(struct file *file) /* switch to video/vbi mode */ video_mux(dev,dev->ctl_input); } + v4l2_fh_add(&fh->fh); + return 0; } @@ -1504,7 +1498,8 @@ static int video_release(struct file *file) saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi); - v4l2_prio_close(&dev->prio, fh->prio); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); file->private_data = NULL; kfree(fh); return 0; @@ -1557,6 +1552,7 @@ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; struct saa7134_tvnorm *norm = dev->tvnorm; + memset(&f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -1755,7 +1751,6 @@ static int saa7134_enum_input(struct file *file, void *priv, strcpy(i->name, card_in(dev, n).name); if (card_in(dev, n).tv) i->type = V4L2_INPUT_TYPE_TUNER; - i->audioset = 1; if (n == dev->ctl_input) { int v1 = saa_readb(SAA7134_STATUS_VIDEO1); int v2 = saa_readb(SAA7134_STATUS_VIDEO2); @@ -1784,11 +1779,6 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - int err; - - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; if (i >= SAA7134_INPUT_MAX) return -EINVAL; @@ -1805,6 +1795,8 @@ static int saa7134_querycap(struct file *file, void *priv, { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + u32 radio_caps, video_caps, vbi_caps; unsigned int tuner_type = dev->tuner_type; @@ -1812,54 +1804,67 @@ static int saa7134_querycap(struct file *file, void *priv, strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_TUNER; + + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if ((tuner_type != TUNER_ABSENT) && (tuner_type != UNSET)) + cap->device_caps |= V4L2_CAP_TUNER; + + radio_caps = V4L2_CAP_RADIO; if (dev->has_rds) - cap->capabilities |= V4L2_CAP_RDS_CAPTURE; + radio_caps |= V4L2_CAP_RDS_CAPTURE; + + video_caps = V4L2_CAP_VIDEO_CAPTURE; if (saa7134_no_overlay <= 0) - cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; + video_caps |= V4L2_CAP_VIDEO_OVERLAY; + + vbi_caps = V4L2_CAP_VBI_CAPTURE; + + switch (vdev->vfl_type) { + case VFL_TYPE_RADIO: + cap->device_caps |= radio_caps; + break; + case VFL_TYPE_GRABBER: + cap->device_caps |= video_caps; + break; + case VFL_TYPE_VBI: + cap->device_caps |= vbi_caps; + break; + } + cap->capabilities = radio_caps | video_caps | vbi_caps | + cap->device_caps | V4L2_CAP_DEVICE_CAPS; + if (vdev->vfl_type == VFL_TYPE_RADIO) { + cap->device_caps &= ~V4L2_CAP_STREAMING; + if (!dev->has_rds) + cap->device_caps &= ~V4L2_CAP_READWRITE; + } - if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET)) - cap->capabilities &= ~V4L2_CAP_TUNER; return 0; } -int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id) +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id id) { unsigned long flags; unsigned int i; v4l2_std_id fixup; - int err; - /* When called from the empress code fh == NULL. - That needs to be fixed somehow, but for now this is - good enough. */ - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } else if (res_locked(dev, RESOURCE_OVERLAY)) { + if (!fh && res_locked(dev, RESOURCE_OVERLAY)) { /* Don't change the std from the mpeg device if overlay is active. */ return -EBUSY; } for (i = 0; i < TVNORMS; i++) - if (*id == tvnorms[i].id) + if (id == tvnorms[i].id) break; if (i == TVNORMS) for (i = 0; i < TVNORMS; i++) - if (*id & tvnorms[i].id) + if (id & tvnorms[i].id) break; if (i == TVNORMS) return -EINVAL; - if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { + if ((id & V4L2_STD_SECAM) && (secam[0] != '-')) { if (secam[0] == 'L' || secam[0] == 'l') { if (secam[1] == 'C' || secam[1] == 'c') fixup = V4L2_STD_SECAM_LC; @@ -1879,7 +1884,7 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ return -EINVAL; } - *id = tvnorms[i].id; + id = tvnorms[i].id; mutex_lock(&dev->lock); if (fh && res_check(fh, RESOURCE_OVERLAY)) { @@ -1901,7 +1906,7 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ } EXPORT_SYMBOL_GPL(saa7134_s_std_internal); -static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7134_fh *fh = priv; @@ -2009,11 +2014,11 @@ static int saa7134_g_tuner(struct file *file, void *priv, if (NULL != card_in(dev, n).name) { strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; + saa_call_all(dev, tuner, g_tuner, t); t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - t->rangehigh = 0xffffffffUL; t->rxsubchans = saa7134_tvaudio_getstereo(dev); t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); } @@ -2023,15 +2028,14 @@ static int saa7134_g_tuner(struct file *file, void *priv, } static int saa7134_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - int rx, mode, err; + int rx, mode; - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; + if (0 != t->index) + return -EINVAL; mode = dev->thread.mode; if (UNSET == mode) { @@ -2050,22 +2054,20 @@ static int saa7134_g_frequency(struct file *file, void *priv, struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; + if (0 != f->tuner) + return -EINVAL; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; + saa_call_all(dev, tuner, g_frequency, f); return 0; } static int saa7134_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - int err; - - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; if (0 != f->tuner) return -EINVAL; @@ -2074,7 +2076,6 @@ static int saa7134_s_frequency(struct file *file, void *priv, if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) return -EINVAL; mutex_lock(&dev->lock); - dev->ctl_freq = f->frequency; saa_call_all(dev, tuner, s_frequency, f); @@ -2083,35 +2084,6 @@ static int saa7134_s_frequency(struct file *file, void *priv, return 0; } -static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - strcpy(a->name, "audio"); - return 0; -} - -static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) -{ - return 0; -} - -static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p) -{ - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; - - *p = v4l2_prio_max(&dev->prio); - return 0; -} - -static int saa7134_s_priority(struct file *file, void *f, - enum v4l2_priority prio) -{ - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; - - return v4l2_prio_change(&dev->prio, &fh->prio, prio); -} - static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -2279,12 +2251,6 @@ static int saa7134_streamoff(struct file *file, void *priv, return 0; } -static int saa7134_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *priv, struct v4l2_dbg_register *reg) @@ -2300,7 +2266,7 @@ static int vidioc_g_register (struct file *file, void *priv, } static int vidioc_s_register (struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; @@ -2312,19 +2278,6 @@ static int vidioc_s_register (struct file *file, void *priv, } #endif -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; - - strcpy(cap->driver, "saa7134"); - strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); - sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2339,6 +2292,7 @@ static int radio_g_tuner(struct file *file, void *priv, t->type = V4L2_TUNER_RADIO; saa_call_all(dev, tuner, g_tuner, t); + t->audmode &= V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO; if (dev->input->amux == TV) { t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); t->rxsubchans = (saa_readb(0x529) & 0x08) ? @@ -2347,7 +2301,7 @@ static int radio_g_tuner(struct file *file, void *priv, return 0; } static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; @@ -2377,26 +2331,12 @@ static int radio_g_input(struct file *filp, void *priv, unsigned int *i) return 0; } -static int radio_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - memset(a, 0, sizeof(*a)); - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - return 0; -} - static int radio_s_input(struct file *filp, void *priv, unsigned int i) { return 0; } -static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int radio_s_std(struct file *file, void *fh, v4l2_std_id norm) { return 0; } @@ -2441,8 +2381,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, - .vidioc_g_audio = saa7134_g_audio, - .vidioc_s_audio = saa7134_s_audio, .vidioc_cropcap = saa7134_cropcap, .vidioc_reqbufs = saa7134_reqbufs, .vidioc_querybuf = saa7134_querybuf, @@ -2465,9 +2403,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fbuf = saa7134_g_fbuf, .vidioc_s_fbuf = saa7134_s_fbuf, .vidioc_overlay = saa7134_overlay, - .vidioc_g_priority = saa7134_g_priority, - .vidioc_s_priority = saa7134_s_priority, - .vidioc_g_parm = saa7134_g_parm, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2486,12 +2421,10 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = saa7134_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, .vidioc_s_input = radio_s_input, .vidioc_s_std = radio_s_std, .vidioc_queryctrl = radio_queryctrl, diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 71eefef..d2ad16c 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -36,6 +36,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> #include <media/tuner.h> #include <media/rc-core.h> #include <media/ir-kbd-i2c.h> @@ -45,6 +46,7 @@ #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) #include <media/videobuf-dvb.h> #endif +#include "tda8290.h" #define UNSET (-1U) @@ -334,6 +336,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_KWORLD_PC150U 189 #define SAA7134_BOARD_ASUSTeK_PS3_100 190 #define SAA7134_BOARD_HAWELL_HW_9004V1 191 +#define SAA7134_BOARD_AVERMEDIA_A706 192 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -390,7 +393,7 @@ struct saa7134_board { unsigned char rds_addr; unsigned int tda9887_conf; - unsigned int tuner_config; + struct tda829x_config tda829x_conf; /* peripheral I/O */ enum saa7134_video_out video_out; @@ -466,11 +469,11 @@ struct saa7134_dmaqueue { /* video filehandle status */ struct saa7134_fh { + struct v4l2_fh fh; struct saa7134_dev *dev; unsigned int radio; enum v4l2_buf_type type; unsigned int resources; - enum v4l2_priority prio; struct pm_qos_request qos_request; /* video overlay */ @@ -542,7 +545,6 @@ struct saa7134_dev { struct list_head devlist; struct mutex lock; spinlock_t slock; - struct v4l2_prio_state prio; struct v4l2_device v4l2_dev; /* workstruct for loading modules */ struct work_struct request_module_wk; @@ -605,7 +607,6 @@ struct saa7134_dev { int ctl_contrast; int ctl_hue; int ctl_saturation; - int ctl_freq; int ctl_mute; /* audio */ int ctl_volume; int ctl_invert; /* private */ @@ -766,7 +767,7 @@ extern struct video_device saa7134_radio_template; int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); -int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id); +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id id); int saa7134_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index 91369da..71e8bea 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -560,7 +560,7 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) return call_all(dev, tuner, g_tuner, t); } -static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; @@ -595,7 +595,7 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency return 0; } -static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; @@ -612,8 +612,8 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency /* tune in desired frequency */ tuner_call(mxb, tuner, s_frequency, f); /* let the tuner subdev clamp the frequency to the tuner range */ - tuner_call(mxb, tuner, g_frequency, f); mxb->cur_freq = *f; + tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); if (mxb->cur_audinput == 0) mxb_update_audmode(mxb); @@ -680,7 +680,7 @@ static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_regist return 0; } -static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; @@ -688,7 +688,6 @@ static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_regist return -EPERM; if (v4l2_chip_match_host(®->match)) { saa7146_write(dev, reg->reg, reg->val); - reg->size = 4; return 0; } return call_all(dev, core, s_register, reg); diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c index 9bb0903..0b74fb2 100644 --- a/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/drivers/media/pci/saa7164/saa7164-encoder.c @@ -211,17 +211,17 @@ static int saa7164_encoder_initialize(struct saa7164_port *port) } /* -- V4L2 --------------------------------------------------------- */ -static int vidioc_s_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 saa7164_encoder_fh *fh = file->private_data; struct saa7164_port *port = fh->port; struct saa7164_dev *dev = port->dev; unsigned int i; - dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id); + dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)id); for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { - if (*id & saa7164_tvnorms[i].id) + if (id & saa7164_tvnorms[i].id) break; } if (i == ARRAY_SIZE(saa7164_tvnorms)) @@ -234,7 +234,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) */ saa7164_api_set_audio_std(port); - dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id); + dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)id); return 0; } @@ -318,7 +318,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { /* Update the A/V core */ return 0; @@ -337,7 +337,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct saa7164_encoder_fh *fh = file->private_data; struct saa7164_port *port = fh->port; @@ -1313,7 +1313,7 @@ static int saa7164_g_register(struct file *file, void *fh, } static int saa7164_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; struct saa7164_dev *dev = port->dev; diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c index b453229..da224eb 100644 --- a/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/drivers/media/pci/saa7164/saa7164-vbi.c @@ -183,17 +183,17 @@ static int saa7164_vbi_initialize(struct saa7164_port *port) } /* -- V4L2 --------------------------------------------------------- */ -static int vidioc_s_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 saa7164_vbi_fh *fh = file->private_data; struct saa7164_port *port = fh->port; struct saa7164_dev *dev = port->dev; unsigned int i; - dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id); + dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)id); for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { - if (*id & saa7164_tvnorms[i].id) + if (id & saa7164_tvnorms[i].id) break; } if (i == ARRAY_SIZE(saa7164_tvnorms)) @@ -206,7 +206,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) */ saa7164_api_set_audio_std(port); - dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id); + dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)id); return 0; } @@ -290,7 +290,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { /* Update the A/V core */ return 0; @@ -309,7 +309,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct saa7164_vbi_fh *fh = file->private_data; struct saa7164_port *port = fh->port; diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index a94ccad..0313015 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -4,6 +4,7 @@ config STA2X11_VIP select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS + depends on I2C help Say Y for support for STA2X11 VIP (Video Input Port) capture device. diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 4b703fe..7005695 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -439,22 +439,22 @@ static int vidioc_querycap(struct file *file, void *priv, * * other, returned from video DAC. */ -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) { struct sta2x11_vip *vip = video_drvdata(file); v4l2_std_id oldstd = vip->std, newstd; int status; - if (V4L2_STD_ALL == *std) { - v4l2_subdev_call(vip->decoder, core, s_std, *std); + if (V4L2_STD_ALL == std) { + v4l2_subdev_call(vip->decoder, core, s_std, std); ssleep(2); v4l2_subdev_call(vip->decoder, video, querystd, &newstd); v4l2_subdev_call(vip->decoder, video, g_input_status, &status); if (status & V4L2_IN_ST_NO_SIGNAL) return -EIO; - *std = vip->std = newstd; - if (oldstd != *std) { - if (V4L2_STD_525_60 & (*std)) + std = vip->std = newstd; + if (oldstd != std) { + if (V4L2_STD_525_60 & std) vip->format = formats_60[0]; else vip->format = formats_50[0]; @@ -462,14 +462,14 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } - if (oldstd != *std) { - if (V4L2_STD_525_60 & (*std)) + if (oldstd != std) { + if (V4L2_STD_525_60 & std) vip->format = formats_60[0]; else vip->format = formats_50[0]; } - return v4l2_subdev_call(vip->decoder, core, s_std, *std); + return v4l2_subdev_call(vip->decoder, core, s_std, std); } /** diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 3dc7aa9..f38329d 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -990,7 +990,7 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) if (feed->type == DMX_TYPE_TS) { if ((feed->ts_type & TS_DECODER) && - (feed->pes_type <= DMX_TS_PES_PCR)) { + (feed->pes_type <= DMX_PES_PCR)) { switch (demux->dmx.frontend->source) { case DMX_MEMORY_FE: if (feed->ts_type & TS_DECODER) @@ -1051,14 +1051,14 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed) if (feed->type == DMX_TYPE_TS) { if (feed->ts_type & TS_DECODER) { - if (feed->pes_type >= DMX_TS_PES_OTHER || + if (feed->pes_type >= DMX_PES_OTHER || !demux->pesfilter[feed->pes_type]) return -EINVAL; demux->pids[feed->pes_type] |= 0x8000; demux->pesfilter[feed->pes_type] = NULL; } if (feed->ts_type & TS_DECODER && - feed->pes_type < DMX_TS_PES_OTHER) { + feed->pes_type < DMX_PES_OTHER) { ret = dvb_feed_stop_pid(feed); } else if ((feed->ts_type & TS_PACKET) && diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index 730e906..6c4076a 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -366,7 +366,7 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) return 0; } -static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; @@ -426,7 +426,7 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency return 0; } -static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c index 7e6e43a..6ccc488 100644 --- a/drivers/media/pci/ttpci/budget.c +++ b/drivers/media/pci/ttpci/budget.c @@ -537,6 +537,16 @@ static void frontend_init(struct budget *budget) } break; + case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + break; + } + break; + case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ { int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); @@ -818,6 +828,7 @@ MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps front MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC); static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), @@ -832,6 +843,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), + MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52), { .vendor = 0, } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 2e8f518..1168a84 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -2435,14 +2435,14 @@ static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) return 0; } -static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std) +static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; int res = 0; mutex_lock(&zr->resource_lock); - res = zoran_set_norm(zr, *std); + res = zoran_set_norm(zr, std); if (res) goto sstd_unlock_and_return; diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c index e084b0a..1512b5d 100644 --- a/drivers/media/pci/zoran/zoran_procfs.c +++ b/drivers/media/pci/zoran/zoran_procfs.c @@ -201,7 +201,7 @@ zoran_proc_init (struct zoran *zr) dprintk(2, KERN_INFO "%s: procfs entry /proc/%s allocated. data=%p\n", - ZR_DEVNAME(zr), name, zr->zoran_proc->data); + ZR_DEVNAME(zr), name, zr); } else { dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n", ZR_DEVNAME(zr), name); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 2650009..0494d27 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -122,7 +122,7 @@ config VIDEO_S3C_CAMIF will be called s3c-camif. source "drivers/media/platform/soc_camera/Kconfig" -source "drivers/media/platform/s5p-fimc/Kconfig" +source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 42089ba..eee28dd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -30,7 +30,7 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ +obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 5f209d5..0e55b08 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -384,7 +384,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities - & V4L2_IN_CAP_CUSTOM_TIMINGS) { + & V4L2_IN_CAP_DV_TIMINGS) { struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; params.hdelay = bt->hsync + bt->hbackporch; @@ -633,7 +633,7 @@ static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) { struct bcap_device *bcap_dev = video_drvdata(file); int ret; @@ -641,11 +641,11 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; - ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std); + ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std); if (ret < 0) return ret; - bcap_dev->std = *std; + bcap_dev->std = std; return 0; } @@ -890,7 +890,7 @@ static int bcap_dbg_g_register(struct file *file, void *priv, } static int bcap_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct bcap_device *bcap_dev = video_drvdata(file); @@ -1029,6 +1029,7 @@ static int bcap_probe(struct platform_device *pdev) q->buf_struct_size = sizeof(struct bcap_buffer); q->ops = &bcap_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb2_queue_init(q); @@ -1110,7 +1111,7 @@ static int bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } - if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_DV_TIMINGS) { struct v4l2_dv_timings dv_timings; ret = v4l2_subdev_call(bcap_dev->sd, video, g_dv_timings, &dv_timings); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index b931c2a..48b8d7a 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1427,6 +1427,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, 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_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1438,6 +1439,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, 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_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1633,6 +1635,9 @@ static irqreturn_t coda_irq_handler(int irq, void *data) dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; } + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index ccfde4e..afb3aec 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -1,79 +1,47 @@ config VIDEO_DAVINCI_VPIF_DISPLAY - tristate "DM646x/DA850/OMAPL138 EVM Video Display" - depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) + tristate "TI DaVinci VPIF V4L2-Display driver" + depends on VIDEO_DEV && ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG - select VIDEO_DAVINCI_VPIF select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT help Enables Davinci VPIF module used for display devices. - This module is common for following DM6467/DA850/OMAPL138 - based display devices. + This module is used for display on TI DM6467/DA850/OMAPL138 + SoCs. - To compile this driver as a module, choose M here: the - module will be called vpif_display. + To compile this driver as a module, choose M here. There will + be two modules called vpif.ko and vpif_display.ko config VIDEO_DAVINCI_VPIF_CAPTURE - tristate "DM646x/DA850/OMAPL138 EVM Video Capture" - depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) + tristate "TI DaVinci VPIF video capture driver" + depends on VIDEO_DEV && ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG - select VIDEO_DAVINCI_VPIF help - Enables Davinci VPIF module used for captur devices. - This module is common for following DM6467/DA850/OMAPL138 - based capture devices. + Enables Davinci VPIF module used for capture devices. + This module is used for capture on TI DM6467/DA850/OMAPL138 + SoCs. - To compile this driver as a module, choose M here: the - module will be called vpif_capture. + To compile this driver as a module, choose M here. There will + be two modules called vpif.ko and vpif_capture.ko -config VIDEO_DAVINCI_VPIF - tristate "DaVinci VPIF Driver" - depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE - help - Support for DaVinci VPIF Driver. - - To compile this driver as a module, choose M here: the - module will be called vpif. - -config VIDEO_VPSS_SYSTEM - tristate "VPSS System module driver" - depends on ARCH_DAVINCI - help - Support for vpss system module for video driver - -config VIDEO_VPFE_CAPTURE - tristate "VPFE Video Capture Driver" +config VIDEO_DM6446_CCDC + tristate "TI DM6446 CCDC video capture driver" depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) - depends on I2C select VIDEOBUF_DMA_CONTIG help - Support for DMx/AMx VPFE based frame grabber. This is the - common V4L2 module for following DMx/AMx SoCs from Texas - Instruments:- DM6446, DM365, DM355 & AM3517/05. - - To compile this driver as a module, choose M here: the - module will be called vpfe-capture. - -config VIDEO_DM6446_CCDC - tristate "DM6446 CCDC HW module" - depends on VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces with decoder modules such as TVP5146 over BT656 or sensor module such as MT9T001 over a raw interface. This module configures the interface and CCDC/ISIF to do video frame capture from slave decoders. - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and dm644x_ccdc.ko config VIDEO_DM355_CCDC - tristate "DM355 CCDC HW module" - depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y + tristate "TI DM355 CCDC video capture driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG help Enables DM355 CCD hw module. DM355 CCDC hw interfaces with decoder modules such as TVP5146 over BT656 or @@ -81,31 +49,30 @@ config VIDEO_DM355_CCDC module configures the interface and CCDC/ISIF to do video frame capture from a slave decoders - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and dm355_ccdc.ko -config VIDEO_ISIF - tristate "ISIF HW module" - depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y +config VIDEO_DM365_ISIF + tristate "TI DM365 ISIF video capture driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG help Enables ISIF hw module. This is the hardware module for - configuring ISIF in VPFE to capture Raw Bayer RGB data from + configuring ISIF in VPFE to capture Raw Bayer RGB data from a image sensor or YUV data from a YUV source. - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and isif.ko config VIDEO_DAVINCI_VPBE_DISPLAY - tristate "DM644X/DM365/DM355 VPBE HW module" - depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 - select VIDEO_VPSS_SYSTEM + tristate "TI DaVinci VPBE V4L2-Display driver" + depends on ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG help Enables Davinci VPBE module used for display devices. - This module is common for following DM644x/DM365/DM355 + This module is used for display on TI DM644x/DM365/DM355 based display devices. - To compile this driver as a module, choose M here: the - module will be called vpbe. + To compile this driver as a module, choose M here. There will + be five modules created called vpss.ko, vpbe.ko, vpbe_osd.ko, + vpbe_venc.ko and vpbe_display.ko diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index f40f521..d74d9ee 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -2,19 +2,14 @@ # Makefile for the davinci video device drivers. # -# VPIF -obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o - #VPIF Display driver -obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o #VPIF Capture driver -obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o +obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o # Capture: DM6446 and DM355 -obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o -obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o -obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o -obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o -obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ +obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o vpss.o dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o vpss.o dm355_ccdc.o +obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o vpss.o isif.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \ vpbe_venc.o vpbe_display.o diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index 4277e4a..05f8fb7 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -37,7 +37,6 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/videodev2.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -59,10 +58,6 @@ static struct ccdc_oper_config { struct ccdc_params_raw bayer; /* YCbCr configuration */ struct ccdc_params_ycbcr ycbcr; - /* Master clock */ - struct clk *mclk; - /* slave clock */ - struct clk *sclk; /* ccdc base address */ void __iomem *base_addr; } ccdc_cfg = { @@ -85,7 +80,7 @@ static struct ccdc_oper_config { .mfilt1 = CCDC_NO_MEDIAN_FILTER1, .mfilt2 = CCDC_NO_MEDIAN_FILTER2, .alaw = { - .gama_wd = 2, + .gamma_wd = 2, }, .blk_clamp = { .sample_pixel = 1, @@ -303,8 +298,8 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) } if (ccdcparam->alaw.enable) { - if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || - ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) { dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); return -EINVAL; } @@ -680,8 +675,8 @@ static int ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { val |= (CCDC_ALAW_ENABLE | - ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) << + ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) << CCDC_GAMMAWD_INPUT_SHIFT)); } @@ -997,32 +992,10 @@ static int dm355_ccdc_probe(struct platform_device *pdev) goto fail_nomem; } - /* Get and enable Master clock */ - ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(ccdc_cfg.mclk)) { - status = PTR_ERR(ccdc_cfg.mclk); - goto fail_nomap; - } - if (clk_prepare_enable(ccdc_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Get and enable Slave clock */ - ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); - if (IS_ERR(ccdc_cfg.sclk)) { - status = PTR_ERR(ccdc_cfg.sclk); - goto fail_mclk; - } - if (clk_prepare_enable(ccdc_cfg.sclk)) { - status = -ENODEV; - goto fail_sclk; - } - /* Platform data holds setup_pinmux function ptr */ if (NULL == pdev->dev.platform_data) { status = -ENODEV; - goto fail_sclk; + goto fail_nomap; } setup_pinmux = pdev->dev.platform_data; /* @@ -1033,12 +1006,6 @@ static int dm355_ccdc_probe(struct platform_device *pdev) ccdc_cfg.dev = &pdev->dev; printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; -fail_sclk: - clk_disable_unprepare(ccdc_cfg.sclk); - clk_put(ccdc_cfg.sclk); -fail_mclk: - clk_disable_unprepare(ccdc_cfg.mclk); - clk_put(ccdc_cfg.mclk); fail_nomap: iounmap(ccdc_cfg.base_addr); fail_nomem: @@ -1052,10 +1019,6 @@ static int dm355_ccdc_remove(struct platform_device *pdev) { struct resource *res; - clk_disable_unprepare(ccdc_cfg.sclk); - clk_disable_unprepare(ccdc_cfg.mclk); - clk_put(ccdc_cfg.mclk); - clk_put(ccdc_cfg.sclk); iounmap(ccdc_cfg.base_addr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h index d6d2ef0..2e1946e 100644 --- a/drivers/media/platform/davinci/dm355_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h @@ -153,7 +153,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE 1 -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_REC656IF_BT656_EN 3 #define CCDC_FMTCFG_FMTMODE_MASK 3 diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 318e805..30fa084 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -38,7 +38,6 @@ #include <linux/uaccess.h> #include <linux/videodev2.h> #include <linux/gfp.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -60,10 +59,6 @@ static struct ccdc_oper_config { struct ccdc_params_raw bayer; /* YCbCr configuration */ struct ccdc_params_ycbcr ycbcr; - /* Master clock */ - struct clk *mclk; - /* slave clock */ - struct clk *sclk; /* ccdc base address */ void __iomem *base_addr; } ccdc_cfg = { @@ -228,9 +223,12 @@ static void ccdc_readregs(void) static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) { if (ccdcparam->alaw.enable) { - if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || - (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || - (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); + u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); + + if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) || + (max_gamma > max_data)) { dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); return -1; } @@ -560,8 +558,8 @@ void ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { - val = ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + val = ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); regw(val, CCDC_ALAW); dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); } @@ -988,38 +986,9 @@ static int dm644x_ccdc_probe(struct platform_device *pdev) goto fail_nomem; } - /* Get and enable Master clock */ - ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(ccdc_cfg.mclk)) { - status = PTR_ERR(ccdc_cfg.mclk); - goto fail_nomap; - } - if (clk_prepare_enable(ccdc_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Get and enable Slave clock */ - ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); - if (IS_ERR(ccdc_cfg.sclk)) { - status = PTR_ERR(ccdc_cfg.sclk); - goto fail_mclk; - } - if (clk_prepare_enable(ccdc_cfg.sclk)) { - status = -ENODEV; - goto fail_sclk; - } ccdc_cfg.dev = &pdev->dev; printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; -fail_sclk: - clk_disable_unprepare(ccdc_cfg.sclk); - clk_put(ccdc_cfg.sclk); -fail_mclk: - clk_disable_unprepare(ccdc_cfg.mclk); - clk_put(ccdc_cfg.mclk); -fail_nomap: - iounmap(ccdc_cfg.base_addr); fail_nomem: release_mem_region(res->start, resource_size(res)); fail_nores: @@ -1031,10 +1000,6 @@ static int dm644x_ccdc_remove(struct platform_device *pdev) { struct resource *res; - clk_disable_unprepare(ccdc_cfg.mclk); - clk_disable_unprepare(ccdc_cfg.sclk); - clk_put(ccdc_cfg.mclk); - clk_put(ccdc_cfg.sclk); iounmap(ccdc_cfg.base_addr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) @@ -1049,18 +1014,12 @@ static int dm644x_ccdc_suspend(struct device *dev) ccdc_save_context(); /* Disable CCDC */ ccdc_enable(0); - /* Disable both master and slave clock */ - clk_disable_unprepare(ccdc_cfg.mclk); - clk_disable_unprepare(ccdc_cfg.sclk); return 0; } static int dm644x_ccdc_resume(struct device *dev) { - /* Enable both master and slave clock */ - clk_prepare_enable(ccdc_cfg.mclk); - clk_prepare_enable(ccdc_cfg.sclk); /* Restore CCDC context */ ccdc_restore_context(); diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h index 90370e4..2b0aca5 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h @@ -84,7 +84,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE (1 << 3) -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_BLK_CLAMP_ENABLE (1 << 31) #define CCDC_BLK_SGAIN_MASK 0x1F #define CCDC_BLK_ST_PXL_MASK 0x7FFF diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index 5050f92..3332cca 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -32,7 +32,6 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/videodev2.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -88,8 +87,6 @@ static struct isif_oper_config { struct isif_ycbcr_config ycbcr; struct isif_params_raw bayer; enum isif_data_pack data_pack; - /* Master clock */ - struct clk *mclk; /* ISIF base address */ void __iomem *base_addr; /* ISIF Linear Table 0 */ @@ -604,7 +601,7 @@ static int isif_config_raw(void) if (module_params->compress.alg == ISIF_ALAW) val |= ISIF_ALAW_ENABLE; - val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT); + val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); regw(val, CGAMMAWD); /* Configure DPCM compression settings */ @@ -1039,6 +1036,10 @@ static int isif_probe(struct platform_device *pdev) void *__iomem addr; int status = 0, i; + /* Platform data holds setup_pinmux function ptr */ + if (!pdev->dev.platform_data) + return -ENODEV; + /* * first try to register with vpfe. If not correct platform, then we * don't have to iomap @@ -1047,22 +1048,6 @@ static int isif_probe(struct platform_device *pdev) if (status < 0) return status; - /* Get and enable Master clock */ - isif_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(isif_cfg.mclk)) { - status = PTR_ERR(isif_cfg.mclk); - goto fail_mclk; - } - if (clk_prepare_enable(isif_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Platform data holds setup_pinmux function ptr */ - if (NULL == pdev->dev.platform_data) { - status = -ENODEV; - goto fail_mclk; - } setup_pinmux = pdev->dev.platform_data; /* * setup Mux configuration for ccdc which may be different for @@ -1124,9 +1109,6 @@ fail_nobase_res: release_mem_region(res->start, resource_size(res)); i--; } -fail_mclk: - clk_disable_unprepare(isif_cfg.mclk); - clk_put(isif_cfg.mclk); vpfe_unregister_ccdc_device(&isif_hw_dev); return status; } @@ -1146,8 +1128,6 @@ static int isif_remove(struct platform_device *pdev) i++; } vpfe_unregister_ccdc_device(&isif_hw_dev); - clk_disable_unprepare(isif_cfg.mclk); - clk_put(isif_cfg.mclk); return 0; } diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h index aa69a46..3993aec 100644 --- a/drivers/media/platform/davinci/isif_regs.h +++ b/drivers/media/platform/davinci/isif_regs.h @@ -203,8 +203,8 @@ #define ISIF_LPF_MASK 1 /* GAMMAWD registers */ -#define ISIF_ALAW_GAMA_WD_MASK 0xF -#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_GAMMA_WD_MASK 0xF +#define ISIF_ALAW_GAMMA_WD_SHIFT 1 #define ISIF_ALAW_ENABLE 1 #define ISIF_GAMMAWD_CFA_SHIFT 5 diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 4ca0f9a..33b9660 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -344,7 +344,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS && + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS && !memcmp(&output->modes[i].dv_timings, dv_timings, sizeof(*dv_timings))) break; @@ -385,7 +385,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev, struct v4l2_dv_timings *dv_timings) { if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; return 0; } @@ -412,7 +412,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS) { + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) { if (j == timings->index) break; j++; @@ -431,7 +431,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, * Sets the standard if supported by the current encoder. Return the status. * 0 - success & -EINVAL on error */ -static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id) { struct vpbe_config *cfg = vpbe_dev->cfg; int out_index = vpbe_dev->current_out_index; @@ -442,14 +442,14 @@ static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) V4L2_OUT_CAP_STD)) return -EINVAL; - ret = vpbe_get_std_info(vpbe_dev, *std_id); + ret = vpbe_get_std_info(vpbe_dev, std_id); if (ret) return ret; mutex_lock(&vpbe_dev->lock); ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, - s_std_output, *std_id); + s_std_output, std_id); /* set the lcd controller output for the given mode */ if (!ret) { struct osd_state *osd_device = vpbe_dev->osd_device; @@ -513,9 +513,9 @@ static int vpbe_set_mode(struct vpbe_device *vpbe_dev, */ if (preset_mode->timings_type & VPBE_ENC_STD) return vpbe_s_std(vpbe_dev, - &preset_mode->std_id); + preset_mode->std_id); if (preset_mode->timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { dv_timings = preset_mode->dv_timings; return vpbe_s_dv_timings(vpbe_dev, &dv_timings); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 5e6b0ca..1802f11 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -983,7 +983,7 @@ static int vpbe_display_try_fmt(struct file *file, void *priv, * 0 - success & -EINVAL on error */ static int vpbe_display_s_std(struct file *file, void *priv, - v4l2_std_id *std_id) + v4l2_std_id std_id) { struct vpbe_fh *fh = priv; struct vpbe_layer *layer = fh->layer; @@ -1176,10 +1176,6 @@ vpbe_display_s_dv_timings(struct file *file, void *priv, "Failed to set the dv timings info\n"); return -EINVAL; } - /* set the current norm to zero to be consistent. If STD is used - * v4l2 layer will set the norm properly on successful s_std call - */ - layer->video_dev.current_norm = 0; return 0; } @@ -1202,7 +1198,7 @@ vpbe_display_g_dv_timings(struct file *file, void *priv, /* Get the given standard in the encoder */ if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; } else { return -EINVAL; @@ -1404,6 +1400,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpbe_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1600,7 +1597,7 @@ static int vpbe_display_g_register(struct file *file, void *priv, } static int vpbe_display_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { return 0; } @@ -1693,12 +1690,8 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->vfl_dir = VFL_DIR_TX; if (disp_dev->vpbe_dev->current_timings.timings_type & - VPBE_ENC_STD) { + VPBE_ENC_STD) vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); - vbd->current_norm = - disp_dev->vpbe_dev->current_timings.std_id; - } else - vbd->current_norm = 0; snprintf(vbd->name, sizeof(vbd->name), "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 12ad17c..396a51c 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -52,6 +52,9 @@ static struct platform_device_id vpbe_osd_devtype[] = { .name = DM355_VPBE_OSD_SUBDEV_NAME, .driver_data = VPBE_VERSION_3, }, + { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index bdbebd5..87eef9b 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -51,6 +51,9 @@ static struct platform_device_id vpbe_venc_devtype[] = { .name = DM355_VPBE_VENC_SUBDEV_NAME, .driver_data = VPBE_VERSION_3, }, + { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); @@ -199,6 +202,25 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) } } +static void +venc_enable_vpss_clock(int venc_type, + enum vpbe_enc_timings_type type, + unsigned int pclock) +{ + if (venc_type == VPBE_VERSION_1) + return; + + if (venc_type == VPBE_VERSION_2 && (type == VPBE_ENC_STD || (type == + VPBE_ENC_DV_TIMINGS && pclock <= 27000000))) { + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + vpss_enable_clock(VPSS_VPBE_CLOCK, 1); + return; + } + + if (venc_type == VPBE_VERSION_3 && type == VPBE_ENC_STD) + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 0); +} + #define VDAC_CONFIG_SD_V3 0x0E21A6B6 #define VDAC_CONFIG_SD_V2 0x081141CF /* @@ -217,6 +239,7 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_525_60); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_3) { @@ -262,6 +285,7 @@ static int venc_set_pal(struct v4l2_subdev *sd) if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_625_50); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_3) { @@ -313,9 +337,10 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_2) @@ -360,9 +385,10 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_2) @@ -400,9 +426,10 @@ static int venc_set_720p60_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000); venc_enabledigitaloutput(sd, 0); venc_write(sd, VENC_OSDCLK0, 0); @@ -428,9 +455,10 @@ static int venc_set_1080i30_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000); venc_enabledigitaloutput(sd, 0); venc_write(sd, VENC_OSDCLK0, 0); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 28d019d..8c50d30 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -376,7 +376,7 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) * values in ccdc */ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, - const v4l2_std_id *std_id) + v4l2_std_id std_id) { struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; struct v4l2_mbus_framefmt mbus_fmt; @@ -384,7 +384,7 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, int i, ret = 0; for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { - if (vpfe_standards[i].std_id & *std_id) { + if (vpfe_standards[i].std_id & std_id) { vpfe_dev->std_info.active_pixels = vpfe_standards[i].width; vpfe_dev->std_info.active_lines = @@ -461,7 +461,7 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) /* Configure the default format information */ ret = vpfe_config_image_format(vpfe_dev, - &vpfe_standards[vpfe_dev->std_index].std_id); + vpfe_standards[vpfe_dev->std_index].std_id); if (ret) return ret; @@ -1107,6 +1107,7 @@ static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) static int vpfe_s_input(struct file *file, void *priv, unsigned int index) { struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_subdev *sd; struct vpfe_subdev_info *sdinfo; int subdev_index, inp_index; struct vpfe_route *route; @@ -1138,14 +1139,15 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) } sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + sd = vpfe_dev->sd[subdev_index]; route = &sdinfo->routes[inp_index]; if (route && sdinfo->can_route) { input = route->input; output = route->output; } - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, s_routing, input, output, 0); + if (sd) + ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); if (ret) { v4l2_err(&vpfe_dev->v4l2_dev, @@ -1154,6 +1156,8 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) goto unlock_out; } vpfe_dev->current_subdev = sdinfo; + if (sd) + vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler; vpfe_dev->current_input = index; vpfe_dev->std_index = 0; @@ -1164,7 +1168,7 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) /* set the default image parameters in the device */ ret = vpfe_config_image_format(vpfe_dev, - &vpfe_standards[vpfe_dev->std_index].std_id); + vpfe_standards[vpfe_dev->std_index].std_id); unlock_out: mutex_unlock(&vpfe_dev->lock); return ret; @@ -1189,7 +1193,7 @@ static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) return ret; } -static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpfe_device *vpfe_dev = video_drvdata(file); struct vpfe_subdev_info *sdinfo; @@ -1211,7 +1215,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, *std_id); + core, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); goto unlock_out; @@ -1439,41 +1443,6 @@ static int vpfe_dqbuf(struct file *file, void *priv, buf, file->f_flags & O_NONBLOCK); } -static int vpfe_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, queryctrl, qctrl); - -} - -static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, g_ctrl, ctrl); -} - -static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_ctrl, ctrl); -} - /* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field @@ -1717,7 +1686,7 @@ unlock_out: static long vpfe_param_handler(struct file *file, void *priv, - bool valid_prio, int cmd, void *param) + bool valid_prio, unsigned int cmd, void *param) { struct vpfe_device *vpfe_dev = video_drvdata(file); int ret = 0; @@ -1781,9 +1750,6 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querystd = vpfe_querystd, .vidioc_s_std = vpfe_s_std, .vidioc_g_std = vpfe_g_std, - .vidioc_queryctrl = vpfe_queryctrl, - .vidioc_g_ctrl = vpfe_g_ctrl, - .vidioc_s_ctrl = vpfe_s_ctrl, .vidioc_reqbufs = vpfe_reqbufs, .vidioc_querybuf = vpfe_querybuf, .vidioc_qbuf = vpfe_qbuf, @@ -1918,7 +1884,6 @@ static int vpfe_probe(struct platform_device *pdev) vfd->fops = &vpfe_fops; vfd->ioctl_ops = &vpfe_ioctl_ops; vfd->tvnorms = 0; - vfd->current_norm = V4L2_STD_PAL; vfd->v4l2_dev = &vpfe_dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s_V%d.%d.%d", @@ -2007,6 +1972,7 @@ static int vpfe_probe(struct platform_device *pdev) /* set first sub device as current one */ vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler; /* We have at least one sub device to work with */ mutex_unlock(&ccdc_lock); diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index 28638a8..ea82a8b 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -23,8 +23,8 @@ #include <linux/spinlock.h> #include <linux/kernel.h> #include <linux/io.h> -#include <linux/clk.h> #include <linux/err.h> +#include <linux/pm_runtime.h> #include <linux/v4l2-dv-timings.h> #include <mach/hardware.h> @@ -44,13 +44,13 @@ static struct resource *res; spinlock_t vpif_lock; void __iomem *vpif_base; -struct clk *vpif_clk; +EXPORT_SYMBOL_GPL(vpif_base); /** - * ch_params: video standard configuration parameters for vpif + * vpif_ch_params: video standard configuration parameters for vpif * The table must include all presets from supported subdevices. */ -const struct vpif_channel_config_params ch_params[] = { +const struct vpif_channel_config_params vpif_ch_params[] = { /* HDTV formats */ { .name = "480p59_94", @@ -220,8 +220,10 @@ const struct vpif_channel_config_params ch_params[] = { .stdid = V4L2_STD_625_50, }, }; +EXPORT_SYMBOL_GPL(vpif_ch_params); -const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params); +const unsigned int vpif_ch_params_count = ARRAY_SIZE(vpif_ch_params); +EXPORT_SYMBOL_GPL(vpif_ch_params_count); static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) { @@ -439,19 +441,13 @@ static int vpif_probe(struct platform_device *pdev) goto fail; } - vpif_clk = clk_get(&pdev->dev, "vpif"); - if (IS_ERR(vpif_clk)) { - status = PTR_ERR(vpif_clk); - goto clk_fail; - } - clk_prepare_enable(vpif_clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_get(&pdev->dev); spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); return 0; -clk_fail: - iounmap(vpif_base); fail: release_mem_region(res->start, res_len); return status; @@ -459,11 +455,7 @@ fail: static int vpif_remove(struct platform_device *pdev) { - if (vpif_clk) { - clk_disable_unprepare(vpif_clk); - clk_put(vpif_clk); - } - + pm_runtime_disable(&pdev->dev); iounmap(vpif_base); release_mem_region(res->start, res_len); return 0; @@ -472,13 +464,13 @@ static int vpif_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int vpif_suspend(struct device *dev) { - clk_disable_unprepare(vpif_clk); + pm_runtime_put(dev); return 0; } static int vpif_resume(struct device *dev) { - clk_prepare_enable(vpif_clk); + pm_runtime_get(dev); return 0; } diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h index a1ab6a0..9956e67 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/davinci/vpif.h @@ -638,7 +638,7 @@ struct vpif_channel_config_params { }; extern const unsigned int vpif_ch_params_count; -extern const struct vpif_channel_config_params ch_params[]; +extern const struct vpif_channel_config_params vpif_ch_params[]; struct vpif_video_params; struct vpif_params; diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 5892d2b..5f98df1 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -563,7 +563,7 @@ static int vpif_update_std_info(struct channel_obj *ch) vpif_dbg(2, debug, "vpif_update_std_info\n"); for (index = 0; index < vpif_ch_params_count; index++) { - config = &ch_params[index]; + config = &vpif_ch_params[index]; if (config->hd_sd == 0) { vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) { @@ -1035,6 +1035,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_cap_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1394,7 +1395,7 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) * @priv: file handle * @std_id: ptr to std id */ -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; @@ -1423,7 +1424,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) fh->initialized = 1; /* Call encoder subdevice function to set the standard */ - ch->video.stdid = *std_id; + ch->video.stdid = std_id; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); /* Get the information about the standard */ @@ -1436,7 +1437,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) vpif_config_format(ch); /* set standard in the sub device */ - ret = v4l2_subdev_call(ch->sd, core, s_std, *std_id); + ret = v4l2_subdev_call(ch->sd, core, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); return ret; @@ -1923,7 +1924,8 @@ static int vpif_dbg_g_register(struct file *file, void *priv, * Returns zero or -EINVAL if write operations fails. */ static int vpif_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ + const struct v4l2_dbg_register *reg) +{ struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index dd249c9..1b3fb5c 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -511,7 +511,7 @@ static int vpif_update_std_info(struct channel_obj *ch) int i; for (i = 0; i < vpif_ch_params_count; i++) { - config = &ch_params[i]; + config = &vpif_ch_params[i]; if (config->hd_sd == 0) { vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) { @@ -1001,6 +1001,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1058,14 +1059,14 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return vb2_qbuf(&common->buffer_queue, buf); } -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; int ret = 0; - if (!(*std_id & VPIF_V4L2_STD)) + if (!(std_id & VPIF_V4L2_STD)) return -EINVAL; if (common->started) { @@ -1074,7 +1075,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } /* Call encoder subdevice function to set the standard */ - ch->video.stdid = *std_id; + ch->video.stdid = std_id; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); /* Get the information about the standard */ if (vpif_update_resolution(ch)) @@ -1092,14 +1093,14 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) vpif_config_format(ch); ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, - s_std_output, *std_id); + s_std_output, std_id); if (ret < 0) { vpif_err("Failed to set output standard\n"); return ret; } ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, - s_std, *std_id); + s_std, std_id); if (ret < 0) vpif_err("Failed to set standard for sub devices\n"); return ret; @@ -1567,7 +1568,8 @@ static int vpif_dbg_g_register(struct file *file, void *priv, * Returns zero or -EINVAL if write operations fails. */ static int vpif_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ + const struct v4l2_dbg_register *reg) +{ struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index a19c552..8a2f01e 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -17,14 +17,11 @@ * * common vpss system module platform driver for all video drivers. */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/compiler.h> #include <linux/io.h> +#include <linux/pm_runtime.h> + #include <media/davinci/vpss.h> MODULE_LICENSE("GPL"); @@ -99,7 +96,7 @@ enum vpss_platform_type { /* * vpss operations. Depends on platform. Not all functions are available - * on all platforms. The api, first check if a functio is available before + * on all platforms. The api, first check if a function is available before * invoking it. In the probe, the function ptrs are initialized based on * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. */ @@ -114,7 +111,7 @@ struct vpss_hw_ops { void (*set_sync_pol)(struct vpss_sync_pol); /* set the PG_FRAME_SIZE register*/ void (*set_pg_frame_size)(struct vpss_pg_frame_size); - /* check and clear interrupt if occured */ + /* check and clear interrupt if occurred */ int (*dma_complete_interrupt)(void); }; @@ -233,7 +230,7 @@ EXPORT_SYMBOL(vpss_clear_wbl_overflow); /* * dm355_enable_clock - Enable VPSS Clock - * @clock_sel: CLock to be enabled/disabled + * @clock_sel: Clock to be enabled/disabled * @en: enable/disable flag * * This is called to enable or disable a vpss clock @@ -490,6 +487,10 @@ static int vpss_probe(struct platform_device *pdev) } else oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + pm_runtime_enable(&pdev->dev); + + pm_runtime_get(&pdev->dev); + spin_lock_init(&oper_cfg.vpss_lock); dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); return 0; @@ -507,6 +508,7 @@ static int vpss_remove(struct platform_device *pdev) { struct resource *res; + pm_runtime_disable(&pdev->dev); iounmap(oper_cfg.vpss_regs_base0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); @@ -518,10 +520,28 @@ static int vpss_remove(struct platform_device *pdev) return 0; } +static int vpss_suspend(struct device *dev) +{ + pm_runtime_put(dev); + return 0; +} + +static int vpss_resume(struct device *dev) +{ + pm_runtime_get(dev); + return 0; +} + +static const struct dev_pm_ops vpss_pm_ops = { + .suspend = vpss_suspend, + .resume = vpss_resume, +}; + static struct platform_driver vpss_driver = { .driver = { .name = "vpss", .owner = THIS_MODULE, + .pm = &vpss_pm_ops, }, .remove = vpss_remove, .probe = vpss_probe, diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 386c0a7..40a73f7 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -80,6 +80,9 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (src_vb && dst_vb) { + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_vb, vb_state); v4l2_m2m_buf_done(dst_vb, vb_state); @@ -584,6 +587,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -596,6 +600,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 6f5b5a4..e22d147 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -12,7 +12,6 @@ #include <linux/io.h> #include <linux/delay.h> -#include <mach/map.h> #include "gsc-core.h" diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index f997a52..6ff99b5 100644 --- a/drivers/media/platform/s5p-fimc/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -1,21 +1,22 @@ -config VIDEO_SAMSUNG_S5P_FIMC - bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)" +config VIDEO_SAMSUNG_EXYNOS4_IS + bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME help Say Y here to enable camera host interface devices for Samsung S5P and EXYNOS SoC series. -if VIDEO_SAMSUNG_S5P_FIMC +if VIDEO_SAMSUNG_EXYNOS4_IS config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV + select MFD_SYSCON if OF help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host - interface and video postprocessor (FIMC and FIMC-LITE) devices. + interface and video postprocessor (FIMC) devices. To compile this driver as a module, choose M here: the module will be called s5p-fimc. @@ -45,4 +46,16 @@ config VIDEO_EXYNOS_FIMC_LITE module will be called exynos-fimc-lite. endif +config VIDEO_EXYNOS4_FIMC_IS + tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" + select VIDEOBUF2_DMA_CONTIG + depends on OF + select FW_LOADER + help + This is a V4L2 driver for Samsung EXYNOS4x12 SoC series + FIMC-IS (Imaging Subsystem). + + To compile this driver as a module, choose M here: the + module will be called exynos4-fimc-is. + endif # VIDEO_SAMSUNG_S5P_FIMC diff --git a/drivers/media/platform/s5p-fimc/Makefile b/drivers/media/platform/exynos4-is/Makefile index 4648514..f25f463 100644 --- a/drivers/media/platform/s5p-fimc/Makefile +++ b/drivers/media/platform/exynos4-is/Makefile @@ -1,7 +1,10 @@ -s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o +exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o +exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o +obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index f553cc2..528f413 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -27,32 +27,33 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include "fimc-mdevice.h" +#include "media-dev.h" #include "fimc-core.h" #include "fimc-reg.h" static int fimc_capture_hw_init(struct fimc_dev *fimc) { + struct fimc_source_info *si = &fimc->vid_cap.source_config; struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct fimc_pipeline *p = &fimc->pipeline; - struct fimc_sensor_info *sensor; + int ret; unsigned long flags; - int ret = 0; - if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL) - return -ENXIO; - if (ctx->s_frame.fmt == NULL) + if (ctx == NULL || ctx->s_frame.fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]); + if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) { + ret = fimc_hw_camblk_cfg_writeback(fimc); + if (ret < 0) + return ret; + } spin_lock_irqsave(&fimc->slock, flags); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_set_yuv_order(ctx); - fimc_hw_set_camera_polarity(fimc, &sensor->pdata); - fimc_hw_set_camera_type(fimc, &sensor->pdata); - fimc_hw_set_camera_source(fimc, &sensor->pdata); + fimc_hw_set_camera_polarity(fimc, si); + fimc_hw_set_camera_type(fimc, si); + fimc_hw_set_camera_source(fimc, si); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); ret = fimc_set_scaler_info(ctx); @@ -65,7 +66,7 @@ static int fimc_capture_hw_init(struct fimc_dev *fimc) fimc_hw_set_effect(ctx); fimc_hw_set_output_path(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); } @@ -168,7 +169,7 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) fimc_hw_set_effect(ctx); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); @@ -286,8 +287,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc_activate_capture(ctx); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + return fimc_pipeline_call(fimc, set_stream, + &fimc->pipeline, 1); } return 0; @@ -443,35 +444,28 @@ static void buffer_queue(struct vb2_buffer *vb) if (vb2_is_streaming(&vid_cap->vbq) && vid_cap->active_buf_cnt >= min_bufs && !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { + int ret; + fimc_activate_capture(ctx); spin_unlock_irqrestore(&fimc->slock, flags); - if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) + return; + + ret = fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 1); + if (ret < 0) + v4l2_err(&vid_cap->vfd, "stream on failed: %d\n", ret); return; } spin_unlock_irqrestore(&fimc->slock, flags); } -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->fimc_dev->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->fimc_dev->lock); -} - static struct vb2_ops fimc_capture_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -530,7 +524,7 @@ static int fimc_capture_open(struct file *file) goto unlock; } - if (++fimc->vid_cap.refcnt == 1) { + if (v4l2_fh_is_singular_file(file)) { ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vid_cap.vfd.entity, true); @@ -543,8 +537,9 @@ static int fimc_capture_open(struct file *file) if (ret < 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); pm_runtime_put_sync(&fimc->pdev->dev); - fimc->vid_cap.refcnt--; v4l2_fh_release(file); + } else { + fimc->vid_cap.refcnt++; } } unlock: @@ -553,59 +548,34 @@ unlock: return ret; } -static int fimc_capture_close(struct file *file) +static int fimc_capture_release(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); + struct fimc_vid_cap *vc = &fimc->vid_cap; int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); mutex_lock(&fimc->lock); - if (--fimc->vid_cap.refcnt == 0) { + if (v4l2_fh_is_singular_file(file)) { + if (vc->streaming) { + media_entity_pipeline_stop(&vc->vfd.entity); + vc->streaming = false; + } clear_bit(ST_CAPT_BUSY, &fimc->state); fimc_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); + fimc->vid_cap.refcnt--; } pm_runtime_put(&fimc->pdev->dev); - if (fimc->vid_cap.refcnt == 0) { - vb2_queue_release(&fimc->vid_cap.vbq); + if (v4l2_fh_is_singular_file(file)) fimc_ctrls_delete(fimc->vid_cap.ctx); - } - - ret = v4l2_fh_release(file); - - mutex_unlock(&fimc->lock); - return ret; -} - -static unsigned int fimc_capture_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_dev *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return POLL_ERR; - ret = vb2_poll(&fimc->vid_cap.vbq, file, wait); - mutex_unlock(&fimc->lock); - - return ret; -} - -static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_dev *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = vb2_mmap(&fimc->vid_cap.vbq, vma); + ret = vb2_fop_release(file); mutex_unlock(&fimc->lock); return ret; @@ -614,10 +584,10 @@ static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) static const struct v4l2_file_operations fimc_capture_fops = { .owner = THIS_MODULE, .open = fimc_capture_open, - .release = fimc_capture_close, - .poll = fimc_capture_poll, + .release = fimc_capture_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = fimc_capture_mmap, + .mmap = vb2_fop_mmap, }; /* @@ -642,18 +612,22 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, fimc_fmt_is_user_defined(ctx->s_frame.fmt->color)) *code = ctx->s_frame.fmt->mbus_code; - if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK) + if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE) mask |= FMT_FLAGS_M2M; + if (pad == FIMC_SD_PAD_SINK_FIFO) + mask = FMT_FLAGS_WRITEBACK; + ffmt = fimc_find_format(fourcc, code, mask, 0); if (WARN_ON(!ffmt)) return NULL; + if (code) *code = ffmt->mbus_code; if (fourcc) *fourcc = ffmt->fourcc; - if (pad == FIMC_SD_PAD_SINK) { + if (pad != FIMC_SD_PAD_SOURCE) { max_w = fimc_fmt_is_user_defined(ffmt->color) ? pl->scaler_dis_w : pl->scaler_en_w; /* Apply the camera input interface pixel constraints */ @@ -768,16 +742,13 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, /* * The video node ioctl operations */ -static int fimc_vidioc_querycap_capture(struct file *file, void *priv, +static int fimc_cap_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct fimc_dev *fimc = video_drvdata(file); - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; - + __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE); return 0; } @@ -887,7 +858,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, tfmt->width = mf->width; tfmt->height = mf->height; ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SINK); + NULL, &fcc, FIMC_SD_PAD_SINK_CAM); ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, NULL, &fcc, FIMC_SD_PAD_SOURCE); if (ffmt && ffmt->mbus_code) @@ -974,7 +945,7 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, - FIMC_SD_PAD_SINK); + FIMC_SD_PAD_SINK_CAM); ctx->s_frame.f_width = pix->width; ctx->s_frame.f_height = pix->height; } @@ -1028,7 +999,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf; + struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt; struct fimc_frame *ff = &ctx->d_frame; struct fimc_fmt *s_fmt = NULL; int ret, i; @@ -1040,7 +1011,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, - FIMC_SD_PAD_SINK); + FIMC_SD_PAD_SINK_CAM); ctx->s_frame.f_width = pix->width; ctx->s_frame.f_height = pix->height; } @@ -1157,44 +1128,51 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) static int fimc_pipeline_validate(struct fimc_dev *fimc) { struct v4l2_subdev_format sink_fmt, src_fmt; - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct v4l2_subdev *sd; - struct media_pad *pad; - int ret; - - /* Start with the video capture node pad */ - pad = media_entity_remote_source(&vid_cap->vd_pad); - if (pad == NULL) - return -EPIPE; - /* FIMC.{N} subdevice */ - sd = media_entity_to_v4l2_subdev(pad->entity); + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct v4l2_subdev *sd = &vc->subdev; + struct media_pad *sink_pad, *src_pad; + int i, ret; while (1) { - /* Retrieve format at the sink pad */ - pad = &sd->entity.pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) + /* + * Find current entity sink pad and any remote sink pad linked + * to it. We stop if there is no sink pad in current entity or + * it is not linked to any other remote entity. + */ + src_pad = NULL; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct media_pad *p = &sd->entity.pads[i]; + + if (p->flags & MEDIA_PAD_FL_SINK) { + sink_pad = p; + src_pad = media_entity_remote_source(sink_pad); + if (src_pad) + break; + } + } + + if (src_pad == NULL || + media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; + /* Don't call FIMC subdev operation to avoid nested locking */ - if (sd == &fimc->vid_cap.subdev) { - struct fimc_frame *ff = &vid_cap->ctx->s_frame; + if (sd == &vc->subdev) { + struct fimc_frame *ff = &vc->ctx->s_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; } else { - sink_fmt.pad = pad->index; + sink_fmt.pad = sink_pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; } - /* Retrieve format at the source pad */ - pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - sd = media_entity_to_v4l2_subdev(pad->entity); - src_fmt.pad = pad->index; + /* Retrieve format at the source pad */ + sd = media_entity_to_v4l2_subdev(src_pad->entity); + src_fmt.pad = src_pad->index; src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) @@ -1208,7 +1186,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (sd == fimc->pipeline.subdevs[IDX_SENSOR] && fimc_user_defined_mbus_fmt(src_fmt.format.code)) { struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES]; - struct fimc_frame *frame = &vid_cap->ctx->d_frame; + struct fimc_frame *frame = &vc->ctx->d_frame; unsigned int i; ret = fimc_get_sensor_frame_desc(sd, plane_fmt, @@ -1230,96 +1208,80 @@ static int fimc_cap_streamon(struct file *file, void *priv, { struct fimc_dev *fimc = video_drvdata(file); struct fimc_pipeline *p = &fimc->pipeline; - struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct media_entity *entity = &vc->vfd.entity; + struct fimc_source_info *si = NULL; + struct v4l2_subdev *sd; int ret; if (fimc_capture_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, p->m_pipeline); if (ret < 0) return ret; - if (fimc->vid_cap.user_subdev_api) { + sd = p->subdevs[IDX_SENSOR]; + if (sd) + si = v4l2_get_subdev_hostdata(sd); + + if (si == NULL) { + ret = -EPIPE; + goto err_p_stop; + } + /* + * Save configuration data related to currently attached image + * sensor or other data source, e.g. FIMC-IS. + */ + vc->source_config = *si; + + if (vc->input == GRP_ID_FIMC_IS) + vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; + + if (vc->user_subdev_api) { ret = fimc_pipeline_validate(fimc); - if (ret < 0) { - media_entity_pipeline_stop(&sd->entity); - return ret; - } + if (ret < 0) + goto err_p_stop; } - return vb2_streamon(&fimc->vid_cap.vbq, type); + + ret = vb2_ioctl_streamon(file, priv, type); + if (!ret) { + vc->streaming = true; + return ret; + } + +err_p_stop: + media_entity_pipeline_stop(entity); + return ret; } static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; - ret = vb2_streamoff(&fimc->vid_cap.vbq, type); - if (ret == 0) - media_entity_pipeline_stop(&sd->entity); - return ret; + ret = vb2_ioctl_streamoff(file, priv, type); + if (ret < 0) + return ret; + + media_entity_pipeline_stop(&fimc->vid_cap.vfd.entity); + fimc->vid_cap.streaming = false; + return 0; } static int fimc_cap_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { struct fimc_dev *fimc = video_drvdata(file); - int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs); + int ret; + + ret = vb2_ioctl_reqbufs(file, priv, reqbufs); if (!ret) fimc->vid_cap.reqbufs_count = reqbufs->count; - return ret; -} - -static int fimc_cap_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_querybuf(&fimc->vid_cap.vbq, buf); -} - -static int fimc_cap_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_qbuf(&fimc->vid_cap.vbq, buf); -} - -static int fimc_cap_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_expbuf(&fimc->vid_cap.vbq, eb); -} - -static int fimc_cap_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); -} - -static int fimc_cap_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct fimc_dev *fimc = video_drvdata(file); - return vb2_create_bufs(&fimc->vid_cap.vbq, create); -} - -static int fimc_cap_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_prepare_buf(&fimc->vid_cap.vbq, b); + return ret; } static int fimc_cap_g_selection(struct file *file, void *fh, @@ -1410,7 +1372,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh, } static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { - .vidioc_querycap = fimc_vidioc_querycap_capture, + .vidioc_querycap = fimc_cap_querycap, .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, @@ -1418,14 +1380,12 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, .vidioc_reqbufs = fimc_cap_reqbufs, - .vidioc_querybuf = fimc_cap_querybuf, - - .vidioc_qbuf = fimc_cap_qbuf, - .vidioc_dqbuf = fimc_cap_dqbuf, - .vidioc_expbuf = fimc_cap_expbuf, - - .vidioc_prepare_buf = fimc_cap_prepare_buf, - .vidioc_create_bufs = fimc_cap_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_streamon = fimc_cap_streamon, .vidioc_streamoff = fimc_cap_streamoff, @@ -1487,7 +1447,7 @@ static const struct media_entity_operations fimc_sd_media_ops = { void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { - struct fimc_sensor_info *sensor; + struct fimc_source_info *si; struct fimc_vid_buffer *buf; struct fimc_md *fmd; struct fimc_dev *fimc; @@ -1496,11 +1456,12 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, if (sd == NULL) return; - sensor = v4l2_get_subdev_hostdata(sd); + si = v4l2_get_subdev_hostdata(sd); fmd = entity_to_fimc_mdev(&sd->entity); spin_lock_irqsave(&fmd->slock, flags); - fimc = sensor ? sensor->host : NULL; + + fimc = si ? source_to_sensor_info(si)->host : NULL; if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY && test_bit(ST_CAPT_PEND, &fimc->state)) { @@ -1537,25 +1498,37 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *ff = &ctx->s_frame; struct v4l2_mbus_framefmt *mf; - struct fimc_frame *ff; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); fmt->format = *mf; return 0; } - mf = &fmt->format; - mf->colorspace = V4L2_COLORSPACE_JPEG; - ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame; + mf = &fmt->format; mutex_lock(&fimc->lock); - /* The pixel code is same on both input and output pad */ - if (!WARN_ON(ctx->s_frame.fmt == NULL)) - mf->code = ctx->s_frame.fmt->mbus_code; - mf->width = ff->f_width; - mf->height = ff->f_height; + + switch (fmt->pad) { + case FIMC_SD_PAD_SOURCE: + if (!WARN_ON(ff->fmt == NULL)) + mf->code = ff->fmt->mbus_code; + /* Sink pads crop rectangle size */ + mf->width = ff->width; + mf->height = ff->height; + break; + case FIMC_SD_PAD_SINK_FIFO: + *mf = fimc->vid_cap.wb_fmt; + break; + case FIMC_SD_PAD_SINK_CAM: + default: + *mf = fimc->vid_cap.ci_fmt; + break; + } + mutex_unlock(&fimc->lock); + mf->colorspace = V4L2_COLORSPACE_JPEG; return 0; } @@ -1566,15 +1539,15 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; - struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct fimc_ctx *ctx = vc->ctx; struct fimc_frame *ff; struct fimc_fmt *ffmt; dbg("pad%d: code: 0x%x, %dx%d", fmt->pad, mf->code, mf->width, mf->height); - if (fmt->pad == FIMC_SD_PAD_SOURCE && - vb2_is_busy(&fimc->vid_cap.vbq)) + if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq)) return -EBUSY; mutex_lock(&fimc->lock); @@ -1596,21 +1569,32 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, fimc_alpha_ctrl_update(ctx); fimc_capture_mark_jpeg_xfer(ctx, ffmt->color); - - ff = fmt->pad == FIMC_SD_PAD_SINK ? - &ctx->s_frame : &ctx->d_frame; + if (fmt->pad == FIMC_SD_PAD_SOURCE) { + ff = &ctx->d_frame; + /* Sink pads crop rectangle size */ + mf->width = ctx->s_frame.width; + mf->height = ctx->s_frame.height; + } else { + ff = &ctx->s_frame; + } mutex_lock(&fimc->lock); set_frame_bounds(ff, mf->width, mf->height); - fimc->vid_cap.mf = *mf; + + if (fmt->pad == FIMC_SD_PAD_SINK_FIFO) + vc->wb_fmt = *mf; + else if (fmt->pad == FIMC_SD_PAD_SINK_CAM) + vc->ci_fmt = *mf; + ff->fmt = ffmt; /* Reset the crop rectangle if required. */ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE))) set_frame_crop(ff, 0, 0, mf->width, mf->height); - if (fmt->pad == FIMC_SD_PAD_SINK) + if (fmt->pad != FIMC_SD_PAD_SOURCE) ctx->state &= ~FIMC_COMPOSE; + mutex_unlock(&fimc->lock); return 0; } @@ -1625,7 +1609,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_rect *r = &sel->r; struct v4l2_rect *try_sel; - if (sel->pad != FIMC_SD_PAD_SINK) + if (sel->pad == FIMC_SD_PAD_SOURCE) return -EINVAL; mutex_lock(&fimc->lock); @@ -1681,7 +1665,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_rect *try_sel; unsigned long flags; - if (sel->pad != FIMC_SD_PAD_SINK) + if (sel->pad == FIMC_SD_PAD_SOURCE) return -EINVAL; mutex_lock(&fimc->lock); @@ -1752,9 +1736,9 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev) { struct video_device *vfd = &fimc->vid_cap.vfd; - struct fimc_vid_cap *vid_cap; + struct vb2_queue *q = &fimc->vid_cap.vbq; struct fimc_ctx *ctx; - struct vb2_queue *q; + struct fimc_vid_cap *vid_cap; int ret = -ENOMEM; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -1776,27 +1760,27 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->v4l2_dev = v4l2_dev; vfd->minor = -1; vfd->release = video_device_release_empty; + vfd->queue = q; vfd->lock = &fimc->lock; video_set_drvdata(vfd, fimc); - vid_cap = &fimc->vid_cap; vid_cap->active_buf_cnt = 0; - vid_cap->reqbufs_count = 0; - vid_cap->refcnt = 0; + vid_cap->reqbufs_count = 0; + vid_cap->ctx = ctx; INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - vid_cap->ctx = ctx; - q = &fimc->vid_cap.vbq; memset(q, 0, sizeof(*q)); q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; - q->drv_priv = fimc->vid_cap.ctx; + q->drv_priv = ctx; q->ops = &fimc_capture_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct fimc_vid_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &fimc->lock; ret = vb2_queue_init(q); if (ret) @@ -1882,10 +1866,11 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) int ret; v4l2_subdev_init(sd, &fimc_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id); - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, fimc->vid_cap.sd_pads, 0); diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 0f513dd..379a5e9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -20,7 +20,10 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/list.h> +#include <linux/mfd/syscon.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <linux/clk.h> #include <media/v4l2-ioctl.h> @@ -29,7 +32,7 @@ #include "fimc-core.h" #include "fimc-reg.h" -#include "fimc-mdevice.h" +#include "media-dev.h" static char *fimc_clocks[MAX_FIMC_CLOCKS] = { "sclk_fimc", "fimc" @@ -77,6 +80,10 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, }, { + .name = "YUV 4:4:4", + .mbus_code = V4L2_MBUS_FMT_YUV10_1X30, + .flags = FMT_FLAGS_WRITEBACK, + }, { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = { 16 }, @@ -206,6 +213,17 @@ struct fimc_fmt *fimc_get_format(unsigned int index) return &fimc_formats[index]; } +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps) +{ + strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + cap->device_caps = caps; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; +} + int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation) { @@ -405,34 +423,34 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { case FIMC_FMT_YCRYCB422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; break; case FIMC_FMT_CBYCRY422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; break; case FIMC_FMT_CRYCBY422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; break; case FIMC_FMT_YCBYCR422: default: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; break; } dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { case FIMC_FMT_YCRYCB422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; break; case FIMC_FMT_CBYCRY422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; break; case FIMC_FMT_CRYCBY422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; break; case FIMC_FMT_YCBYCR422: default: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; break; } dbg("ctx->out_order_1p= %d", ctx->out_order_1p); @@ -440,14 +458,14 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; + bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) depth += f->fmt->depth[i]; f->dma_offset.y_h = f->offs_h; - if (!variant->pix_hoff) + if (!pix_hoff) f->dma_offset.y_h *= (depth >> 3); f->dma_offset.y_v = f->offs_v; @@ -458,7 +476,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cr_h = f->offs_h; f->dma_offset.cr_v = f->offs_v; - if (!variant->pix_hoff) { + if (!pix_hoff) { if (f->fmt->colplanes == 3) { f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; @@ -589,7 +607,6 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -606,7 +623,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx) ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - if (variant->has_alpha) + if (ctx->fimc_dev->drv_data->alpha_color) ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, max_alpha, 1, 0); @@ -677,7 +694,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; - if (ctrl == NULL || !fimc->variant->has_alpha) + if (ctrl == NULL || !fimc->drv_data->alpha_color) return; v4l2_ctrl_lock(ctrl); @@ -865,43 +882,113 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) return 0; } +static const struct of_device_id fimc_of_match[]; + +static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq) +{ + struct device *dev = &fimc->pdev->dev; + struct device_node *node = dev->of_node; + const struct of_device_id *of_id; + struct fimc_variant *v; + struct fimc_pix_limit *lim; + u32 args[FIMC_PIX_LIMITS_MAX]; + int ret; + + if (of_property_read_bool(node, "samsung,lcd-wb")) + return -ENODEV; + + v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL); + if (!v) + return -ENOMEM; + + of_id = of_match_node(fimc_of_match, node); + if (!of_id) + return -EINVAL; + fimc->drv_data = of_id->data; + ret = of_property_read_u32_array(node, "samsung,pix-limits", + args, FIMC_PIX_LIMITS_MAX); + if (ret < 0) + return ret; + + lim = (struct fimc_pix_limit *)&v[1]; + + lim->scaler_en_w = args[0]; + lim->scaler_dis_w = args[1]; + lim->out_rot_en_w = args[2]; + lim->out_rot_dis_w = args[3]; + v->pix_limit = lim; + + ret = of_property_read_u32_array(node, "samsung,min-pix-sizes", + args, 2); + v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0]; + v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1]; + ret = of_property_read_u32_array(node, "samsung,min-pix-alignment", + args, 2); + v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0]; + v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1]; + + ret = of_property_read_u32(node, "samsung,rotators", &args[1]); + v->has_inp_rot = ret ? 1 : args[1] & 0x01; + v->has_out_rot = ret ? 1 : args[1] & 0x10; + v->has_mainscaler_ext = of_property_read_bool(node, + "samsung,mainscaler-ext"); + + v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb"); + v->has_cam_if = of_property_read_bool(node, "samsung,cam-if"); + of_property_read_u32(node, "clock-frequency", clk_freq); + fimc->id = of_alias_get_id(node, "fimc"); + + fimc->variant = v; + return 0; +} + static int fimc_probe(struct platform_device *pdev) { - const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); - struct s5p_platform_fimc *pdata; + struct device *dev = &pdev->dev; + u32 lclk_freq = 0; struct fimc_dev *fimc; struct resource *res; int ret = 0; - if (pdev->id >= drv_data->num_entities) { - dev_err(&pdev->dev, "Invalid platform device id: %d\n", - pdev->id); - return -EINVAL; - } - - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->id = pdev->id; - - fimc->variant = drv_data->variant[fimc->id]; fimc->pdev = pdev; - pdata = pdev->dev.platform_data; - fimc->pdata = pdata; + + if (dev->of_node) { + ret = fimc_parse_dt(fimc, &lclk_freq); + if (ret < 0) + return ret; + } else { + fimc->drv_data = fimc_get_drvdata(pdev); + fimc->id = pdev->id; + } + if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities || + fimc->id < 0) { + dev_err(dev, "Invalid driver data or device id (%d)\n", + fimc->id); + return -EINVAL; + } + if (!dev->of_node) + fimc->variant = fimc->drv_data->variant[fimc->id]; init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); + fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node); + if (IS_ERR(fimc->sysreg)) + return PTR_ERR(fimc->sysreg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -909,7 +996,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + if (lclk_freq == 0) + lclk_freq = fimc->drv_data->lclk_frequency; + + ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq); if (ret < 0) return ret; @@ -917,10 +1007,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, fimc_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + dev_err(dev, "failed to install irq (%d)\n", ret); goto err_clk; } @@ -929,23 +1019,23 @@ static int fimc_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id); + dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_unregister_capture_subdev(fimc); err_clk: @@ -1048,35 +1138,21 @@ static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [1] = { .scaler_en_w = 4224, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [2] = { .scaler_en_w = 1920, .scaler_dis_w = 8192, - .in_rot_en_h = 1280, - .in_rot_dis_w = 8192, .out_rot_en_w = 1280, .out_rot_dis_w = 1920, }, - [3] = { - .scaler_en_w = 1920, - .scaler_dis_w = 8192, - .in_rot_en_h = 1366, - .in_rot_dis_w = 8192, - .out_rot_en_w = 1366, - .out_rot_dis_w = 1920, - }, }; static const struct fimc_variant fimc0_variant_s5p = { @@ -1087,7 +1163,6 @@ static const struct fimc_variant fimc0_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[0], }; @@ -1097,12 +1172,10 @@ static const struct fimc_variant fimc2_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc0_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1110,12 +1183,10 @@ static const struct fimc_variant fimc0_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc1_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1124,80 +1195,18 @@ static const struct fimc_variant fimc1_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 1, .min_vsize_align = 1, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, - .pix_hoff = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; -static const struct fimc_variant fimc0_variant_exynos4210 = { - .pix_hoff = 1, - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc3_variant_exynos4210 = { - .pix_hoff = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[3], -}; - -static const struct fimc_variant fimc0_variant_exynos4x12 = { - .pix_hoff = 1, - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_isp_wb = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc3_variant_exynos4x12 = { - .pix_hoff = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[3], -}; - /* S5PC100 */ static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { @@ -1205,8 +1214,9 @@ static const struct fimc_drvdata fimc_drvdata_s5p = { [1] = &fimc0_variant_s5p, [2] = &fimc2_variant_s5p, }, - .num_entities = 3, + .num_entities = 3, .lclk_frequency = 133000000UL, + .out_buf_count = 4, }; /* S5PV210, S5PC110 */ @@ -1216,32 +1226,30 @@ static const struct fimc_drvdata fimc_drvdata_s5pv210 = { [1] = &fimc1_variant_s5pv210, [2] = &fimc2_variant_s5pv210, }, - .num_entities = 3, - .lclk_frequency = 166000000UL, + .num_entities = 3, + .lclk_frequency = 166000000UL, + .out_buf_count = 4, + .dma_pix_hoff = 1, }; /* EXYNOS4210, S5PV310, S5PC210 */ static const struct fimc_drvdata fimc_drvdata_exynos4210 = { - .variant = { - [0] = &fimc0_variant_exynos4210, - [1] = &fimc0_variant_exynos4210, - [2] = &fimc0_variant_exynos4210, - [3] = &fimc3_variant_exynos4210, - }, - .num_entities = 4, + .num_entities = 4, .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; /* EXYNOS4212, EXYNOS4412 */ static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { - .variant = { - [0] = &fimc0_variant_exynos4x12, - [1] = &fimc0_variant_exynos4x12, - [2] = &fimc0_variant_exynos4x12, - [3] = &fimc3_variant_exynos4x12, - }, - .num_entities = 4, - .lclk_frequency = 166000000UL, + .num_entities = 4, + .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; static const struct platform_device_id fimc_driver_ids[] = { @@ -1258,9 +1266,22 @@ static const struct platform_device_id fimc_driver_ids[] = { .name = "exynos4x12-fimc", .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, - {}, + { }, +}; + +static const struct of_device_id fimc_of_match[] = { + { + .compatible = "samsung,s5pv210-fimc", + .data = &fimc_drvdata_s5pv210, + }, { + .compatible = "samsung,exynos4210-fimc", + .data = &fimc_drvdata_exynos4210, + }, { + .compatible = "samsung,exynos4212-fimc", + .data = &fimc_drvdata_exynos4x12, + }, + { /* sentinel */ }, }; -MODULE_DEVICE_TABLE(platform, fimc_driver_ids); static const struct dev_pm_ops fimc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) @@ -1272,9 +1293,10 @@ static struct platform_driver fimc_driver = { .remove = fimc_remove, .id_table = fimc_driver_ids, .driver = { - .name = FIMC_MODULE_NAME, - .owner = THIS_MODULE, - .pm = &fimc_pm_ops, + .of_match_table = fimc_of_match, + .name = FIMC_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &fimc_pm_ops, } }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 412d507..539a3f7 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -12,8 +12,10 @@ /*#define DEBUG*/ #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/spinlock.h> +#include <linux/mfd/syscon.h> #include <linux/types.h> #include <linux/videodev2.h> #include <linux/io.h> @@ -33,7 +35,7 @@ /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) #define MAX_FIMC_CLOCKS 2 -#define FIMC_MODULE_NAME "s5p-fimc" +#define FIMC_DRIVER_NAME "exynos4-fimc" #define FIMC_MAX_DEVS 4 #define FIMC_MAX_OUT_BUFS 4 #define SCALER_MAX_HRATIO 64 @@ -42,6 +44,10 @@ #define FIMC_CAMIF_MAX_HEIGHT 0x2000 #define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M) #define FIMC_MAX_PLANES 3 +#define FIMC_PIX_LIMITS_MAX 4 +#define FIMC_DEF_MIN_SIZE 16 +#define FIMC_DEF_HEIGHT_ALIGN 2 +#define FIMC_DEF_HOR_OFFS_ALIGN 1 /* indices to the clocks array */ enum { @@ -132,36 +138,6 @@ enum fimc_color_fmt { #define FIMC_COLOR_RANGE_NARROW (1 << 3) /** - * struct fimc_fmt - the driver's internal color format data - * @mbus_code: Media Bus pixel code, -1 if not applicable - * @name: format description - * @fourcc: the fourcc code for this format, 0 if not applicable - * @color: the corresponding fimc_color_fmt - * @memplanes: number of physically non-contiguous data planes - * @colplanes: number of physically contiguous data planes - * @depth: per plane driver's private 'number of bits per pixel' - * @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no) - * @flags: flags indicating which operation mode format applies to - */ -struct fimc_fmt { - enum v4l2_mbus_pixelcode mbus_code; - char *name; - u32 fourcc; - u32 color; - u16 memplanes; - u16 colplanes; - u8 depth[VIDEO_MAX_PLANES]; - u16 mdataplanes; - u16 flags; -#define FMT_FLAGS_CAM (1 << 0) -#define FMT_FLAGS_M2M_IN (1 << 1) -#define FMT_FLAGS_M2M_OUT (1 << 2) -#define FMT_FLAGS_M2M (1 << 1 | 1 << 2) -#define FMT_HAS_ALPHA (1 << 3) -#define FMT_FLAGS_COMPRESSED (1 << 4) -}; - -/** * struct fimc_dma_offset - pixel offset information for DMA * @y_h: y value horizontal offset * @y_v: y value vertical offset @@ -299,9 +275,10 @@ struct fimc_m2m_device { int refcnt; }; -#define FIMC_SD_PAD_SINK 0 -#define FIMC_SD_PAD_SOURCE 1 -#define FIMC_SD_PADS_NUM 2 +#define FIMC_SD_PAD_SINK_CAM 0 +#define FIMC_SD_PAD_SINK_FIFO 1 +#define FIMC_SD_PAD_SOURCE 2 +#define FIMC_SD_PADS_NUM 3 /** * struct fimc_vid_cap - camera capture device information @@ -310,7 +287,9 @@ struct fimc_m2m_device { * @subdev: subdev exposing the FIMC processing block * @vd_pad: fimc video capture node pad * @sd_pads: fimc video processing block pads - * @mf: media bus format at the FIMC camera input (and the scaler output) pad + * @ci_fmt: image format at the FIMC camera input (and the scaler output) + * @wb_fmt: image format at the FIMC ISP Writeback input + * @source_config: external image source related configuration structure * @pending_buf_q: the pending buffer queue head * @active_buf_q: the queue head of buffers scheduled in hardware * @vbq: the capture am video buffer queue @@ -329,8 +308,10 @@ struct fimc_vid_cap { struct video_device vfd; struct v4l2_subdev subdev; struct media_pad vd_pad; - struct v4l2_mbus_framefmt mf; struct media_pad sd_pads[FIMC_SD_PADS_NUM]; + struct v4l2_mbus_framefmt ci_fmt; + struct v4l2_mbus_framefmt wb_fmt; + struct fimc_source_info source_config; struct list_head pending_buf_q; struct list_head active_buf_q; struct vb2_queue vbq; @@ -338,6 +319,7 @@ struct fimc_vid_cap { int buf_index; unsigned int frame_count; unsigned int reqbufs_count; + bool streaming; int input_index; int refcnt; u32 input; @@ -365,10 +347,8 @@ struct fimc_pix_limit { /** * struct fimc_variant - FIMC device variant information - * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator - * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface @@ -378,23 +358,18 @@ struct fimc_pix_limit { * @min_out_pixsize: minimum output pixel size * @hor_offs_align: horizontal pixel offset aligment * @min_vsize_align: minimum vertical pixel size alignment - * @out_buf_count: the number of buffers in output DMA sequence */ struct fimc_variant { - unsigned int pix_hoff:1; unsigned int has_inp_rot:1; unsigned int has_out_rot:1; - unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; unsigned int has_isp_wb:1; - unsigned int has_alpha:1; const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; u16 min_vsize_align; - u16 out_buf_count; }; /** @@ -402,11 +377,20 @@ struct fimc_variant { * @variant: variant information for this device * @num_entities: number of fimc instances available in a SoC * @lclk_frequency: local bus clock frequency + * @cistatus2: 1 if the FIMC IPs have CISTATUS2 register + * @dma_pix_hoff: the horizontal DMA offset unit: 1 - pixels, 0 - bytes + * @alpha_color: 1 if alpha color component is supported + * @out_buf_count: maximum number of output DMA buffers supported */ struct fimc_drvdata { const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; + /* Fields common to all FIMC IP instances */ + u8 cistatus2; + u8 dma_pix_hoff; + u8 alpha_color; + u8 out_buf_count; }; #define fimc_get_drvdata(_pdev) \ @@ -420,6 +404,7 @@ struct fimc_ctx; * @lock: the mutex protecting this data structure * @pdev: pointer to the FIMC platform device * @pdata: pointer to the device platform data + * @sysreg: pointer to the SYSREG regmap * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) * @clock: clocks required for FIMC operation @@ -437,8 +422,10 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; + struct regmap *sysreg; const struct fimc_variant *variant; - u16 id; + const struct fimc_drvdata *drv_data; + int id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; wait_queue_head_t irq_queue; @@ -633,6 +620,8 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps); int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); @@ -660,6 +649,15 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc); int fimc_register_driver(void); void fimc_unregister_driver(void); +#ifdef CONFIG_MFD_SYSCON +static inline struct regmap * fimc_get_sysreg_regmap(struct device_node *node) +{ + return syscon_regmap_lookup_by_phandle(node, "samsung,sysreg"); +} +#else +#define fimc_get_sysreg_regmap(node) (NULL) +#endif + /* -----------------------------------------------------*/ /* fimc-m2m.c */ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/exynos4-is/fimc-is-command.h new file mode 100644 index 0000000..0d1f52e --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-command.h @@ -0,0 +1,137 @@ +/* + * Samsung Exynos4x12 FIMC-IS (Imaging Subsystem) driver + * + * FIMC-IS command set definitions + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ + +#ifndef FIMC_IS_CMD_H_ +#define FIMC_IS_CMD_H_ + +#define FIMC_IS_COMMAND_VER 110 /* FIMC-IS command set version 1.10 */ + +/* Enumeration of commands beetween the FIMC-IS and the host processor. */ + +/* HOST to FIMC-IS */ +#define HIC_PREVIEW_STILL 0x0001 +#define HIC_PREVIEW_VIDEO 0x0002 +#define HIC_CAPTURE_STILL 0x0003 +#define HIC_CAPTURE_VIDEO 0x0004 +#define HIC_STREAM_ON 0x0005 +#define HIC_STREAM_OFF 0x0006 +#define HIC_SET_PARAMETER 0x0007 +#define HIC_GET_PARAMETER 0x0008 +#define HIC_SET_TUNE 0x0009 +#define HIC_GET_STATUS 0x000b +/* Sensor part */ +#define HIC_OPEN_SENSOR 0x000c +#define HIC_CLOSE_SENSOR 0x000d +#define HIC_SIMMIAN_INIT 0x000e +#define HIC_SIMMIAN_WRITE 0x000f +#define HIC_SIMMIAN_READ 0x0010 +#define HIC_POWER_DOWN 0x0011 +#define HIC_GET_SET_FILE_ADDR 0x0012 +#define HIC_LOAD_SET_FILE 0x0013 +#define HIC_MSG_CONFIG 0x0014 +#define HIC_MSG_TEST 0x0015 +/* FIMC-IS to HOST */ +#define IHC_GET_SENSOR_NUM 0x1000 +#define IHC_SET_SHOT_MARK 0x1001 +/* parameter1: frame number */ +/* parameter2: confidence level (smile 0~100) */ +/* parameter3: confidence level (blink 0~100) */ +#define IHC_SET_FACE_MARK 0x1002 +/* parameter1: coordinate count */ +/* parameter2: coordinate buffer address */ +#define IHC_FRAME_DONE 0x1003 +/* parameter1: frame start number */ +/* parameter2: frame count */ +#define IHC_AA_DONE 0x1004 +#define IHC_NOT_READY 0x1005 + +#define IH_REPLY_DONE 0x2000 +#define IH_REPLY_NOT_DONE 0x2001 + +enum fimc_is_scenario { + IS_SC_PREVIEW_STILL, + IS_SC_PREVIEW_VIDEO, + IS_SC_CAPTURE_STILL, + IS_SC_CAPTURE_VIDEO, + IS_SC_MAX +}; + +enum fimc_is_sub_scenario { + IS_SC_SUB_DEFAULT, + IS_SC_SUB_PS_VTCALL, + IS_SC_SUB_CS_VTCALL, + IS_SC_SUB_PV_VTCALL, + IS_SC_SUB_CV_VTCALL, +}; + +struct is_common_regs { + u32 hicmd; + u32 hic_sensorid; + u32 hic_param[4]; + u32 reserved1[4]; + + u32 ihcmd; + u32 ihc_sensorid; + u32 ihc_param[4]; + u32 reserved2[4]; + + u32 isp_sensor_id; + u32 isp_param[2]; + u32 reserved3[1]; + + u32 scc_sensor_id; + u32 scc_param[2]; + u32 reserved4[1]; + + u32 dnr_sensor_id; + u32 dnr_param[2]; + u32 reserved5[1]; + + u32 scp_sensor_id; + u32 scp_param[2]; + u32 reserved6[29]; +} __packed; + +struct is_mcuctl_reg { + u32 mcuctl; + u32 bboar; + + u32 intgr0; + u32 intcr0; + u32 intmr0; + u32 intsr0; + u32 intmsr0; + + u32 intgr1; + u32 intcr1; + u32 intmr1; + u32 intsr1; + u32 intmsr1; + + u32 intcr2; + u32 intmr2; + u32 intsr2; + u32 intmsr2; + + u32 gpoctrl; + u32 cpoenctlr; + u32 gpictlr; + + u32 reserved[0xd]; + + struct is_common_regs common; +} __packed; + +#endif /* FIMC_IS_CMD_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c new file mode 100644 index 0000000..e8519e1 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c @@ -0,0 +1,272 @@ +/* + * Samsung Exynos4 SoC series FIMC-IS slave interface driver + * + * Error log interface functions + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ + +#include "fimc-is-errno.h" + +const char * const fimc_is_param_strerr(unsigned int error) +{ + switch (error) { + case ERROR_COMMON_CMD: + return "ERROR_COMMON_CMD: Invalid Command"; + case ERROR_COMMON_PARAMETER: + return "ERROR_COMMON_PARAMETER: Invalid Parameter"; + case ERROR_COMMON_SETFILE_LOAD: + return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading"; + case ERROR_COMMON_SETFILE_ADJUST: + return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted"; + case ERROR_COMMON_SETFILE_INDEX: + return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index"; + case ERROR_COMMON_INPUT_PATH: + return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state"; + case ERROR_COMMON_INPUT_INIT: + return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set"; + case ERROR_COMMON_OUTPUT_PATH: + return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)"; + case ERROR_COMMON_OUTPUT_INIT: + return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set"; + case ERROR_CONTROL_BYPASS: + return "ERROR_CONTROL_BYPASS"; + case ERROR_OTF_INPUT_FORMAT: + return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)"; + case ERROR_OTF_INPUT_WIDTH: + return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; + case ERROR_OTF_INPUT_HEIGHT: + return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_OTF_INPUT_BIT_WIDTH: + return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_INPUT_WIDTH: + return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; + case ERROR_DMA_INPUT_HEIGHT: + return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)"; + case ERROR_DMA_INPUT_FORMAT: + return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)"; + case ERROR_DMA_INPUT_BIT_WIDTH: + return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_INPUT_ORDER: + return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)"; + case ERROR_DMA_INPUT_PLANE: + return "ERROR_DMA_INPUT_PLANE: Invalid palne (DRC: 3, FD: 1, 2, 3)"; + case ERROR_OTF_OUTPUT_WIDTH: + return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)"; + case ERROR_OTF_OUTPUT_HEIGHT: + return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)"; + case ERROR_OTF_OUTPUT_FORMAT: + return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)"; + case ERROR_OTF_OUTPUT_BIT_WIDTH: + return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_OUTPUT_WIDTH: + return "ERROR_DMA_OUTPUT_WIDTH"; + case ERROR_DMA_OUTPUT_HEIGHT: + return "ERROR_DMA_OUTPUT_HEIGHT"; + case ERROR_DMA_OUTPUT_FORMAT: + return "ERROR_DMA_OUTPUT_FORMAT"; + case ERROR_DMA_OUTPUT_BIT_WIDTH: + return "ERROR_DMA_OUTPUT_BIT_WIDTH"; + case ERROR_DMA_OUTPUT_PLANE: + return "ERROR_DMA_OUTPUT_PLANE"; + case ERROR_DMA_OUTPUT_ORDER: + return "ERROR_DMA_OUTPUT_ORDER"; + + /* Sensor Error(100~199) */ + case ERROR_SENSOR_I2C_FAIL: + return "ERROR_SENSOR_I2C_FAIL"; + case ERROR_SENSOR_INVALID_FRAMERATE: + return "ERROR_SENSOR_INVALID_FRAMERATE"; + case ERROR_SENSOR_INVALID_EXPOSURETIME: + return "ERROR_SENSOR_INVALID_EXPOSURETIME"; + case ERROR_SENSOR_INVALID_SIZE: + return "ERROR_SENSOR_INVALID_SIZE"; + case ERROR_SENSOR_INVALID_SETTING: + return "ERROR_SENSOR_INVALID_SETTING"; + case ERROR_SENSOR_ACTURATOR_INIT_FAIL: + return "ERROR_SENSOR_ACTURATOR_INIT_FAIL"; + case ERROR_SENSOR_INVALID_AF_POS: + return "ERROR_SENSOR_INVALID_AF_POS"; + case ERROR_SENSOR_UNSUPPORT_FUNC: + return "ERROR_SENSOR_UNSUPPORT_FUNC"; + case ERROR_SENSOR_UNSUPPORT_PERI: + return "ERROR_SENSOR_UNSUPPORT_PERI"; + case ERROR_SENSOR_UNSUPPORT_AF: + return "ERROR_SENSOR_UNSUPPORT_AF"; + + /* ISP Error (200~299) */ + case ERROR_ISP_AF_BUSY: + return "ERROR_ISP_AF_BUSY"; + case ERROR_ISP_AF_INVALID_COMMAND: + return "ERROR_ISP_AF_INVALID_COMMAND"; + case ERROR_ISP_AF_INVALID_MODE: + return "ERROR_ISP_AF_INVALID_MODE"; + + /* DRC Error (300~399) */ + /* FD Error (400~499) */ + case ERROR_FD_CONFIG_MAX_NUMBER_STATE: + return "ERROR_FD_CONFIG_MAX_NUMBER_STATE"; + case ERROR_FD_CONFIG_MAX_NUMBER_INVALID: + return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID"; + case ERROR_FD_CONFIG_YAW_ANGLE_STATE: + return "ERROR_FD_CONFIG_YAW_ANGLE_STATE"; + case ERROR_FD_CONFIG_YAW_ANGLE_INVALID: + return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n"; + case ERROR_FD_CONFIG_ROLL_ANGLE_STATE: + return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE"; + case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID: + return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID"; + case ERROR_FD_CONFIG_SMILE_MODE_INVALID: + return "ERROR_FD_CONFIG_SMILE_MODE_INVALID"; + case ERROR_FD_CONFIG_BLINK_MODE_INVALID: + return "ERROR_FD_CONFIG_BLINK_MODE_INVALID"; + case ERROR_FD_CONFIG_EYES_DETECT_INVALID: + return "ERROR_FD_CONFIG_EYES_DETECT_INVALID"; + case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID: + return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID"; + case ERROR_FD_CONFIG_ORIENTATION_STATE: + return "ERROR_FD_CONFIG_ORIENTATION_STATE"; + case ERROR_FD_CONFIG_ORIENTATION_INVALID: + return "ERROR_FD_CONFIG_ORIENTATION_INVALID"; + case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID: + return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID"; + case ERROR_FD_RESULT: + return "ERROR_FD_RESULT"; + case ERROR_FD_MODE: + return "ERROR_FD_MODE"; + default: + return "Unknown"; + } +} + +const char * const fimc_is_strerr(unsigned int error) +{ + error &= ~IS_ERROR_TIME_OUT_FLAG; + + switch (error) { + /* General */ + case IS_ERROR_INVALID_COMMAND: + return "IS_ERROR_INVALID_COMMAND"; + case IS_ERROR_REQUEST_FAIL: + return "IS_ERROR_REQUEST_FAIL"; + case IS_ERROR_INVALID_SCENARIO: + return "IS_ERROR_INVALID_SCENARIO"; + case IS_ERROR_INVALID_SENSORID: + return "IS_ERROR_INVALID_SENSORID"; + case IS_ERROR_INVALID_MODE_CHANGE: + return "IS_ERROR_INVALID_MODE_CHANGE"; + case IS_ERROR_INVALID_MAGIC_NUMBER: + return "IS_ERROR_INVALID_MAGIC_NUMBER"; + case IS_ERROR_INVALID_SETFILE_HDR: + return "IS_ERROR_INVALID_SETFILE_HDR"; + case IS_ERROR_BUSY: + return "IS_ERROR_BUSY"; + case IS_ERROR_SET_PARAMETER: + return "IS_ERROR_SET_PARAMETER"; + case IS_ERROR_INVALID_PATH: + return "IS_ERROR_INVALID_PATH"; + case IS_ERROR_OPEN_SENSOR_FAIL: + return "IS_ERROR_OPEN_SENSOR_FAIL"; + case IS_ERROR_ENTRY_MSG_THREAD_DOWN: + return "IS_ERROR_ENTRY_MSG_THREAD_DOWN"; + case IS_ERROR_ISP_FRAME_END_NOT_DONE: + return "IS_ERROR_ISP_FRAME_END_NOT_DONE"; + case IS_ERROR_DRC_FRAME_END_NOT_DONE: + return "IS_ERROR_DRC_FRAME_END_NOT_DONE"; + case IS_ERROR_SCALERC_FRAME_END_NOT_DONE: + return "IS_ERROR_SCALERC_FRAME_END_NOT_DONE"; + case IS_ERROR_ODC_FRAME_END_NOT_DONE: + return "IS_ERROR_ODC_FRAME_END_NOT_DONE"; + case IS_ERROR_DIS_FRAME_END_NOT_DONE: + return "IS_ERROR_DIS_FRAME_END_NOT_DONE"; + case IS_ERROR_TDNR_FRAME_END_NOT_DONE: + return "IS_ERROR_TDNR_FRAME_END_NOT_DONE"; + case IS_ERROR_SCALERP_FRAME_END_NOT_DONE: + return "IS_ERROR_SCALERP_FRAME_END_NOT_DONE"; + case IS_ERROR_WAIT_STREAM_OFF_NOT_DONE: + return "IS_ERROR_WAIT_STREAM_OFF_NOT_DONE"; + case IS_ERROR_NO_MSG_IS_RECEIVED: + return "IS_ERROR_NO_MSG_IS_RECEIVED"; + case IS_ERROR_SENSOR_MSG_FAIL: + return "IS_ERROR_SENSOR_MSG_FAIL"; + case IS_ERROR_ISP_MSG_FAIL: + return "IS_ERROR_ISP_MSG_FAIL"; + case IS_ERROR_DRC_MSG_FAIL: + return "IS_ERROR_DRC_MSG_FAIL"; + case IS_ERROR_LHFD_MSG_FAIL: + return "IS_ERROR_LHFD_MSG_FAIL"; + case IS_ERROR_UNKNOWN: + return "IS_ERROR_UNKNOWN"; + + /* Sensor */ + case IS_ERROR_SENSOR_PWRDN_FAIL: + return "IS_ERROR_SENSOR_PWRDN_FAIL"; + + /* ISP */ + case IS_ERROR_ISP_PWRDN_FAIL: + return "IS_ERROR_ISP_PWRDN_FAIL"; + case IS_ERROR_ISP_MULTIPLE_INPUT: + return "IS_ERROR_ISP_MULTIPLE_INPUT"; + case IS_ERROR_ISP_ABSENT_INPUT: + return "IS_ERROR_ISP_ABSENT_INPUT"; + case IS_ERROR_ISP_ABSENT_OUTPUT: + return "IS_ERROR_ISP_ABSENT_OUTPUT"; + case IS_ERROR_ISP_NONADJACENT_OUTPUT: + return "IS_ERROR_ISP_NONADJACENT_OUTPUT"; + case IS_ERROR_ISP_FORMAT_MISMATCH: + return "IS_ERROR_ISP_FORMAT_MISMATCH"; + case IS_ERROR_ISP_WIDTH_MISMATCH: + return "IS_ERROR_ISP_WIDTH_MISMATCH"; + case IS_ERROR_ISP_HEIGHT_MISMATCH: + return "IS_ERROR_ISP_HEIGHT_MISMATCH"; + case IS_ERROR_ISP_BITWIDTH_MISMATCH: + return "IS_ERROR_ISP_BITWIDTH_MISMATCH"; + case IS_ERROR_ISP_FRAME_END_TIME_OUT: + return "IS_ERROR_ISP_FRAME_END_TIME_OUT"; + + /* DRC */ + case IS_ERROR_DRC_PWRDN_FAIL: + return "IS_ERROR_DRC_PWRDN_FAIL"; + case IS_ERROR_DRC_MULTIPLE_INPUT: + return "IS_ERROR_DRC_MULTIPLE_INPUT"; + case IS_ERROR_DRC_ABSENT_INPUT: + return "IS_ERROR_DRC_ABSENT_INPUT"; + case IS_ERROR_DRC_NONADJACENT_INPUT: + return "IS_ERROR_DRC_NONADJACENT_INPUT"; + case IS_ERROR_DRC_ABSENT_OUTPUT: + return "IS_ERROR_DRC_ABSENT_OUTPUT"; + case IS_ERROR_DRC_NONADJACENT_OUTPUT: + return "IS_ERROR_DRC_NONADJACENT_OUTPUT"; + case IS_ERROR_DRC_FORMAT_MISMATCH: + return "IS_ERROR_DRC_FORMAT_MISMATCH"; + case IS_ERROR_DRC_WIDTH_MISMATCH: + return "IS_ERROR_DRC_WIDTH_MISMATCH"; + case IS_ERROR_DRC_HEIGHT_MISMATCH: + return "IS_ERROR_DRC_HEIGHT_MISMATCH"; + case IS_ERROR_DRC_BITWIDTH_MISMATCH: + return "IS_ERROR_DRC_BITWIDTH_MISMATCH"; + case IS_ERROR_DRC_FRAME_END_TIME_OUT: + return "IS_ERROR_DRC_FRAME_END_TIME_OUT"; + + /* FD */ + case IS_ERROR_FD_PWRDN_FAIL: + return "IS_ERROR_FD_PWRDN_FAIL"; + case IS_ERROR_FD_MULTIPLE_INPUT: + return "IS_ERROR_FD_MULTIPLE_INPUT"; + case IS_ERROR_FD_ABSENT_INPUT: + return "IS_ERROR_FD_ABSENT_INPUT"; + case IS_ERROR_FD_NONADJACENT_INPUT: + return "IS_ERROR_FD_NONADJACENT_INPUT"; + case IS_ERROR_LHFD_FRAME_END_TIME_OUT: + return "IS_ERROR_LHFD_FRAME_END_TIME_OUT"; + default: + return "Unknown"; + } +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h new file mode 100644 index 0000000..3de6f6d --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h @@ -0,0 +1,248 @@ +/* + * Samsung Exynos4 SoC series FIMC-IS slave interface driver + * + * FIMC-IS error code definition + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. +*/ + +#ifndef FIMC_IS_ERR_H_ +#define FIMC_IS_ERR_H_ + +#define IS_ERROR_VER 011 /* IS ERROR VERSION 0.11 */ + +enum { + IS_ERROR_NONE, + + /* General 1 ~ 99 */ + IS_ERROR_INVALID_COMMAND, + IS_ERROR_REQUEST_FAIL, + IS_ERROR_INVALID_SCENARIO, + IS_ERROR_INVALID_SENSORID, + IS_ERROR_INVALID_MODE_CHANGE, + IS_ERROR_INVALID_MAGIC_NUMBER, + IS_ERROR_INVALID_SETFILE_HDR, + IS_ERROR_BUSY, + IS_ERROR_SET_PARAMETER, + IS_ERROR_INVALID_PATH, + IS_ERROR_OPEN_SENSOR_FAIL, + IS_ERROR_ENTRY_MSG_THREAD_DOWN, + IS_ERROR_ISP_FRAME_END_NOT_DONE, + IS_ERROR_DRC_FRAME_END_NOT_DONE, + IS_ERROR_SCALERC_FRAME_END_NOT_DONE, + IS_ERROR_ODC_FRAME_END_NOT_DONE, + IS_ERROR_DIS_FRAME_END_NOT_DONE, + IS_ERROR_TDNR_FRAME_END_NOT_DONE, + IS_ERROR_SCALERP_FRAME_END_NOT_DONE, + IS_ERROR_WAIT_STREAM_OFF_NOT_DONE, + IS_ERROR_NO_MSG_IS_RECEIVED, + IS_ERROR_SENSOR_MSG_FAIL, + IS_ERROR_ISP_MSG_FAIL, + IS_ERROR_DRC_MSG_FAIL, + IS_ERROR_SCALERC_MSG_FAIL, + IS_ERROR_ODC_MSG_FAIL, + IS_ERROR_DIS_MSG_FAIL, + IS_ERROR_TDNR_MSG_FAIL, + IS_ERROR_SCALERP_MSG_FAIL, + IS_ERROR_LHFD_MSG_FAIL, + IS_ERROR_LHFD_INTERNAL_STOP, + + /* Sensor 100 ~ 199 */ + IS_ERROR_SENSOR_PWRDN_FAIL = 100, + IS_ERROR_SENSOR_STREAM_ON_FAIL, + IS_ERROR_SENSOR_STREAM_OFF_FAIL, + + /* ISP 200 ~ 299 */ + IS_ERROR_ISP_PWRDN_FAIL = 200, + IS_ERROR_ISP_MULTIPLE_INPUT, + IS_ERROR_ISP_ABSENT_INPUT, + IS_ERROR_ISP_ABSENT_OUTPUT, + IS_ERROR_ISP_NONADJACENT_OUTPUT, + IS_ERROR_ISP_FORMAT_MISMATCH, + IS_ERROR_ISP_WIDTH_MISMATCH, + IS_ERROR_ISP_HEIGHT_MISMATCH, + IS_ERROR_ISP_BITWIDTH_MISMATCH, + IS_ERROR_ISP_FRAME_END_TIME_OUT, + + /* DRC 300 ~ 399 */ + IS_ERROR_DRC_PWRDN_FAIL = 300, + IS_ERROR_DRC_MULTIPLE_INPUT, + IS_ERROR_DRC_ABSENT_INPUT, + IS_ERROR_DRC_NONADJACENT_INPUT, + IS_ERROR_DRC_ABSENT_OUTPUT, + IS_ERROR_DRC_NONADJACENT_OUTPUT, + IS_ERROR_DRC_FORMAT_MISMATCH, + IS_ERROR_DRC_WIDTH_MISMATCH, + IS_ERROR_DRC_HEIGHT_MISMATCH, + IS_ERROR_DRC_BITWIDTH_MISMATCH, + IS_ERROR_DRC_FRAME_END_TIME_OUT, + + /* SCALERC 400 ~ 499 */ + IS_ERROR_SCALERC_PWRDN_FAIL = 400, + + /* ODC 500 ~ 599 */ + IS_ERROR_ODC_PWRDN_FAIL = 500, + + /* DIS 600 ~ 699 */ + IS_ERROR_DIS_PWRDN_FAIL = 600, + + /* TDNR 700 ~ 799 */ + IS_ERROR_TDNR_PWRDN_FAIL = 700, + + /* SCALERC 800 ~ 899 */ + IS_ERROR_SCALERP_PWRDN_FAIL = 800, + + /* FD 900 ~ 999 */ + IS_ERROR_FD_PWRDN_FAIL = 900, + IS_ERROR_FD_MULTIPLE_INPUT, + IS_ERROR_FD_ABSENT_INPUT, + IS_ERROR_FD_NONADJACENT_INPUT, + IS_ERROR_LHFD_FRAME_END_TIME_OUT, + + IS_ERROR_UNKNOWN = 1000, +}; + +#define IS_ERROR_TIME_OUT_FLAG 0x80000000 + +/* Set parameter error enum */ +enum fimc_is_error { + /* Common error (0~99) */ + ERROR_COMMON_NONE = 0, + ERROR_COMMON_CMD = 1, /* Invalid command */ + ERROR_COMMON_PARAMETER = 2, /* Invalid parameter */ + /* setfile is not loaded before adjusting */ + ERROR_COMMON_SETFILE_LOAD = 3, + /* setfile is not Adjusted before runnng. */ + ERROR_COMMON_SETFILE_ADJUST = 4, + /* Index of setfile is not valid (0~MAX_SETFILE_NUM-1) */ + ERROR_COMMON_SETFILE_INDEX = 5, + /* Input path can be changed in ready state(stop) */ + ERROR_COMMON_INPUT_PATH = 6, + /* IP can not start if input path is not set */ + ERROR_COMMON_INPUT_INIT = 7, + /* Output path can be changed in ready state (stop) */ + ERROR_COMMON_OUTPUT_PATH = 8, + /* IP can not start if output path is not set */ + ERROR_COMMON_OUTPUT_INIT = 9, + + ERROR_CONTROL_NONE = ERROR_COMMON_NONE, + ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */ + + ERROR_OTF_INPUT_NONE = ERROR_COMMON_NONE, + ERROR_OTF_INPUT_CMD = 21, + /* invalid format (DRC: YUV444, FD: YUV444, 422, 420) */ + ERROR_OTF_INPUT_FORMAT = 22, + /* invalid width (DRC: 128~8192, FD: 32~8190) */ + ERROR_OTF_INPUT_WIDTH = 23, + /* invalid height (DRC: 64~8192, FD: 16~8190) */ + ERROR_OTF_INPUT_HEIGHT = 24, + /* invalid bit-width (DRC: 8~12bits, FD: 8bit) */ + ERROR_OTF_INPUT_BIT_WIDTH = 25, + /* invalid FrameTime for ISP */ + ERROR_OTF_INPUT_USER_FRAMETIIME = 26, + + ERROR_DMA_INPUT_NONE = ERROR_COMMON_NONE, + /* invalid width (DRC: 128~8192, FD: 32~8190) */ + ERROR_DMA_INPUT_WIDTH = 31, + /* invalid height (DRC: 64~8192, FD: 16~8190) */ + ERROR_DMA_INPUT_HEIGHT = 32, + /* invalid format (DRC: YUV444 or YUV422, FD: YUV444, 422, 420) */ + ERROR_DMA_INPUT_FORMAT = 33, + /* invalid bit-width (DRC: 8~12bit, FD: 8bit) */ + ERROR_DMA_INPUT_BIT_WIDTH = 34, + /* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */ + ERROR_DMA_INPUT_ORDER = 35, + /* invalid palne (DRC: 3, FD: 1, 2, 3) */ + ERROR_DMA_INPUT_PLANE = 36, + + ERROR_OTF_OUTPUT_NONE = ERROR_COMMON_NONE, + /* invalid width (DRC: 128~8192) */ + ERROR_OTF_OUTPUT_WIDTH = 41, + /* invalid height (DRC: 64~8192) */ + ERROR_OTF_OUTPUT_HEIGHT = 42, + /* invalid format (DRC: YUV444) */ + ERROR_OTF_OUTPUT_FORMAT = 43, + /* invalid bit-width (DRC: 8~12bits) */ + ERROR_OTF_OUTPUT_BIT_WIDTH = 44, + + ERROR_DMA_OUTPUT_NONE = ERROR_COMMON_NONE, + ERROR_DMA_OUTPUT_WIDTH = 51, /* invalid width */ + ERROR_DMA_OUTPUT_HEIGHT = 52, /* invalid height */ + ERROR_DMA_OUTPUT_FORMAT = 53, /* invalid format */ + ERROR_DMA_OUTPUT_BIT_WIDTH = 54, /* invalid bit-width */ + ERROR_DMA_OUTPUT_PLANE = 55, /* invalid plane */ + ERROR_DMA_OUTPUT_ORDER = 56, /* invalid order */ + + ERROR_GLOBAL_SHOTMODE_NONE = ERROR_COMMON_NONE, + + /* SENSOR Error(100~199) */ + ERROR_SENSOR_NONE = ERROR_COMMON_NONE, + ERROR_SENSOR_I2C_FAIL = 101, + ERROR_SENSOR_INVALID_FRAMERATE, + ERROR_SENSOR_INVALID_EXPOSURETIME, + ERROR_SENSOR_INVALID_SIZE, + ERROR_SENSOR_INVALID_SETTING, + ERROR_SENSOR_ACTURATOR_INIT_FAIL, + ERROR_SENSOR_INVALID_AF_POS, + ERROR_SENSOR_UNSUPPORT_FUNC, + ERROR_SENSOR_UNSUPPORT_PERI, + ERROR_SENSOR_UNSUPPORT_AF, + + /* ISP Error (200~299) */ + ERROR_ISP_AF_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AF_BUSY = 201, + ERROR_ISP_AF_INVALID_COMMAND = 202, + ERROR_ISP_AF_INVALID_MODE = 203, + ERROR_ISP_FLASH_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AWB_NONE = ERROR_COMMON_NONE, + ERROR_ISP_IMAGE_EFFECT_NONE = ERROR_COMMON_NONE, + ERROR_ISP_ISO_NONE = ERROR_COMMON_NONE, + ERROR_ISP_ADJUST_NONE = ERROR_COMMON_NONE, + ERROR_ISP_METERING_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AFC_NONE = ERROR_COMMON_NONE, + + /* DRC Error (300~399) */ + + /* FD Error (400~499) */ + ERROR_FD_NONE = ERROR_COMMON_NONE, + /* Invalid max number (1~16) */ + ERROR_FD_CONFIG_MAX_NUMBER_STATE = 401, + ERROR_FD_CONFIG_MAX_NUMBER_INVALID = 402, + ERROR_FD_CONFIG_YAW_ANGLE_STATE = 403, + ERROR_FD_CONFIG_YAW_ANGLE_INVALID = 404, + ERROR_FD_CONFIG_ROLL_ANGLE_STATE = 405, + ERROR_FD_CONFIG_ROLL_ANGLE_INVALID = 406, + ERROR_FD_CONFIG_SMILE_MODE_INVALID = 407, + ERROR_FD_CONFIG_BLINK_MODE_INVALID = 408, + ERROR_FD_CONFIG_EYES_DETECT_INVALID = 409, + ERROR_FD_CONFIG_MOUTH_DETECT_INVALID = 410, + ERROR_FD_CONFIG_ORIENTATION_STATE = 411, + ERROR_FD_CONFIG_ORIENTATION_INVALID = 412, + ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID = 413, + /* PARAM_FdResultStr can be only applied in ready-state or stream off */ + ERROR_FD_RESULT = 414, + /* PARAM_FdModeStr can be only applied in ready-state or stream off */ + ERROR_FD_MODE = 415, + /* Scaler Error (500 ~ 599) */ + ERROR_SCALER_NO_NONE = ERROR_COMMON_NONE, + ERROR_SCALER_DMA_OUTSEL = 501, + ERROR_SCALER_H_RATIO = 502, + ERROR_SCALER_V_RATIO = 503, + + ERROR_SCALER_IMAGE_EFFECT = 510, + + ERROR_SCALER_ROTATE = 520, + ERROR_SCALER_FLIP = 521, +}; + +const char * const fimc_is_strerr(unsigned int error); +const char * const fimc_is_param_strerr(unsigned int error); + +#endif /* FIMC_IS_ERR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c new file mode 100644 index 0000000..c397777 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -0,0 +1,126 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_i2c.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include "fimc-is-i2c.h" + +struct fimc_is_i2c { + struct i2c_adapter adapter; + struct clk *clock; +}; + +/* + * An empty algorithm is used as the actual I2C bus controller driver + * is implemented in the FIMC-IS subsystem firmware and the host CPU + * doesn't access the I2C bus controller. + */ +static const struct i2c_algorithm fimc_is_i2c_algorithm; + +static int fimc_is_i2c_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct fimc_is_i2c *isp_i2c; + struct i2c_adapter *i2c_adap; + int ret; + + isp_i2c = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c), GFP_KERNEL); + if (!isp_i2c) + return -ENOMEM; + + isp_i2c->clock = devm_clk_get(&pdev->dev, "i2c_isp"); + if (IS_ERR(isp_i2c->clock)) { + dev_err(&pdev->dev, "failed to get the clock\n"); + return PTR_ERR(isp_i2c->clock); + } + + i2c_adap = &isp_i2c->adapter; + i2c_adap->dev.of_node = node; + i2c_adap->dev.parent = &pdev->dev; + strlcpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name)); + i2c_adap->owner = THIS_MODULE; + i2c_adap->algo = &fimc_is_i2c_algorithm; + i2c_adap->class = I2C_CLASS_SPD; + + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add I2C bus %s\n", + node->full_name); + return ret; + } + + platform_set_drvdata(pdev, isp_i2c); + + pm_runtime_enable(&pdev->dev); + pm_runtime_enable(&i2c_adap->dev); + + of_i2c_register_devices(i2c_adap); + + return 0; +} + +static int fimc_is_i2c_remove(struct platform_device *pdev) +{ + struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev); + + pm_runtime_disable(&isp_i2c->adapter.dev); + pm_runtime_disable(&pdev->dev); + i2c_del_adapter(&isp_i2c->adapter); + + return 0; +} + +static int fimc_is_i2c_suspend(struct device *dev) +{ + struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + clk_disable_unprepare(isp_i2c->clock); + return 0; +} + +static int fimc_is_i2c_resume(struct device *dev) +{ + struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + return clk_prepare_enable(isp_i2c->clock); +} + +UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, + fimc_is_i2c_resume, NULL); + +static const struct of_device_id fimc_is_i2c_of_match[] = { + { .compatible = FIMC_IS_I2C_COMPATIBLE }, + { }, +}; + +static struct platform_driver fimc_is_i2c_driver = { + .probe = fimc_is_i2c_probe, + .remove = fimc_is_i2c_remove, + .driver = { + .of_match_table = fimc_is_i2c_of_match, + .name = "fimc-isp-i2c", + .owner = THIS_MODULE, + .pm = &fimc_is_i2c_pm_ops, + } +}; + +int fimc_is_register_i2c_driver(void) +{ + return platform_driver_register(&fimc_is_i2c_driver); +} + +void fimc_is_unregister_i2c_driver(void) +{ + platform_driver_unregister(&fimc_is_i2c_driver); +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/exynos4-is/fimc-is-i2c.h new file mode 100644 index 0000000..0d38d6b --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.h @@ -0,0 +1,15 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ + +#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp" + +int fimc_is_register_i2c_driver(void); +void fimc_is_unregister_i2c_driver(void); diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c new file mode 100644 index 0000000..53fe2a2 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -0,0 +1,900 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-errno.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" +#include "fimc-is-sensor.h" + +static void __hw_param_copy(void *dst, void *src) +{ + memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE); +} + +void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) +{ + struct param_global_shotmode *dst, *src; + + dst = &is->is_p_region->parameter.global.shotmode; + src = &is->config[is->config_index].global.shotmode; + __hw_param_copy(dst, src); +} + +void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) +{ + struct param_sensor_framerate *dst, *src; + + dst = &is->is_p_region->parameter.sensor.frame_rate; + src = &is->config[is->config_index].sensor.frame_rate; + __hw_param_copy(dst, src); +} + +int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) +{ + struct is_param_region *par = &is->is_p_region->parameter; + struct chain_config *cfg = &is->config[is->config_index]; + + switch (offset) { + case PARAM_ISP_CONTROL: + __hw_param_copy(&par->isp.control, &cfg->isp.control); + break; + + case PARAM_ISP_OTF_INPUT: + __hw_param_copy(&par->isp.otf_input, &cfg->isp.otf_input); + break; + + case PARAM_ISP_DMA1_INPUT: + __hw_param_copy(&par->isp.dma1_input, &cfg->isp.dma1_input); + break; + + case PARAM_ISP_DMA2_INPUT: + __hw_param_copy(&par->isp.dma2_input, &cfg->isp.dma2_input); + break; + + case PARAM_ISP_AA: + __hw_param_copy(&par->isp.aa, &cfg->isp.aa); + break; + + case PARAM_ISP_FLASH: + __hw_param_copy(&par->isp.flash, &cfg->isp.flash); + break; + + case PARAM_ISP_AWB: + __hw_param_copy(&par->isp.awb, &cfg->isp.awb); + break; + + case PARAM_ISP_IMAGE_EFFECT: + __hw_param_copy(&par->isp.effect, &cfg->isp.effect); + break; + + case PARAM_ISP_ISO: + __hw_param_copy(&par->isp.iso, &cfg->isp.iso); + break; + + case PARAM_ISP_ADJUST: + __hw_param_copy(&par->isp.adjust, &cfg->isp.adjust); + break; + + case PARAM_ISP_METERING: + __hw_param_copy(&par->isp.metering, &cfg->isp.metering); + break; + + case PARAM_ISP_AFC: + __hw_param_copy(&par->isp.afc, &cfg->isp.afc); + break; + + case PARAM_ISP_OTF_OUTPUT: + __hw_param_copy(&par->isp.otf_output, &cfg->isp.otf_output); + break; + + case PARAM_ISP_DMA1_OUTPUT: + __hw_param_copy(&par->isp.dma1_output, &cfg->isp.dma1_output); + break; + + case PARAM_ISP_DMA2_OUTPUT: + __hw_param_copy(&par->isp.dma2_output, &cfg->isp.dma2_output); + break; + + case PARAM_DRC_CONTROL: + __hw_param_copy(&par->drc.control, &cfg->drc.control); + break; + + case PARAM_DRC_OTF_INPUT: + __hw_param_copy(&par->drc.otf_input, &cfg->drc.otf_input); + break; + + case PARAM_DRC_DMA_INPUT: + __hw_param_copy(&par->drc.dma_input, &cfg->drc.dma_input); + break; + + case PARAM_DRC_OTF_OUTPUT: + __hw_param_copy(&par->drc.otf_output, &cfg->drc.otf_output); + break; + + case PARAM_FD_CONTROL: + __hw_param_copy(&par->fd.control, &cfg->fd.control); + break; + + case PARAM_FD_OTF_INPUT: + __hw_param_copy(&par->fd.otf_input, &cfg->fd.otf_input); + break; + + case PARAM_FD_DMA_INPUT: + __hw_param_copy(&par->fd.dma_input, &cfg->fd.dma_input); + break; + + case PARAM_FD_CONFIG: + __hw_param_copy(&par->fd.config, &cfg->fd.config); + break; + + default: + return -EINVAL; + } + + return 0; +} + +unsigned int __get_pending_param_count(struct fimc_is *is) +{ + struct chain_config *config = &is->config[is->config_index]; + unsigned long flags; + unsigned int count; + + spin_lock_irqsave(&is->slock, flags); + count = hweight32(config->p_region_index1); + count += hweight32(config->p_region_index2); + spin_unlock_irqrestore(&is->slock, flags); + + return count; +} + +int __is_hw_update_params(struct fimc_is *is) +{ + unsigned long *p_index1, *p_index2; + int i, id, ret = 0; + + id = is->config_index; + p_index1 = &is->config[id].p_region_index1; + p_index2 = &is->config[id].p_region_index2; + + if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index1)) + __fimc_is_hw_update_param_global_shotmode(is); + + if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) + __fimc_is_hw_update_param_sensor_framerate(is); + + for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) { + if (test_bit(i, p_index1)) + ret = __fimc_is_hw_update_param(is, i); + } + + for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) { + if (test_bit(i, p_index1)) + ret = __fimc_is_hw_update_param(is, i); + } + + for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) { + if (test_bit((i - 32), p_index2)) + ret = __fimc_is_hw_update_param(is, i); + } + + return ret; +} + +void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) +{ + struct isp_param *isp; + + isp = &is->config[is->config_index].isp; + mf->width = isp->otf_input.width; + mf->height = isp->otf_input.height; +} + +void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + struct drc_param *drc; + struct fd_param *fd; + + isp = &is->config[index].isp; + drc = &is->config[index].drc; + fd = &is->config[index].fd; + + /* Update isp size info (OTF only) */ + isp->otf_input.width = mf->width; + isp->otf_input.height = mf->height; + isp->otf_output.width = mf->width; + isp->otf_output.height = mf->height; + /* Update drc size info (OTF only) */ + drc->otf_input.width = mf->width; + drc->otf_input.height = mf->height; + drc->otf_output.width = mf->width; + drc->otf_output.height = mf->height; + /* Update fd size info (OTF only) */ + fd->otf_input.width = mf->width; + fd->otf_input.height = mf->height; + + if (test_bit(PARAM_ISP_OTF_INPUT, + &is->config[index].p_region_index1)) + return; + + /* Update field */ + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); + fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); + fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); + fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); + fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); +} + +int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is) +{ + switch (is->sensor->drvdata->id) { + case FIMC_IS_SENSOR_ID_S5K6A3: + return 30; + default: + return 15; + } +} + +void __is_set_sensor(struct fimc_is *is, int fps) +{ + unsigned int index = is->config_index; + struct sensor_param *sensor; + struct isp_param *isp; + + sensor = &is->config[index].sensor; + isp = &is->config[index].isp; + + if (fps == 0) { + sensor->frame_rate.frame_rate = + fimc_is_hw_get_sensor_max_framerate(is); + isp->otf_input.frametime_min = 0; + isp->otf_input.frametime_max = 66666; + } else { + sensor->frame_rate.frame_rate = fps; + isp->otf_input.frametime_min = 0; + isp->otf_input.frametime_max = (u32)1000000 / fps; + } + + fimc_is_set_param_bit(is, PARAM_SENSOR_FRAME_RATE); + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); +} + +void __is_set_init_isp_aa(struct fimc_is *is) +{ + struct isp_param *isp; + + isp = &is->config[is->config_index].isp; + + isp->aa.cmd = ISP_AA_COMMAND_START; + isp->aa.target = ISP_AA_TARGET_AF | ISP_AA_TARGET_AE | + ISP_AA_TARGET_AWB; + isp->aa.mode = 0; + isp->aa.scene = 0; + isp->aa.sleep = 0; + isp->aa.face = 0; + isp->aa.touch_x = 0; + isp->aa.touch_y = 0; + isp->aa.manual_af_setting = 0; + isp->aa.err = ISP_AF_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AA); +} + +void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye) +{ + unsigned int index = is->config_index; + struct isp_param *isp = &is->config[index].isp; + + isp->flash.cmd = cmd; + isp->flash.redeye = redeye; + isp->flash.err = ISP_FLASH_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_FLASH); +} + +void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->awb.cmd = cmd; + isp->awb.illumination = val; + isp->awb.err = ISP_AWB_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AWB); +} + +void __is_set_isp_effect(struct fimc_is *is, u32 cmd) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->effect.cmd = cmd; + isp->effect.err = ISP_IMAGE_EFFECT_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_IMAGE_EFFECT); +} + +void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->iso.cmd = cmd; + isp->iso.value = val; + isp->iso.err = ISP_ISO_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_ISO); +} + +void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + unsigned long *p_index; + struct isp_param *isp; + + p_index = &is->config[index].p_region_index1; + isp = &is->config[index].isp; + + switch (cmd) { + case ISP_ADJUST_COMMAND_MANUAL_CONTRAST: + isp->adjust.contrast = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_SATURATION: + isp->adjust.saturation = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_SHARPNESS: + isp->adjust.sharpness = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_EXPOSURE: + isp->adjust.exposure = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS: + isp->adjust.brightness = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_HUE: + isp->adjust.hue = val; + break; + case ISP_ADJUST_COMMAND_AUTO: + isp->adjust.contrast = 0; + isp->adjust.saturation = 0; + isp->adjust.sharpness = 0; + isp->adjust.exposure = 0; + isp->adjust.brightness = 0; + isp->adjust.hue = 0; + break; + } + + if (!test_bit(PARAM_ISP_ADJUST, p_index)) { + isp->adjust.cmd = cmd; + isp->adjust.err = ISP_ADJUST_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_ADJUST); + } else { + isp->adjust.cmd |= cmd; + } +} + +void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index1; + isp = &is->config[index].isp; + + switch (id) { + case IS_METERING_CONFIG_CMD: + isp->metering.cmd = val; + break; + case IS_METERING_CONFIG_WIN_POS_X: + isp->metering.win_pos_x = val; + break; + case IS_METERING_CONFIG_WIN_POS_Y: + isp->metering.win_pos_y = val; + break; + case IS_METERING_CONFIG_WIN_WIDTH: + isp->metering.win_width = val; + break; + case IS_METERING_CONFIG_WIN_HEIGHT: + isp->metering.win_height = val; + break; + default: + return; + } + + if (!test_bit(PARAM_ISP_METERING, p_index)) { + isp->metering.err = ISP_METERING_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_METERING); + } +} + +void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->afc.cmd = cmd; + isp->afc.manual = val; + isp->afc.err = ISP_AFC_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AFC); +} + +void __is_set_drc_control(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct drc_param *drc; + + drc = &is->config[index].drc; + + drc->control.bypass = val; + + fimc_is_set_param_bit(is, PARAM_DRC_CONTROL); +} + +void __is_set_fd_control(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->control.cmd = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) + fimc_is_set_param_bit(is, PARAM_FD_CONTROL); +} + +void __is_set_fd_config_maxface(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.max_number = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_MAXIMUM_NUMBER; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_MAXIMUM_NUMBER; + } +} + +void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.roll_angle = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ROLL_ANGLE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ROLL_ANGLE; + } +} + +void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.yaw_angle = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_YAW_ANGLE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_YAW_ANGLE; + } +} + +void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.smile_mode = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_SMILE_MODE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_SMILE_MODE; + } +} + +void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.blink_mode = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_BLINK_MODE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_BLINK_MODE; + } +} + +void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.eye_detect = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_EYES_DETECT; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_EYES_DETECT; + } +} + +void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.mouth_detect = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_MOUTH_DETECT; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_MOUTH_DETECT; + } +} + +void __is_set_fd_config_orientation(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.orientation = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION; + } +} + +void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.orientation_value = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION_VALUE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION_VALUE; + } +} + +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; + unsigned long *p_index1, *p_index2; + unsigned int index; + + 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; + p_index1 = &is->config[index].p_region_index1; + p_index2 = &is->config[index].p_region_index2; + + /* Global */ + global->shotmode.cmd = 1; + fimc_is_set_param_bit(is, PARAM_GLOBAL_SHOTMODE); + + /* ISP */ + isp->control.cmd = CONTROL_COMMAND_START; + isp->control.bypass = CONTROL_BYPASS_DISABLE; + isp->control.err = CONTROL_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_CONTROL); + + isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_ISP_OTF_INPUT, p_index1)) { + isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); + } + if (is->sensor->test_pattern) + isp->otf_input.format = OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER; + else + isp->otf_input.format = OTF_INPUT_FORMAT_BAYER; + isp->otf_input.bitwidth = 10; + isp->otf_input.order = OTF_INPUT_ORDER_BAYER_GR_BG; + isp->otf_input.crop_offset_x = 0; + isp->otf_input.crop_offset_y = 0; + isp->otf_input.err = OTF_INPUT_ERROR_NONE; + + isp->dma1_input.cmd = DMA_INPUT_COMMAND_DISABLE; + isp->dma1_input.width = 0; + isp->dma1_input.height = 0; + isp->dma1_input.format = 0; + isp->dma1_input.bitwidth = 0; + isp->dma1_input.plane = 0; + isp->dma1_input.order = 0; + isp->dma1_input.buffer_number = 0; + isp->dma1_input.width = 0; + isp->dma1_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA1_INPUT); + + isp->dma2_input.cmd = DMA_INPUT_COMMAND_DISABLE; + isp->dma2_input.width = 0; + isp->dma2_input.height = 0; + isp->dma2_input.format = 0; + isp->dma2_input.bitwidth = 0; + isp->dma2_input.plane = 0; + isp->dma2_input.order = 0; + isp->dma2_input.buffer_number = 0; + isp->dma2_input.width = 0; + isp->dma2_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA2_INPUT); + + isp->aa.cmd = ISP_AA_COMMAND_START; + isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + + if (!test_bit(PARAM_ISP_FLASH, p_index1)) + __is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE, + ISP_FLASH_REDEYE_DISABLE); + + if (!test_bit(PARAM_ISP_AWB, p_index1)) + __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); + + if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index1)) + __is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE); + + if (!test_bit(PARAM_ISP_ISO, p_index1)) + __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); + + if (!test_bit(PARAM_ISP_ADJUST, p_index1)) { + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0); + __is_set_isp_adjust(is, + ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, 0); + __is_set_isp_adjust(is, + ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0); + } + + if (!test_bit(PARAM_ISP_METERING, p_index1)) { + __is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER); + __is_set_isp_metering(is, 1, 0); + __is_set_isp_metering(is, 2, 0); + __is_set_isp_metering(is, 3, 0); + __is_set_isp_metering(is, 4, 0); + } + + if (!test_bit(PARAM_ISP_AFC, p_index1)) + __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); + + isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index1)) { + isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; + isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); + } + isp->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; + isp->otf_output.bitwidth = 12; + isp->otf_output.order = 0; + isp->otf_output.err = OTF_OUTPUT_ERROR_NONE; + + if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index1)) { + isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + isp->dma1_output.width = 0; + isp->dma1_output.height = 0; + isp->dma1_output.format = 0; + isp->dma1_output.bitwidth = 0; + isp->dma1_output.plane = 0; + isp->dma1_output.order = 0; + isp->dma1_output.buffer_number = 0; + isp->dma1_output.buffer_address = 0; + isp->dma1_output.notify_dma_done = 0; + isp->dma1_output.dma_out_mask = 0; + isp->dma1_output.err = DMA_OUTPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT); + } + + if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index1)) { + isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + isp->dma2_output.width = 0; + isp->dma2_output.height = 0; + isp->dma2_output.format = 0; + isp->dma2_output.bitwidth = 0; + isp->dma2_output.plane = 0; + isp->dma2_output.order = 0; + isp->dma2_output.buffer_number = 0; + isp->dma2_output.buffer_address = 0; + isp->dma2_output.notify_dma_done = 0; + isp->dma2_output.dma_out_mask = 0; + isp->dma2_output.err = DMA_OUTPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); + } + + /* Sensor */ + if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) { + if (is->config_index == 0) + __is_set_sensor(is, 0); + } + + /* DRC */ + drc->control.cmd = CONTROL_COMMAND_START; + __is_set_drc_control(is, CONTROL_BYPASS_ENABLE); + + drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_DRC_OTF_INPUT, p_index1)) { + drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); + } + drc->otf_input.format = OTF_INPUT_FORMAT_YUV444; + drc->otf_input.bitwidth = 12; + drc->otf_input.order = 0; + drc->otf_input.err = OTF_INPUT_ERROR_NONE; + + drc->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; + drc->dma_input.width = 0; + drc->dma_input.height = 0; + drc->dma_input.format = 0; + drc->dma_input.bitwidth = 0; + drc->dma_input.plane = 0; + drc->dma_input.order = 0; + drc->dma_input.buffer_number = 0; + drc->dma_input.width = 0; + drc->dma_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT); + + drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index1)) { + drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; + drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); + } + drc->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; + drc->otf_output.bitwidth = 8; + drc->otf_output.order = 0; + drc->otf_output.err = OTF_OUTPUT_ERROR_NONE; + + /* FD */ + __is_set_fd_control(is, CONTROL_COMMAND_STOP); + fd->control.bypass = CONTROL_BYPASS_DISABLE; + + fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit((PARAM_FD_OTF_INPUT - 32), p_index2)) { + fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); + } + + fd->otf_input.format = OTF_INPUT_FORMAT_YUV444; + fd->otf_input.bitwidth = 8; + fd->otf_input.order = 0; + fd->otf_input.err = OTF_INPUT_ERROR_NONE; + + fd->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; + fd->dma_input.width = 0; + fd->dma_input.height = 0; + fd->dma_input.format = 0; + fd->dma_input.bitwidth = 0; + fd->dma_input.plane = 0; + fd->dma_input.order = 0; + fd->dma_input.buffer_number = 0; + fd->dma_input.width = 0; + fd->dma_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_FD_DMA_INPUT); + + __is_set_fd_config_maxface(is, 5); + __is_set_fd_config_rollangle(is, FD_CONFIG_ROLL_ANGLE_FULL); + __is_set_fd_config_yawangle(is, FD_CONFIG_YAW_ANGLE_45_90); + __is_set_fd_config_smilemode(is, FD_CONFIG_SMILE_MODE_DISABLE); + __is_set_fd_config_blinkmode(is, FD_CONFIG_BLINK_MODE_DISABLE); + __is_set_fd_config_eyedetect(is, FD_CONFIG_EYES_DETECT_ENABLE); + __is_set_fd_config_mouthdetect(is, FD_CONFIG_MOUTH_DETECT_DISABLE); + __is_set_fd_config_orientation(is, FD_CONFIG_ORIENTATION_DISABLE); + __is_set_fd_config_orientation_val(is, 0); +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h new file mode 100644 index 0000000..f9358c2 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.h @@ -0,0 +1,1020 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#ifndef FIMC_IS_PARAM_H_ +#define FIMC_IS_PARAM_H_ + +#include <linux/compiler.h> + +#define FIMC_IS_CONFIG_TIMEOUT 3000 /* ms */ +#define IS_DEFAULT_WIDTH 1280 +#define IS_DEFAULT_HEIGHT 720 + +#define DEFAULT_PREVIEW_STILL_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_PREVIEW_STILL_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_CAPTURE_STILL_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_CAPTURE_STILL_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_PREVIEW_VIDEO_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_PREVIEW_VIDEO_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_CAPTURE_VIDEO_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_CAPTURE_VIDEO_HEIGHT IS_DEFAULT_HEIGHT + +#define DEFAULT_PREVIEW_STILL_FRAMERATE 30 +#define DEFAULT_CAPTURE_STILL_FRAMERATE 15 +#define DEFAULT_PREVIEW_VIDEO_FRAMERATE 30 +#define DEFAULT_CAPTURE_VIDEO_FRAMERATE 30 + +#define FIMC_IS_REGION_VER 124 /* IS REGION VERSION 1.24 */ +#define FIMC_IS_PARAM_SIZE (FIMC_IS_REGION_SIZE + 1) +#define FIMC_IS_MAGIC_NUMBER 0x01020304 +#define FIMC_IS_PARAM_MAX_SIZE 64 /* in bytes */ +#define FIMC_IS_PARAM_MAX_ENTRIES (FIMC_IS_PARAM_MAX_SIZE / 4) + +/* The parameter bitmask bit definitions. */ +enum is_param_bit { + PARAM_GLOBAL_SHOTMODE, + PARAM_SENSOR_CONTROL, + PARAM_SENSOR_OTF_OUTPUT, + PARAM_SENSOR_FRAME_RATE, + PARAM_BUFFER_CONTROL, + PARAM_BUFFER_OTF_INPUT, + PARAM_BUFFER_OTF_OUTPUT, + PARAM_ISP_CONTROL, + PARAM_ISP_OTF_INPUT, + PARAM_ISP_DMA1_INPUT, + /* 10 */ + PARAM_ISP_DMA2_INPUT, + PARAM_ISP_AA, + PARAM_ISP_FLASH, + PARAM_ISP_AWB, + PARAM_ISP_IMAGE_EFFECT, + PARAM_ISP_ISO, + PARAM_ISP_ADJUST, + PARAM_ISP_METERING, + PARAM_ISP_AFC, + PARAM_ISP_OTF_OUTPUT, + /* 20 */ + PARAM_ISP_DMA1_OUTPUT, + PARAM_ISP_DMA2_OUTPUT, + PARAM_DRC_CONTROL, + PARAM_DRC_OTF_INPUT, + PARAM_DRC_DMA_INPUT, + PARAM_DRC_OTF_OUTPUT, + PARAM_SCALERC_CONTROL, + PARAM_SCALERC_OTF_INPUT, + PARAM_SCALERC_IMAGE_EFFECT, + PARAM_SCALERC_INPUT_CROP, + /* 30 */ + PARAM_SCALERC_OUTPUT_CROP, + PARAM_SCALERC_OTF_OUTPUT, + PARAM_SCALERC_DMA_OUTPUT, + PARAM_ODC_CONTROL, + PARAM_ODC_OTF_INPUT, + PARAM_ODC_OTF_OUTPUT, + PARAM_DIS_CONTROL, + PARAM_DIS_OTF_INPUT, + PARAM_DIS_OTF_OUTPUT, + PARAM_TDNR_CONTROL, + /* 40 */ + PARAM_TDNR_OTF_INPUT, + PARAM_TDNR_1ST_FRAME, + PARAM_TDNR_OTF_OUTPUT, + PARAM_TDNR_DMA_OUTPUT, + PARAM_SCALERP_CONTROL, + PARAM_SCALERP_OTF_INPUT, + PARAM_SCALERP_IMAGE_EFFECT, + PARAM_SCALERP_INPUT_CROP, + PARAM_SCALERP_OUTPUT_CROP, + PARAM_SCALERP_ROTATION, + /* 50 */ + PARAM_SCALERP_FLIP, + PARAM_SCALERP_OTF_OUTPUT, + PARAM_SCALERP_DMA_OUTPUT, + PARAM_FD_CONTROL, + PARAM_FD_OTF_INPUT, + PARAM_FD_DMA_INPUT, + PARAM_FD_CONFIG, +}; + +/* Interrupt map */ +#define FIMC_IS_INT_GENERAL 0 +#define FIMC_IS_INT_FRAME_DONE_ISP 1 + +/* Input */ + +#define CONTROL_COMMAND_STOP 0 +#define CONTROL_COMMAND_START 1 + +#define CONTROL_BYPASS_DISABLE 0 +#define CONTROL_BYPASS_ENABLE 1 + +#define CONTROL_ERROR_NONE 0 + +/* OTF (On-The-Fly) input interface commands */ +#define OTF_INPUT_COMMAND_DISABLE 0 +#define OTF_INPUT_COMMAND_ENABLE 1 + +/* OTF input interface color formats */ +enum oft_input_fmt { + OTF_INPUT_FORMAT_BAYER = 0, /* 1 channel */ + OTF_INPUT_FORMAT_YUV444 = 1, /* 3 channels */ + OTF_INPUT_FORMAT_YUV422 = 2, /* 3 channels */ + OTF_INPUT_FORMAT_YUV420 = 3, /* 3 channels */ + OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER = 10, + OTF_INPUT_FORMAT_BAYER_DMA = 11, +}; + +#define OTF_INPUT_ORDER_BAYER_GR_BG 0 + +/* OTF input error codes */ +#define OTF_INPUT_ERROR_NONE 0 /* Input setting is done */ + +/* DMA input commands */ +#define DMA_INPUT_COMMAND_DISABLE 0 +#define DMA_INPUT_COMMAND_ENABLE 1 + +/* DMA input color formats */ +enum dma_input_fmt { + DMA_INPUT_FORMAT_BAYER = 0, + DMA_INPUT_FORMAT_YUV444 = 1, + DMA_INPUT_FORMAT_YUV422 = 2, + DMA_INPUT_FORMAT_YUV420 = 3, +}; + +enum dma_input_order { + /* (for DMA_INPUT_PLANE_3) */ + DMA_INPUT_ORDER_NO = 0, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CBCR = 1, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CRCB = 2, + /* (only valid at DMA_INPUT_PLANE_1 & DMA_INPUT_FORMAT_YUV444) */ + DMA_INPUT_ORDER_YCBCR = 3, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YYCBCR = 4, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCBYCR = 5, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCRYCB = 6, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CBYCRY = 7, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CRYCBY = 8, + /* (only valid at DMA_INPUT_FORMAT_BAYER) */ + DMA_INPUT_ORDER_GR_BG = 9 +}; + +#define DMA_INPUT_ERROR_NONE 0 /* DMA input setting + is done */ +/* + * Data output parameter definitions + */ +#define OTF_OUTPUT_CROP_DISABLE 0 +#define OTF_OUTPUT_CROP_ENABLE 1 + +#define OTF_OUTPUT_COMMAND_DISABLE 0 +#define OTF_OUTPUT_COMMAND_ENABLE 1 + +enum otf_output_fmt { + OTF_OUTPUT_FORMAT_YUV444 = 1, + OTF_OUTPUT_FORMAT_YUV422 = 2, + OTF_OUTPUT_FORMAT_YUV420 = 3, + OTF_OUTPUT_FORMAT_RGB = 4, +}; + +#define OTF_OUTPUT_ORDER_BAYER_GR_BG 0 + +#define OTF_OUTPUT_ERROR_NONE 0 /* Output Setting is done */ + +#define DMA_OUTPUT_COMMAND_DISABLE 0 +#define DMA_OUTPUT_COMMAND_ENABLE 1 + +enum dma_output_fmt { + DMA_OUTPUT_FORMAT_BAYER = 0, + DMA_OUTPUT_FORMAT_YUV444 = 1, + DMA_OUTPUT_FORMAT_YUV422 = 2, + DMA_OUTPUT_FORMAT_YUV420 = 3, + DMA_OUTPUT_FORMAT_RGB = 4, +}; + +enum dma_output_order { + DMA_OUTPUT_ORDER_NO = 0, + /* for DMA_OUTPUT_PLANE_3 */ + DMA_OUTPUT_ORDER_CBCR = 1, + /* only valid at DMA_INPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_CRCB = 2, + /* only valid at DMA_OUTPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_YYCBCR = 3, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCBYCR = 4, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCRYCB = 5, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBYCRY = 6, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRYCBY = 7, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCBCR = 8, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRYCB = 9, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRCBY = 10, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBYCR = 11, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCRCB = 12, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBCRY = 13, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_BGR = 14, + /* only valid at DMA_OUTPUT_FORMAT_RGB */ + DMA_OUTPUT_ORDER_GB_BG = 15 + /* only valid at DMA_OUTPUT_FORMAT_BAYER */ +}; + +/* enum dma_output_notify_dma_done */ +#define DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE 0 +#define DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE 1 + +/* DMA output error codes */ +#define DMA_OUTPUT_ERROR_NONE 0 /* DMA output setting + is done */ + +/* ---------------------- Global ----------------------------------- */ +#define GLOBAL_SHOTMODE_ERROR_NONE 0 /* shot-mode setting + is done */ +/* 3A lock commands */ +#define ISP_AA_COMMAND_START 0 +#define ISP_AA_COMMAND_STOP 1 + +/* 3A lock target */ +#define ISP_AA_TARGET_AF 1 +#define ISP_AA_TARGET_AE 2 +#define ISP_AA_TARGET_AWB 4 + +enum isp_af_mode { + ISP_AF_MODE_MANUAL = 0, + ISP_AF_MODE_SINGLE = 1, + ISP_AF_MODE_CONTINUOUS = 2, + ISP_AF_MODE_TOUCH = 3, + ISP_AF_MODE_SLEEP = 4, + ISP_AF_MODE_INIT = 5, + ISP_AF_MODE_SET_CENTER_WINDOW = 6, + ISP_AF_MODE_SET_TOUCH_WINDOW = 7 +}; + +/* Face AF commands */ +#define ISP_AF_FACE_DISABLE 0 +#define ISP_AF_FACE_ENABLE 1 + +/* AF range */ +#define ISP_AF_RANGE_NORMAL 0 +#define ISP_AF_RANGE_MACRO 1 + +/* AF sleep */ +#define ISP_AF_SLEEP_OFF 0 +#define ISP_AF_SLEEP_ON 1 + +/* Continuous AF commands */ +#define ISP_AF_CONTINUOUS_DISABLE 0 +#define ISP_AF_CONTINUOUS_ENABLE 1 + +/* ISP AF error codes */ +#define ISP_AF_ERROR_NONE 0 /* AF mode change is done */ +#define ISP_AF_ERROR_NONE_LOCK_DONE 1 /* AF lock is done */ + +/* Flash commands */ +#define ISP_FLASH_COMMAND_DISABLE 0 +#define ISP_FLASH_COMMAND_MANUAL_ON 1 /* (forced flash) */ +#define ISP_FLASH_COMMAND_AUTO 2 +#define ISP_FLASH_COMMAND_TORCH 3 /* 3 sec */ + +/* Flash red-eye commads */ +#define ISP_FLASH_REDEYE_DISABLE 0 +#define ISP_FLASH_REDEYE_ENABLE 1 + +/* Flash error codes */ +#define ISP_FLASH_ERROR_NONE 0 /* Flash setting is done */ + +/* -------------------------- AWB ------------------------------------ */ +enum isp_awb_command { + ISP_AWB_COMMAND_AUTO = 0, + ISP_AWB_COMMAND_ILLUMINATION = 1, + ISP_AWB_COMMAND_MANUAL = 2 +}; + +enum isp_awb_illumination { + ISP_AWB_ILLUMINATION_DAYLIGHT = 0, + ISP_AWB_ILLUMINATION_CLOUDY = 1, + ISP_AWB_ILLUMINATION_TUNGSTEN = 2, + ISP_AWB_ILLUMINATION_FLUORESCENT = 3 +}; + +/* ISP AWN error codes */ +#define ISP_AWB_ERROR_NONE 0 /* AWB setting is done */ + +/* -------------------------- Effect ----------------------------------- */ +enum isp_imageeffect_command { + ISP_IMAGE_EFFECT_DISABLE = 0, + ISP_IMAGE_EFFECT_MONOCHROME = 1, + ISP_IMAGE_EFFECT_NEGATIVE_MONO = 2, + ISP_IMAGE_EFFECT_NEGATIVE_COLOR = 3, + ISP_IMAGE_EFFECT_SEPIA = 4 +}; + +/* Image effect error codes */ +#define ISP_IMAGE_EFFECT_ERROR_NONE 0 /* Image effect setting + is done */ +/* ISO commands */ +#define ISP_ISO_COMMAND_AUTO 0 +#define ISP_ISO_COMMAND_MANUAL 1 + +/* ISO error codes */ +#define ISP_ISO_ERROR_NONE 0 /* ISO setting is done */ + +/* ISP adjust commands */ +#define ISP_ADJUST_COMMAND_AUTO (0 << 0) +#define ISP_ADJUST_COMMAND_MANUAL_CONTRAST (1 << 0) +#define ISP_ADJUST_COMMAND_MANUAL_SATURATION (1 << 1) +#define ISP_ADJUST_COMMAND_MANUAL_SHARPNESS (1 << 2) +#define ISP_ADJUST_COMMAND_MANUAL_EXPOSURE (1 << 3) +#define ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS (1 << 4) +#define ISP_ADJUST_COMMAND_MANUAL_HUE (1 << 5) +#define ISP_ADJUST_COMMAND_MANUAL_ALL 0x7f + +/* ISP adjustment error codes */ +#define ISP_ADJUST_ERROR_NONE 0 /* Adjust setting is done */ + +/* + * Exposure metering + */ +enum isp_metering_command { + ISP_METERING_COMMAND_AVERAGE = 0, + ISP_METERING_COMMAND_SPOT = 1, + ISP_METERING_COMMAND_MATRIX = 2, + ISP_METERING_COMMAND_CENTER = 3 +}; + +/* ISP metering error codes */ +#define ISP_METERING_ERROR_NONE 0 /* Metering setting is done */ + +/* + * AFC + */ +enum isp_afc_command { + ISP_AFC_COMMAND_DISABLE = 0, + ISP_AFC_COMMAND_AUTO = 1, + ISP_AFC_COMMAND_MANUAL = 2, +}; + +#define ISP_AFC_MANUAL_50HZ 50 +#define ISP_AFC_MANUAL_60HZ 60 + +/* ------------------------ SCENE MODE--------------------------------- */ +enum isp_scene_mode { + ISP_SCENE_NONE = 0, + ISP_SCENE_PORTRAIT = 1, + ISP_SCENE_LANDSCAPE = 2, + ISP_SCENE_SPORTS = 3, + ISP_SCENE_PARTYINDOOR = 4, + ISP_SCENE_BEACHSNOW = 5, + ISP_SCENE_SUNSET = 6, + ISP_SCENE_DAWN = 7, + ISP_SCENE_FALL = 8, + ISP_SCENE_NIGHT = 9, + ISP_SCENE_AGAINSTLIGHTWLIGHT = 10, + ISP_SCENE_AGAINSTLIGHTWOLIGHT = 11, + ISP_SCENE_FIRE = 12, + ISP_SCENE_TEXT = 13, + ISP_SCENE_CANDLE = 14 +}; + +/* AFC error codes */ +#define ISP_AFC_ERROR_NONE 0 /* AFC setting is done */ + +/* ---------------------------- FD ------------------------------------- */ +enum fd_config_command { + FD_CONFIG_COMMAND_MAXIMUM_NUMBER = 0x1, + FD_CONFIG_COMMAND_ROLL_ANGLE = 0x2, + FD_CONFIG_COMMAND_YAW_ANGLE = 0x4, + FD_CONFIG_COMMAND_SMILE_MODE = 0x8, + FD_CONFIG_COMMAND_BLINK_MODE = 0x10, + FD_CONFIG_COMMAND_EYES_DETECT = 0x20, + FD_CONFIG_COMMAND_MOUTH_DETECT = 0x40, + FD_CONFIG_COMMAND_ORIENTATION = 0x80, + FD_CONFIG_COMMAND_ORIENTATION_VALUE = 0x100 +}; + +enum fd_config_roll_angle { + FD_CONFIG_ROLL_ANGLE_BASIC = 0, + FD_CONFIG_ROLL_ANGLE_PRECISE_BASIC = 1, + FD_CONFIG_ROLL_ANGLE_SIDES = 2, + FD_CONFIG_ROLL_ANGLE_PRECISE_SIDES = 3, + FD_CONFIG_ROLL_ANGLE_FULL = 4, + FD_CONFIG_ROLL_ANGLE_PRECISE_FULL = 5, +}; + +enum fd_config_yaw_angle { + FD_CONFIG_YAW_ANGLE_0 = 0, + FD_CONFIG_YAW_ANGLE_45 = 1, + FD_CONFIG_YAW_ANGLE_90 = 2, + FD_CONFIG_YAW_ANGLE_45_90 = 3, +}; + +/* Smile mode configuration */ +#define FD_CONFIG_SMILE_MODE_DISABLE 0 +#define FD_CONFIG_SMILE_MODE_ENABLE 1 + +/* Blink mode configuration */ +#define FD_CONFIG_BLINK_MODE_DISABLE 0 +#define FD_CONFIG_BLINK_MODE_ENABLE 1 + +/* Eyes detection configuration */ +#define FD_CONFIG_EYES_DETECT_DISABLE 0 +#define FD_CONFIG_EYES_DETECT_ENABLE 1 + +/* Mouth detection configuration */ +#define FD_CONFIG_MOUTH_DETECT_DISABLE 0 +#define FD_CONFIG_MOUTH_DETECT_ENABLE 1 + +#define FD_CONFIG_ORIENTATION_DISABLE 0 +#define FD_CONFIG_ORIENTATION_ENABLE 1 + +struct param_control { + u32 cmd; + u32 bypass; + u32 buffer_address; + u32 buffer_size; + u32 skip_frames; /* only valid at ISP */ + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; + u32 err; +}; + +struct param_otf_input { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 order; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 frametime_min; + u32 frametime_max; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 13]; + u32 err; +}; + +struct param_dma_input { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 plane; + u32 order; + u32 buffer_number; + u32 buffer_address; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_otf_output { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 order; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; + u32 err; +}; + +struct param_dma_output { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 plane; + u32 order; + u32 buffer_number; + u32 buffer_address; + u32 notify_dma_done; + u32 dma_out_mask; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 12]; + u32 err; +}; + +struct param_global_shotmode { + u32 cmd; + u32 skip_frames; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_sensor_framerate { + u32 frame_rate; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_isp_aa { + u32 cmd; + u32 target; + u32 mode; + u32 scene; + u32 sleep; + u32 face; + u32 touch_x; + u32 touch_y; + u32 manual_af_setting; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_isp_flash { + u32 cmd; + u32 redeye; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_awb { + u32 cmd; + u32 illumination; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_imageeffect { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_isp_iso { + u32 cmd; + u32 value; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_adjust { + u32 cmd; + s32 contrast; + s32 saturation; + s32 sharpness; + s32 exposure; + s32 brightness; + s32 hue; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 8]; + u32 err; +}; + +struct param_isp_metering { + u32 cmd; + u32 win_pos_x; + u32 win_pos_y; + u32 win_width; + u32 win_height; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; + u32 err; +}; + +struct param_isp_afc { + u32 cmd; + u32 manual; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_scaler_imageeffect { + u32 cmd; + u32 arbitrary_cb; + u32 arbitrary_cr; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 4]; + u32 err; +}; + +struct param_scaler_input_crop { + u32 cmd; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 in_width; + u32 in_height; + u32 out_width; + u32 out_height; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_scaler_output_crop { + u32 cmd; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 out_format; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; + u32 err; +}; + +struct param_scaler_rotation { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_scaler_flip { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_3dnr_1stframe { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_fd_config { + u32 cmd; + u32 max_number; + u32 roll_angle; + u32 yaw_angle; + u32 smile_mode; + u32 blink_mode; + u32 eye_detect; + u32 mouth_detect; + u32 orientation; + u32 orientation_value; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 11]; + u32 err; +}; + +struct global_param { + struct param_global_shotmode shotmode; +}; + +struct sensor_param { + struct param_control control; + struct param_otf_output otf_output; + struct param_sensor_framerate frame_rate; +} __packed; + +struct buffer_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_otf_output otf_output; +} __packed; + +struct isp_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma1_input; + struct param_dma_input dma2_input; + struct param_isp_aa aa; + struct param_isp_flash flash; + struct param_isp_awb awb; + struct param_isp_imageeffect effect; + struct param_isp_iso iso; + struct param_isp_adjust adjust; + struct param_isp_metering metering; + struct param_isp_afc afc; + struct param_otf_output otf_output; + struct param_dma_output dma1_output; + struct param_dma_output dma2_output; +} __packed; + +struct drc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_otf_output otf_output; +} __packed; + +struct scalerc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_scaler_imageeffect effect; + struct param_scaler_input_crop input_crop; + struct param_scaler_output_crop output_crop; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct odc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_otf_output otf_output; +} __packed; + +struct dis_param { + struct param_control control; + struct param_otf_output otf_input; + struct param_otf_output otf_output; +} __packed; + +struct tdnr_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_3dnr_1stframe frame; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct scalerp_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_scaler_imageeffect effect; + struct param_scaler_input_crop input_crop; + struct param_scaler_output_crop output_crop; + struct param_scaler_rotation rotation; + struct param_scaler_flip flip; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct fd_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_fd_config config; +} __packed; + +struct is_param_region { + struct global_param global; + struct sensor_param sensor; + struct buffer_param buf; + struct isp_param isp; + struct drc_param drc; + struct scalerc_param scalerc; + struct odc_param odc; + struct dis_param dis; + struct tdnr_param tdnr; + struct scalerp_param scalerp; + struct fd_param fd; +} __packed; + +#define NUMBER_OF_GAMMA_CURVE_POINTS 32 + +struct is_tune_sensor { + u32 exposure; + u32 analog_gain; + u32 frame_rate; + u32 actuator_position; +}; + +struct is_tune_gammacurve { + u32 num_pts_x[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_r[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_g[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_b[NUMBER_OF_GAMMA_CURVE_POINTS]; +}; + +struct is_tune_isp { + /* Brightness level: range 0...100, default 7. */ + u32 brightness_level; + /* Contrast level: range -127...127, default 0. */ + s32 contrast_level; + /* Saturation level: range -127...127, default 0. */ + s32 saturation_level; + s32 gamma_level; + struct is_tune_gammacurve gamma_curve[4]; + /* Hue: range -127...127, default 0. */ + s32 hue; + /* Sharpness blur: range -127...127, default 0. */ + s32 sharpness_blur; + /* Despeckle : range -127~127, default : 0 */ + s32 despeckle; + /* Edge color supression: range -127...127, default 0. */ + s32 edge_color_supression; + /* Noise reduction: range -127...127, default 0. */ + s32 noise_reduction; + /* (32 * 4 + 9) * 4 = 548 bytes */ +} __packed; + +struct is_tune_region { + struct is_tune_sensor sensor; + struct is_tune_isp isp; +} __packed; + +struct rational { + u32 num; + u32 den; +}; + +struct srational { + s32 num; + s32 den; +}; + +#define FLASH_FIRED_SHIFT 0 +#define FLASH_NOT_FIRED 0 +#define FLASH_FIRED 1 + +#define FLASH_STROBE_SHIFT 1 +#define FLASH_STROBE_NO_DETECTION 0 +#define FLASH_STROBE_RESERVED 1 +#define FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED 2 +#define FLASH_STROBE_RETURN_LIGHT_DETECTED 3 + +#define FLASH_MODE_SHIFT 3 +#define FLASH_MODE_UNKNOWN 0 +#define FLASH_MODE_COMPULSORY_FLASH_FIRING 1 +#define FLASH_MODE_COMPULSORY_FLASH_SUPPRESSION 2 +#define FLASH_MODE_AUTO_MODE 3 + +#define FLASH_FUNCTION_SHIFT 5 +#define FLASH_FUNCTION_PRESENT 0 +#define FLASH_FUNCTION_NONE 1 + +#define FLASH_RED_EYE_SHIFT 6 +#define FLASH_RED_EYE_DISABLED 0 +#define FLASH_RED_EYE_SUPPORTED 1 + +enum apex_aperture_value { + F1_0 = 0, + F1_4 = 1, + F2_0 = 2, + F2_8 = 3, + F4_0 = 4, + F5_6 = 5, + F8_9 = 6, + F11_0 = 7, + F16_0 = 8, + F22_0 = 9, + F32_0 = 10, +}; + +struct exif_attribute { + struct rational exposure_time; + struct srational shutter_speed; + u32 iso_speed_rating; + u32 flash; + struct srational brightness; +} __packed; + +struct is_frame_header { + u32 valid; + u32 bad_mark; + u32 captured; + u32 frame_number; + struct exif_attribute exif; +} __packed; + +struct is_fd_rect { + u32 offset_x; + u32 offset_y; + u32 width; + u32 height; +}; + +struct is_face_marker { + u32 frame_number; + struct is_fd_rect face; + struct is_fd_rect left_eye; + struct is_fd_rect right_eye; + struct is_fd_rect mouth; + u32 roll_angle; + u32 yaw_angle; + u32 confidence; + s32 smile_level; + s32 blink_level; +} __packed; + +#define MAX_FRAME_COUNT 8 +#define MAX_FRAME_COUNT_PREVIEW 4 +#define MAX_FRAME_COUNT_CAPTURE 1 +#define MAX_FACE_COUNT 16 +#define MAX_SHARED_COUNT 500 + +struct is_region { + struct is_param_region parameter; + struct is_tune_region tune; + struct is_frame_header header[MAX_FRAME_COUNT]; + struct is_face_marker face[MAX_FACE_COUNT]; + u32 shared[MAX_SHARED_COUNT]; +} __packed; + +struct is_debug_frame_descriptor { + u32 sensor_frame_time; + u32 sensor_exposure_time; + s32 sensor_analog_gain; + /* monitor for AA */ + u32 req_lei; + + u32 next_next_lei_exp; + u32 next_next_lei_a_gain; + u32 next_next_lei_d_gain; + u32 next_next_lei_statlei; + u32 next_next_lei_lei; + + u32 dummy0; +}; + +#define MAX_FRAMEDESCRIPTOR_CONTEXT_NUM (30*20) /* 600 frames */ +#define MAX_VERSION_DISPLAY_BUF 32 + +struct is_share_region { + u32 frame_time; + u32 exposure_time; + s32 analog_gain; + + u32 r_gain; + u32 g_gain; + u32 b_gain; + + u32 af_position; + u32 af_status; + /* 0 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_NOMESSAGE */ + /* 1 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_REACHED */ + /* 2 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_UNABLETOREACH */ + /* 3 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_LOST */ + /* default : unknown */ + u32 af_scene_type; + + u32 frame_descp_onoff_control; + u32 frame_descp_update_done; + u32 frame_descp_idx; + u32 frame_descp_max_idx; + struct is_debug_frame_descriptor + dbg_frame_descp_ctx[MAX_FRAMEDESCRIPTOR_CONTEXT_NUM]; + + u32 chip_id; + u32 chip_rev_no; + u8 isp_fw_ver_no[MAX_VERSION_DISPLAY_BUF]; + u8 isp_fw_ver_date[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_ver_no[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_rev_no[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_rev_date[MAX_VERSION_DISPLAY_BUF]; +} __packed; + +struct is_debug_control { + u32 write_point; /* 0~ 500KB boundary */ + u32 assert_flag; /* 0: Not invoked, 1: Invoked */ + u32 pabort_flag; /* 0: Not invoked, 1: Invoked */ + u32 dabort_flag; /* 0: Not invoked, 1: Invoked */ +}; + +struct sensor_open_extended { + u32 actuator_type; + u32 mclk; + u32 mipi_lane_num; + u32 mipi_speed; + /* Skip setfile loading when fast_open_sensor is not 0 */ + u32 fast_open_sensor; + /* Activating sensor self calibration mode (6A3) */ + u32 self_calibration_mode; + /* This field is to adjust I2c clock based on ACLK200 */ + /* This value is varied in case of rev 0.2 */ + u32 i2c_sclk; +}; + +struct fimc_is; + +int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); +void fimc_is_set_initial_params(struct fimc_is *is); +unsigned int __get_pending_param_count(struct fimc_is *is); + +int __is_hw_update_params(struct fimc_is *is); +void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); +void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); +void __is_set_sensor(struct fimc_is *is, int fps); +void __is_set_isp_aa_ae(struct fimc_is *is); +void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye); +void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_effect(struct fimc_is *is, u32 cmd); +void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val); +void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_drc_control(struct fimc_is *is, u32 val); +void __is_set_fd_control(struct fimc_is *is, u32 val); +void __is_set_fd_config_maxface(struct fimc_is *is, u32 val); +void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val); +void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val); +void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val); +void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val); +void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val); +void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val); +void __is_set_fd_config_orientation(struct fimc_is *is, u32 val); +void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val); +void __is_set_isp_aa_af_mode(struct fimc_is *is, int cmd); +void __is_set_isp_aa_af_start_stop(struct fimc_is *is, int cmd); + +#endif diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c new file mode 100644 index 0000000..b0ff67b --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -0,0 +1,243 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#include <linux/delay.h> + +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-regs.h" +#include "fimc-is-sensor.h" + +void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int nr) +{ + mcuctl_write(1UL << nr, is, MCUCTL_REG_INTCR1); +} + +void fimc_is_fw_clear_irq2(struct fimc_is *is) +{ + u32 cfg = mcuctl_read(is, MCUCTL_REG_INTSR2); + mcuctl_write(cfg, is, MCUCTL_REG_INTCR2); +} + +void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is) +{ + mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0); +} + +int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is) +{ + unsigned int timeout = 2000; + u32 cfg, status; + + cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); + status = INTSR0_GET_INTSD(0, cfg); + + while (status) { + cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); + status = INTSR0_GET_INTSD(0, cfg); + if (timeout == 0) { + dev_warn(&is->pdev->dev, "%s timeout\n", + __func__); + return -ETIME; + } + timeout--; + udelay(1); + } + return 0; +} + +int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is) +{ + unsigned int timeout = 2000; + u32 cfg, status; + + cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); + status = INTMSR0_GET_INTMSD(0, cfg); + + while (status) { + cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); + status = INTMSR0_GET_INTMSD(0, cfg); + if (timeout == 0) { + dev_warn(&is->pdev->dev, "%s timeout\n", + __func__); + return -ETIME; + } + timeout--; + udelay(1); + } + return 0; +} + +int fimc_is_hw_set_param(struct fimc_is *is) +{ + struct chain_config *config = &is->config[is->config_index]; + unsigned int param_count = __get_pending_param_count(is); + + fimc_is_hw_wait_intmsr0_intmsd0(is); + + mcuctl_write(HIC_SET_PARAMETER, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2)); + + mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3)); + mcuctl_write(config->p_region_index1, is, MCUCTL_REG_ISSR(4)); + mcuctl_write(config->p_region_index2, is, MCUCTL_REG_ISSR(5)); + + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +int fimc_is_hw_set_tune(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + + mcuctl_write(HIC_SET_TUNE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->h2i_cmd.entry_id, is, MCUCTL_REG_ISSR(2)); + + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +#define FIMC_IS_MAX_PARAMS 4 + +int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args) +{ + int i; + + if (num_args > FIMC_IS_MAX_PARAMS) + return -EINVAL; + + is->i2h_cmd.num_args = num_args; + + for (i = 0; i < FIMC_IS_MAX_PARAMS; i++) { + if (i < num_args) + is->i2h_cmd.args[i] = mcuctl_read(is, + MCUCTL_REG_ISSR(12 + i)); + else + is->i2h_cmd.args[i] = 0; + } + return 0; +} + +void fimc_is_hw_set_sensor_num(struct fimc_is *is) +{ + pr_debug("setting sensor index to: %d\n", is->sensor_index); + + mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); + mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3)); +} + +void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) +{ + if (is->sensor_index != index) + return; + + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_CLOSE_SENSOR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_get_setfile_addr(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_GET_SET_FILE_ADDR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_load_setfile(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_LOAD_SET_FILE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +int fimc_is_hw_change_mode(struct fimc_is *is) +{ + const u8 cmd[] = { + HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO, + HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO, + }; + + if (WARN_ON(is->config_index > ARRAY_SIZE(cmd))) + return -EINVAL; + + mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +void fimc_is_hw_stream_on(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_STREAM_ON, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(0, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_stream_off(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_STREAM_OFF, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_subip_power_off(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_POWER_DOWN, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +int fimc_is_itf_s_param(struct fimc_is *is, bool update) +{ + int ret; + + if (update) + __is_hw_update_params(is); + + fimc_is_mem_barrier(); + + clear_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + fimc_is_hw_set_param(is); + ret = fimc_is_wait_event(is, IS_ST_BLOCK_CMD_CLEARED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) + dev_err(&is->pdev->dev, "%s() timeout\n", __func__); + + return ret; +} + +int fimc_is_itf_mode_change(struct fimc_is *is) +{ + int ret; + + clear_bit(IS_ST_CHANGE_MODE, &is->state); + fimc_is_hw_change_mode(is); + ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (!ret < 0) + dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n", + __func__, is->config_index); + return ret; +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h new file mode 100644 index 0000000..5fa2fda --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h @@ -0,0 +1,164 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * 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. + */ +#ifndef FIMC_IS_REG_H_ +#define FIMC_IS_REG_H_ + +/* WDT_ISP register */ +#define REG_WDT_ISP 0x00170000 + +/* MCUCTL registers base offset */ +#define MCUCTL_BASE 0x00180000 + +/* MCU Controller Register */ +#define MCUCTL_REG_MCUCTRL (MCUCTL_BASE + 0x00) +#define MCUCTRL_MSWRST (1 << 0) + +/* Boot Base Offset Address Register */ +#define MCUCTL_REG_BBOAR (MCUCTL_BASE + 0x04) + +/* Interrupt Generation Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTGR0 (MCUCTL_BASE + 0x08) +/* __n = 0...9 */ +#define INTGR0_INTGC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTGR0_INTGD(__n) (1 << (__n)) + +/* Interrupt Clear Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTCR0 (MCUCTL_BASE + 0x0c) +/* __n = 0...9 */ +#define INTCR0_INTGC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTCR0_INTCD(__n) (1 << ((__n) + 16)) + +/* Interrupt Mask Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTMR0 (MCUCTL_BASE + 0x10) +/* __n = 0...9 */ +#define INTMR0_INTMC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTMR0_INTMD(__n) (1 << (__n)) + +/* Interrupt Status Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTSR0 (MCUCTL_BASE + 0x14) +/* __n (bit number) = 0...4 */ +#define INTSR0_GET_INTSD(x, __n) (((x) >> (__n)) & 0x1) +/* __n (bit number) = 0...9 */ +#define INTSR0_GET_INTSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) + +/* Interrupt Mask Status Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTMSR0 (MCUCTL_BASE + 0x18) +/* __n (bit number) = 0...4 */ +#define INTMSR0_GET_INTMSD(x, __n) (((x) >> (__n)) & 0x1) +/* __n (bit number) = 0...9 */ +#define INTMSR0_GET_INTMSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) + +/* Interrupt Generation Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTGR1 (MCUCTL_BASE + 0x1c) +/* __n = 0...9 */ +#define INTGR1_INTGC(__n) (1 << (__n)) + +/* Interrupt Clear Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTCR1 (MCUCTL_BASE + 0x20) +/* __n = 0...9 */ +#define INTCR1_INTCC(__n) (1 << (__n)) + +/* Interrupt Mask Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTMR1 (MCUCTL_BASE + 0x24) +/* __n = 0...9 */ +#define INTMR1_INTMC(__n) (1 << (__n)) + +/* Interrupt Status Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTSR1 (MCUCTL_BASE + 0x28) +/* Interrupt Mask Status Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTMSR1 (MCUCTL_BASE + 0x2c) + +/* Interrupt Clear Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTCR2 (MCUCTL_BASE + 0x30) +/* __n = 0...5 */ +#define INTCR2_INTCC(__n) (1 << ((__n) + 16)) + +/* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTMR2 (MCUCTL_BASE + 0x34) +/* __n = 0...25 */ +#define INTMR2_INTMCIS(__n) (1 << (__n)) + +/* Interrupt Status Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTSR2 (MCUCTL_BASE + 0x38) +/* Interrupt Mask Status Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTMSR2 (MCUCTL_BASE + 0x3c) + +/* General Purpose Output Control Register (0~17) */ +#define MCUCTL_REG_GPOCTLR (MCUCTL_BASE + 0x40) +/* __n = 0...17 */ +#define GPOCTLR_GPOG(__n) (1 << (__n)) + +/* General Purpose Pad Output Enable Register (0~17) */ +#define MCUCTL_REG_GPOENCTLR (MCUCTL_BASE + 0x44) +/* __n = 0...17 */ +#define GPOENCTLR_GPOEN(__n) (1 << (__n)) + +/* General Purpose Input Control Register (0~17) */ +#define MCUCTL_REG_GPICTLR (MCUCTL_BASE + 0x48) + +/* Shared registers between ISP CPU and the host CPU - ISSRxx */ + +/* ISSR(1): Command Host -> IS */ +/* ISSR(1): Sensor ID for Command, ISSR2...5 = Parameter 1...4 */ + +/* ISSR(10): Reply IS -> Host */ +/* ISSR(11): Sensor ID for Reply, ISSR12...15 = Parameter 1...4 */ + +/* ISSR(20): ISP_FRAME_DONE : SENSOR ID */ +/* ISSR(21): ISP_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(24): SCALERC_FRAME_DONE : SENSOR ID */ +/* ISSR(25): SCALERC_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(28): 3DNR_FRAME_DONE : SENSOR ID */ +/* ISSR(29): 3DNR_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(32): SCALERP_FRAME_DONE : SENSOR ID */ +/* ISSR(33): SCALERP_FRAME_DONE : PARAMETER 1 */ + +/* __n = 0...63 */ +#define MCUCTL_REG_ISSR(__n) (MCUCTL_BASE + 0x80 + ((__n) * 4)) + +/* PMU ISP register offsets */ +#define REG_CMU_RESET_ISP_SYS_PWR_REG 0x1174 +#define REG_CMU_SYSCLK_ISP_SYS_PWR_REG 0x13b8 +#define REG_PMU_ISP_ARM_SYS 0x1050 +#define REG_PMU_ISP_ARM_CONFIGURATION 0x2280 +#define REG_PMU_ISP_ARM_STATUS 0x2284 +#define REG_PMU_ISP_ARM_OPTION 0x2288 + +void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int bit); +void fimc_is_fw_clear_irq2(struct fimc_is *is); +int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num); + +void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); +int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is); +int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is); +void fimc_is_hw_set_sensor_num(struct fimc_is *is); +void fimc_is_hw_stream_on(struct fimc_is *is); +void fimc_is_hw_stream_off(struct fimc_is *is); +int fimc_is_hw_set_param(struct fimc_is *is); +int fimc_is_hw_change_mode(struct fimc_is *is); + +void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index); +void fimc_is_hw_get_setfile_addr(struct fimc_is *is); +void fimc_is_hw_load_setfile(struct fimc_is *is); +void fimc_is_hw_subip_power_off(struct fimc_is *is); + +int fimc_is_itf_s_param(struct fimc_is *is, bool update); +int fimc_is_itf_mode_change(struct fimc_is *is); + +#endif /* FIMC_IS_REG_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c new file mode 100644 index 0000000..6647421 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c @@ -0,0 +1,305 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <media/v4l2-subdev.h> + +#include "fimc-is.h" +#include "fimc-is-sensor.h" + +#define DRIVER_NAME "FIMC-IS-SENSOR" + +static const char * const sensor_supply_names[] = { + "svdda", + "svddio", +}; + +static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { + { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + } +}; + +static const struct v4l2_mbus_framefmt *find_sensor_format( + struct v4l2_mbus_framefmt *mf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) + if (mf->code == fimc_is_sensor_formats[i].code) + return &fimc_is_sensor_formats[i]; + + return &fimc_is_sensor_formats[0]; +} + +static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) + return -EINVAL; + + code->code = fimc_is_sensor_formats[code->index].code; + return 0; +} + +static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, + struct v4l2_mbus_framefmt *mf) +{ + const struct sensor_drv_data *dd = sensor->drvdata; + const struct v4l2_mbus_framefmt *fmt; + + fmt = find_sensor_format(mf); + mf->code = fmt->code; + v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, + &mf->height, 12 + 8, dd->height, 0, 0); +} + +static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( + struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, + u32 pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + + return &sensor->format; +} + +static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + struct v4l2_mbus_framefmt *mf; + + fimc_is_sensor_try_format(sensor, &fmt->format); + + mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); + if (mf) { + mutex_lock(&sensor->lock); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + *mf = fmt->format; + mutex_unlock(&sensor->lock); + } + return 0; +} + +static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + struct v4l2_mbus_framefmt *mf; + + mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); + + mutex_lock(&sensor->lock); + fmt->format = *mf; + mutex_unlock(&sensor->lock); + return 0; +} + +static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { + .enum_mbus_code = fimc_is_sensor_enum_mbus_code, + .get_fmt = fimc_is_sensor_get_fmt, + .set_fmt = fimc_is_sensor_set_fmt, +}; + +static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + + *format = fimc_is_sensor_formats[0]; + format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; + format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; + + return 0; +} + +static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { + .open = fimc_is_sensor_open, +}; + +static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + int gpio = sensor->gpio_reset; + int ret; + + if (on) { + ret = pm_runtime_get(sensor->dev); + if (ret < 0) + return ret; + + ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (ret < 0) { + pm_runtime_put(sensor->dev); + return ret; + } + if (gpio_is_valid(gpio)) { + gpio_set_value(gpio, 1); + usleep_range(600, 800); + gpio_set_value(gpio, 0); + usleep_range(10000, 11000); + gpio_set_value(gpio, 1); + } + + /* A delay needed for the sensor initialization. */ + msleep(20); + } else { + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, 0); + + ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (!ret) + pm_runtime_put(sensor->dev); + } + + pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); + + return ret; +} + +static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { + .s_power = fimc_is_sensor_s_power, +}; + +static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { + .core = &fimc_is_sensor_core_ops, + .pad = &fimc_is_sensor_pad_ops, +}; + +static const struct of_device_id fimc_is_sensor_of_match[]; + +static int fimc_is_sensor_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct fimc_is_sensor *sensor; + const struct of_device_id *of_id; + struct v4l2_subdev *sd; + int gpio, i, ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + mutex_init(&sensor->lock); + sensor->gpio_reset = -EINVAL; + + gpio = of_get_gpio_flags(dev->of_node, 0, NULL); + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, + DRIVER_NAME); + if (ret < 0) + return ret; + } + sensor->gpio_reset = gpio; + + for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = sensor_supply_names[i]; + + ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (ret < 0) + return ret; + + of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); + if (!of_id) + return -ENODEV; + + sensor->drvdata = of_id->data; + sensor->dev = dev; + + sd = &sensor->subdev; + v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); + snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sensor->format.code = fimc_is_sensor_formats[0].code; + sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; + sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; + + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); + if (ret < 0) + return ret; + + pm_runtime_no_callbacks(dev); + pm_runtime_enable(dev); + + return ret; +} + +static int fimc_is_sensor_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + media_entity_cleanup(&sd->entity); + return 0; +} + +static const struct i2c_device_id fimc_is_sensor_ids[] = { + { } +}; + +static const struct sensor_drv_data s5k6a3_drvdata = { + .id = FIMC_IS_SENSOR_ID_S5K6A3, + .subdev_name = "S5K6A3", + .width = S5K6A3_SENSOR_WIDTH, + .height = S5K6A3_SENSOR_HEIGHT, +}; + +static const struct of_device_id fimc_is_sensor_of_match[] = { + { + .compatible = "samsung,s5k6a3", + .data = &s5k6a3_drvdata, + }, + { } +}; + +static struct i2c_driver fimc_is_sensor_driver = { + .driver = { + .of_match_table = fimc_is_sensor_of_match, + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = fimc_is_sensor_probe, + .remove = fimc_is_sensor_remove, + .id_table = fimc_is_sensor_ids, +}; + +int fimc_is_register_sensor_driver(void) +{ + return i2c_add_driver(&fimc_is_sensor_driver); +} + +void fimc_is_unregister_sensor_driver(void) +{ + i2c_del_driver(&fimc_is_sensor_driver); +} + +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h new file mode 100644 index 0000000..6036d49 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h @@ -0,0 +1,89 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * 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. + */ +#ifndef FIMC_IS_SENSOR_H_ +#define FIMC_IS_SENSOR_H_ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> + +#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */ + +#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296 +#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732 + +#define S5K6A3_SENSOR_WIDTH 1392 +#define S5K6A3_SENSOR_HEIGHT 1392 + +#define SENSOR_NUM_SUPPLIES 2 + +enum fimc_is_sensor_id { + FIMC_IS_SENSOR_ID_S5K3H2 = 1, + FIMC_IS_SENSOR_ID_S5K6A3, + FIMC_IS_SENSOR_ID_S5K4E5, + FIMC_IS_SENSOR_ID_S5K3H7, + FIMC_IS_SENSOR_ID_CUSTOM, + FIMC_IS_SENSOR_ID_END +}; + +#define IS_SENSOR_CTRL_BUS_I2C0 0 +#define IS_SENSOR_CTRL_BUS_I2C1 1 + +struct sensor_drv_data { + enum fimc_is_sensor_id id; + const char * const subdev_name; + unsigned int width; + unsigned int height; +}; + +/** + * struct fimc_is_sensor - fimc-is sensor data structure + * @dev: pointer to this I2C client device structure + * @subdev: the image sensor's v4l2 subdev + * @pad: subdev media source pad + * @supplies: image sensor's voltage regulator supplies + * @gpio_reset: GPIO connected to the sensor's reset pin + * @drvdata: a pointer to the sensor's parameters data structure + * @i2c_bus: ISP I2C bus index (0...1) + * @test_pattern: true to enable video test pattern + * @lock: mutex protecting the structure's members below + * @format: media bus format at the sensor's source pad + */ +struct fimc_is_sensor { + struct device *dev; + struct v4l2_subdev subdev; + struct media_pad pad; + struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES]; + int gpio_reset; + const struct sensor_drv_data *drvdata; + unsigned int i2c_bus; + bool test_pattern; + + struct mutex lock; + struct v4l2_mbus_framefmt format; +}; + +static inline +struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) +{ + return container_of(sd, struct fimc_is_sensor, subdev); +} + +int fimc_is_register_sensor_driver(void); +void fimc_is_unregister_sensor_driver(void); + +#endif /* FIMC_IS_SENSOR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c new file mode 100644 index 0000000..47c6363 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -0,0 +1,1007 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * 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. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/dma-contiguous.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_i2c.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <media/v4l2-of.h> +#include <media/videobuf2-dma-contig.h> + +#include "media-dev.h" +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-errno.h" +#include "fimc-is-i2c.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" + + +static char *fimc_is_clocks[ISS_CLKS_MAX] = { + [ISS_CLK_PPMUISPX] = "ppmuispx", + [ISS_CLK_PPMUISPMX] = "ppmuispmx", + [ISS_CLK_LITE0] = "lite0", + [ISS_CLK_LITE1] = "lite1", + [ISS_CLK_MPLL] = "mpll", + [ISS_CLK_SYSREG] = "sysreg", + [ISS_CLK_ISP] = "isp", + [ISS_CLK_DRC] = "drc", + [ISS_CLK_FD] = "fd", + [ISS_CLK_MCUISP] = "mcuisp", + [ISS_CLK_UART] = "uart", + [ISS_CLK_ISP_DIV0] = "ispdiv0", + [ISS_CLK_ISP_DIV1] = "ispdiv1", + [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0", + [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1", + [ISS_CLK_ACLK200] = "aclk200", + [ISS_CLK_ACLK200_DIV] = "div_aclk200", + [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp", + [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp", +}; + +static void fimc_is_put_clocks(struct fimc_is *is) +{ + int i; + + for (i = 0; i < ISS_CLKS_MAX; i++) { + if (IS_ERR(is->clocks[i])) + continue; + clk_unprepare(is->clocks[i]); + clk_put(is->clocks[i]); + is->clocks[i] = ERR_PTR(-EINVAL); + } +} + +static int fimc_is_get_clocks(struct fimc_is *is) +{ + int i, ret; + + for (i = 0; i < ISS_CLKS_MAX; i++) + is->clocks[i] = ERR_PTR(-EINVAL); + + for (i = 0; i < ISS_CLKS_MAX; i++) { + is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]); + if (IS_ERR(is->clocks[i])) { + ret = PTR_ERR(is->clocks[i]); + goto err; + } + ret = clk_prepare(is->clocks[i]); + if (ret < 0) { + clk_put(is->clocks[i]); + is->clocks[i] = ERR_PTR(-EINVAL); + goto err; + } + } + + return 0; +err: + fimc_is_put_clocks(is); + dev_err(&is->pdev->dev, "failed to get clock: %s\n", + fimc_is_clocks[i]); + return -ENXIO; +} + +static int fimc_is_setup_clocks(struct fimc_is *is) +{ + int ret; + + ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200], + is->clocks[ISS_CLK_ACLK200_DIV]); + if (ret < 0) + return ret; + + ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP], + is->clocks[ISS_CLK_ACLK400MCUISP_DIV]); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0], + ATCLK_MCUISP_FREQUENCY); + if (ret < 0) + return ret; + + return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1], + ATCLK_MCUISP_FREQUENCY); +} + +int fimc_is_enable_clocks(struct fimc_is *is) +{ + int i, ret; + + for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { + if (IS_ERR(is->clocks[i])) + continue; + ret = clk_enable(is->clocks[i]); + if (ret < 0) { + dev_err(&is->pdev->dev, "clock %s enable failed\n", + fimc_is_clocks[i]); + for (--i; i >= 0; i--) + clk_disable(is->clocks[i]); + return ret; + } + pr_debug("enabled clock: %s\n", fimc_is_clocks[i]); + } + return 0; +} + +void fimc_is_disable_clocks(struct fimc_is *is) +{ + int i; + + for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { + if (!IS_ERR(is->clocks[i])) { + clk_disable(is->clocks[i]); + pr_debug("disabled clock: %s\n", fimc_is_clocks[i]); + } + } +} + +static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, + struct device_node *np) +{ + u32 tmp = 0; + int ret; + + np = v4l2_of_get_next_endpoint(np, NULL); + if (!np) + return -ENXIO; + np = v4l2_of_get_remote_port(np); + if (!np) + return -ENXIO; + + /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ + ret = of_property_read_u32(np, "reg", &tmp); + sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; + + return ret; +} + +static int fimc_is_register_subdevs(struct fimc_is *is) +{ + struct device_node *adapter, *child; + int ret; + + ret = fimc_isp_subdev_create(&is->isp); + if (ret < 0) + return ret; + + for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { + if (!of_find_device_by_node(adapter)) { + of_node_put(adapter); + return -EPROBE_DEFER; + } + + for_each_available_child_of_node(adapter, child) { + struct i2c_client *client; + struct v4l2_subdev *sd; + + client = of_find_i2c_device_by_node(child); + if (!client) + goto e_retry; + + sd = i2c_get_clientdata(client); + if (!sd) + goto e_retry; + + /* FIXME: Add support for multiple sensors. */ + if (WARN_ON(is->sensor)) + continue; + + is->sensor = sd_to_fimc_is_sensor(sd); + + if (fimc_is_parse_sensor_config(is->sensor, child)) { + dev_warn(&is->pdev->dev, "DT parse error: %s\n", + child->full_name); + } + pr_debug("%s(): registered subdev: %p\n", + __func__, sd->name); + } + } + return 0; + +e_retry: + of_node_put(child); + return -EPROBE_DEFER; +} + +static int fimc_is_unregister_subdevs(struct fimc_is *is) +{ + fimc_isp_subdev_destroy(&is->isp); + is->sensor = NULL; + return 0; +} + +static int fimc_is_load_setfile(struct fimc_is *is, char *file_name) +{ + const struct firmware *fw; + void *buf; + int ret; + + ret = request_firmware(&fw, file_name, &is->pdev->dev); + if (ret < 0) { + dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret); + return ret; + } + buf = is->memory.vaddr + is->setfile.base; + memcpy(buf, fw->data, fw->size); + fimc_is_mem_barrier(); + is->setfile.size = fw->size; + + pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf); + + memcpy(is->fw.setfile_info, + fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN, + FIMC_IS_SETFILE_INFO_LEN - 1); + + is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0'; + is->setfile.state = 1; + + pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n", + is->setfile.base, fw->size); + + release_firmware(fw); + return ret; +} + +int fimc_is_cpu_set_power(struct fimc_is *is, int on) +{ + unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT; + + if (on) { + /* Disable watchdog */ + mcuctl_write(0, is, REG_WDT_ISP); + + /* Cortex-A5 start address setting */ + mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR); + + /* Enable and start Cortex-A5 */ + pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION); + pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION); + } else { + /* A5 power off */ + pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION); + pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION); + + while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) { + if (timeout == 0) + return -ETIME; + timeout--; + udelay(1); + } + } + + return 0; +} + +/* Wait until @bit of @is->state is set to @state in the interrupt handler. */ +int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, + unsigned int state, unsigned int timeout) +{ + + int ret = wait_event_timeout(is->irq_queue, + !state ^ test_bit(bit, &is->state), + timeout); + if (ret == 0) { + dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__); + return -ETIME; + } + return 0; +} + +int fimc_is_start_firmware(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + int ret; + + memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size); + wmb(); + + ret = fimc_is_cpu_set_power(is, 1); + if (ret < 0) + return ret; + + ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1, + msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT)); + if (ret < 0) + dev_err(dev, "FIMC-IS CPU power on failed\n"); + + return ret; +} + +/* Allocate working memory for the FIMC-IS CPU. */ +static int fimc_is_alloc_cpu_memory(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + + is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE, + &is->memory.paddr, GFP_KERNEL); + if (is->memory.vaddr == NULL) + return -ENOMEM; + + is->memory.size = FIMC_IS_CPU_MEM_SIZE; + memset(is->memory.vaddr, 0, is->memory.size); + + dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr); + + if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) { + dev_err(dev, "invalid firmware memory alignment: %#x\n", + (u32)is->memory.paddr); + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, + is->memory.paddr); + return -EIO; + } + + is->is_p_region = (struct is_region *)(is->memory.vaddr + + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE); + + is->is_dma_p_region = is->memory.paddr + + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE; + + is->is_shared_region = (struct is_share_region *)(is->memory.vaddr + + FIMC_IS_SHARED_REGION_OFFSET); + return 0; +} + +static void fimc_is_free_cpu_memory(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, + is->memory.paddr); +} + +static void fimc_is_load_firmware(const struct firmware *fw, void *context) +{ + struct fimc_is *is = context; + struct device *dev = &is->pdev->dev; + void *buf; + int ret; + + if (fw == NULL) { + dev_err(dev, "firmware request failed\n"); + return; + } + 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); + goto done; + } + + is->fw.size = fw->size; + + ret = fimc_is_alloc_cpu_memory(is); + if (ret < 0) { + dev_err(dev, "failed to allocate FIMC-IS CPU memory\n"); + goto done; + } + + memcpy(is->memory.vaddr, fw->data, fw->size); + wmb(); + + /* Read firmware description. */ + buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN); + memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN); + is->fw.info[FIMC_IS_FW_INFO_LEN] = 0; + + buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN); + memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN); + is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0; + + is->fw.state = 1; + + 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); + + is->is_shared_region->chip_id = 0xe4412; + is->is_shared_region->chip_rev_no = 1; + + fimc_is_mem_barrier(); + + /* + * FIXME: The firmware is not being released for now, as it is + * needed around for copying to the IS working memory every + * time before the Cortex-A5 is restarted. + */ + if (is->fw.f_w) + release_firmware(is->fw.f_w); + is->fw.f_w = fw; +done: + mutex_unlock(&is->lock); +} + +static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name) +{ + return request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev, + GFP_KERNEL, is, fimc_is_load_firmware); +} + +/* General IS interrupt handler */ +static void fimc_is_general_irq_handler(struct fimc_is *is) +{ + is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10)); + + switch (is->i2h_cmd.cmd) { + case IHC_GET_SENSOR_NUM: + fimc_is_hw_get_params(is, 1); + fimc_is_hw_wait_intmsr0_intmsd0(is); + fimc_is_hw_set_sensor_num(is); + pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]); + break; + case IHC_SET_FACE_MARK: + case IHC_FRAME_DONE: + fimc_is_hw_get_params(is, 2); + break; + case IHC_SET_SHOT_MARK: + case IHC_AA_DONE: + case IH_REPLY_DONE: + fimc_is_hw_get_params(is, 3); + break; + case IH_REPLY_NOT_DONE: + fimc_is_hw_get_params(is, 4); + break; + case IHC_NOT_READY: + break; + default: + pr_info("unknown command: %#x\n", is->i2h_cmd.cmd); + } + + fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL); + + switch (is->i2h_cmd.cmd) { + case IHC_GET_SENSOR_NUM: + fimc_is_hw_set_intgr0_gd0(is); + set_bit(IS_ST_A5_PWR_ON, &is->state); + break; + + case IHC_SET_SHOT_MARK: + break; + + case IHC_SET_FACE_MARK: + is->fd_header.count = is->i2h_cmd.args[0]; + is->fd_header.index = is->i2h_cmd.args[1]; + is->fd_header.offset = 0; + break; + + case IHC_FRAME_DONE: + break; + + case IHC_AA_DONE: + pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0], + is->i2h_cmd.args[1], is->i2h_cmd.args[2]); + break; + + case IH_REPLY_DONE: + pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]); + + switch (is->i2h_cmd.args[0]) { + case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO: + /* Get CAC margin */ + set_bit(IS_ST_CHANGE_MODE, &is->state); + is->isp.cac_margin_x = is->i2h_cmd.args[1]; + is->isp.cac_margin_y = is->i2h_cmd.args[2]; + pr_debug("CAC margin (x,y): (%d,%d)\n", + is->isp.cac_margin_x, is->isp.cac_margin_y); + break; + + case HIC_STREAM_ON: + clear_bit(IS_ST_STREAM_OFF, &is->state); + set_bit(IS_ST_STREAM_ON, &is->state); + break; + + case HIC_STREAM_OFF: + clear_bit(IS_ST_STREAM_ON, &is->state); + set_bit(IS_ST_STREAM_OFF, &is->state); + break; + + case HIC_SET_PARAMETER: + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + pr_debug("HIC_SET_PARAMETER\n"); + break; + + case HIC_GET_PARAMETER: + break; + + case HIC_SET_TUNE: + break; + + case HIC_GET_STATUS: + break; + + case HIC_OPEN_SENSOR: + set_bit(IS_ST_OPEN_SENSOR, &is->state); + pr_debug("data lanes: %d, settle line: %d\n", + is->i2h_cmd.args[2], is->i2h_cmd.args[1]); + break; + + case HIC_CLOSE_SENSOR: + clear_bit(IS_ST_OPEN_SENSOR, &is->state); + is->sensor_index = 0; + break; + + case HIC_MSG_TEST: + pr_debug("config MSG level completed\n"); + break; + + case HIC_POWER_DOWN: + clear_bit(IS_ST_PWR_SUBIP_ON, &is->state); + break; + + case HIC_GET_SET_FILE_ADDR: + is->setfile.base = is->i2h_cmd.args[1]; + set_bit(IS_ST_SETFILE_LOADED, &is->state); + break; + + case HIC_LOAD_SET_FILE: + set_bit(IS_ST_SETFILE_LOADED, &is->state); + break; + } + break; + + case IH_REPLY_NOT_DONE: + pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0], + is->i2h_cmd.args[1], + fimc_is_strerr(is->i2h_cmd.args[1])); + + if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG) + pr_err("IS_ERROR_TIME_OUT\n"); + + switch (is->i2h_cmd.args[1]) { + case IS_ERROR_SET_PARAMETER: + fimc_is_mem_barrier(); + } + + switch (is->i2h_cmd.args[0]) { + case HIC_SET_PARAMETER: + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + break; + } + break; + + case IHC_NOT_READY: + pr_err("IS control sequence error: Not Ready\n"); + break; + } + + wake_up(&is->irq_queue); +} + +static irqreturn_t fimc_is_irq_handler(int irq, void *priv) +{ + struct fimc_is *is = priv; + unsigned long flags; + u32 status; + + spin_lock_irqsave(&is->slock, flags); + status = mcuctl_read(is, MCUCTL_REG_INTSR1); + + if (status & (1UL << FIMC_IS_INT_GENERAL)) + fimc_is_general_irq_handler(is); + + if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP)) + fimc_isp_irq_handler(is); + + spin_unlock_irqrestore(&is->slock, flags); + return IRQ_HANDLED; +} + +static int fimc_is_hw_open_sensor(struct fimc_is *is, + struct fimc_is_sensor *sensor) +{ + struct sensor_open_extended *soe = (void *)&is->is_p_region->shared; + + fimc_is_hw_wait_intmsr0_intmsd0(is); + + soe->self_calibration_mode = 1; + soe->actuator_type = 0; + soe->mipi_lane_num = 0; + soe->mclk = 0; + soe->mipi_speed = 0; + soe->fast_open_sensor = 0; + soe->i2c_sclk = 88000000; + + fimc_is_mem_barrier(); + + mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2)); + mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3)); + mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4)); + + fimc_is_hw_set_intgr0_gd0(is); + + return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, + FIMC_IS_SENSOR_OPEN_TIMEOUT); +} + + +int fimc_is_hw_initialize(struct fimc_is *is) +{ + const int config_ids[] = { + IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO, + IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO + }; + struct device *dev = &is->pdev->dev; + u32 prev_id; + int i, ret; + + /* Sensor initialization. */ + ret = fimc_is_hw_open_sensor(is, is->sensor); + if (ret < 0) + return ret; + + /* Get the setfile address. */ + fimc_is_hw_get_setfile_addr(is); + + ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "get setfile address timed out\n"); + return ret; + } + pr_debug("setfile.base: %#x\n", is->setfile.base); + + /* Load the setfile. */ + fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3); + clear_bit(IS_ST_SETFILE_LOADED, &is->state); + fimc_is_hw_load_setfile(is); + ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "loading setfile timed out\n"); + return ret; + } + + pr_debug("setfile: base: %#x, size: %d\n", + is->setfile.base, is->setfile.size); + pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info); + + /* Check magic number. */ + if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] != + FIMC_IS_MAGIC_NUMBER) { + dev_err(dev, "magic number error!\n"); + return -EIO; + } + + pr_debug("shared region: %#x, parameter region: %#x\n", + is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, + is->is_dma_p_region); + + is->setfile.sub_index = 0; + + /* Stream off. */ + fimc_is_hw_stream_off(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "stream off timeout\n"); + return ret; + } + + /* Preserve previous mode. */ + prev_id = is->config_index; + + /* Set initial parameter values. */ + for (i = 0; i < ARRAY_SIZE(config_ids); i++) { + is->config_index = config_ids[i]; + fimc_is_set_initial_params(is); + ret = fimc_is_itf_s_param(is, true); + if (ret < 0) { + is->config_index = prev_id; + return ret; + } + } + is->config_index = prev_id; + + set_bit(IS_ST_INIT_DONE, &is->state); + dev_info(dev, "initialization sequence completed (%d)\n", + is->config_index); + return 0; +} + +static int fimc_is_log_show(struct seq_file *s, void *data) +{ + struct fimc_is *is = s->private; + const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; + + if (is->memory.vaddr == NULL) { + dev_err(&is->pdev->dev, "firmware memory is not initialized\n"); + return -EIO; + } + + seq_printf(s, "%s\n", buf); + return 0; +} + +static int fimc_is_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, fimc_is_log_show, inode->i_private); +} + +static const struct file_operations fimc_is_debugfs_fops = { + .open = fimc_is_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void fimc_is_debugfs_remove(struct fimc_is *is) +{ + debugfs_remove_recursive(is->debugfs_entry); + is->debugfs_entry = NULL; +} + +static int fimc_is_debugfs_create(struct fimc_is *is) +{ + struct dentry *dentry; + + is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); + + dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, + is, &fimc_is_debugfs_fops); + if (!dentry) + fimc_is_debugfs_remove(is); + + return is->debugfs_entry == NULL ? -EIO : 0; +} + +static int fimc_is_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_is *is; + struct resource res; + struct device_node *node; + int ret; + + is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL); + if (!is) + return -ENOMEM; + + is->pdev = pdev; + is->isp.pdev = pdev; + + init_waitqueue_head(&is->irq_queue); + spin_lock_init(&is->slock); + mutex_init(&is->lock); + + ret = of_address_to_resource(dev->of_node, 0, &res); + if (ret < 0) + return ret; + + is->regs = devm_ioremap_resource(dev, &res); + if (IS_ERR(is->regs)) + return PTR_ERR(is->regs); + + node = of_get_child_by_name(dev->of_node, "pmu"); + if (!node) + return -ENODEV; + + is->pmu_regs = of_iomap(node, 0); + if (!is->pmu_regs) + return -ENOMEM; + + is->irq = irq_of_parse_and_map(dev->of_node, 0); + if (is->irq < 0) { + dev_err(dev, "no irq found\n"); + return is->irq; + } + + ret = fimc_is_get_clocks(is); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, is); + + ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_clk; + } + pm_runtime_enable(dev); + /* + * Enable only the ISP power domain, keep FIMC-IS clocks off until + * the whole clock tree is configured. The ISP power domain needs + * be active in order to acces any CMU_ISP clock registers. + */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_irq; + + ret = fimc_is_setup_clocks(is); + pm_runtime_put_sync(dev); + + if (ret < 0) + goto err_irq; + + is->clk_init = true; + + is->alloc_ctx = vb2_dma_contig_init_ctx(dev); + if (IS_ERR(is->alloc_ctx)) { + ret = PTR_ERR(is->alloc_ctx); + goto err_irq; + } + /* + * Register FIMC-IS V4L2 subdevs to this driver. The video nodes + * will be created within the subdev's registered() callback. + */ + ret = fimc_is_register_subdevs(is); + if (ret < 0) + goto err_vb; + + ret = fimc_is_debugfs_create(is); + if (ret < 0) + goto err_sd; + + ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME); + if (ret < 0) + goto err_dfs; + + dev_dbg(dev, "FIMC-IS registered successfully\n"); + return 0; + +err_dfs: + fimc_is_debugfs_remove(is); +err_vb: + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); +err_sd: + fimc_is_unregister_subdevs(is); +err_irq: + free_irq(is->irq, is); +err_clk: + fimc_is_put_clocks(is); + return ret; +} + +static int fimc_is_runtime_resume(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + if (!is->clk_init) + return 0; + + return fimc_is_enable_clocks(is); +} + +static int fimc_is_runtime_suspend(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + if (is->clk_init) + fimc_is_disable_clocks(is); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_is_resume(struct device *dev) +{ + /* TODO: */ + return 0; +} + +static int fimc_is_suspend(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + /* TODO: */ + if (test_bit(IS_ST_A5_PWR_ON, &is->state)) + return -EBUSY; + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static int fimc_is_remove(struct platform_device *pdev) +{ + struct fimc_is *is = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + free_irq(is->irq, is); + fimc_is_unregister_subdevs(is); + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + fimc_is_put_clocks(is); + fimc_is_debugfs_remove(is); + release_firmware(is->fw.f_w); + fimc_is_free_cpu_memory(is); + + return 0; +} + +static const struct of_device_id fimc_is_of_match[] = { + { .compatible = "samsung,exynos4212-fimc-is" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, fimc_is_of_match); + +static const struct dev_pm_ops fimc_is_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume) + SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume, + NULL) +}; + +static struct platform_driver fimc_is_driver = { + .probe = fimc_is_probe, + .remove = fimc_is_remove, + .driver = { + .of_match_table = fimc_is_of_match, + .name = FIMC_IS_DRV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_pm_ops, + } +}; + +static int fimc_is_module_init(void) +{ + int ret; + + ret = fimc_is_register_sensor_driver(); + if (ret < 0) + return ret; + + ret = fimc_is_register_i2c_driver(); + if (ret < 0) + goto err_sens; + + ret = platform_driver_register(&fimc_is_driver); + if (!ret) + return ret; + + fimc_is_unregister_i2c_driver(); +err_sens: + fimc_is_unregister_sensor_driver(); + return ret; +} + +static void fimc_is_module_exit(void) +{ + fimc_is_unregister_sensor_driver(); + fimc_is_unregister_i2c_driver(); + platform_driver_unregister(&fimc_is_driver); +} + +module_init(fimc_is_module_init); +module_exit(fimc_is_module_exit); + +MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); +MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h new file mode 100644 index 0000000..f5275a5 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -0,0 +1,345 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#ifndef FIMC_IS_H_ +#define FIMC_IS_H_ + +#include <asm/barrier.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <media/videobuf2-core.h> +#include <media/v4l2-ctrls.h> + +#include "fimc-isp.h" +#include "fimc-is-command.h" +#include "fimc-is-sensor.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" + +#define FIMC_IS_DRV_NAME "exynos4-fimc-is" + +#define FIMC_IS_FW_FILENAME "fimc_is_fw.bin" +#define FIMC_IS_SETFILE_6A3 "setfile.bin" + +#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ +#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ + +#define FIMC_IS_SENSOR_NUM 2 + +/* Memory definitions */ +#define FIMC_IS_CPU_MEM_SIZE (0xa00000) +#define FIMC_IS_CPU_BASE_MASK ((1 << 26) - 1) +#define FIMC_IS_REGION_SIZE 0x5000 + +#define FIMC_IS_DEBUG_REGION_OFFSET 0x0084b000 +#define FIMC_IS_SHARED_REGION_OFFSET 0x008c0000 +#define FIMC_IS_FW_INFO_LEN 31 +#define FIMC_IS_FW_VER_LEN 7 +#define FIMC_IS_FW_DESC_LEN (FIMC_IS_FW_INFO_LEN + \ + FIMC_IS_FW_VER_LEN) +#define FIMC_IS_SETFILE_INFO_LEN 39 + +#define FIMC_IS_EXTRA_MEM_SIZE (FIMC_IS_EXTRA_FW_SIZE + \ + FIMC_IS_EXTRA_SETFILE_SIZE + 0x1000) +#define FIMC_IS_EXTRA_FW_SIZE 0x180000 +#define FIMC_IS_EXTRA_SETFILE_SIZE 0x4b000 + +/* TODO: revisit */ +#define FIMC_IS_FW_ADDR_MASK ((1 << 26) - 1) +#define FIMC_IS_FW_SIZE_MAX (SZ_4M) +#define FIMC_IS_FW_SIZE_MIN (SZ_32K) + +#define ATCLK_MCUISP_FREQUENCY 100000000UL +#define ACLK_AXI_FREQUENCY 100000000UL + +enum { + ISS_CLK_PPMUISPX, + ISS_CLK_PPMUISPMX, + ISS_CLK_LITE0, + ISS_CLK_LITE1, + ISS_CLK_MPLL, + ISS_CLK_SYSREG, + ISS_CLK_ISP, + ISS_CLK_DRC, + ISS_CLK_FD, + ISS_CLK_MCUISP, + ISS_CLK_UART, + ISS_GATE_CLKS_MAX, + ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX, + ISS_CLK_ISP_DIV1, + ISS_CLK_MCUISP_DIV0, + ISS_CLK_MCUISP_DIV1, + ISS_CLK_ACLK200, + ISS_CLK_ACLK200_DIV, + ISS_CLK_ACLK400MCUISP, + ISS_CLK_ACLK400MCUISP_DIV, + ISS_CLKS_MAX +}; + +/* The driver's internal state flags */ +enum { + IS_ST_IDLE, + IS_ST_PWR_ON, + IS_ST_A5_PWR_ON, + IS_ST_FW_LOADED, + IS_ST_OPEN_SENSOR, + IS_ST_SETFILE_LOADED, + IS_ST_INIT_DONE, + IS_ST_STREAM_ON, + IS_ST_STREAM_OFF, + IS_ST_CHANGE_MODE, + IS_ST_BLOCK_CMD_CLEARED, + IS_ST_SET_ZOOM, + IS_ST_PWR_SUBIP_ON, + IS_ST_END, +}; + +enum af_state { + FIMC_IS_AF_IDLE = 0, + FIMC_IS_AF_SETCONFIG = 1, + FIMC_IS_AF_RUNNING = 2, + FIMC_IS_AF_LOCK = 3, + FIMC_IS_AF_ABORT = 4, + FIMC_IS_AF_FAILED = 5, +}; + +enum af_lock_state { + FIMC_IS_AF_UNLOCKED = 0, + FIMC_IS_AF_LOCKED = 2 +}; + +enum ae_lock_state { + FIMC_IS_AE_UNLOCKED = 0, + FIMC_IS_AE_LOCKED = 1 +}; + +enum awb_lock_state { + FIMC_IS_AWB_UNLOCKED = 0, + FIMC_IS_AWB_LOCKED = 1 +}; + +enum { + IS_METERING_CONFIG_CMD, + IS_METERING_CONFIG_WIN_POS_X, + IS_METERING_CONFIG_WIN_POS_Y, + IS_METERING_CONFIG_WIN_WIDTH, + IS_METERING_CONFIG_WIN_HEIGHT, + IS_METERING_CONFIG_MAX +}; + +struct is_setfile { + const struct firmware *info; + int state; + u32 sub_index; + u32 base; + size_t size; +}; + +struct is_fd_result_header { + u32 offset; + u32 count; + u32 index; + u32 curr_index; + u32 width; + u32 height; +}; + +struct is_af_info { + u16 mode; + u32 af_state; + u32 af_lock_state; + u32 ae_lock_state; + u32 awb_lock_state; + u16 pos_x; + u16 pos_y; + u16 prev_pos_x; + u16 prev_pos_y; + u16 use_af; +}; + +struct fimc_is_firmware { + const struct firmware *f_w; + + dma_addr_t paddr; + void *vaddr; + unsigned int size; + + char info[FIMC_IS_FW_INFO_LEN + 1]; + char version[FIMC_IS_FW_VER_LEN + 1]; + char setfile_info[FIMC_IS_SETFILE_INFO_LEN + 1]; + u8 state; +}; + +struct fimc_is_memory { + /* physical base address */ + dma_addr_t paddr; + /* virtual base address */ + void *vaddr; + /* total length */ + unsigned int size; +}; + +#define FIMC_IS_I2H_MAX_ARGS 12 + +struct i2h_cmd { + u32 cmd; + u32 sensor_id; + u16 num_args; + u32 args[FIMC_IS_I2H_MAX_ARGS]; +}; + +struct h2i_cmd { + u16 cmd_type; + u32 entry_id; +}; + +#define FIMC_IS_DEBUG_MSG 0x3f +#define FIMC_IS_DEBUG_LEVEL 3 + +struct fimc_is_setfile { + const struct firmware *info; + unsigned int state; + unsigned int size; + u32 sub_index; + u32 base; +}; + +struct chain_config { + struct global_param global; + struct sensor_param sensor; + struct isp_param isp; + struct drc_param drc; + struct fd_param fd; + + unsigned long p_region_index1; + unsigned long p_region_index2; +}; + +/** + * struct fimc_is - fimc-is data structure + * @pdev: pointer to FIMC-IS platform device + * @pctrl: pointer to pinctrl structure for this device + * @v4l2_dev: pointer to top the level v4l2_device + * @alloc_ctx: videobuf2 memory allocator context + * @lock: mutex serializing video device and the subdev operations + * @slock: spinlock protecting this data structure and the hw registers + * @clocks: FIMC-LITE gate clock + * @regs: MCUCTL mmapped registers region + * @pmu_regs: PMU ISP mmapped registers region + * @irq_queue: interrupt handling waitqueue + * @lpm: low power mode flag + * @state: internal driver's state flags + */ +struct fimc_is { + struct platform_device *pdev; + struct pinctrl *pctrl; + struct v4l2_device *v4l2_dev; + + struct fimc_is_firmware fw; + struct fimc_is_memory memory; + struct firmware *f_w; + + struct fimc_isp isp; + struct fimc_is_sensor *sensor; + struct fimc_is_setfile setfile; + + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_ctrl_handler ctrl_handler; + + struct mutex lock; + spinlock_t slock; + + struct clk *clocks[ISS_CLKS_MAX]; + bool clk_init; + void __iomem *regs; + void __iomem *pmu_regs; + int irq; + wait_queue_head_t irq_queue; + u8 lpm; + + unsigned long state; + unsigned int sensor_index; + + struct i2h_cmd i2h_cmd; + struct h2i_cmd h2i_cmd; + struct is_fd_result_header fd_header; + + struct chain_config config[IS_SC_MAX]; + unsigned config_index; + + struct is_region *is_p_region; + dma_addr_t is_dma_p_region; + struct is_share_region *is_shared_region; + struct is_af_info af; + + struct dentry *debugfs_entry; +}; + +static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp) +{ + return container_of(isp, struct fimc_is, isp); +} + +static inline void fimc_is_mem_barrier(void) +{ + mb(); +} + +static inline void fimc_is_set_param_bit(struct fimc_is *is, int num) +{ + struct chain_config *cfg = &is->config[is->config_index]; + + if (num >= 32) + set_bit(num - 32, &cfg->p_region_index2); + else + set_bit(num, &cfg->p_region_index1); +} + +static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd) +{ + is->is_p_region->parameter.isp.control.cmd = cmd; +} + +static inline void mcuctl_write(u32 v, struct fimc_is *is, unsigned int offset) +{ + writel(v, is->regs + offset); +} + +static inline u32 mcuctl_read(struct fimc_is *is, unsigned int offset) +{ + return readl(is->regs + offset); +} + +static inline void pmuisp_write(u32 v, struct fimc_is *is, unsigned int offset) +{ + writel(v, is->pmu_regs + offset); +} + +static inline u32 pmuisp_read(struct fimc_is *is, unsigned int offset) +{ + return readl(is->pmu_regs + offset); +} + +int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, + unsigned int state, unsigned int timeout); +int fimc_is_cpu_set_power(struct fimc_is *is, int on); +int fimc_is_start_firmware(struct fimc_is *is); +int fimc_is_hw_initialize(struct fimc_is *is); +void fimc_is_log_dump(const char *level, const void *buf, size_t len); + +#endif /* FIMC_IS_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c new file mode 100644 index 0000000..d63947f --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -0,0 +1,703 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * 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. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/printk.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/v4l2-device.h> + +#include "media-dev.h" +#include "fimc-is-command.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" +#include "fimc-is.h" + +static int debug; +module_param_named(debug_isp, debug, int, S_IRUGO | S_IWUSR); + +static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = { + { + .name = "RAW8 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = { 8 }, + .color = FIMC_FMT_RAW8, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + }, { + .name = "RAW10 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG10, + .depth = { 10 }, + .color = FIMC_FMT_RAW10, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + }, { + .name = "RAW12 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG12, + .depth = { 12 }, + .color = FIMC_FMT_RAW12, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + }, +}; + +/** + * fimc_isp_find_format - lookup color format by fourcc or media bus code + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + * @index: index to the fimc_isp_formats array, ignored if negative + */ +const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index) +{ + const struct fimc_fmt *fmt, *def_fmt = NULL; + unsigned int i; + int id = 0; + + if (index >= (int)ARRAY_SIZE(fimc_isp_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(fimc_isp_formats); ++i) { + fmt = &fimc_isp_formats[i]; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == id) + def_fmt = fmt; + id++; + } + return def_fmt; +} + +void fimc_isp_irq_handler(struct fimc_is *is) +{ + is->i2h_cmd.args[0] = mcuctl_read(is, MCUCTL_REG_ISSR(20)); + is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); + + fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); + + /* TODO: Complete ISP DMA interrupt handler */ + wake_up(&is->irq_queue); +} + +/* Capture subdev media entity operations */ +static int fimc_is_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + return 0; +} + +static const struct media_entity_operations fimc_is_subdev_media_ops = { + .link_setup = fimc_is_link_setup, +}; + +static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct fimc_fmt *fmt; + + fmt = fimc_isp_find_format(NULL, NULL, code->index); + if (!fmt) + return -EINVAL; + code->code = fmt->mbus_code; + return 0; +} + +static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct v4l2_mbus_framefmt cur_fmt; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&isp->subdev_lock); + __is_get_frame_size(is, &cur_fmt); + + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + /* full camera input frame size */ + mf->width = cur_fmt.width + FIMC_ISP_CAC_MARGIN_WIDTH; + mf->height = cur_fmt.height + FIMC_ISP_CAC_MARGIN_HEIGHT; + mf->code = V4L2_MBUS_FMT_SGRBG10_1X10; + } else { + /* crop size */ + mf->width = cur_fmt.width; + mf->height = cur_fmt.height; + mf->code = V4L2_MBUS_FMT_YUV10_1X30; + } + + mutex_unlock(&isp->subdev_lock); + + v4l2_dbg(1, debug, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", + __func__, fmt->pad, mf->code, mf->width, mf->height); + + return 0; +} + +static void __isp_subdev_try_format(struct fimc_isp *isp, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mf = &fmt->format; + + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN, + FIMC_ISP_SINK_WIDTH_MAX, 0, + &mf->height, FIMC_ISP_SINK_HEIGHT_MIN, + FIMC_ISP_SINK_HEIGHT_MAX, 0, 0); + isp->subdev_fmt = *mf; + } else { + /* Allow changing format only on sink pad */ + mf->width = isp->subdev_fmt.width - FIMC_ISP_CAC_MARGIN_WIDTH; + mf->height = isp->subdev_fmt.height - FIMC_ISP_CAC_MARGIN_HEIGHT; + mf->code = isp->subdev_fmt.code; + } +} + +static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + struct v4l2_mbus_framefmt *mf = &fmt->format; + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: pad%d: code: 0x%x, %dx%d\n", + __func__, fmt->pad, mf->code, mf->width, mf->height); + + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&isp->subdev_lock); + __isp_subdev_try_format(isp, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + mutex_unlock(&isp->subdev_lock); + return 0; + } + + if (sd->entity.stream_count == 0) + __is_set_frame_size(is, mf); + else + ret = -EBUSY; + mutex_unlock(&isp->subdev_lock); + + return ret; +} + +static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + int ret; + + v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); + + if (!test_bit(IS_ST_INIT_DONE, &is->state)) + return -EBUSY; + + fimc_is_mem_barrier(); + + if (on) { + if (__get_pending_param_count(is)) { + ret = fimc_is_itf_s_param(is, true); + if (ret < 0) + return ret; + } + + v4l2_dbg(1, debug, sd, "changing mode to %d\n", + is->config_index); + ret = fimc_is_itf_mode_change(is); + if (ret) + return -EINVAL; + + clear_bit(IS_ST_STREAM_ON, &is->state); + fimc_is_hw_stream_on(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_ON, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "stream on timeout\n"); + return ret; + } + } else { + clear_bit(IS_ST_STREAM_OFF, &is->state); + fimc_is_hw_stream_off(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "stream off timeout\n"); + return ret; + } + is->setfile.sub_index = 0; + } + + return 0; +} + +static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + int ret = 0; + + pr_debug("on: %d\n", on); + + if (on) { + ret = pm_runtime_get_sync(&is->pdev->dev); + if (ret < 0) + return ret; + set_bit(IS_ST_PWR_ON, &is->state); + + ret = fimc_is_start_firmware(is); + if (ret < 0) { + v4l2_err(sd, "firmware booting failed\n"); + pm_runtime_put(&is->pdev->dev); + return ret; + } + set_bit(IS_ST_PWR_SUBIP_ON, &is->state); + + ret = fimc_is_hw_initialize(is); + } else { + /* Close sensor */ + if (!test_bit(IS_ST_PWR_ON, &is->state)) { + fimc_is_hw_close_sensor(is, 0); + + ret = fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 0, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "sensor close timeout\n"); + return ret; + } + } + + /* SUB IP power off */ + if (test_bit(IS_ST_PWR_SUBIP_ON, &is->state)) { + fimc_is_hw_subip_power_off(is); + ret = fimc_is_wait_event(is, IS_ST_PWR_SUBIP_ON, 0, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "sub-IP power off timeout\n"); + return ret; + } + } + + fimc_is_cpu_set_power(is, 0); + pm_runtime_put_sync(&is->pdev->dev); + + clear_bit(IS_ST_PWR_ON, &is->state); + clear_bit(IS_ST_INIT_DONE, &is->state); + is->state = 0; + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_IDLE, &is->state); + wmb(); + } + + return ret; +} + +static int fimc_isp_subdev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt fmt; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK); + + fmt.colorspace = V4L2_COLORSPACE_SRGB; + fmt.code = fimc_isp_formats[0].mbus_code; + fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + FIMC_ISP_CAC_MARGIN_WIDTH; + fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + FIMC_ISP_CAC_MARGIN_HEIGHT; + fmt.field = V4L2_FIELD_NONE; + *format = fmt; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO); + fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; + fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; + *format = fmt; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA); + *format = fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { + .open = fimc_isp_subdev_open, +}; + +static const struct v4l2_subdev_pad_ops fimc_is_subdev_pad_ops = { + .enum_mbus_code = fimc_is_subdev_enum_mbus_code, + .get_fmt = fimc_isp_subdev_get_fmt, + .set_fmt = fimc_isp_subdev_set_fmt, +}; + +static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = { + .s_stream = fimc_isp_subdev_s_stream, +}; + +static const struct v4l2_subdev_core_ops fimc_is_core_ops = { + .s_power = fimc_isp_subdev_s_power, +}; + +static struct v4l2_subdev_ops fimc_is_subdev_ops = { + .core = &fimc_is_core_ops, + .video = &fimc_is_subdev_video_ops, + .pad = &fimc_is_subdev_pad_ops, +}; + +static int __ctrl_set_white_balance(struct fimc_is *is, int value) +{ + switch (value) { + case V4L2_WHITE_BALANCE_AUTO: + __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); + break; + case V4L2_WHITE_BALANCE_DAYLIGHT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_DAYLIGHT); + break; + case V4L2_WHITE_BALANCE_CLOUDY: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_CLOUDY); + break; + case V4L2_WHITE_BALANCE_INCANDESCENT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_TUNGSTEN); + break; + case V4L2_WHITE_BALANCE_FLUORESCENT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_FLUORESCENT); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __ctrl_set_aewb_lock(struct fimc_is *is, + struct v4l2_ctrl *ctrl) +{ + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + struct isp_param *isp = &is->is_p_region->parameter.isp; + int cmd, ret; + + cmd = ae_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; + isp->aa.cmd = cmd; + isp->aa.target = ISP_AA_TARGET_AE; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + is->af.ae_lock_state = ae_lock; + wmb(); + + ret = fimc_is_itf_s_param(is, false); + if (ret < 0) + return ret; + + cmd = awb_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; + isp->aa.cmd = cmd; + isp->aa.target = ISP_AA_TARGET_AE; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + is->af.awb_lock_state = awb_lock; + wmb(); + + return fimc_is_itf_s_param(is, false); +} + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + 50, 100, 200, 400, 800, +}; + +static int __ctrl_set_iso(struct fimc_is *is, int value) +{ + unsigned int idx, iso; + + if (value == V4L2_ISO_SENSITIVITY_AUTO) { + __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); + return 0; + } + idx = is->isp.ctrls.iso->val; + if (idx >= ARRAY_SIZE(iso_qmenu)) + return -EINVAL; + + iso = iso_qmenu[idx]; + __is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso); + return 0; +} + +static int __ctrl_set_metering(struct fimc_is *is, unsigned int value) +{ + unsigned int val; + + switch (value) { + case V4L2_EXPOSURE_METERING_AVERAGE: + val = ISP_METERING_COMMAND_AVERAGE; + break; + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + val = ISP_METERING_COMMAND_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + val = ISP_METERING_COMMAND_SPOT; + break; + case V4L2_EXPOSURE_METERING_MATRIX: + val = ISP_METERING_COMMAND_MATRIX; + break; + default: + return -EINVAL; + }; + + __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val); + return 0; +} + +static int __ctrl_set_afc(struct fimc_is *is, int value) +{ + switch (value) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + __is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: + __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __ctrl_set_image_effect(struct fimc_is *is, int value) +{ + static const u8 effects[][2] = { + { V4L2_COLORFX_NONE, ISP_IMAGE_EFFECT_DISABLE }, + { V4L2_COLORFX_BW, ISP_IMAGE_EFFECT_MONOCHROME }, + { V4L2_COLORFX_SEPIA, ISP_IMAGE_EFFECT_SEPIA }, + { V4L2_COLORFX_NEGATIVE, ISP_IMAGE_EFFECT_NEGATIVE_MONO }, + { 16 /* TODO */, ISP_IMAGE_EFFECT_NEGATIVE_COLOR }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(effects); i++) { + if (effects[i][0] != value) + continue; + + __is_set_isp_effect(is, effects[i][1]); + return 0; + } + + return -EINVAL; +} + +static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl); + struct fimc_is *is = fimc_isp_to_is(isp); + bool set_param = true; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, + ctrl->val); + break; + + case V4L2_CID_SATURATION: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION, + ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE_ABSOLUTE: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, + ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, + ctrl->val); + break; + + case V4L2_CID_HUE: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE_METERING: + ret = __ctrl_set_metering(is, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = __ctrl_set_white_balance(is, ctrl->val); + break; + + case V4L2_CID_3A_LOCK: + ret = __ctrl_set_aewb_lock(is, ctrl); + set_param = false; + break; + + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = __ctrl_set_iso(is, ctrl->val); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = __ctrl_set_afc(is, ctrl->val); + break; + + case V4L2_CID_COLORFX: + __ctrl_set_image_effect(is, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + if (ret < 0) { + v4l2_err(&isp->subdev, "Failed to set control: %s (%d)\n", + ctrl->name, ctrl->val); + return ret; + } + + if (set_param && test_bit(IS_ST_STREAM_ON, &is->state)) + return fimc_is_itf_s_param(is, true); + + return 0; +} + +static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { + .s_ctrl = fimc_is_s_ctrl, +}; + +int fimc_isp_subdev_create(struct fimc_isp *isp) +{ + const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops; + struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; + struct v4l2_subdev *sd = &isp->subdev; + struct fimc_isp_ctrls *ctrls = &isp->ctrls; + int ret; + + mutex_init(&isp->subdev_lock); + + v4l2_subdev_init(sd, &fimc_is_subdev_ops); + sd->grp_id = GRP_ID_FIMC_IS; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); + + isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; + isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, + isp->subdev_pads, 0); + if (ret) + return ret; + + v4l2_ctrl_handler_init(handler, 20); + + ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION, + -2, 2, 1, 0); + ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS, + -4, 4, 1, 0); + ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST, + -2, 2, 1, 0); + ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS, + -2, 2, 1, 0); + ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE, + -2, 2, 1, 0); + + ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO); + + ctrls->exposure = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + -4, 4, 1, 0); + + ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_EXPOSURE_METERING, 3, + ~0xf, V4L2_EXPOSURE_METERING_AVERAGE); + + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + /* ISO sensitivity */ + ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, + V4L2_ISO_SENSITIVITY_AUTO); + + ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_3A_LOCK, 0, 0x3, 0, 0); + + /* TODO: Add support for NEGATIVE_COLOR option */ + ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SET_CBCR + 1, ~0x1000f, V4L2_COLORFX_NONE); + + if (handler->error) { + media_entity_cleanup(&sd->entity); + return handler->error; + } + + v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, + V4L2_ISO_SENSITIVITY_MANUAL, false); + + sd->ctrl_handler = handler; + sd->internal_ops = &fimc_is_subdev_internal_ops; + sd->entity.ops = &fimc_is_subdev_media_ops; + v4l2_set_subdevdata(sd, isp); + + return 0; +} + +void fimc_isp_subdev_destroy(struct fimc_isp *isp) +{ + struct v4l2_subdev *sd = &isp->subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&isp->ctrls.handler); + v4l2_set_subdevdata(sd, NULL); +} diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h new file mode 100644 index 0000000..800aba7 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -0,0 +1,181 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * 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. + */ +#ifndef FIMC_ISP_H_ +#define FIMC_ISP_H_ + +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/videobuf2-core.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/s5p_fimc.h> + +/* FIXME: revisit these constraints */ +#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) +#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) +#define FIMC_ISP_SOURCE_WIDTH_MIN 8 +#define FIMC_ISP_SOURC_HEIGHT_MIN 8 +#define FIMC_ISP_CAC_MARGIN_WIDTH 16 +#define FIMC_ISP_CAC_MARGIN_HEIGHT 12 + +#define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16) +#define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12) +#define FIMC_ISP_SOURCE_WIDTH_MAX 4000 +#define FIMC_ISP_SOURC_HEIGHT_MAX 4000 + +#define FIMC_ISP_NUM_FORMATS 3 +#define FIMC_ISP_REQ_BUFS_MIN 2 + +#define FIMC_ISP_SD_PAD_SINK 0 +#define FIMC_ISP_SD_PAD_SRC_FIFO 1 +#define FIMC_ISP_SD_PAD_SRC_DMA 2 +#define FIMC_ISP_SD_PADS_NUM 3 +#define FIMC_ISP_MAX_PLANES 1 + +/** + * struct fimc_isp_frame - source/target frame properties + * @width: full image width + * @height: full image height + * @rect: crop/composition rectangle + */ +struct fimc_isp_frame { + u16 width; + u16 height; + struct v4l2_rect rect; +}; + +struct fimc_isp_ctrls { + struct v4l2_ctrl_handler handler; + + /* Auto white balance */ + struct v4l2_ctrl *auto_wb; + /* Auto ISO control cluster */ + struct { + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + /* Adjust - contrast */ + struct v4l2_ctrl *contrast; + /* Adjust - saturation */ + struct v4l2_ctrl *saturation; + /* Adjust - sharpness */ + struct v4l2_ctrl *sharpness; + /* Adjust - brightness */ + struct v4l2_ctrl *brightness; + /* Adjust - hue */ + struct v4l2_ctrl *hue; + + /* Auto/manual exposure */ + struct v4l2_ctrl *auto_exp; + /* Manual exposure value */ + struct v4l2_ctrl *exposure; + /* AE/AWB lock/unlock */ + struct v4l2_ctrl *aewb_lock; + /* Exposure metering mode */ + struct v4l2_ctrl *exp_metering; + /* AFC */ + struct v4l2_ctrl *afc; + /* ISP image effect */ + struct v4l2_ctrl *colorfx; +}; + +/** + * struct fimc_is_video - fimc-is video device structure + * @vdev: video_device structure + * @type: video device type (CAPTURE/OUTPUT) + * @pad: video device media (sink) pad + * @pending_buf_q: pending buffers queue head + * @active_buf_q: a queue head of buffers scheduled in hardware + * @vb_queue: vb2 buffer queue + * @active_buf_count: number of video buffers scheduled in hardware + * @frame_count: counter of frames dequeued to user space + * @reqbufs_count: number of buffers requested with REQBUFS ioctl + * @format: current pixel format + */ +struct fimc_is_video { + struct video_device vdev; + enum v4l2_buf_type type; + struct media_pad pad; + struct list_head pending_buf_q; + struct list_head active_buf_q; + struct vb2_queue vb_queue; + unsigned int frame_count; + unsigned int reqbufs_count; + int streaming; + unsigned long payload[FIMC_ISP_MAX_PLANES]; + const struct fimc_fmt *format; +}; + +/** + * struct fimc_isp - FIMC-IS ISP data structure + * @pdev: pointer to FIMC-IS platform device + * @alloc_ctx: videobuf2 memory allocator context + * @subdev: ISP v4l2_subdev + * @subdev_pads: the ISP subdev media pads + * @ctrl_handler: v4l2 controls handler + * @test_pattern: test pattern controls + * @pipeline: video capture pipeline data structure + * @video_lock: mutex serializing video device and the subdev operations + * @fmt: pointer to color format description structure + * @payload: image size in bytes (w x h x bpp) + * @inp_frame: camera input frame structure + * @out_frame: DMA output frame structure + * @source_subdev_grp_id: group id of remote source subdev + * @cac_margin_x: horizontal CAC margin in pixels + * @cac_margin_y: vertical CAC margin in pixels + * @state: driver state flags + * @video_capture: the ISP block video capture device + */ +struct fimc_isp { + struct platform_device *pdev; + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_subdev subdev; + struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; + struct v4l2_mbus_framefmt subdev_fmt; + struct v4l2_ctrl *test_pattern; + struct fimc_isp_ctrls ctrls; + + struct mutex video_lock; + struct mutex subdev_lock; + + struct fimc_isp_frame inp_frame; + struct fimc_isp_frame out_frame; + unsigned int source_subdev_grp_id; + + unsigned int cac_margin_x; + unsigned int cac_margin_y; + + unsigned long state; + + struct fimc_is_video video_capture; +}; + +#define ctrl_to_fimc_isp(_ctrl) \ + container_of(ctrl->handler, struct fimc_isp, ctrls.handler) + +struct fimc_is; + +int fimc_isp_subdev_create(struct fimc_isp *isp); +void fimc_isp_subdev_destroy(struct fimc_isp *isp); +void fimc_isp_irq_handler(struct fimc_is *is); +int fimc_is_create_controls(struct fimc_isp *isp); +int fimc_is_delete_controls(struct fimc_isp *isp); +const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index); +#endif /* FIMC_ISP_H_ */ diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index ac9663c..8cc0d39 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -127,7 +127,7 @@ static const u32 src_pixfmt_map[8][3] = { /* Set camera input pixel format and resolution */ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) { - enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code; + enum v4l2_mbus_pixelcode pixelcode = f->fmt->mbus_code; int i = ARRAY_SIZE(src_pixfmt_map); u32 cfg; @@ -227,7 +227,7 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) int i = ARRAY_SIZE(pixcode); while (--i >= 0) - if (pixcode[i][0] == dev->fmt->mbus_code) + if (pixcode[i][0] == f->fmt->mbus_code) break; cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h index 0e34584..3903839 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h @@ -72,10 +72,10 @@ #define FLITE_REG_CIODMAFMT 0x18 #define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15) #define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) -#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4) -#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4) -#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4) -#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4) +#define FLITE_REG_CIODMAFMT_YCBYCR (0 << 4) +#define FLITE_REG_CIODMAFMT_YCRYCB (1 << 4) +#define FLITE_REG_CIODMAFMT_CBYCRY (2 << 4) +#define FLITE_REG_CIODMAFMT_CRYCBY (3 << 4) #define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4) /* Camera Output Canvas */ diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index bbc35de..14bb7bc 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -11,12 +11,14 @@ #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ #include <linux/bug.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/types.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -30,7 +32,6 @@ #include <media/videobuf2-dma-contig.h> #include <media/s5p_fimc.h> -#include "fimc-mdevice.h" #include "fimc-core.h" #include "fimc-lite.h" #include "fimc-lite-reg.h" @@ -46,6 +47,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, CbYCrY", .fourcc = V4L2_PIX_FMT_UYVY, @@ -53,6 +55,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_CBYCRY422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, CrYCbY", .fourcc = V4L2_PIX_FMT_VYUY, @@ -60,6 +63,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_CRYCBY422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, @@ -67,6 +71,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "RAW8 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG8, @@ -74,6 +79,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW8, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + .flags = FMT_FLAGS_RAW_BAYER, }, { .name = "RAW10 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG10, @@ -81,6 +87,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW10, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + .flags = FMT_FLAGS_RAW_BAYER, }, { .name = "RAW12 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG12, @@ -88,6 +95,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW12, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + .flags = FMT_FLAGS_RAW_BAYER, }, }; @@ -95,10 +103,11 @@ static const struct fimc_fmt fimc_lite_formats[] = { * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code * @pixelformat: fourcc to match, ignored if null * @mbus_code: media bus code to match, ignored if null + * @mask: the color format flags to match * @index: index to the fimc_lite_formats array, ignored if negative */ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, - const u32 *mbus_code, int index) + const u32 *mbus_code, unsigned int mask, int index) { const struct fimc_fmt *fmt, *def_fmt = NULL; unsigned int i; @@ -109,6 +118,8 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { fmt = &fimc_lite_formats[i]; + if (mask && !(fmt->flags & mask)) + continue; if (pixelformat && fmt->fourcc == *pixelformat) return fmt; if (mbus_code && fmt->mbus_code == *mbus_code) @@ -120,26 +131,49 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } +/* Called with the media graph mutex held or @me stream_count > 0. */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR || + sd->grp_id == GRP_ID_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { - struct fimc_pipeline *pipeline = &fimc->pipeline; - struct v4l2_subdev *sensor; - struct fimc_sensor_info *si; + struct fimc_source_info *si; unsigned long flags; - sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; - - if (sensor == NULL) + if (fimc->sensor == NULL) return -ENXIO; - if (fimc->fmt == NULL) + if (fimc->inp_frame.fmt == NULL || fimc->out_frame.fmt == NULL) return -EINVAL; /* Get sensor configuration data from the sensor subdev */ - si = v4l2_get_subdev_hostdata(sensor); + si = v4l2_get_subdev_hostdata(fimc->sensor); + if (!si) + return -EINVAL; + spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &si->pdata); + flite_hw_set_camera_bus(fimc, si); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); @@ -339,13 +373,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, const struct v4l2_pix_format_mplane *pixm = NULL; struct fimc_lite *fimc = vq->drv_priv; struct flite_frame *frame = &fimc->out_frame; - const struct fimc_fmt *fmt = fimc->fmt; + const struct fimc_fmt *fmt = frame->fmt; unsigned long wh; int i; if (pfmt) { pixm = &pfmt->fmt.pix_mp; - fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1); + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0, -1); wh = pixm->width * pixm->height; } else { wh = frame->f_width * frame->f_height; @@ -374,10 +408,10 @@ static int buffer_prepare(struct vb2_buffer *vb) struct fimc_lite *fimc = vq->drv_priv; int i; - if (fimc->fmt == NULL) + if (fimc->out_frame.fmt == NULL) return -EINVAL; - for (i = 0; i < fimc->fmt->memplanes; i++) { + for (i = 0; i < fimc->out_frame.fmt->memplanes; i++) { unsigned long size = fimc->payload[i]; if (vb2_plane_size(vb, i) < size) { @@ -425,24 +459,12 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); } -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_lite *fimc = vb2_get_drv_priv(vq); - mutex_lock(&fimc->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_lite *fimc = vb2_get_drv_priv(vq); - mutex_unlock(&fimc->lock); -} - static const struct vb2_ops fimc_lite_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -467,99 +489,73 @@ static int fimc_lite_open(struct file *file) mutex_lock(&fimc->lock); if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { ret = -EBUSY; - goto done; + goto unlock; } set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) - goto done; + goto unlock; ret = v4l2_fh_open(file); if (ret < 0) - goto done; + goto err_pm; - if (++fimc->ref_count == 1 && - atomic_read(&fimc->out_path) == FIMC_IO_DMA) { - ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, - &fimc->vfd.entity, true); - if (ret < 0) { - pm_runtime_put_sync(&fimc->pdev->dev); - fimc->ref_count--; - v4l2_fh_release(file); - clear_bit(ST_FLITE_IN_USE, &fimc->state); - } + if (!v4l2_fh_is_singular_file(file) || + atomic_read(&fimc->out_path) != FIMC_IO_DMA) + goto unlock; + ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, + me, true); + if (!ret) { fimc_lite_clear_event_counters(fimc); + fimc->ref_count++; + goto unlock; } -done: + + v4l2_fh_release(file); +err_pm: + pm_runtime_put_sync(&fimc->pdev->dev); + clear_bit(ST_FLITE_IN_USE, &fimc->state); +unlock: mutex_unlock(&fimc->lock); mutex_unlock(&me->parent->graph_mutex); return ret; } -static int fimc_lite_close(struct file *file) +static int fimc_lite_release(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); - int ret; mutex_lock(&fimc->lock); - if (--fimc->ref_count == 0 && + if (v4l2_fh_is_singular_file(file) && atomic_read(&fimc->out_path) == FIMC_IO_DMA) { + if (fimc->streaming) { + media_entity_pipeline_stop(&fimc->vfd.entity); + fimc->streaming = false; + } clear_bit(ST_FLITE_IN_USE, &fimc->state); fimc_lite_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); - clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + fimc->ref_count--; } + vb2_fop_release(file); pm_runtime_put(&fimc->pdev->dev); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); - if (fimc->ref_count == 0) - vb2_queue_release(&fimc->vb_queue); - - ret = v4l2_fh_release(file); - - mutex_unlock(&fimc->lock); - return ret; -} - -static unsigned int fimc_lite_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_lite *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return POLL_ERR; - - ret = vb2_poll(&fimc->vb_queue, file, wait); - mutex_unlock(&fimc->lock); - - return ret; -} - -static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_lite *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = vb2_mmap(&fimc->vb_queue, vma); mutex_unlock(&fimc->lock); - - return ret; + return 0; } static const struct v4l2_file_operations fimc_lite_fops = { .owner = THIS_MODULE, .open = fimc_lite_open, - .release = fimc_lite_close, - .poll = fimc_lite_poll, + .release = fimc_lite_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = fimc_lite_mmap, + .mmap = vb2_fop_mmap, }; /* @@ -570,10 +566,23 @@ static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, u32 *width, u32 *height, u32 *code, u32 *fourcc, int pad) { - struct flite_variant *variant = fimc->variant; + struct flite_drvdata *dd = fimc->dd; const struct fimc_fmt *fmt; + unsigned int flags = 0; - fmt = fimc_lite_find_format(fourcc, code, 0); + if (pad == FLITE_SD_PAD_SINK) { + v4l_bound_align_image(width, 8, dd->max_width, + ffs(dd->out_width_align) - 1, + height, 0, dd->max_height, 0, 0); + } else { + v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, + ffs(dd->out_width_align) - 1, + height, 0, fimc->inp_frame.rect.height, + 0, 0); + flags = fimc->inp_frame.fmt->flags; + } + + fmt = fimc_lite_find_format(fourcc, code, flags, 0); if (WARN_ON(!fmt)) return NULL; @@ -582,17 +591,6 @@ static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, if (fourcc) *fourcc = fmt->fourcc; - if (pad == FLITE_SD_PAD_SINK) { - v4l_bound_align_image(width, 8, variant->max_width, - ffs(variant->out_width_align) - 1, - height, 0, variant->max_height, 0, 0); - } else { - v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, - ffs(variant->out_width_align) - 1, - height, 0, fimc->inp_frame.rect.height, - 0, 0); - } - v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", code ? *code : 0, *width, *height); @@ -608,7 +606,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) /* Adjust left/top if cropping rectangle got out of bounds */ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); - r->left = round_down(r->left, fimc->variant->win_hor_offs_align); + r->left = round_down(r->left, fimc->dd->win_hor_offs_align); r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", @@ -628,7 +626,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) /* Adjust left/top if the composing rectangle got out of bounds */ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); - r->left = round_down(r->left, fimc->variant->out_hor_offs_align); + r->left = round_down(r->left, fimc->dd->out_hor_offs_align); r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", @@ -671,7 +669,7 @@ static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; struct flite_frame *frame = &fimc->out_frame; - const struct fimc_fmt *fmt = fimc->fmt; + const struct fimc_fmt *fmt = frame->fmt; plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; @@ -689,18 +687,31 @@ static int fimc_lite_try_fmt(struct fimc_lite *fimc, struct v4l2_pix_format_mplane *pixm, const struct fimc_fmt **ffmt) { - struct flite_variant *variant = fimc->variant; u32 bpl = pixm->plane_fmt[0].bytesperline; + struct flite_drvdata *dd = fimc->dd; + const struct fimc_fmt *inp_fmt = fimc->inp_frame.fmt; const struct fimc_fmt *fmt; - fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0); + if (WARN_ON(inp_fmt == NULL)) + return -EINVAL; + /* + * We allow some flexibility only for YUV formats. In case of raw + * raw Bayer the FIMC-LITE's output format must match its camera + * interface input format. + */ + if (inp_fmt->flags & FMT_FLAGS_YUV) + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, + inp_fmt->flags, 0); + else + fmt = inp_fmt; + if (WARN_ON(fmt == NULL)) return -EINVAL; if (ffmt) *ffmt = fmt; - v4l_bound_align_image(&pixm->width, 8, variant->max_width, - ffs(variant->out_width_align) - 1, - &pixm->height, 0, variant->max_height, 0, 0); + v4l_bound_align_image(&pixm->width, 8, dd->max_width, + ffs(dd->out_width_align) - 1, + &pixm->height, 0, dd->max_height, 0, 0); if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) pixm->plane_fmt[0].bytesperline = (pixm->width * @@ -720,7 +731,6 @@ static int fimc_lite_try_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_lite *fimc = video_drvdata(file); - return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); } @@ -740,7 +750,7 @@ static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, if (ret < 0) return ret; - fimc->fmt = fmt; + frame->fmt = fmt; fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, pixm->plane_fmt[0].sizeimage); frame->f_width = pixm->width; @@ -766,7 +776,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) struct flite_frame *ff = &fimc->out_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; - sink_fmt.format.code = fimc->fmt->mbus_code; + sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; } else { sink_fmt.pad = pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -800,37 +810,47 @@ static int fimc_lite_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_lite *fimc = video_drvdata(file); - struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; + struct media_entity *entity = &fimc->vfd.entity; struct fimc_pipeline *p = &fimc->pipeline; int ret; if (fimc_lite_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, p->m_pipeline); if (ret < 0) return ret; ret = fimc_pipeline_validate(fimc); - if (ret) { - media_entity_pipeline_stop(&sensor->entity); + if (ret < 0) + goto err_p_stop; + + fimc->sensor = __find_remote_sensor(&fimc->subdev.entity); + + ret = vb2_ioctl_streamon(file, priv, type); + if (!ret) { + fimc->streaming = true; return ret; } - return vb2_streamon(&fimc->vb_queue, type); +err_p_stop: + media_entity_pipeline_stop(entity); + return 0; } static int fimc_lite_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_lite *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; - ret = vb2_streamoff(&fimc->vb_queue, type); - if (ret == 0) - media_entity_pipeline_stop(&sd->entity); - return ret; + ret = vb2_ioctl_streamoff(file, priv, type); + if (ret < 0) + return ret; + + media_entity_pipeline_stop(&fimc->vfd.entity); + fimc->streaming = false; + return 0; } static int fimc_lite_reqbufs(struct file *file, void *priv, @@ -840,53 +860,13 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, int ret; reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); - ret = vb2_reqbufs(&fimc->vb_queue, reqbufs); + ret = vb2_ioctl_reqbufs(file, priv, reqbufs); if (!ret) fimc->reqbufs_count = reqbufs->count; return ret; } -static int fimc_lite_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_querybuf(&fimc->vb_queue, buf); -} - -static int fimc_lite_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_qbuf(&fimc->vb_queue, buf); -} - -static int fimc_lite_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK); -} - -static int fimc_lite_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_create_bufs(&fimc->vb_queue, create); -} - -static int fimc_lite_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_prepare_buf(&fimc->vb_queue, b); -} - /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) { @@ -966,38 +946,15 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_g_selection = fimc_lite_g_selection, .vidioc_s_selection = fimc_lite_s_selection, .vidioc_reqbufs = fimc_lite_reqbufs, - .vidioc_querybuf = fimc_lite_querybuf, - .vidioc_prepare_buf = fimc_lite_prepare_buf, - .vidioc_create_bufs = fimc_lite_create_bufs, - .vidioc_qbuf = fimc_lite_qbuf, - .vidioc_dqbuf = fimc_lite_dqbuf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_streamon = fimc_lite_streamon, .vidioc_streamoff = fimc_lite_streamoff, }; -/* Called with the media graph mutex held */ -static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) -{ - struct media_pad *pad = &me->pads[0]; - struct v4l2_subdev *sd; - - while (pad->flags & MEDIA_PAD_FL_SINK) { - /* source pad */ - pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - sd = media_entity_to_v4l2_subdev(pad->entity); - - if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) - return sd; - /* sink pad */ - pad = &sd->entity.pads[0]; - } - return NULL; -} - /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -1072,7 +1029,7 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, { const struct fimc_fmt *fmt; - fmt = fimc_lite_find_format(NULL, NULL, code->index); + fmt = fimc_lite_find_format(NULL, NULL, 0, code->index); if (!fmt) return -EINVAL; code->code = fmt->mbus_code; @@ -1085,7 +1042,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; - struct flite_frame *f = &fimc->out_frame; + struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); @@ -1095,7 +1052,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); - mf->code = fimc->fmt->mbus_code; + mf->code = f->fmt->mbus_code; if (fmt->pad == FLITE_SD_PAD_SINK) { /* full camera input frame size */ @@ -1147,7 +1104,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == FLITE_SD_PAD_SINK) { sink->f_width = mf->width; sink->f_height = mf->height; - fimc->fmt = ffmt; + sink->fmt = ffmt; /* Set sink crop rectangle */ sink->rect.width = mf->width; sink->rect.height = mf->height; @@ -1159,7 +1116,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, source->f_height = mf->height; } else { /* Allow changing format only on sink pad */ - mf->code = fimc->fmt->mbus_code; + mf->code = sink->fmt->mbus_code; mf->width = sink->rect.width; mf->height = sink->rect.height; } @@ -1300,7 +1257,8 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) memset(vfd, 0, sizeof(*vfd)); - fimc->fmt = &fimc_lite_formats[0]; + fimc->inp_frame.fmt = &fimc_lite_formats[0]; + fimc->out_frame.fmt = &fimc_lite_formats[0]; atomic_set(&fimc->out_path, FIMC_IO_DMA); snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", @@ -1311,8 +1269,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) vfd->v4l2_dev = sd->v4l2_dev; vfd->minor = -1; vfd->release = video_device_release_empty; - vfd->lock = &fimc->lock; - fimc->ref_count = 0; + vfd->queue = q; fimc->reqbufs_count = 0; INIT_LIST_HEAD(&fimc->pending_buf_q); @@ -1325,6 +1282,8 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct flite_buffer); q->drv_priv = fimc; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &fimc->lock; ret = vb2_queue_init(q); if (ret < 0) @@ -1418,7 +1377,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) int ret; v4l2_subdev_init(sd, &fimc_lite_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; @@ -1440,6 +1399,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->ctrl_handler = handler; sd->internal_ops = &fimc_lite_subdev_internal_ops; sd->entity.ops = &fimc_lite_subdev_media_ops; + sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, fimc); return 0; @@ -1481,19 +1441,35 @@ static int fimc_lite_clk_get(struct fimc_lite *fimc) return ret; } +static const struct of_device_id flite_of_match[]; + static int fimc_lite_probe(struct platform_device *pdev) { - struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev); + struct flite_drvdata *drv_data = NULL; + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; struct fimc_lite *fimc; struct resource *res; int ret; - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->index = pdev->id; - fimc->variant = drv_data->variant[fimc->index]; + if (dev->of_node) { + of_id = of_match_node(flite_of_match, dev->of_node); + if (of_id) + drv_data = (struct flite_drvdata *)of_id->data; + fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); + } else { + drv_data = fimc_lite_get_drvdata(pdev); + fimc->index = pdev->id; + } + + if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS) + return -EINVAL; + + fimc->dd = drv_data; fimc->pdev = pdev; init_waitqueue_head(&fimc->irq_queue); @@ -1501,13 +1477,13 @@ static int fimc_lite_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -1515,10 +1491,10 @@ static int fimc_lite_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, flite_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); + dev_err(dev, "Failed to install irq (%d)\n", ret); goto err_clk; } @@ -1528,23 +1504,23 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); - dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n", + dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", fimc->index); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_lite_unregister_capture_subdev(fimc); err_clk: @@ -1635,7 +1611,14 @@ static int fimc_lite_remove(struct platform_device *pdev) return 0; } -static struct flite_variant fimc_lite0_variant_exynos4 = { +static const struct dev_pm_ops fimc_lite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) + SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, + NULL) +}; + +/* EXYNOS4212, EXYNOS4412 */ +static struct flite_drvdata fimc_lite_drvdata_exynos4 = { .max_width = 8192, .max_height = 8192, .out_width_align = 8, @@ -1643,14 +1626,6 @@ static struct flite_variant fimc_lite0_variant_exynos4 = { .out_hor_offs_align = 8, }; -/* EXYNOS4212, EXYNOS4412 */ -static struct flite_drvdata fimc_lite_drvdata_exynos4 = { - .variant = { - [0] = &fimc_lite0_variant_exynos4, - [1] = &fimc_lite0_variant_exynos4, - }, -}; - static struct platform_device_id fimc_lite_driver_ids[] = { { .name = "exynos-fimc-lite", @@ -1660,17 +1635,21 @@ static struct platform_device_id fimc_lite_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); -static const struct dev_pm_ops fimc_lite_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) - SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, - NULL) +static const struct of_device_id flite_of_match[] = { + { + .compatible = "samsung,exynos4212-fimc-lite", + .data = &fimc_lite_drvdata_exynos4, + }, + { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, .remove = fimc_lite_remove, .id_table = fimc_lite_driver_ids, .driver = { + .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, .owner = THIS_MODULE, .pm = &fimc_lite_pm_ops, diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 7085761..47da5e0 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -20,12 +20,11 @@ #include <media/media-entity.h> #include <media/videobuf2-core.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> #include <media/s5p_fimc.h> -#include "fimc-core.h" - #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" #define FLITE_CLK_NAME "flite" #define FIMC_LITE_MAX_DEVS 2 @@ -49,7 +48,7 @@ enum { #define FLITE_SD_PAD_SOURCE_ISP 2 #define FLITE_SD_PADS_NUM 3 -struct flite_variant { +struct flite_drvdata { unsigned short max_width; unsigned short max_height; unsigned short out_width_align; @@ -57,10 +56,6 @@ struct flite_variant { unsigned short out_hor_offs_align; }; -struct flite_drvdata { - struct flite_variant *variant[FIMC_LITE_MAX_DEVS]; -}; - #define fimc_lite_get_drvdata(_pdev) \ ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) @@ -75,11 +70,13 @@ struct fimc_lite_events { * @f_width: full pixel width * @f_height: full pixel height * @rect: crop/composition rectangle + * @fmt: pointer to pixel format description data structure */ struct flite_frame { u16 f_width; u16 f_height; struct v4l2_rect rect; + const struct fimc_fmt *fmt; }; /** @@ -97,7 +94,7 @@ struct flite_buffer { /** * struct fimc_lite - fimc lite structure * @pdev: pointer to FIMC-LITE platform device - * @variant: variant information for this IP + * @dd: SoC specific driver data structure * @v4l2_dev: pointer to top the level v4l2_device * @vfd: video device node * @fh: v4l2 file handle @@ -116,7 +113,6 @@ struct flite_buffer { * @clock: FIMC-LITE gate clock * @regs: memory mapped io registers * @irq_queue: interrupt handler waitqueue - * @fmt: pointer to color format description structure * @payload: image size in bytes (w x h x bpp) * @inp_frame: camera input frame structure * @out_frame: DMA output frame structure @@ -133,7 +129,7 @@ struct flite_buffer { */ struct fimc_lite { struct platform_device *pdev; - struct flite_variant *variant; + struct flite_drvdata *dd; struct v4l2_device *v4l2_dev; struct video_device vfd; struct v4l2_fh fh; @@ -144,7 +140,7 @@ struct fimc_lite { struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; - u32 index; + int index; struct fimc_pipeline pipeline; const struct fimc_pipeline_ops *pipeline_ops; @@ -155,7 +151,6 @@ struct fimc_lite { void __iomem *regs; wait_queue_head_t irq_queue; - const struct fimc_fmt *fmt; unsigned long payload[FLITE_MAX_PLANES]; struct flite_frame inp_frame; struct flite_frame out_frame; @@ -171,6 +166,7 @@ struct fimc_lite { int ref_count; struct fimc_lite_events events; + bool streaming; }; static inline bool fimc_lite_active(struct fimc_lite *fimc) diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index f3d535c..bde1f47 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -29,8 +29,7 @@ #include "fimc-core.h" #include "fimc-reg.h" -#include "fimc-mdevice.h" - +#include "media-dev.h" static unsigned int get_m2m_fmt_flags(unsigned int stream_type) { @@ -100,7 +99,7 @@ static int stop_streaming(struct vb2_queue *q) static void fimc_device_run(void *priv) { - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; struct fimc_ctx *ctx = priv; struct fimc_frame *sf, *df; struct fimc_dev *fimc; @@ -123,16 +122,18 @@ static void fimc_device_run(void *priv) fimc_prepare_dma_offset(ctx, df); } - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); if (ret) goto dma_unlock; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); if (ret) goto dma_unlock; + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + /* Reconfigure hardware if the context has changed. */ if (fimc->m2m.ctx != ctx) { ctx->state |= FIMC_PARAMS; @@ -152,7 +153,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); fimc_hw_set_output_path(ctx); } @@ -250,22 +251,20 @@ static struct vb2_ops fimc_qops = { * V4L2 ioctl handlers */ static int fimc_m2m_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) + struct v4l2_capability *cap) { - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); + unsigned int caps; - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; /* * 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->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | + caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); return 0; } @@ -623,6 +622,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &fimc_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -634,6 +634,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &fimc_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -667,16 +668,15 @@ static int fimc_m2m_open(struct file *file) struct fimc_ctx *ctx; int ret = -EBUSY; - dbg("pid: %d, state: 0x%lx, refcnt: %d", - task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); + pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state); if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; /* - * Return if the corresponding video capture node - * is already opened. + * Don't allow simultaneous open() of the mem-to-mem and the + * capture video node that belong to same FIMC IP instance. */ - if (fimc->vid_cap.refcnt > 0) + if (test_bit(ST_CAPT_BUSY, &fimc->state)) goto unlock; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 50b97c7..f079f36 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -1,22 +1,24 @@ /* * Register interface file for Samsung Camera Interface (FIMC) driver * - * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki, <s.nawrocki@samsung.com> + * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> * * 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. */ -#include <linux/io.h> #include <linux/delay.h> +#include <linux/io.h> +#include <linux/regmap.h> + #include <media/s5p_fimc.h> +#include "media-dev.h" #include "fimc-reg.h" #include "fimc-core.h" - void fimc_hw_reset(struct fimc_dev *dev) { u32 cfg; @@ -35,7 +37,7 @@ void fimc_hw_reset(struct fimc_dev *dev) cfg &= ~FIMC_REG_CIGCTRL_SWRST; writel(cfg, dev->regs + FIMC_REG_CIGCTRL); - if (dev->variant->out_buf_count > 4) + if (dev->drv_data->out_buf_count > 4) fimc_hw_set_dma_seq(dev, 0xF); } @@ -447,7 +449,8 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK | FIMC_REG_MSCTRL_INPUT_MASK | FIMC_REG_MSCTRL_C_INT_IN_MASK - | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK); + | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK + | FIMC_REG_MSCTRL_ORDER422_MASK); cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4) | FIMC_REG_MSCTRL_INPUT_MEMORY @@ -598,7 +601,8 @@ static const struct mbus_pixfmt_desc pix_desc[] = { int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct fimc_source_info *source) { - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct fimc_frame *f = &vc->ctx->s_frame; u32 bus_width, cfg = 0; int i; @@ -606,7 +610,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, case FIMC_BUS_TYPE_ITU_601: case FIMC_BUS_TYPE_ITU_656: for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { - if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { + if (vc->ci_fmt.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; bus_width = pix_desc[i].bus_width; break; @@ -614,9 +618,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, } if (i == ARRAY_SIZE(pix_desc)) { - v4l2_err(&fimc->vid_cap.vfd, + v4l2_err(&vc->vfd, "Camera color format not supported: %d\n", - fimc->vid_cap.mf.code); + vc->ci_fmt.code); return -EINVAL; } @@ -631,6 +635,10 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, if (fimc_fmt_is_user_defined(f->fmt->color)) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; break; + default: + case FIMC_BUS_TYPE_ISP_WRITEBACK: + /* Anything to do here ? */ + break; } cfg |= (f->o_width << 16) | f->o_height; @@ -660,16 +668,17 @@ void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) int fimc_hw_set_camera_type(struct fimc_dev *fimc, struct fimc_source_info *source) { - u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; u32 csis_data_alignment = 32; + u32 cfg, tmp; cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); /* Select ITU B interface, disable Writeback path and test pattern. */ cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A | FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | - FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); + FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG | + FIMC_REG_CIGCTRL_SELWB_A); switch (source->fimc_bus_type) { case FIMC_BUS_TYPE_MIPI_CSI2: @@ -679,7 +688,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ - switch (vid_cap->mf.code) { + switch (vid_cap->ci_fmt.code) { case V4L2_MBUS_FMT_VYUY8_2X8: tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT; break; @@ -691,7 +700,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, default: v4l2_err(&vid_cap->vfd, "Not supported camera pixel format: %#x\n", - vid_cap->mf.code); + vid_cap->ci_fmt.code); return -EINVAL; } tmp |= (csis_data_alignment == 32) << 8; @@ -704,6 +713,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, break; case FIMC_BUS_TYPE_LCD_WRITEBACK_A: cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; + /* fall through */ + case FIMC_BUS_TYPE_ISP_WRITEBACK: + if (fimc->variant->has_isp_wb) + cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; + else + WARN_ONCE(1, "ISP Writeback input is not supported\n"); break; default: v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", @@ -747,7 +762,7 @@ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { s32 reg; - if (dev->variant->has_cistatus2) { + if (dev->drv_data->cistatus2) { reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3f; return reg - 1; } @@ -763,7 +778,7 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) { s32 reg; - if (!dev->variant->has_cistatus2) + if (!dev->drv_data->cistatus2) return -1; reg = readl(dev->regs + FIMC_REG_CISTATUS2); @@ -784,3 +799,43 @@ void fimc_deactivate_capture(struct fimc_dev *fimc) fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } + +int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc) +{ + struct regmap *map = fimc->sysreg; + unsigned int mask, val, camblk_cfg; + int ret; + + if (map == NULL) + return 0; + + ret = regmap_read(map, SYSREG_CAMBLK, &camblk_cfg); + if (ret < 0 || ((camblk_cfg & 0x00700000) >> 20 != 0x3)) + return ret; + + if (!WARN(fimc->id >= 3, "not supported id: %d\n", fimc->id)) + val = 0x1 << (fimc->id + 20); + else + val = 0; + + mask = SYSREG_CAMBLK_FIFORST_ISP | SYSREG_CAMBLK_ISPWB_FULL_EN; + ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + val |= SYSREG_CAMBLK_FIFORST_ISP; + ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); + if (ret < 0) + return ret; + + mask = SYSREG_ISPBLK_FIFORST_CAM_BLK; + ret = regmap_update_bits(map, SYSREG_ISPBLK, mask, ~mask); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + return regmap_update_bits(map, SYSREG_ISPBLK, mask, mask); +} diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h index 1a40df6..6c97798 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-reg.h @@ -52,6 +52,8 @@ #define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19) #define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16) #define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12) +/* 0 - selects Writeback A (LCD), 1 - selects Writeback B (LCD/ISP) */ +#define FIMC_REG_CIGCTRL_SELWB_A (1 << 10) #define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8) #define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7) #define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6) @@ -93,10 +95,10 @@ /* Output DMA control */ #define FIMC_REG_CIOCTRL 0x4c #define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (0 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (1 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (3 << 0) #define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2) #define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3) #define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3) @@ -218,10 +220,10 @@ #define FIMC_REG_MSCTRL_FLIP_180 (3 << 13) #define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12) #define FIMC_REG_MSCTRL_ORDER422_SHIFT 4 -#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4) -#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4) -#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4) -#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (0 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (1 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (2 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (3 << 4) #define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4) #define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3) #define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3) @@ -276,6 +278,14 @@ /* Output frame buffer sequence mask */ #define FIMC_REG_CIFCNTSEQ 0x1fc +/* SYSREG ISP Writeback register address offsets */ +#define SYSREG_ISPBLK 0x020c +#define SYSREG_ISPBLK_FIFORST_CAM_BLK (1 << 7) + +#define SYSREG_CAMBLK 0x0218 +#define SYSREG_CAMBLK_FIFORST_ISP (1 << 15) +#define SYSREG_CAMBLK_ISPWB_FULL_EN (7 << 20) + /* * Function declarations */ @@ -309,6 +319,7 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); +int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc); void fimc_activate_capture(struct fimc_ctx *ctx); void fimc_deactivate_capture(struct fimc_dev *fimc); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/exynos4-is/media-dev.c index cd38d70..15ef8f2 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -17,32 +17,37 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/of_i2c.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/types.h> #include <linux/slab.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-of.h> #include <media/media-device.h> #include <media/s5p_fimc.h> +#include "media-dev.h" #include "fimc-core.h" +#include "fimc-is.h" #include "fimc-lite.h" -#include "fimc-mdevice.h" #include "mipi-csis.h" static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, + struct fimc_source_info *si, bool on); /** * fimc_pipeline_prepare - update pipeline information with subdevice pointers - * @fimc: fimc device terminating the pipeline + * @me: media entity terminating the pipeline * * Caller holds the graph mutex. */ static void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { - struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; int i; @@ -50,15 +55,21 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, p->subdevs[i] = NULL; while (1) { - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; + struct media_pad *pad = NULL; + + /* Find remote source pad */ + for (i = 0; i < me->num_pads; i++) { + struct media_pad *spad = &me->pads[i]; + if (!(spad->flags & MEDIA_PAD_FL_SINK)) + continue; + pad = media_entity_remote_source(spad); + if (pad) + break; + } - /* source pad */ - pad = media_entity_remote_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; - sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { @@ -75,12 +86,15 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; + case GRP_ID_FIMC_IS: + p->subdevs[IDX_IS_ISP] = sd; + break; default: - pr_warn("%s: Unknown subdev grp_id: %#x\n", - __func__, sd->grp_id); + break; } - /* sink pad */ - pad = &sd->entity.pads[0]; + me = &sd->entity; + if (me->num_pads == 1) + break; } } @@ -117,49 +131,81 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on) * * Needs to be called with the graph mutex held. */ -static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) +static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool on) { - unsigned int i; - int ret; + static const u8 seq[2][IDX_MAX - 1] = { + { IDX_IS_ISP, IDX_SENSOR, IDX_CSIS, IDX_FLITE }, + { IDX_CSIS, IDX_FLITE, IDX_SENSOR, IDX_IS_ISP }, + }; + int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) return -ENXIO; - for (i = 0; i < IDX_MAX; i++) { - unsigned int idx = state ? (IDX_MAX - 1) - i : i; + for (i = 0; i < IDX_MAX - 1; i++) { + unsigned int idx = seq[on][i]; + + ret = __subdev_set_power(p->subdevs[idx], on); + - ret = __subdev_set_power(p->subdevs[idx], state); if (ret < 0 && ret != -ENXIO) - return ret; + goto error; } - return 0; +error: + for (; i >= 0; i--) { + unsigned int idx = seq[on][i]; + __subdev_set_power(p->subdevs[idx], !on); + } + return ret; } /** * __fimc_pipeline_open - update the pipeline information, enable power * of all pipeline subdevs and the sensor clock * @me: media entity to start graph walk with - * @prep: true to acquire sensor (and csis) subdevs + * @prepare: true to walk the current pipeline and acquire all subdevs * * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct fimc_pipeline *p, - struct media_entity *me, bool prep) + struct media_entity *me, bool prepare) { + struct fimc_md *fmd = entity_to_fimc_mdev(me); + struct v4l2_subdev *sd; int ret; - if (prep) + if (WARN_ON(p == NULL || me == NULL)) + return -EINVAL; + + if (prepare) fimc_pipeline_prepare(p, me); - if (p->subdevs[IDX_SENSOR] == NULL) + sd = p->subdevs[IDX_SENSOR]; + if (sd == NULL) return -EINVAL; - ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true); - if (ret) - return ret; + /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) { + ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]); + if (ret < 0) + return ret; + } + ret = fimc_md_set_camclk(sd, true); + if (ret < 0) + goto err_wbclk; + + ret = fimc_pipeline_s_power(p, 1); + if (!ret) + return 0; + + fimc_md_set_camclk(sd, false); + +err_wbclk: + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) + clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); - return fimc_pipeline_s_power(p, 1); + return ret; } /** @@ -170,41 +216,58 @@ static int __fimc_pipeline_open(struct fimc_pipeline *p, */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { + struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; + struct fimc_md *fmd; int ret = 0; - if (!p || !p->subdevs[IDX_SENSOR]) + if (WARN_ON(sd == NULL)) return -EINVAL; if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); - fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); + fimc_md_set_camclk(sd, false); } + + fmd = entity_to_fimc_mdev(&sd->entity); + + /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) + clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); + return ret == -ENXIO ? 0 : ret; } /** - * __fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs * @pipeline: video pipeline structure - * @on: passed as the s_stream call argument + * @on: passed as the s_stream() callback argument */ static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { - int i, ret; + static const u8 seq[2][IDX_MAX] = { + { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE }, + { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, + }; + int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; for (i = 0; i < IDX_MAX; i++) { - unsigned int idx = on ? (IDX_MAX - 1) - i : i; + unsigned int idx = seq[on][i]; ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; + goto error; } - return 0; - +error: + for (; i >= 0; i--) { + unsigned int idx = seq[on][i]; + v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on); + } + return ret; } /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ @@ -218,35 +281,40 @@ static const struct fimc_pipeline_ops fimc_pipeline_ops = { * Sensor subdevice helper functions */ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, - struct fimc_sensor_info *s_info) + struct fimc_source_info *si) { struct i2c_adapter *adapter; struct v4l2_subdev *sd = NULL; - if (!s_info || !fmd) + if (!si || !fmd) return NULL; + /* + * If FIMC bus type is not Writeback FIFO assume it is same + * as sensor_bus_type. + */ + si->fimc_bus_type = si->sensor_bus_type; - adapter = i2c_get_adapter(s_info->pdata.i2c_bus_num); + adapter = i2c_get_adapter(si->i2c_bus_num); if (!adapter) { v4l2_warn(&fmd->v4l2_dev, "Failed to get I2C adapter %d, deferring probe\n", - s_info->pdata.i2c_bus_num); + si->i2c_bus_num); return ERR_PTR(-EPROBE_DEFER); } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, - s_info->pdata.board_info, NULL); + si->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); v4l2_warn(&fmd->v4l2_dev, "Failed to acquire subdev %s, deferring probe\n", - s_info->pdata.board_info->type); + si->board_info->type); return ERR_PTR(-EPROBE_DEFER); } - v4l2_set_subdev_hostdata(sd, s_info); + v4l2_set_subdev_hostdata(sd, si); sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - s_info->pdata.board_info->type); + sd->name); return sd; } @@ -257,57 +325,262 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) if (!client) return; + v4l2_device_unregister_subdev(sd); - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); + + if (!client->dev.of_node) { + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); + } +} + +#ifdef CONFIG_OF +/* Register I2C client subdev associated with @node. */ +static int fimc_md_of_add_sensor(struct fimc_md *fmd, + struct device_node *node, int index) +{ + struct fimc_sensor_info *si; + struct i2c_client *client; + struct v4l2_subdev *sd; + int ret; + + if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) + return -EINVAL; + si = &fmd->sensor[index]; + + client = of_find_i2c_device_by_node(node); + if (!client) + return -EPROBE_DEFER; + + device_lock(&client->dev); + + if (!client->driver || + !try_module_get(client->driver->driver.owner)) { + ret = -EPROBE_DEFER; + v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", + node->full_name); + goto dev_put; + } + + /* Enable sensor's master clock */ + ret = __fimc_md_set_camclk(fmd, &si->pdata, true); + if (ret < 0) + goto mod_put; + sd = i2c_get_clientdata(client); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + __fimc_md_set_camclk(fmd, &si->pdata, false); + if (ret < 0) + goto mod_put; + + v4l2_set_subdev_hostdata(sd, &si->pdata); + if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) + sd->grp_id = GRP_ID_FIMC_IS_SENSOR; + else + sd->grp_id = GRP_ID_SENSOR; + + si->subdev = sd; + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", + sd->name, fmd->num_sensors); + fmd->num_sensors++; + +mod_put: + module_put(client->driver->driver.owner); +dev_put: + device_unlock(&client->dev); + put_device(&client->dev); + return ret; +} + +/* Parse port node and register as a sub-device any sensor specified there. */ +static int fimc_md_parse_port_node(struct fimc_md *fmd, + struct device_node *port, + unsigned int index) +{ + struct device_node *rem, *ep, *np; + struct fimc_source_info *pd; + struct v4l2_of_endpoint endpoint; + int ret; + u32 val; + + pd = &fmd->sensor[index].pdata; + + /* Assume here a port node can have only one endpoint node. */ + ep = of_get_next_child(port, NULL); + if (!ep) + return 0; + + v4l2_of_parse_endpoint(ep, &endpoint); + if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS) + return -EINVAL; + + pd->mux_id = (endpoint.port - 1) & 0x1; + + rem = v4l2_of_get_remote_port_parent(ep); + of_node_put(ep); + if (rem == NULL) { + v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n", + ep->full_name); + return 0; + } + if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) + pd->clk_id = val; + + if (!of_property_read_u32(rem, "clock-frequency", &val)) + pd->clk_frequency = val; + + if (pd->clk_frequency == 0) { + v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", + rem->full_name); + of_node_put(rem); + return -EINVAL; + } + + if (fimc_input_is_parallel(endpoint.port)) { + if (endpoint.bus_type == V4L2_MBUS_PARALLEL) + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601; + else + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656; + pd->flags = endpoint.bus.parallel.flags; + } else if (fimc_input_is_mipi_csi(endpoint.port)) { + /* + * MIPI CSI-2: only input mux selection and + * the sensor's clock frequency is needed. + */ + pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2; + } else { + v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n", + endpoint.port, rem->full_name); + } + /* + * For FIMC-IS handled sensors, that are placed under i2c-isp device + * node, FIMC is connected to the FIMC-IS through its ISP Writeback + * input. Sensors are attached to the FIMC-LITE hostdata interface + * directly or through MIPI-CSIS, depending on the external media bus + * used. This needs to be handled in a more reliable way, not by just + * checking parent's node name. + */ + np = of_get_parent(rem); + + if (np && !of_node_cmp(np->name, "i2c-isp")) + pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; + else + pd->fimc_bus_type = pd->sensor_bus_type; + + ret = fimc_md_of_add_sensor(fmd, rem, index); + of_node_put(rem); + + return ret; +} + +/* Register all SoC external sub-devices */ +static int fimc_md_of_sensors_register(struct fimc_md *fmd, + struct device_node *np) +{ + struct device_node *parent = fmd->pdev->dev.of_node; + struct device_node *node, *ports; + int index = 0; + int ret; + + /* Attach sensors linked to MIPI CSI-2 receivers */ + for_each_available_child_of_node(parent, node) { + struct device_node *port; + + if (of_node_cmp(node->name, "csis")) + continue; + /* The csis node can have only port subnode. */ + port = of_get_next_child(node, NULL); + if (!port) + continue; + + ret = fimc_md_parse_port_node(fmd, port, index); + if (ret < 0) + return ret; + index++; + } + + /* Attach sensors listed in the parallel-ports node */ + ports = of_get_child_by_name(parent, "parallel-ports"); + if (!ports) + return 0; + + for_each_child_of_node(ports, node) { + ret = fimc_md_parse_port_node(fmd, node, index); + if (ret < 0) + break; + index++; + } + + return 0; +} + +static int __of_get_csis_id(struct device_node *np) +{ + u32 reg = 0; + + np = of_get_child_by_name(np, "port"); + if (!np) + return -EINVAL; + of_property_read_u32(np, "reg", ®); + return reg - FIMC_INPUT_MIPI_CSI2_0; } +#else +#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) +#define __of_get_csis_id(np) (-ENOSYS) +#endif static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct fimc_dev *fd = NULL; - int num_clients, ret, i; + struct device_node *of_node = fmd->pdev->dev.of_node; + int num_clients = 0; + int ret, i; /* * Runtime resume one of the FIMC entities to make sure * the sclk_cam clocks are not globally disabled. */ - for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++) - if (fmd->fimc[i]) - fd = fmd->fimc[i]; - if (!fd) + if (!fmd->pmf) return -ENXIO; - ret = pm_runtime_get_sync(&fd->pdev->dev); + + ret = pm_runtime_get_sync(fmd->pmf); if (ret < 0) return ret; - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); - - fmd->num_sensors = num_clients; - for (i = 0; i < num_clients; i++) { - struct v4l2_subdev *sd; - - fmd->sensor[i].pdata = pdata->source_info[i]; - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); - - if (!IS_ERR(sd)) { - fmd->sensor[i].subdev = sd; - } else { - fmd->sensor[i].subdev = NULL; - ret = PTR_ERR(sd); - break; + if (of_node) { + fmd->num_sensors = 0; + ret = fimc_md_of_sensors_register(fmd, of_node); + } else if (pdata) { + WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, + ARRAY_SIZE(fmd->sensor)); + fmd->num_sensors = num_clients; + + for (i = 0; i < num_clients; i++) { + struct fimc_sensor_info *si = &fmd->sensor[i]; + struct v4l2_subdev *sd; + + si->pdata = pdata->source_info[i]; + ret = __fimc_md_set_camclk(fmd, &si->pdata, true); + if (ret) + break; + sd = fimc_md_register_sensor(fmd, &si->pdata); + ret = __fimc_md_set_camclk(fmd, &si->pdata, false); + + if (IS_ERR(sd)) { + si->subdev = NULL; + ret = PTR_ERR(sd); + break; + } + si->subdev = sd; + if (ret) + break; } - if (ret) - break; } - pm_runtime_put(&fd->pdev->dev); + + pm_runtime_put(fmd->pmf); return ret; } @@ -352,6 +625,8 @@ static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) { + if (!fmd->pmf && fimc->pdev) + fmd->pmf = &fimc->pdev->dev; fmd->fimc[fimc->id] = fimc; fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; } else { @@ -368,13 +643,13 @@ static int register_csis_entity(struct fimc_md *fmd, struct device_node *node = pdev->dev.of_node; int id, ret; - id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id); + id = node ? __of_get_csis_id(node) : max(0, pdev->id); - if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd)) - return -EBUSY; + if (WARN_ON(id < 0 || id >= CSIS_MAX_ENTITIES)) + return -ENOENT; - if (WARN_ON(id >= CSIS_MAX_ENTITIES)) - return 0; + if (WARN_ON(fmd->csis[id].sd)) + return -EBUSY; sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -386,6 +661,22 @@ static int register_csis_entity(struct fimc_md *fmd, return ret; } +static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) +{ + struct v4l2_subdev *sd = &is->isp.subdev; + int ret; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, + "Failed to register FIMC-ISP (%d)\n", ret); + return ret; + } + + fmd->fimc_is = is; + return 0; +} + static int fimc_md_register_platform_entity(struct fimc_md *fmd, struct platform_device *pdev, int plat_entity) @@ -413,6 +704,9 @@ static int fimc_md_register_platform_entity(struct fimc_md *fmd, case IDX_CSIS: ret = register_csis_entity(fmd, pdev, drvdata); break; + case IDX_IS_ISP: + ret = register_fimc_is_entity(fmd, drvdata); + break; default: ret = -ENODEV; } @@ -457,6 +751,47 @@ static int fimc_md_pdev_match(struct device *dev, void *data) return 0; } +/* Register FIMC, FIMC-LITE and CSIS media entities */ +#ifdef CONFIG_OF +static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, + struct device_node *parent) +{ + struct device_node *node; + int ret = 0; + + for_each_available_child_of_node(parent, node) { + struct platform_device *pdev; + int plat_entity = -1; + + pdev = of_find_device_by_node(node); + if (!pdev) + continue; + + /* If driver of any entity isn't ready try all again later. */ + if (!strcmp(node->name, CSIS_OF_NODE_NAME)) + plat_entity = IDX_CSIS; + else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME)) + plat_entity = IDX_IS_ISP; + else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) + plat_entity = IDX_FLITE; + else if (!strcmp(node->name, FIMC_OF_NODE_NAME) && + !of_property_read_bool(node, "samsung,lcd-wb")) + plat_entity = IDX_FIMC; + + if (plat_entity >= 0) + ret = fimc_md_register_platform_entity(fmd, pdev, + plat_entity); + put_device(&pdev->dev); + if (ret < 0) + break; + } + + return ret; +} +#else +#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) +#endif + static void fimc_md_unregister_entities(struct fimc_md *fmd) { int i; @@ -479,7 +814,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); - module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -488,6 +822,10 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) fimc_md_unregister_sensor(fmd->sensor[i].subdev); fmd->sensor[i].subdev = NULL; } + + if (fmd->fimc_is) + v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); + v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); } @@ -504,12 +842,19 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct v4l2_subdev *sensor, int pad, int link_mask) { - struct fimc_sensor_info *s_info = NULL; + struct fimc_source_info *si = NULL; struct media_entity *sink; unsigned int flags = 0; - int ret, i; + int i, ret = 0; - for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (sensor) { + si = v4l2_get_subdev_hostdata(sensor); + /* Skip direct FIMC links in the logical FIMC-IS sensor path */ + if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) + ret = 1; + } + + for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; /* @@ -523,7 +868,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, - FIMC_SD_PAD_SINK, flags); + FIMC_SD_PAD_SINK_CAM, flags); if (ret) return ret; @@ -538,11 +883,13 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (flags == 0 || sensor == NULL) continue; - s_info = v4l2_get_subdev_hostdata(sensor); - if (!WARN_ON(s_info == NULL)) { + + if (!WARN_ON(si == NULL)) { unsigned long irq_flags; + struct fimc_sensor_info *inf = source_to_sensor_info(si); + spin_lock_irqsave(&fmd->slock, irq_flags); - s_info->host = fmd->fimc[i]; + inf->host = fmd->fimc[i]; spin_unlock_irqrestore(&fmd->slock, irq_flags); } } @@ -551,25 +898,20 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (!fmd->fimc_lite[i]) continue; - if (link_mask & (1 << (i + FIMC_MAX_DEVS))) - flags = MEDIA_LNK_FL_ENABLED; - else - flags = 0; - sink = &fmd->fimc_lite[i]->subdev.entity; ret = media_entity_create_link(source, pad, sink, - FLITE_SD_PAD_SINK, flags); + FLITE_SD_PAD_SINK, 0); if (ret) return ret; /* Notify FIMC-LITE subdev entity */ ret = media_entity_call(sink, link_setup, &sink->pads[0], - &source->pads[pad], flags); + &source->pads[pad], 0); if (ret) break; - v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", - source->name, flags ? '=' : '-', sink->name); + v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n", + source->name, sink->name); } return 0; } @@ -578,21 +920,50 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) { struct media_entity *source, *sink; - unsigned int flags = MEDIA_LNK_FL_ENABLED; int i, ret = 0; for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { struct fimc_lite *fimc = fmd->fimc_lite[i]; + if (fimc == NULL) continue; + source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, - sink, 0, flags); + sink, 0, 0); + if (ret) + break; + /* Link from FIMC-LITE to IS-ISP subdev */ + sink = &fmd->fimc_is->isp.subdev.entity; + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP, + sink, 0, 0); if (ret) break; - /* TODO: create links to other entities */ + } + + return ret; +} + +/* Create FIMC-IS links */ +static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) +{ + struct media_entity *source, *sink; + int i, ret; + + source = &fmd->fimc_is->isp.subdev.entity; + + for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (fmd->fimc[i] == NULL) + continue; + + /* Link from IS-ISP subdev to FIMC */ + sink = &fmd->fimc[i]->vid_cap.subdev.entity; + ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, + sink, FIMC_SD_PAD_SINK_FIFO, 0); + if (ret) + return ret; } return ret; @@ -615,7 +986,6 @@ static int fimc_md_create_links(struct fimc_md *fmd) struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct fimc_source_info *pdata; - struct fimc_sensor_info *s_info; struct media_entity *source, *sink; int i, pad, fimc_id = 0, ret = 0; u32 flags, link_mask = 0; @@ -625,12 +995,11 @@ static int fimc_md_create_links(struct fimc_md *fmd) continue; sensor = fmd->sensor[i].subdev; - s_info = v4l2_get_subdev_hostdata(sensor); - if (!s_info) + pdata = v4l2_get_subdev_hostdata(sensor); + if (!pdata) continue; source = NULL; - pdata = &s_info->pdata; switch (pdata->sensor_bus_type) { case FIMC_BUS_TYPE_MIPI_CSI2: @@ -680,6 +1049,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; + source = &fmd->csis[i].sd->entity; pad = CSIS_PAD_SOURCE; sensor = csi_sensors[i]; @@ -694,19 +1064,28 @@ static int fimc_md_create_links(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; + source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.vfd.entity; + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); if (ret) break; } - return __fimc_md_create_flite_source_links(fmd); + ret = __fimc_md_create_flite_source_links(fmd); + if (ret < 0) + return ret; + + if (fmd->use_isp) + ret = __fimc_md_create_fimc_is_links(fmd); + + return ret; } /* - * The peripheral sensor clock management. + * The peripheral sensor and CAM_BLK (PIXELASYNCMx) clocks management. */ static void fimc_md_put_clocks(struct fimc_md *fmd) { @@ -719,6 +1098,14 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) clk_put(fmd->camclk[i].clock); fmd->camclk[i].clock = ERR_PTR(-EINVAL); } + + /* Writeback (PIXELASYNCMx) clocks */ + for (i = 0; i < FIMC_MAX_WBCLKS; i++) { + if (IS_ERR(fmd->wbclk[i])) + continue; + clk_put(fmd->wbclk[i]); + fmd->wbclk[i] = ERR_PTR(-EINVAL); + } } static int fimc_md_get_clocks(struct fimc_md *fmd) @@ -755,35 +1142,59 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) if (ret) fimc_md_put_clocks(fmd); + if (!fmd->use_isp) + return 0; + /* + * For now get only PIXELASYNCM1 clock (Writeback B/ISP), + * leave PIXELASYNCM0 out for the LCD Writeback driver. + */ + fmd->wbclk[CLK_IDX_WB_A] = ERR_PTR(-EINVAL); + + for (i = CLK_IDX_WB_B; i < FIMC_MAX_WBCLKS; i++) { + snprintf(clk_name, sizeof(clk_name), "pxl_async%u", i); + clock = clk_get(dev, clk_name); + if (IS_ERR(clock)) { + v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s\n", + clk_name); + ret = PTR_ERR(clock); + break; + } + fmd->wbclk[i] = clock; + } + if (ret) + fimc_md_put_clocks(fmd); + return ret; } static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, + struct fimc_source_info *si, bool on) { - struct fimc_source_info *pdata = &s_info->pdata; struct fimc_camclk_info *camclk; int ret = 0; - if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL) + if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) return -EINVAL; - camclk = &fmd->camclk[pdata->clk_id]; + camclk = &fmd->camclk[si->clk_id]; dbg("camclk %d, f: %lu, use_count: %d, on: %d", - pdata->clk_id, pdata->clk_frequency, camclk->use_count, on); + si->clk_id, si->clk_frequency, camclk->use_count, on); if (on) { if (camclk->use_count > 0 && - camclk->frequency != pdata->clk_frequency) + camclk->frequency != si->clk_frequency) return -EINVAL; if (camclk->use_count++ == 0) { - clk_set_rate(camclk->clock, pdata->clk_frequency); - camclk->frequency = pdata->clk_frequency; + clk_set_rate(camclk->clock, si->clk_frequency); + camclk->frequency = si->clk_frequency; + ret = pm_runtime_get_sync(fmd->pmf); + if (ret < 0) + return ret; ret = clk_enable(camclk->clock); - dbg("Enabled camclk %d: f: %lu", pdata->clk_id, + dbg("Enabled camclk %d: f: %lu", si->clk_id, clk_get_rate(camclk->clock)); } return ret; @@ -794,7 +1205,8 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (--camclk->use_count == 0) { clk_disable(camclk->clock); - dbg("Disabled camclk %d", pdata->clk_id); + pm_runtime_put(fmd->pmf); + dbg("Disabled camclk %d", si->clk_id); } return ret; } @@ -813,10 +1225,10 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, */ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) { - struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd); + struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); - return __fimc_md_set_camclk(fmd, s_info, on); + return __fimc_md_set_camclk(fmd, si, on); } static int fimc_md_link_notify(struct media_pad *source, @@ -926,13 +1338,33 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); +static int fimc_md_get_pinctrl(struct fimc_md *fmd) +{ + struct device *dev = &fmd->pdev->dev; + struct fimc_pinctrl *pctl = &fmd->pinctl; + + pctl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pctl->pinctrl)) + return PTR_ERR(pctl->pinctrl); + + pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(pctl->state_default)) + return PTR_ERR(pctl->state_default); + + pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_IDLE); + return 0; +} + static int fimc_md_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct v4l2_device *v4l2_dev; struct fimc_md *fmd; int ret; - fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL); + fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL); if (!fmd) return -ENOMEM; @@ -942,15 +1374,16 @@ static int fimc_md_probe(struct platform_device *pdev) strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); fmd->media_dev.link_notify = fimc_md_link_notify; - fmd->media_dev.dev = &pdev->dev; + fmd->media_dev.dev = dev; v4l2_dev = &fmd->v4l2_dev; v4l2_dev->mdev = &fmd->media_dev; v4l2_dev->notify = fimc_sensor_notify; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s", - dev_name(&pdev->dev)); + strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); - ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev); + fmd->use_isp = fimc_md_is_isp_available(dev->of_node); + + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; @@ -964,21 +1397,32 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_clk; - fmd->user_subdev_api = false; + fmd->user_subdev_api = (dev->of_node != NULL); /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, - fimc_md_pdev_match); + ret = fimc_md_get_pinctrl(fmd); + if (ret < 0) { + if (ret != EPROBE_DEFER) + dev_err(dev, "Failed to get pinctrl: %d\n", ret); + goto err_unlock; + } + + if (dev->of_node) + ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); + else + ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, + fimc_md_pdev_match); if (ret) goto err_unlock; - if (pdev->dev.platform_data) { + if (dev->platform_data || dev->of_node) { ret = fimc_md_register_sensor_entities(fmd); if (ret) goto err_unlock; } + ret = fimc_md_create_links(fmd); if (ret) goto err_unlock; @@ -1018,12 +1462,25 @@ static int fimc_md_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id fimc_driver_ids[] __always_unused = { + { .name = "s5p-fimc-md" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, fimc_driver_ids); + +static const struct of_device_id fimc_md_of_match[] = { + { .compatible = "samsung,fimc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, fimc_md_of_match); + static struct platform_driver fimc_md_driver = { .probe = fimc_md_probe, .remove = fimc_md_remove, .driver = { - .name = "s5p-fimc-md", - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fimc_md_of_match), + .name = "s5p-fimc-md", + .owner = THIS_MODULE, } }; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/exynos4-is/media-dev.h index 06b0d82..44d86b6 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -12,6 +12,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/mutex.h> +#include <linux/of.h> +#include <linux/pinctrl/consumer.h> #include <media/media-device.h> #include <media/media-entity.h> #include <media/v4l2-device.h> @@ -21,18 +23,23 @@ #include "fimc-lite.h" #include "mipi-csis.h" -/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define GRP_ID_SENSOR (1 << 8) -#define GRP_ID_FIMC_IS_SENSOR (1 << 9) -#define GRP_ID_WRITEBACK (1 << 10) -#define GRP_ID_CSIS (1 << 11) -#define GRP_ID_FIMC (1 << 12) -#define GRP_ID_FLITE (1 << 13) -#define GRP_ID_FIMC_IS (1 << 14) +#define FIMC_OF_NODE_NAME "fimc" +#define FIMC_LITE_OF_NODE_NAME "fimc-lite" +#define FIMC_IS_OF_NODE_NAME "fimc-is" +#define CSIS_OF_NODE_NAME "csis" + +#define PINCTRL_STATE_IDLE "idle" #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 +/* LCD/ISP Writeback clocks (PIXELASYNCMx) */ +enum { + CLK_IDX_WB_A, + CLK_IDX_WB_B, + FIMC_MAX_WBCLKS +}; + struct fimc_csis_info { struct v4l2_subdev *sd; int id; @@ -65,9 +72,15 @@ struct fimc_sensor_info { * @num_sensors: actual number of registered sensors * @camclk: external sensor clock information * @fimc: array of registered fimc devices + * @fimc_is: fimc-is data structure + * @use_isp: set to true when FIMC-IS subsystem is used + * @pmf: handle to the CAMCLK clock control FIMC helper device * @media_dev: top level media device * @v4l2_dev: top level v4l2_device holding up the subdevs * @pdev: platform device this media device is hooked up into + * @pinctrl: camera port pinctrl handle + * @state_default: pinctrl default state handle + * @state_idle: pinctrl idle state handle * @user_subdev_api: true if subdevs are not configured by the host driver * @slock: spinlock protecting @sensor array */ @@ -76,11 +89,20 @@ struct fimc_md { struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; int num_sensors; struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; + struct clk *wbclk[FIMC_MAX_WBCLKS]; struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS]; struct fimc_dev *fimc[FIMC_MAX_DEVS]; + struct fimc_is *fimc_is; + bool use_isp; + struct device *pmf; struct media_device media_dev; struct v4l2_device v4l2_dev; struct platform_device *pdev; + struct fimc_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *state_default; + struct pinctrl_state *state_idle; + } pinctl; bool user_subdev_api; spinlock_t slock; }; @@ -93,6 +115,12 @@ struct fimc_md { #define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) +static inline +struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) +{ + return container_of(si, struct fimc_sensor_info, pdata); +} + static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) { return me->parent == NULL ? NULL : @@ -111,4 +139,14 @@ static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); +#ifdef CONFIG_OF +static inline bool fimc_md_is_isp_available(struct device_node *node) +{ + node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); + return node ? of_device_is_available(node) : false; +} +#else +#define fimc_md_is_isp_available(node) (false) +#endif /* CONFIG_OF */ + #endif diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 981863d..a2eda9d 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -19,14 +19,18 @@ #include <linux/kernel.h> #include <linux/memory.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_data/mipi-csis.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/videodev2.h> +#include <media/s5p_fimc.h> +#include <media/v4l2-of.h> #include <media/v4l2-subdev.h> -#include <linux/platform_data/mipi-csis.h> + #include "mipi-csis.h" static int debug; @@ -113,6 +117,7 @@ static char *csi_clock_name[] = { [CSIS_CLK_GATE] = "csis", }; #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) +#define DEFAULT_SCLK_CSIS_FREQ 166000000UL static const char * const csis_supply_name[] = { "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ @@ -167,6 +172,11 @@ struct csis_pktbuf { * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number * @flags: the state variable for power and streaming control + * @clock_frequency: device bus clock frequency + * @hs_settle: HS-RX settle time + * @num_lanes: number of MIPI-CSI data lanes used + * @max_num_lanes: maximum number of MIPI-CSI data lanes supported + * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM * @csis_fmt: current CSIS pixel format * @format: common media bus format for the source and sink pad * @slock: spinlock protecting structure members below @@ -184,6 +194,13 @@ struct csis_state { struct clk *clock[NUM_CSIS_CLOCKS]; int irq; u32 flags; + + u32 clk_frequency; + u32 hs_settle; + u32 num_lanes; + u32 max_num_lanes; + u8 wclk_ext; + const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; @@ -273,7 +290,6 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); @@ -286,7 +302,7 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); val &= ~S5PCSIS_DPHYCTRL_ENABLE; if (on) { - mask = (1 << (pdata->lanes + 1)) - 1; + mask = (1 << (state->num_lanes + 1)) - 1; val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); @@ -321,15 +337,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) static void s5pcsis_set_params(struct csis_state *state) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val; val = s5pcsis_read(state, S5PCSIS_CONFIG); - val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1); + val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); s5pcsis_write(state, S5PCSIS_CONFIG, val); __s5pcsis_set_format(state); - s5pcsis_set_hsync_settle(state, pdata->hs_settle); + s5pcsis_set_hsync_settle(state, state->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); if (state->csis_fmt->data_alignment == 32) @@ -338,7 +353,7 @@ static void s5pcsis_set_params(struct csis_state *state) val &= ~S5PCSIS_CTRL_ALIGN_32BIT; val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; - if (pdata->wclk_source) + if (state->wclk_ext) val |= S5PCSIS_CTRL_WCLK_EXTCLK; s5pcsis_write(state, S5PCSIS_CTRL, val); @@ -534,10 +549,10 @@ static struct csis_pix_format const *s5pcsis_try_format( static struct v4l2_mbus_framefmt *__s5pcsis_get_format( struct csis_state *state, struct v4l2_subdev_fh *fh, - u32 pad, enum v4l2_subdev_format_whence which) + enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; return &state->format; } @@ -549,10 +564,7 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) - return -EINVAL; - - mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + mf = __s5pcsis_get_format(state, fh, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -579,10 +591,7 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) - return -EINVAL; - - mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + mf = __s5pcsis_get_format(state, fh, fmt->which); if (!mf) return -EINVAL; @@ -701,52 +710,111 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int s5pcsis_get_platform_data(struct platform_device *pdev, + struct csis_state *state) +{ + struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data not specified\n"); + return -EINVAL; + } + + state->clk_frequency = pdata->clk_rate; + state->num_lanes = pdata->lanes; + state->hs_settle = pdata->hs_settle; + state->index = max(0, pdev->id); + state->max_num_lanes = state->index ? CSIS1_MAX_LANES : + CSIS0_MAX_LANES; + return 0; +} + +#ifdef CONFIG_OF +static int s5pcsis_parse_dt(struct platform_device *pdev, + struct csis_state *state) +{ + struct device_node *node = pdev->dev.of_node; + struct v4l2_of_endpoint endpoint; + + if (of_property_read_u32(node, "clock-frequency", + &state->clk_frequency)) + state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; + if (of_property_read_u32(node, "bus-width", + &state->max_num_lanes)) + return -EINVAL; + + node = v4l2_of_get_next_endpoint(node, NULL); + if (!node) { + dev_err(&pdev->dev, "No port node at %s\n", + node->full_name); + return -EINVAL; + } + /* Get port node and validate MIPI-CSI channel id. */ + v4l2_of_parse_endpoint(node, &endpoint); + + state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0; + if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES) + return -ENXIO; + + /* Get MIPI CSI-2 bus configration from the endpoint node. */ + of_property_read_u32(node, "samsung,csis-hs-settle", + &state->hs_settle); + state->wclk_ext = of_property_read_bool(node, + "samsung,csis-wclk"); + + state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; + + of_node_put(node); + return 0; +} +#else +#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) +#endif + static int s5pcsis_probe(struct platform_device *pdev) { - struct s5p_platform_mipi_csis *pdata; + struct device *dev = &pdev->dev; struct resource *mem_res; struct csis_state *state; int ret = -ENOMEM; int i; - state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; mutex_init(&state->lock); spin_lock_init(&state->slock); - state->pdev = pdev; - state->index = max(0, pdev->id); - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform data not fully specified\n"); - return -EINVAL; - } + if (dev->of_node) + ret = s5pcsis_parse_dt(pdev, state); + else + ret = s5pcsis_get_platform_data(pdev, state); + if (ret < 0) + return ret; - if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || - pdata->lanes > CSIS0_MAX_LANES) { - dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", - pdata->lanes); + if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { + dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n", + state->num_lanes, state->max_num_lanes); return -EINVAL; } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_ioremap_resource(&pdev->dev, mem_res); + state->regs = devm_ioremap_resource(dev, mem_res); if (IS_ERR(state->regs)) return PTR_ERR(state->regs); state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { - dev_err(&pdev->dev, "Failed to get irq\n"); + dev_err(dev, "Failed to get irq\n"); return state->irq; } for (i = 0; i < CSIS_NUM_SUPPLIES; i++) state->supplies[i].supply = csis_supply_name[i]; - ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) return ret; @@ -755,11 +823,11 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (pdata->clk_rate) + if (state->clk_frequency) ret = clk_set_rate(state->clock[CSIS_CLK_MUX], - pdata->clk_rate); + state->clk_frequency); else - dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + dev_WARN(dev, "No clock frequency specified!\n"); if (ret < 0) goto e_clkput; @@ -767,16 +835,17 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) goto e_clkput; - ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, - 0, dev_name(&pdev->dev), state); + ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, + 0, dev_name(dev), state); if (ret) { - dev_err(&pdev->dev, "Interrupt request failed\n"); + dev_err(dev, "Interrupt request failed\n"); goto e_clkdis; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); state->sd.owner = THIS_MODULE; - strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name)); + snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", + CSIS_SUBDEV_NAME, state->index); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->csis_fmt = &s5pcsis_formats[0]; @@ -796,10 +865,12 @@ static int s5pcsis_probe(struct platform_device *pdev) /* .. and a pointer to the subdev. */ platform_set_drvdata(pdev, &state->sd); - memcpy(state->events, s5pcsis_events, sizeof(state->events)); + pm_runtime_enable(dev); - pm_runtime_enable(&pdev->dev); + dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", + state->num_lanes, state->hs_settle, state->wclk_ext, + state->clk_frequency); return 0; e_clkdis: @@ -923,13 +994,21 @@ static const struct dev_pm_ops s5pcsis_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) }; +static const struct of_device_id s5pcsis_of_match[] = { + { .compatible = "samsung,s5pv210-csis" }, + { .compatible = "samsung,exynos4210-csis" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s5pcsis_of_match); + static struct platform_driver s5pcsis_driver = { .probe = s5pcsis_probe, .remove = s5pcsis_remove, .driver = { - .name = CSIS_DRIVER_NAME, - .owner = THIS_MODULE, - .pm = &s5pcsis_pm_ops, + .of_match_table = s5pcsis_of_match, + .name = CSIS_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &s5pcsis_pm_ops, }, }; diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.h b/drivers/media/platform/exynos4-is/mipi-csis.h index 2709286..28c11c4 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.h +++ b/drivers/media/platform/exynos4-is/mipi-csis.h @@ -11,6 +11,7 @@ #define S5P_MIPI_CSIS_H_ #define CSIS_DRIVER_NAME "s5p-mipi-csis" +#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME #define CSIS_MAX_ENTITIES 2 #define CSIS0_MAX_LANES 4 #define CSIS1_MAX_LANES 2 diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 5f7db3f..3a6a0dc 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -957,12 +957,12 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) return 0; } -static int vidioc_s_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 viu_fh *fh = priv; - fh->dev->std = *id; - decoder_call(fh->dev, core, s_std, *id); + fh->dev->std = id; + decoder_call(fh->dev, core, s_std, id); return 0; } diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 6c4db9b..7585646 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -207,6 +207,9 @@ static void dma_callback(void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -866,6 +869,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -882,6 +886,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 92a33f0..64ab91e 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1357,7 +1357,7 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i) } /* from vivi.c */ -static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a) { return 0; } @@ -1445,7 +1445,7 @@ static int mcam_vidioc_g_register(struct file *file, void *priv, } static int mcam_vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct mcam_camera *cam = priv; diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 7487d72..4cc7f65d 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -38,6 +38,10 @@ MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1.1"); +static unsigned debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -67,7 +71,7 @@ MODULE_VERSION("0.1.1"); #define MEM2MEM_VFLIP (1 << 1) #define dprintk(dev, fmt, arg...) \ - v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) static void m2mtest_dev_release(struct device *dev) @@ -234,6 +238,10 @@ static int device_process(struct m2mtest_ctx *ctx, bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; + memcpy(&out_vb->v4l2_buf.timestamp, + &in_vb->v4l2_buf.timestamp, + sizeof(struct timeval)); + switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: p_out += bytesperline * height - bytes_left; @@ -844,6 +852,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &m2mtest_qops; src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -855,6 +864,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &m2mtest_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 4b9e0a2..f7440e5 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -377,6 +377,9 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + spin_lock_irqsave(&pcdev->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -763,6 +766,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -774,6 +778,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 96c4a17..477268a 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -648,9 +648,12 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) /* First save the configuration in ovelray structure */ ret = omapvid_init(vout, addr); - if (ret) + if (ret) { printk(KERN_ERR VOUT_NAME "failed to set overlay info\n"); + goto vout_isr_err; + } + /* Enable the pipeline and set the Go bit */ ret = omapvid_apply_changes(vout); if (ret) @@ -1660,13 +1663,16 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2; - omap_dispc_register_isr(omap_vout_isr, vout, mask); - /* First save the configuration in ovelray structure */ ret = omapvid_init(vout, addr); - if (ret) + if (ret) { v4l2_err(&vout->vid_dev->v4l2_dev, "failed to set overlay info\n"); + goto streamon_err1; + } + + omap_dispc_register_isr(omap_vout_isr, vout, mask); + /* Enable the pipeline and set the Go bit */ ret = omapvid_apply_changes(vout); if (ret) diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index a55793c..70438a0 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -934,12 +934,19 @@ static int s3c_camif_reqbufs(struct file *file, void *priv, vp->owner = NULL; ret = vb2_reqbufs(&vp->vb_queue, rb); - if (!ret) { - vp->reqbufs_count = rb->count; - if (vp->owner == NULL && rb->count > 0) - vp->owner = priv; + if (ret < 0) + return ret; + + if (rb->count && rb->count < CAMIF_REQ_BUFS_MIN) { + rb->count = 0; + vb2_reqbufs(&vp->vb_queue, rb); + ret = -ENOMEM; } + vp->reqbufs_count = rb->count; + if (vp->owner == NULL && rb->count > 0) + vp->owner = priv; + return ret; } @@ -1153,6 +1160,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct camif_buffer); q->drv_priv = vp; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index aaaf276..553d87e 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <media/v4l2-mem2mem.h> @@ -157,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &g2d_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -168,6 +170,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &g2d_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -634,6 +637,9 @@ static irqreturn_t g2d_isr(int irq, void *prv) BUG_ON(src == NULL); BUG_ON(dst == NULL); + dst->v4l2_buf.timecode = src->v4l2_buf.timecode; + dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); @@ -695,11 +701,14 @@ static struct v4l2_m2m_ops g2d_m2m_ops = { .unlock = g2d_unlock, }; +static const struct of_device_id exynos_g2d_match[]; + static int g2d_probe(struct platform_device *pdev) { struct g2d_dev *dev; struct video_device *vfd; struct resource *res; + const struct of_device_id *of_id; int ret = 0; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -794,7 +803,17 @@ static int g2d_probe(struct platform_device *pdev) } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - dev->variant = g2d_get_drv_data(pdev); + + if (!pdev->dev.of_node) { + dev->variant = g2d_get_drv_data(pdev); + } else { + of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); + if (!of_id) { + ret = -ENODEV; + goto unreg_video_dev; + } + dev->variant = (struct g2d_variant *)of_id->data; + } return 0; @@ -835,13 +854,25 @@ static int g2d_remove(struct platform_device *pdev) } static struct g2d_variant g2d_drvdata_v3x = { - .hw_rev = TYPE_G2D_3X, + .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ }; static struct g2d_variant g2d_drvdata_v4x = { .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ }; +static const struct of_device_id exynos_g2d_match[] = { + { + .compatible = "samsung,s5pv210-g2d", + .data = &g2d_drvdata_v3x, + }, { + .compatible = "samsung,exynos4212-g2d", + .data = &g2d_drvdata_v4x, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_g2d_match); + static struct platform_device_id g2d_driver_ids[] = { { .name = "s5p-g2d", @@ -861,6 +892,7 @@ static struct platform_driver g2d_pdrv = { .driver = { .name = G2D_NAME, .owner = THIS_MODULE, + .of_match_table = exynos_g2d_match, }, }; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3b02375..15d2396 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1229,6 +1229,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &s5p_jpeg_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1240,6 +1241,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &s5p_jpeg_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1287,6 +1289,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) payload_size = jpeg_compressed_size(jpeg->regs); } + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, state); if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(dst_buf, 0, payload_size); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 1cb6d57..01f9ae0 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -243,12 +243,10 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { - memcpy(&dst_buf->b->v4l2_buf.timecode, - &src_buf->b->v4l2_buf.timecode, - sizeof(struct v4l2_timecode)); - memcpy(&dst_buf->b->v4l2_buf.timestamp, - &src_buf->b->v4l2_buf.timestamp, - sizeof(struct timeval)); + dst_buf->b->v4l2_buf.timecode = + src_buf->b->v4l2_buf.timecode; + dst_buf->b->v4l2_buf.timestamp = + src_buf->b->v4l2_buf.timestamp; switch (frame_type) { case S5P_FIMV_DECODE_FRAME_I_FRAME: dst_buf->b->v4l2_buf.flags |= @@ -386,6 +384,8 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, } else { mfc_debug(2, "MFC needs next buffer\n"); ctx->consumed_stream = 0; + if (src_buf->flags & MFC_BUF_FLAG_EOS) + ctx->state = MFCINST_FINISHING; list_del(&src_buf->list); ctx->src_queue_cnt--; if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0) @@ -804,6 +804,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(capture)\n"); @@ -825,6 +826,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(output)\n"); @@ -1016,7 +1018,7 @@ static void *mfc_get_drv_data(struct platform_device *pdev); static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) { - unsigned int mem_info[2]; + unsigned int mem_info[2] = { }; dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, sizeof(struct device), GFP_KERNEL); @@ -1106,7 +1108,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) } if (pdev->dev.of_node) { - if (s5p_mfc_alloc_memdevs(dev) < 0) + ret = s5p_mfc_alloc_memdevs(dev); + if (ret < 0) goto err_res; } else { dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, 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 13877808..ad4f1df 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c @@ -16,7 +16,7 @@ #include "s5p_mfc_debug.h" /* This function is used to send a command to the MFC */ -int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, +static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, struct s5p_mfc_cmd_args *args) { int cur_cmd; @@ -41,7 +41,7 @@ int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, } /* Initialize the MFC */ -int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -52,7 +52,7 @@ int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) } /* Suspend the MFC hardware */ -int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -61,7 +61,7 @@ int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) } /* Wake up the MFC hardware */ -int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -71,7 +71,7 @@ int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) } -int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; @@ -124,7 +124,7 @@ int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) return ret; } -int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; 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 754bfbc..5708fc3 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -17,7 +17,7 @@ #include "s5p_mfc_intr.h" #include "s5p_mfc_opr.h" -int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, +static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, struct s5p_mfc_cmd_args *args) { mfc_debug(2, "Issue the command: %d\n", cmd); @@ -32,7 +32,7 @@ int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, return 0; } -int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; @@ -44,7 +44,7 @@ int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) &h2r_args); } -int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -53,7 +53,7 @@ int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) &h2r_args); } -int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -63,7 +63,7 @@ int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) } /* Open a new instance and get its number */ -int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; @@ -121,7 +121,7 @@ int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) } /* Close instance */ -int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 4582473..4af53bd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -22,6 +22,7 @@ #include <linux/videodev2.h> #include <linux/workqueue.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/videobuf2-core.h> #include "s5p_mfc_common.h" #include "s5p_mfc_debug.h" @@ -623,17 +624,27 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) /* Dequeue a buffer */ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { + const struct v4l2_event ev = { + .type = V4L2_EVENT_EOS + }; struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + int ret; if (ctx->state == MFCINST_ERROR) { mfc_err("Call on DQBUF after unrecoverable error\n"); return -EIO; } if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - return -EINVAL; + ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + if (ret == 0 && ctx->state == MFCINST_FINISHED && + list_empty(&ctx->vq_dst.done_list)) + v4l2_event_queue_fh(&ctx->fh, &ev); + } else { + ret = -EINVAL; + } + return ret; } /* Export DMA buffer */ @@ -809,6 +820,59 @@ 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) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + struct s5p_mfc_dev *dev = ctx->dev; + struct s5p_mfc_buf *buf; + unsigned long flags; + + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + if (cmd->flags != 0) + return -EINVAL; + + if (!ctx->vq_src.streaming) + return -EINVAL; + + spin_lock_irqsave(&dev->irqlock, flags); + if (list_empty(&ctx->src_queue)) { + mfc_err("EOS: empty src queue, entering finishing state"); + ctx->state = MFCINST_FINISHING; + 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); + } else { + mfc_err("EOS: marking last buffer of stream"); + buf = list_entry(ctx->src_queue.prev, + struct s5p_mfc_buf, list); + if (buf->flags & MFC_BUF_FLAG_USED) + ctx->state = MFCINST_FINISHING; + else + buf->flags |= MFC_BUF_FLAG_EOS; + spin_unlock_irqrestore(&dev->irqlock, flags); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_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, 2, NULL); + default: + return -EINVAL; + } +} + + /* v4l2_ioctl_ops */ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -830,6 +894,9 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_crop = vidioc_g_crop, + .vidioc_decoder_cmd = vidioc_decoder_cmd, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static int s5p_mfc_queue_setup(struct vb2_queue *vq, @@ -1147,3 +1214,4 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n", (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt); } + 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 f61dba8..0af05a2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -34,7 +34,7 @@ #define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT) /* Allocate temporary buffers for decoding */ -int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; @@ -55,13 +55,13 @@ int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) /* Release temporary buffers for decoding */ -void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc); } /* Allocate codec buffers */ -int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int enc_ref_y_size = 0; @@ -193,14 +193,14 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) } /* Release buffers allocated for codec */ -void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2); } /* Allocate memory for instance data buffer */ -int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; @@ -241,20 +241,20 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) } /* Release instance buffer */ -void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm); } -int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) { /* NOP */ return 0; } -void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) { /* NOP */ } @@ -273,7 +273,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, return readl(ctx->shm.virt + ofs); } -void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) { unsigned int guard_width, guard_height; @@ -315,7 +315,7 @@ void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) } } -void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx) { if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) { ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN); @@ -361,8 +361,9 @@ static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) } /* Set registers for decoding stream buffer */ -int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr, - unsigned int start_num_byte, unsigned int buf_size) +static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, + int buf_addr, unsigned int start_num_byte, + unsigned int buf_size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -374,7 +375,7 @@ int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr, } /* Set decoding frame buffer */ -int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) { unsigned int frame_size, i; unsigned int frame_size_ch, frame_size_mv; @@ -506,7 +507,7 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) } /* Set registers for encoding stream buffer */ -int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -516,7 +517,7 @@ int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, return 0; } -void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long y_addr, unsigned long c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -525,7 +526,7 @@ void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR); } -void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long *y_addr, unsigned long *c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -537,7 +538,7 @@ void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, } /* Set encoding ref & codec buffer */ -int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; size_t buf_addr1, buf_addr2; @@ -1041,7 +1042,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) } /* Initialize decoding */ -int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1077,7 +1078,7 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) } /* Decode a single frame */ -int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, enum s5p_mfc_decode_arg last_frame) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1106,7 +1107,7 @@ int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, return 0; } -int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1128,7 +1129,7 @@ int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) } /* Encode a single frame */ -int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; int cmd; @@ -1187,6 +1188,15 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) unsigned long flags; unsigned int index; + if (ctx->state == MFCINST_FINISHING) { + last_frame = MFC_DEC_LAST_FRAME; + s5p_mfc_set_dec_stream_buffer_v5(ctx, 0, 0, 0); + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_decode_one_frame_v5(ctx, last_frame); + return 0; + } + spin_lock_irqsave(&dev->irqlock, flags); /* Frames are being decoded */ if (list_empty(&ctx->src_queue)) { @@ -1353,7 +1363,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) } /* Try running an operation on hardware */ -void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_ctx *ctx; int new_ctx; @@ -1469,7 +1479,7 @@ void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) } -void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) +static void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) { struct s5p_mfc_buf *b; int i; @@ -1483,52 +1493,52 @@ void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) } } -void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev) { mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT); mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD); mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID); } -int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DISPLAY_Y_ADR) << MFC_OFFSET_SHIFT; } -int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DECODE_Y_ADR) << MFC_OFFSET_SHIFT; } -int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DISPLAY_STATUS); } -int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DECODE_STATUS); } -int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_DECODE_FRAME_TYPE) & S5P_FIMV_DECODE_FRAME_MASK; } -int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx) { return (s5p_mfc_read_info_v5(ctx, DISP_PIC_FRAME_TYPE) >> S5P_FIMV_SHARED_DISP_FRAME_TYPE_SHIFT) & S5P_FIMV_DECODE_FRAME_MASK; } -int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_CONSUMED_BYTES); } -int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) { int reason; reason = mfc_read(dev, S5P_FIMV_RISC2HOST_CMD) & @@ -1576,98 +1586,98 @@ int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) return reason; } -int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG2); } -int s5p_mfc_err_dec_v5(unsigned int err) +static int s5p_mfc_err_dec_v5(unsigned int err) { return (err & S5P_FIMV_ERR_DEC_MASK) >> S5P_FIMV_ERR_DEC_SHIFT; } -int s5p_mfc_err_dspl_v5(unsigned int err) +static int s5p_mfc_err_dspl_v5(unsigned int err) { return (err & S5P_FIMV_ERR_DSPL_MASK) >> S5P_FIMV_ERR_DSPL_SHIFT; } -int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_HRESOL); } -int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_VRESOL); } -int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_BUF_NUMBER); } -int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev) { /* NOP */ return -1; } -int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG1); } -int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_STRM_SIZE); } -int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_SLICE_TYPE); } -int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev) { return -1; } -int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT); } -int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, FRAME_PACK_SEI_AVAIL); } -int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev) { return -1; } -int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev) { return -1; } -unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, PIC_TIME_TOP); } -unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, PIC_TIME_BOT); } -unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, CROP_INFO_H); } -unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, CROP_INFO_V); } 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 98a9c09..7e76fce 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -49,7 +49,7 @@ #define OFFSETB(x) (((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET) /* Allocate temporary buffers for decoding */ -int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) { /* NOP */ @@ -57,19 +57,19 @@ int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) } /* Release temproary buffers for decoding */ -void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) { /* NOP */ } -int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) { /* NOP */ return -1; } /* Allocate codec buffers */ -int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; @@ -203,13 +203,13 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) } /* Release buffers allocated for codec */ -void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); } /* Allocate memory for instance data buffer */ -int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; @@ -258,13 +258,13 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) } /* Release instance buffer */ -void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); } /* Allocate context buffers for SYS_INIT */ -int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; int ret; @@ -287,7 +287,7 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) } /* Release context buffers for SYS_INIT */ -void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf); } @@ -306,7 +306,7 @@ static int calc_plane(int width, int height) (mbY * S5P_FIMV_NUM_PIXELS_IN_MB_ROW_V6); } -void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) { ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6); @@ -326,7 +326,7 @@ void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) } } -void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) { unsigned int mb_width, mb_height; @@ -339,8 +339,9 @@ void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) } /* Set registers for decoding stream buffer */ -int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, int buf_addr, - unsigned int start_num_byte, unsigned int strm_size) +static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, + int buf_addr, unsigned int start_num_byte, + unsigned int strm_size) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size; @@ -359,7 +360,7 @@ int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, int buf_addr, } /* Set decoding frame buffer */ -int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) { unsigned int frame_size, i; unsigned int frame_size_ch, frame_size_mv; @@ -440,7 +441,7 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) } /* Set registers for encoding stream buffer */ -int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -454,7 +455,7 @@ int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, return 0; } -void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long y_addr, unsigned long c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -466,7 +467,7 @@ void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, mfc_debug(2, "enc src c buf addr: 0x%08lx", c_addr); } -void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long *y_addr, unsigned long *c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -483,7 +484,7 @@ void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, } /* Set encoding ref & codec buffer */ -int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; size_t buf_addr1; @@ -1147,7 +1148,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) } /* Initialize decoding */ -int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int reg = 0; @@ -1215,7 +1216,7 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) } /* Decode a single frame */ -int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, enum s5p_mfc_decode_arg last_frame) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1244,7 +1245,7 @@ int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, return 0; } -int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1267,7 +1268,7 @@ int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) return 0; } -int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_enc_params *p = &ctx->enc_params; @@ -1283,7 +1284,7 @@ int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) } /* Encode a single frame */ -int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1362,8 +1363,16 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) unsigned long flags; int last_frame = 0; - spin_lock_irqsave(&dev->irqlock, flags); + if (ctx->state == MFCINST_FINISHING) { + last_frame = MFC_DEC_LAST_FRAME; + s5p_mfc_set_dec_stream_buffer_v6(ctx, 0, 0, 0); + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_decode_one_frame_v6(ctx, last_frame); + return 0; + } + spin_lock_irqsave(&dev->irqlock, flags); /* Frames are being decoded */ if (list_empty(&ctx->src_queue)) { mfc_debug(2, "No src buffers.\n"); @@ -1540,7 +1549,7 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx) } /* Try running an operation on hardware */ -void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_ctx *ctx; int new_ctx; @@ -1663,7 +1672,7 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) } -void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) +static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) { struct s5p_mfc_buf *b; int i; @@ -1677,13 +1686,13 @@ void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) } } -void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) { mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6); mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6); } -void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, +static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1693,7 +1702,8 @@ void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, s5p_mfc_clock_off(); } -unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) +static unsigned int +s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) { struct s5p_mfc_dev *dev = ctx->dev; int ret; @@ -1705,140 +1715,140 @@ unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) return ret; } -int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); } -int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); } -int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6); } -int s5p_mfc_get_decoded_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_decoded_status_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6); } -int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) & S5P_FIMV_DECODE_FRAME_MASK_V6; } -int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) { return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) & S5P_FIMV_DECODE_FRAME_MASK_V6; } -int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6); } -int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) & S5P_FIMV_RISC2HOST_CMD_MASK; } -int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6); } -int s5p_mfc_err_dec_v6(unsigned int err) +static int s5p_mfc_err_dec_v6(unsigned int err) { return (err & S5P_FIMV_ERR_DEC_MASK_V6) >> S5P_FIMV_ERR_DEC_SHIFT_V6; } -int s5p_mfc_err_dspl_v6(unsigned int err) +static int s5p_mfc_err_dspl_v6(unsigned int err) { return (err & S5P_FIMV_ERR_DSPL_MASK_V6) >> S5P_FIMV_ERR_DSPL_SHIFT_V6; } -int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); } -int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); } -int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6); } -int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6); } -int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6); } -int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6); } -int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6); } -int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6); } -int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6); } -int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) { return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); } -int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6); } -int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6); } -unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6); } -unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6); } -unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6); } -unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6); } diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8de1b3d..4e86626 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -31,6 +31,7 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <linux/v4l2-dv-timings.h> #include <media/s5p_hdmi.h> #include <media/v4l2-common.h> @@ -43,9 +44,6 @@ MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); -/* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 - struct hdmi_pulse { u32 beg; u32 end; @@ -91,8 +89,8 @@ struct hdmi_device { const struct hdmi_timings *cur_conf; /** flag indicating that timings are dirty */ int cur_conf_dirty; - /** current preset */ - u32 cur_preset; + /** current timings */ + struct v4l2_dv_timings cur_timings; /** other resources */ struct hdmi_resources res; }; @@ -252,7 +250,6 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) { struct device *dev = hdmi_dev->dev; const struct hdmi_timings *conf = hdmi_dev->cur_conf; - struct v4l2_dv_preset preset; int ret; dev_dbg(dev, "%s\n", __func__); @@ -267,11 +264,11 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); - /* configure presets */ - preset.preset = hdmi_dev->cur_preset; - ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset); + /* configure timings */ + ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_timings, + &hdmi_dev->cur_timings); if (ret) { - dev_err(dev, "failed to set preset (%u)\n", preset.preset); + dev_err(dev, "failed to set timings\n"); return ret; } @@ -475,33 +472,26 @@ static const struct hdmi_timings hdmi_timings_1080p50 = { .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; +/* default hdmi_timings index of the timings configured on probe */ +#define HDMI_DEFAULT_TIMINGS_IDX (0) + static const struct { - u32 preset; - const struct hdmi_timings *timings; + bool reduced_fps; + const struct v4l2_dv_timings dv_timings; + const struct hdmi_timings *hdmi_timings; } hdmi_timings[] = { - { V4L2_DV_480P59_94, &hdmi_timings_480p }, - { V4L2_DV_576P50, &hdmi_timings_576p50 }, - { V4L2_DV_720P50, &hdmi_timings_720p50 }, - { V4L2_DV_720P59_94, &hdmi_timings_720p60 }, - { V4L2_DV_720P60, &hdmi_timings_720p60 }, - { V4L2_DV_1080P24, &hdmi_timings_1080p24 }, - { V4L2_DV_1080P30, &hdmi_timings_1080p60 }, - { V4L2_DV_1080P50, &hdmi_timings_1080p50 }, - { V4L2_DV_1080I50, &hdmi_timings_1080i50 }, - { V4L2_DV_1080I60, &hdmi_timings_1080i60 }, - { V4L2_DV_1080P60, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_720X480P59_94, &hdmi_timings_480p }, + { false, V4L2_DV_BT_CEA_720X576P50, &hdmi_timings_576p50 }, + { false, V4L2_DV_BT_CEA_1280X720P50, &hdmi_timings_720p50 }, + { true, V4L2_DV_BT_CEA_1280X720P60, &hdmi_timings_720p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P24, &hdmi_timings_1080p24 }, + { false, V4L2_DV_BT_CEA_1920X1080P30, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P50, &hdmi_timings_1080p50 }, + { false, V4L2_DV_BT_CEA_1920X1080I50, &hdmi_timings_1080i50 }, + { false, V4L2_DV_BT_CEA_1920X1080I60, &hdmi_timings_1080i60 }, + { false, V4L2_DV_BT_CEA_1920X1080P60, &hdmi_timings_1080p60 }, }; -static const struct hdmi_timings *hdmi_preset2timings(u32 preset) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i) - if (hdmi_timings[i].preset == preset) - return hdmi_timings[i].timings; - return NULL; -} - static int hdmi_streamon(struct hdmi_device *hdev) { struct device *dev = hdev->dev; @@ -621,29 +611,33 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on) return IS_ERR_VALUE(ret) ? ret : 0; } -static int hdmi_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); struct device *dev = hdev->dev; - const struct hdmi_timings *conf; + int i; - conf = hdmi_preset2timings(preset->preset); - if (conf == NULL) { - dev_err(dev, "preset (%u) not supported\n", preset->preset); + for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) + if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings, + timings, 0)) + break; + if (i == ARRAY_SIZE(hdmi_timings)) { + dev_err(dev, "timings not supported\n"); return -EINVAL; } - hdev->cur_conf = conf; + hdev->cur_conf = hdmi_timings[i].hdmi_timings; hdev->cur_conf_dirty = 1; - hdev->cur_preset = preset->preset; + hdev->cur_timings = *timings; + if (!hdmi_timings[i].reduced_fps) + hdev->cur_timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; return 0; } -static int hdmi_g_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { - memset(preset, 0, sizeof(*preset)); - preset->preset = sd_to_hdmi_dev(sd)->cur_preset; + *timings = sd_to_hdmi_dev(sd)->cur_timings; return 0; } @@ -670,13 +664,33 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) +static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) { - if (preset->index >= ARRAY_SIZE(hdmi_timings)) + if (timings->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; - return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset, - preset); + timings->timings = hdmi_timings[timings->index].dv_timings; + if (!hdmi_timings[timings->index].reduced_fps) + timings->timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; + return 0; +} + +static int hdmi_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + struct hdmi_device *hdev = sd_to_hdmi_dev(sd); + + /* Let the phy fill in the pixelclock range */ + v4l2_subdev_call(hdev->phy_sd, video, dv_timings_cap, cap); + cap->type = V4L2_DV_BT_656_1120; + cap->bt.min_width = 720; + cap->bt.max_width = 1920; + cap->bt.min_height = 480; + cap->bt.max_height = 1080; + cap->bt.standards = V4L2_DV_BT_STD_CEA861; + cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | + V4L2_DV_BT_CAP_PROGRESSIVE; + return 0; } static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { @@ -684,9 +698,10 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { }; static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { - .s_dv_preset = hdmi_s_dv_preset, - .g_dv_preset = hdmi_g_dv_preset, - .enum_dv_presets = hdmi_enum_dv_presets, + .s_dv_timings = hdmi_s_dv_timings, + .g_dv_timings = hdmi_g_dv_timings, + .enum_dv_timings = hdmi_enum_dv_timings, + .dv_timings_cap = hdmi_dv_timings_cap, .g_mbus_fmt = hdmi_g_mbus_fmt, .s_stream = hdmi_s_stream, }; @@ -956,9 +971,11 @@ static int hdmi_probe(struct platform_device *pdev) sd->owner = THIS_MODULE; strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); - hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; - /* FIXME: missing fail preset is not supported */ - hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); + hdmi_dev->cur_timings = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].dv_timings; + /* FIXME: missing fail timings is not supported */ + hdmi_dev->cur_conf = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].hdmi_timings; hdmi_dev->cur_conf_dirty = 1; /* storing subdev for call that have only access to struct device */ diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index 80717ce..e19a0af 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -176,35 +176,9 @@ static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) return container_of(sd, struct hdmiphy_ctx, sd); } -static unsigned long hdmiphy_preset_to_pixclk(u32 preset) +static const u8 *hdmiphy_find_conf(unsigned long pixclk, + const struct hdmiphy_conf *conf) { - static const unsigned long pixclk[] = { - [V4L2_DV_480P59_94] = 27000000, - [V4L2_DV_576P50] = 27000000, - [V4L2_DV_720P59_94] = 74176000, - [V4L2_DV_720P50] = 74250000, - [V4L2_DV_720P60] = 74250000, - [V4L2_DV_1080P24] = 74250000, - [V4L2_DV_1080P30] = 74250000, - [V4L2_DV_1080I50] = 74250000, - [V4L2_DV_1080I60] = 74250000, - [V4L2_DV_1080P50] = 148500000, - [V4L2_DV_1080P60] = 148500000, - }; - if (preset < ARRAY_SIZE(pixclk)) - return pixclk[preset]; - else - return 0; -} - -static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf) -{ - unsigned long pixclk; - - pixclk = hdmiphy_preset_to_pixclk(preset); - if (!pixclk) - return NULL; - for (; conf->pixclk; ++conf) if (conf->pixclk == pixclk) return conf->data; @@ -217,8 +191,8 @@ static int hdmiphy_s_power(struct v4l2_subdev *sd, int on) return 0; } -static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { const u8 *data; u8 buffer[32]; @@ -226,9 +200,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, struct hdmiphy_ctx *ctx = sd_to_ctx(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; + unsigned long pixclk = timings->bt.pixelclock; - dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset); - data = hdmiphy_find_conf(preset->preset, ctx->conf_tab); + dev_info(dev, "s_dv_timings\n"); + if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000) + pixclk = 74176000; + data = hdmiphy_find_conf(pixclk, ctx->conf_tab); if (!data) { dev_err(dev, "format not supported\n"); return -EINVAL; @@ -245,6 +222,17 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, return 0; } +static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + cap->type = V4L2_DV_BT_656_1120; + /* The phy only determines the pixelclock, leave the other values + * at 0 to signify that we have no information for them. */ + cap->bt.min_pixelclock = 27000000; + cap->bt.max_pixelclock = 148500000; + return 0; +} + static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -270,7 +258,8 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { }; static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { - .s_dv_preset = hdmiphy_s_dv_preset, + .s_dv_timings = hdmiphy_s_dv_timings, + .dv_timings_cap = hdmiphy_dv_timings_cap, .s_stream = hdmiphy_s_stream, }; diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 82142a2..ef0efdf 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -501,8 +501,8 @@ fail: return -ERANGE; } -static int mxr_enum_dv_presets(struct file *file, void *fh, - struct v4l2_dv_enum_preset *preset) +static int mxr_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -510,14 +510,14 @@ static int mxr_enum_dv_presets(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_timings, timings); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; } -static int mxr_s_dv_preset(struct file *file, void *fh, - struct v4l2_dv_preset *preset) +static int mxr_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -526,7 +526,7 @@ static int mxr_s_dv_preset(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - /* preset change cannot be done while there is an entity + /* timings change cannot be done while there is an entity * dependant on output configuration */ if (mdev->n_output > 0) { @@ -534,7 +534,7 @@ static int mxr_s_dv_preset(struct file *file, void *fh, return -EBUSY; } - ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_timings, timings); mutex_unlock(&mdev->mutex); @@ -544,8 +544,8 @@ static int mxr_s_dv_preset(struct file *file, void *fh, return ret ? -EINVAL : 0; } -static int mxr_g_dv_preset(struct file *file, void *fh, - struct v4l2_dv_preset *preset) +static int mxr_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -553,13 +553,28 @@ static int mxr_g_dv_preset(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_timings, timings); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; } -static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int mxr_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + struct mxr_layer *layer = video_drvdata(file); + struct mxr_device *mdev = layer->mdev; + int ret; + + /* lock protects from changing sd_out */ + mutex_lock(&mdev->mutex); + ret = v4l2_subdev_call(to_outsd(mdev), video, dv_timings_cap, cap); + mutex_unlock(&mdev->mutex); + + return ret ? -EINVAL : 0; +} + +static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -576,7 +591,7 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) return -EBUSY; } - ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm); + ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, norm); mutex_unlock(&mdev->mutex); @@ -616,8 +631,8 @@ static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) /* try to obtain supported tv norms */ v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); a->capabilities = 0; - if (sd->ops->video && sd->ops->video->s_dv_preset) - a->capabilities |= V4L2_OUT_CAP_PRESETS; + if (sd->ops->video && sd->ops->video->s_dv_timings) + a->capabilities |= V4L2_OUT_CAP_DV_TIMINGS; if (sd->ops->video && sd->ops->video->s_std_output) a->capabilities |= V4L2_OUT_CAP_STD; a->type = V4L2_OUTPUT_TYPE_ANALOG; @@ -738,10 +753,11 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = { /* Streaming control */ .vidioc_streamon = mxr_streamon, .vidioc_streamoff = mxr_streamoff, - /* Preset functions */ - .vidioc_enum_dv_presets = mxr_enum_dv_presets, - .vidioc_s_dv_preset = mxr_s_dv_preset, - .vidioc_g_dv_preset = mxr_g_dv_preset, + /* DV Timings functions */ + .vidioc_enum_dv_timings = mxr_enum_dv_timings, + .vidioc_s_dv_timings = mxr_s_dv_timings, + .vidioc_g_dv_timings = mxr_g_dv_timings, + .vidioc_dv_timings_cap = mxr_dv_timings_cap, /* analog TV standard functions */ .vidioc_s_std = mxr_s_std, .vidioc_g_std = mxr_g_std, diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index d90d228..39b77d2 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -23,9 +23,6 @@ #include <linux/regulator/machine.h> #include <linux/slab.h> -#include <mach/gpio.h> -#include <plat/gpio-cfg.h> - #include <media/sii9234.h> #include <media/v4l2-subdev.h> diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index cb54c69..0b32cc3 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation */ +#include <linux/err.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/module.h> @@ -1164,9 +1165,9 @@ static int sh_veu_probe(struct platform_device *pdev) veu->is_2h = resource_size(reg_res) == 0x22c; - veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); - if (!veu->base) - return -ENOMEM; + veu->base = devm_ioremap_resource(&pdev->dev, reg_res); + if (IS_ERR(veu->base)) + return PTR_ERR(veu->base); ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, 0, "veu", veu); @@ -1248,18 +1249,7 @@ static struct platform_driver __refdata sh_veu_pdrv = { }, }; -static int __init sh_veu_init(void) -{ - return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); -} - -static void __exit sh_veu_exit(void) -{ - platform_driver_unregister(&sh_veu_pdrv); -} - -module_init(sh_veu_init); -module_exit(sh_veu_exit); +module_platform_driver_probe(sh_veu_pdrv, sh_veu_probe); MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 66c8da1..7d02350 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -881,29 +881,29 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) } } -static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct sh_vou_device *vou_dev = video_drvdata(file); int ret; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); - if (*std_id & ~vou_dev->vdev->tvnorms) + if (std_id & ~vou_dev->vdev->tvnorms) return -EINVAL; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_std_output, *std_id); + s_std_output, std_id); /* Shall we continue, if the subdev doesn't support .s_std_output()? */ if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - if (*std_id & V4L2_STD_525_60) + if (std_id & V4L2_STD_525_60) sh_vou_reg_ab_set(vou_dev, VOUCR, sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); else sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); - vou_dev->std = *std_id; + vou_dev->std = std_id; return 0; } @@ -1266,7 +1266,7 @@ static int sh_vou_g_register(struct file *file, void *fh, } static int sh_vou_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct sh_vou_device *vou_dev = video_drvdata(file); @@ -1485,18 +1485,7 @@ static struct platform_driver __refdata sh_vou = { }, }; -static int __init sh_vou_init(void) -{ - return platform_driver_probe(&sh_vou, sh_vou_probe); -} - -static void __exit sh_vou_exit(void) -{ - platform_driver_unregister(&sh_vou); -} - -module_init(sh_vou_init); -module_exit(sh_vou_exit); +module_platform_driver_probe(sh_vou, sh_vou_probe); MODULE_DESCRIPTION("SuperH VOU driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 82dbf99..1abbb36 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -514,6 +514,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, q->buf_struct_size = sizeof(struct frame_buffer); q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1020,7 +1021,7 @@ static int atmel_isi_probe(struct platform_device *pdev) isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); irq = platform_get_irq(pdev, 0); - if (irq < 0) { + if (IS_ERR_VALUE(irq)) { ret = irq; goto err_req_irq; } @@ -1073,7 +1074,6 @@ err_clk_prepare_pclk: } static struct platform_driver atmel_isi_driver = { - .probe = atmel_isi_probe, .remove = atmel_isi_remove, .driver = { .name = "atmel_isi", @@ -1081,17 +1081,7 @@ static struct platform_driver atmel_isi_driver = { }, }; -static int __init atmel_isi_init_module(void) -{ - return platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe); -} - -static void __exit atmel_isi_exit(void) -{ - platform_driver_unregister(&atmel_isi_driver); -} -module_init(atmel_isi_init_module); -module_exit(atmel_isi_exit); +module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe); MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 25b2a28..a3fd8d6 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -776,7 +776,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev) /* request irq */ err = claim_fiq(&fh); if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed\n"); goto exit_free_dma; } @@ -853,24 +853,13 @@ static int __exit mx1_camera_remove(struct platform_device *pdev) } static struct platform_driver mx1_camera_driver = { - .driver = { + .driver = { .name = DRIVER_NAME, }, .remove = __exit_p(mx1_camera_remove), }; -static int __init mx1_camera_init(void) -{ - return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe); -} - -static void __exit mx1_camera_exit(void) -{ - return platform_driver_unregister(&mx1_camera_driver); -} - -module_init(mx1_camera_init); -module_exit(mx1_camera_exit); +module_platform_driver_probe(mx1_camera_driver, mx1_camera_probe); MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver"); MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>"); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index ffba7d9..5bbeb43 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -797,6 +797,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx2_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx2_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1453,7 +1454,7 @@ static int mx27_camera_emma_init(struct platform_device *pdev) err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0, MX2_CAM_DRV_NAME, pcdev); if (err) { - dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n"); + dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n"); goto out; } @@ -1614,15 +1615,14 @@ static int mx2_camera_remove(struct platform_device *pdev) } static struct platform_driver mx2_camera_driver = { - .driver = { + .driver = { .name = MX2_CAM_DRV_NAME, }, .id_table = mx2_camera_devtype, .remove = mx2_camera_remove, - .probe = mx2_camera_probe, }; -module_platform_driver(mx2_camera_driver); +module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe); MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index f5cbb92..5da3377 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -455,6 +455,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx3_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx3_camera_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1275,7 +1276,7 @@ static int mx3_camera_remove(struct platform_device *pdev) } static struct platform_driver mx3_camera_driver = { - .driver = { + .driver = { .name = MX3_CAM_DRV_NAME, }, .probe = mx3_camera_probe, diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 2547bf8..9689a6e 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1546,7 +1546,7 @@ static struct soc_camera_host_ops omap1_host_ops = { .poll = omap1_cam_poll, }; -static int __init omap1_cam_probe(struct platform_device *pdev) +static int omap1_cam_probe(struct platform_device *pdev) { struct omap1_cam_dev *pcdev; struct resource *res; @@ -1677,7 +1677,7 @@ exit: return err; } -static int __exit omap1_cam_remove(struct platform_device *pdev) +static int omap1_cam_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct omap1_cam_dev *pcdev = container_of(soc_host, @@ -1709,7 +1709,7 @@ static struct platform_driver omap1_cam_driver = { .name = DRIVER_NAME, }, .probe = omap1_cam_probe, - .remove = __exit_p(omap1_cam_remove), + .remove = omap1_cam_remove, }; module_platform_driver(omap1_cam_driver); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 395e2e0..d665242 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -1710,9 +1711,10 @@ static int pxa_camera_probe(struct platform_device *pdev) /* * Request the regions. */ - base = devm_request_and_ioremap(&pdev->dev, res); - if (!base) - return -ENOMEM; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + pcdev->irq = irq; pcdev->base = base; @@ -1794,13 +1796,13 @@ static int pxa_camera_remove(struct platform_device *pdev) return 0; } -static struct dev_pm_ops pxa_camera_pm = { +static const struct dev_pm_ops pxa_camera_pm = { .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, }; static struct platform_driver pxa_camera_driver = { - .driver = { + .driver = { .name = PXA_CAM_DRV_NAME, .pm = &pxa_camera_pm, }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index bb08a46..143d29fe 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -20,6 +20,7 @@ #include <linux/completion.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -2026,6 +2027,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, q->ops = &sh_mobile_ceu_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -2110,11 +2112,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = devm_request_and_ioremap(&pdev->dev, res); - if (!base) { - dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - return -ENXIO; - } + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); pcdev->irq = irq; pcdev->base = base; @@ -2288,7 +2288,7 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { }; static struct platform_driver sh_mobile_ceu_driver = { - .driver = { + .driver = { .name = "sh_mobile_ceu", .pm = &sh_mobile_ceu_dev_pm_ops, }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 42c559e..09cb4fc 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -9,6 +9,7 @@ */ #include <linux/delay.h> +#include <linux/err.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/platform_device.h> @@ -324,11 +325,9 @@ static int sh_csi2_probe(struct platform_device *pdev) priv->irq = irq; - priv->base = devm_request_and_ioremap(&pdev->dev, res); - if (!priv->base) { - dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - return -ENXIO; - } + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); priv->pdev = pdev; platform_set_drvdata(pdev, priv); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 8ec9805..eea832c 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -256,12 +256,12 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, s_std, *a); + return v4l2_subdev_call(sd, core, s_std, a); } static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -508,36 +508,49 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_device *icd; struct soc_camera_host *ici; int ret; - if (!to_soc_camera_control(icd)) - /* No device driver attached */ - return -ENODEV; - /* * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes + * scan_add_host() completes. Also protect against a race with + * soc_camera_host_unregister(). */ if (mutex_lock_interruptible(&list_lock)) return -ERESTARTSYS; + + if (!vdev || !video_is_registered(vdev)) { + mutex_unlock(&list_lock); + return -ENODEV; + } + + icd = dev_get_drvdata(vdev->parent); ici = to_soc_camera_host(icd->parent); + + ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; mutex_unlock(&list_lock); - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - if (!try_module_get(ici->ops->owner)) { + if (ret < 0) { dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - ret = -EINVAL; - goto emodule; + return ret; } + if (!to_soc_camera_control(icd)) { + /* No device driver attached */ + ret = -ENODEV; + goto econtrol; + } + + if (mutex_lock_interruptible(&ici->host_lock)) { + ret = -ERESTARTSYS; + goto elockhost; + } icd->use_count++; /* Now we really have to activate the camera */ if (icd->use_count == 1) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); /* Restore parameters before the last close() per V4L2 API */ struct v4l2_format f = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, @@ -609,9 +622,10 @@ epower: ici->ops->remove(icd); eiciadd: icd->use_count--; - module_put(ici->ops->owner); -emodule: mutex_unlock(&ici->host_lock); +elockhost: +econtrol: + module_put(ici->ops->owner); return ret; } @@ -1042,7 +1056,7 @@ static int soc_camera_g_register(struct file *file, void *fh, } static int soc_camera_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index ce3b1d6..1b7a88c 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -188,7 +188,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev) } static struct platform_driver soc_camera_platform_driver = { - .driver = { + .driver = { .name = "soc_camera_platform", .owner = THIS_MODULE, }, diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index 89dce09..dc02dec 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -73,7 +73,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .name = "RGB555X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, + .order = SOC_MBUS_ORDER_BE, .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { @@ -93,10 +93,46 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .name = "RGB565X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, + .order = SOC_MBUS_ORDER_BE, .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { + .code = V4L2_MBUS_FMT_RGB666_1X18, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB666/32bpp", + .bits_per_sample = 18, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_1X24, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 24, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_2X12_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_BE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_2X12_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { .code = V4L2_MBUS_FMT_SBGGR8_1X8, .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR8, @@ -358,6 +394,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, *numerator = 1; *denominator = 1; return 0; + case SOC_MBUS_PACKING_EXTEND32: + *numerator = 1; + *denominator = 1; + return 0; case SOC_MBUS_PACKING_2X8_PADHI: case SOC_MBUS_PACKING_2X8_PADLO: *numerator = 2; @@ -392,6 +432,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) return width * 3 / 2; case SOC_MBUS_PACKING_VARIABLE: return 0; + case SOC_MBUS_PACKING_EXTEND32: + return width * 4; } return -EINVAL; } diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index c3a2a44..a2f7bdd 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -78,7 +78,7 @@ struct timblogiw_buffer { struct timblogiw_fh *fh; }; -const struct timblogiw_tvnorm timblogiw_tvnorms[] = { +static const struct timblogiw_tvnorm timblogiw_tvnorms[] = { { .std = V4L2_STD_PAL, .width = 720, @@ -336,7 +336,7 @@ static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) { struct video_device *vdev = video_devdata(file); struct timblogiw *lw = video_get_drvdata(vdev); @@ -348,10 +348,10 @@ static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) mutex_lock(&lw->lock); if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std); + err = v4l2_subdev_call(lw->sd_enc, core, s_std, std); if (!err) - fh->cur_norm = timblogiw_get_norm(*std); + fh->cur_norm = timblogiw_get_norm(std); mutex_unlock(&lw->lock); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index b051c4a..a794cd6 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -847,7 +847,7 @@ static int viacam_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id *std) +static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id std) { return 0; } diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index eb5d6f9..c6af974 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3042,7 +3042,7 @@ static int vino_g_std(struct file *file, void *__fh, } static int vino_s_std(struct file *file, void *__fh, - v4l2_std_id *std) + v4l2_std_id std) { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; @@ -3056,7 +3056,7 @@ static int vino_s_std(struct file *file, void *__fh, } /* check if the standard is valid for the current input */ - if ((*std) & vino_inputs[vcs->input].std) { + if (std & vino_inputs[vcs->input].std) { dprintk("standard accepted\n"); /* change the video norm for SAA7191 @@ -3065,13 +3065,13 @@ static int vino_s_std(struct file *file, void *__fh, if (vcs->input == VINO_INPUT_D1) goto out; - if ((*std) & V4L2_STD_PAL) { + if (std & V4L2_STD_PAL) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, &flags); - } else if ((*std) & V4L2_STD_NTSC) { + } else if (std & V4L2_STD_NTSC) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC, &flags); - } else if ((*std) & V4L2_STD_SECAM) { + } else if (std & V4L2_STD_SECAM) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM, &flags); } else { diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 8a33a71..85bc314 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1093,6 +1093,15 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) 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; @@ -1429,6 +1438,7 @@ static int __init vivi_create_instance(int inst) q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 24e64a0..c0beee2 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -18,6 +18,22 @@ config RADIO_SI470X source "drivers/media/radio/si470x/Kconfig" +config RADIO_SI476X + tristate "Silicon Laboratories Si476x I2C FM Radio" + depends on I2C && VIDEO_V4L2 + depends on MFD_SI476X_CORE + select SND_SOC_SI476X + ---help--- + Choose Y here if you have this FM radio chip. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux 2 API. Information on + this API and pointers to "v4l2" programs may be found at + <file:Documentation/video4linux/API.html>. + + To compile this driver as a module, choose M here: the + module will be called radio-si476x. + config USB_MR800 tristate "AverMedia MR 800 USB FM radio support" depends on USB && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 303eaeb..0dcdb32 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o +obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index 63b112b..142c2ee 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -208,13 +208,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { return v->index ? -EINVAL : 0; } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct dsbr100_device *radio = video_drvdata(file); diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 643d80a..545c04c 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -90,6 +90,26 @@ static u16 sigtable[2][4] = { { 2185, 4369, 13107, 65535 }, }; +static const struct v4l2_frequency_band bands[] = { + { + .index = 0, + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 8320, /* 520 kHz */ + .rangehigh = 26400, /* 1650 kHz */ + .modulation = V4L2_BAND_MODULATION_AM, + }, { + .index = 1, + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | + V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1400000, /* 87.5 MHz */ + .rangehigh = 1728000, /* 108.0 MHz */ + .modulation = V4L2_BAND_MODULATION_FM, + }, +}; + static int cadet_getstereo(struct cadet *dev) { @@ -196,6 +216,8 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq) int i, j, test; int curvol; + freq = clamp(freq, bands[dev->is_fm_band].rangelow, + bands[dev->is_fm_band].rangehigh); dev->curfreq = freq; /* * Formulate a fifo command @@ -337,26 +359,6 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static const struct v4l2_frequency_band bands[] = { - { - .index = 0, - .type = V4L2_TUNER_RADIO, - .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 8320, /* 520 kHz */ - .rangehigh = 26400, /* 1650 kHz */ - .modulation = V4L2_BAND_MODULATION_AM, - }, { - .index = 1, - .type = V4L2_TUNER_RADIO, - .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | - V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW | - V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 1400000, /* 87.5 MHz */ - .rangehigh = 1728000, /* 108.0 MHz */ - .modulation = V4L2_BAND_MODULATION_FM, - }, -}; - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { @@ -388,7 +390,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { return v->index ? -EINVAL : 0; } @@ -418,7 +420,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct cadet *dev = video_drvdata(file); @@ -426,8 +428,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, return -EINVAL; dev->is_fm_band = f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2; - clamp(f->frequency, bands[dev->is_fm_band].rangelow, - bands[dev->is_fm_band].rangehigh); cadet_setfreq(dev, f->frequency); return 0; } diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 84b7b9f..6ff3508 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -51,8 +51,8 @@ static int radio_isa_querycap(struct file *file, void *priv, strlcpy(v->card, isa->drv->card, sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS; + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -87,7 +87,7 @@ static int radio_isa_g_tuner(struct file *file, void *priv, } static int radio_isa_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct radio_isa_card *isa = video_drvdata(file); const struct radio_isa_ops *ops = isa->drv->ops; @@ -102,17 +102,18 @@ static int radio_isa_s_tuner(struct file *file, void *priv, } static int radio_isa_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct radio_isa_card *isa = video_drvdata(file); + u32 freq = f->frequency; int res; if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH); - res = isa->drv->ops->s_frequency(isa, f->frequency); + freq = clamp(freq, FREQ_LOW, FREQ_HIGH); + res = isa->drv->ops->s_frequency(isa, freq); if (res == 0) - isa->freq = f->frequency; + isa->freq = freq; return res; } diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 296941a..4c9ae76 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -215,15 +215,15 @@ static int vidioc_s_modulator(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct keene_device *radio = video_drvdata(file); + unsigned freq = f->frequency; if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - f->frequency = clamp(f->frequency, - FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL); - return keene_cmd_main(radio, f->frequency, true); + freq = clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL); + return keene_cmd_main(radio, freq, true); } static int vidioc_g_frequency(struct file *file, void *priv, diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c index 348dafc..a85b064 100644 --- a/drivers/media/radio/radio-ma901.c +++ b/drivers/media/radio/radio-ma901.c @@ -239,7 +239,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct ma901radio_device *radio = video_drvdata(file); @@ -257,7 +257,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, /* vidioc_s_frequency - set tuner radio frequency */ static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct ma901radio_device *radio = video_drvdata(file); diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 3d0ff44..a7e93d7 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -103,16 +103,18 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct pcm20 *dev = video_drvdata(file); if (v->index) return -EINVAL; if (v->audmode > V4L2_TUNER_MODE_STEREO) - v->audmode = V4L2_TUNER_MODE_STEREO; + dev->audmode = V4L2_TUNER_MODE_STEREO; + else + dev->audmode = v->audmode; snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, - v->audmode == V4L2_TUNER_MODE_MONO, -1); + dev->audmode == V4L2_TUNER_MODE_MONO, -1); return 0; } @@ -131,14 +133,14 @@ static int vidioc_g_frequency(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct pcm20 *dev = video_drvdata(file); if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - dev->freq = clamp(f->frequency, 87 * 16000U, 108 * 16000U); + dev->freq = clamp_t(u32, f->frequency, 87 * 16000U, 108 * 16000U); pcm20_setfreq(dev, dev->freq); return 0; } diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 9c5a267..a360227 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -203,10 +203,14 @@ static int amradio_set_mute(struct amradio_device *radio, bool mute) /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ static int amradio_set_freq(struct amradio_device *radio, int freq) { - unsigned short freq_send = 0x10 + (freq >> 3) / 25; + unsigned short freq_send; u8 buf[3]; int retval; + /* we need to be sure that frequency isn't out of range */ + freq = clamp_t(unsigned, freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL); + freq_send = 0x10 + (freq >> 3) / 25; + /* frequency is calculated from freq_send and placed in first 2 bytes */ buf[0] = (freq_send >> 8) & 0xff; buf[1] = freq_send & 0xff; @@ -305,7 +309,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct amradio_device *radio = video_drvdata(file); @@ -323,14 +327,13 @@ static int vidioc_s_tuner(struct file *file, void *priv, /* vidioc_s_frequency - set tuner radio frequency */ static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct amradio_device *radio = video_drvdata(file); if (f->tuner != 0) return -EINVAL; - return amradio_set_freq(radio, clamp_t(unsigned, f->frequency, - FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); + return amradio_set_freq(radio, f->frequency); } /* vidioc_g_frequency - get tuner radio frequency */ @@ -389,6 +392,7 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *priv, continue; amradio_send_cmd(radio, AMRADIO_GET_FREQ, 0, NULL, 0, true); if (radio->buffer[1] || radio->buffer[2]) { + /* To check: sometimes radio->curfreq is set to out of range value */ radio->curfreq = (radio->buffer[1] << 8) | radio->buffer[2]; radio->curfreq = (radio->curfreq - 0x10) * 200; amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index b1f844c..09cfbc3 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -8,6 +8,8 @@ * * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Fully tested with actual hardware and the v4l2-compliance tool. */ #include <linux/module.h> /* Modules */ @@ -81,8 +83,7 @@ static int rtrack2_s_frequency(struct radio_isa_card *isa, u32 freq) zero(isa); outb_p(0xc8, isa->io); - if (!v4l2_ctrl_g_ctrl(isa->mute)) - outb_p(0, isa->io); + outb_p(v4l2_ctrl_g_ctrl(isa->mute), isa->io); return 0; } diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 637a555..adfcc61 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -145,13 +145,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { return v->index ? -EINVAL : 0; } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct fmi *fmi = video_drvdata(file); diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index 1507c9d..ba4cfc9 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -31,6 +31,9 @@ #include <media/v4l2-device.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/radio-si4713.h> /* module parameters */ @@ -39,54 +42,30 @@ module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Minor number for radio device (-1 ==> auto assign)"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>"); MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter"); MODULE_VERSION("0.0.1"); +MODULE_ALIAS("platform:radio-si4713"); /* Driver state struct */ struct radio_si4713_device { struct v4l2_device v4l2_dev; - struct video_device *radio_dev; + struct video_device radio_dev; + struct mutex lock; }; /* radio_si4713_fops - file operations interface */ static const struct v4l2_file_operations radio_si4713_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, /* Note: locking is done at the subdev level in the i2c driver. */ .unlocked_ioctl = video_ioctl2, }; /* Video4Linux Interface */ -static int radio_si4713_fill_audout(struct v4l2_audioout *vao) -{ - /* TODO: check presence of audio output */ - strlcpy(vao->name, "FM Modulator Audio Out", 32); - - return 0; -} - -static int radio_si4713_enumaudout(struct file *file, void *priv, - struct v4l2_audioout *vao) -{ - return radio_si4713_fill_audout(vao); -} - -static int radio_si4713_g_audout(struct file *file, void *priv, - struct v4l2_audioout *vao) -{ - int rval = radio_si4713_fill_audout(vao); - - vao->index = 0; - - return rval; -} - -static int radio_si4713_s_audout(struct file *file, void *priv, - const struct v4l2_audioout *vao) -{ - return vao->index ? -EINVAL : 0; -} /* radio_si4713_querycap - query device capabilities */ static int radio_si4713_querycap(struct file *file, void *priv, @@ -94,67 +73,15 @@ static int radio_si4713_querycap(struct file *file, void *priv, { strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver)); strlcpy(capability->card, "Silicon Labs Si4713 Modulator", - sizeof(capability->card)); - capability->capabilities = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; + sizeof(capability->card)); + strlcpy(capability->bus_info, "platform:radio-si4713", + sizeof(capability->bus_info)); + capability->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; + capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -/* radio_si4713_queryctrl - enumerate control items */ -static int radio_si4713_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - /* Must be sorted from low to high control ID! */ - static const u32 user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_AUDIO_MUTE, - 0 - }; - - /* Must be sorted from low to high control ID! */ - static const u32 fmtx_ctrls[] = { - V4L2_CID_FM_TX_CLASS, - V4L2_CID_RDS_TX_DEVIATION, - V4L2_CID_RDS_TX_PI, - V4L2_CID_RDS_TX_PTY, - V4L2_CID_RDS_TX_PS_NAME, - V4L2_CID_RDS_TX_RADIO_TEXT, - V4L2_CID_AUDIO_LIMITER_ENABLED, - V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, - V4L2_CID_AUDIO_LIMITER_DEVIATION, - V4L2_CID_AUDIO_COMPRESSION_ENABLED, - V4L2_CID_AUDIO_COMPRESSION_GAIN, - V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, - V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, - V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, - V4L2_CID_PILOT_TONE_ENABLED, - V4L2_CID_PILOT_TONE_DEVIATION, - V4L2_CID_PILOT_TONE_FREQUENCY, - V4L2_CID_TUNE_PREEMPHASIS, - V4L2_CID_TUNE_POWER_LEVEL, - V4L2_CID_TUNE_ANTENNA_CAPACITOR, - 0 - }; - static const u32 *ctrl_classes[] = { - user_ctrls, - fmtx_ctrls, - NULL - }; - struct radio_si4713_device *rsdev; - - rsdev = video_get_drvdata(video_devdata(file)); - - qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); - if (qc->id == 0) - return -EINVAL; - - if (qc->id == V4L2_CID_USER_CLASS || qc->id == V4L2_CID_FM_TX_CLASS) - return v4l2_ctrl_query_fill(qc, 0, 0, 0, 0); - - return v4l2_device_call_until_err(&rsdev->v4l2_dev, 0, core, - queryctrl, qc); -} - /* * v4l2 ioctl call backs. * we are just a wrapper for v4l2_sub_devs. @@ -164,83 +91,50 @@ static inline struct v4l2_device *get_v4l2_dev(struct file *file) return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev; } -static int radio_si4713_g_ext_ctrls(struct file *file, void *p, - struct v4l2_ext_controls *vecs) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - g_ext_ctrls, vecs); -} - -static int radio_si4713_s_ext_ctrls(struct file *file, void *p, - struct v4l2_ext_controls *vecs) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - s_ext_ctrls, vecs); -} - -static int radio_si4713_g_ctrl(struct file *file, void *p, - struct v4l2_control *vc) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - g_ctrl, vc); -} - -static int radio_si4713_s_ctrl(struct file *file, void *p, - struct v4l2_control *vc) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - s_ctrl, vc); -} - static int radio_si4713_g_modulator(struct file *file, void *p, - struct v4l2_modulator *vm) + struct v4l2_modulator *vm) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - g_modulator, vm); + g_modulator, vm); } static int radio_si4713_s_modulator(struct file *file, void *p, - const struct v4l2_modulator *vm) + const struct v4l2_modulator *vm) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - s_modulator, vm); + s_modulator, vm); } static int radio_si4713_g_frequency(struct file *file, void *p, - struct v4l2_frequency *vf) + struct v4l2_frequency *vf) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - g_frequency, vf); + g_frequency, vf); } static int radio_si4713_s_frequency(struct file *file, void *p, - struct v4l2_frequency *vf) + const struct v4l2_frequency *vf) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - s_frequency, vf); + s_frequency, vf); } static long radio_si4713_default(struct file *file, void *p, - bool valid_prio, int cmd, void *arg) + bool valid_prio, unsigned int cmd, void *arg) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - ioctl, cmd, arg); + ioctl, cmd, arg); } static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = { - .vidioc_enumaudout = radio_si4713_enumaudout, - .vidioc_g_audout = radio_si4713_g_audout, - .vidioc_s_audout = radio_si4713_s_audout, .vidioc_querycap = radio_si4713_querycap, - .vidioc_queryctrl = radio_si4713_queryctrl, - .vidioc_g_ext_ctrls = radio_si4713_g_ext_ctrls, - .vidioc_s_ext_ctrls = radio_si4713_s_ext_ctrls, - .vidioc_g_ctrl = radio_si4713_g_ctrl, - .vidioc_s_ctrl = radio_si4713_s_ctrl, .vidioc_g_modulator = radio_si4713_g_modulator, .vidioc_s_modulator = radio_si4713_s_modulator, .vidioc_g_frequency = radio_si4713_g_frequency, .vidioc_s_frequency = radio_si4713_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_default = radio_si4713_default, }; @@ -248,7 +142,7 @@ static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = { static struct video_device radio_si4713_vdev_template = { .fops = &radio_si4713_fops, .name = "radio-si4713", - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &radio_si4713_ioctl_ops, .vfl_dir = VFL_DIR_TX, }; @@ -275,6 +169,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) rval = -ENOMEM; goto exit; } + mutex_init(&rsdev->lock); rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev); if (rval) { @@ -285,40 +180,35 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) adapter = i2c_get_adapter(pdata->i2c_bus); if (!adapter) { dev_err(&pdev->dev, "Cannot get i2c adapter %d\n", - pdata->i2c_bus); + pdata->i2c_bus); rval = -ENODEV; goto unregister_v4l2_dev; } sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, - pdata->subdev_board_info, NULL); + pdata->subdev_board_info, NULL); if (!sd) { dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); rval = -ENODEV; goto put_adapter; } - rsdev->radio_dev = video_device_alloc(); - if (!rsdev->radio_dev) { - dev_err(&pdev->dev, "Failed to alloc video device.\n"); - rval = -ENOMEM; - goto put_adapter; - } - - memcpy(rsdev->radio_dev, &radio_si4713_vdev_template, - sizeof(radio_si4713_vdev_template)); - video_set_drvdata(rsdev->radio_dev, rsdev); - if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { + rsdev->radio_dev = radio_si4713_vdev_template; + rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev; + rsdev->radio_dev.ctrl_handler = sd->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &rsdev->radio_dev.flags); + /* Serialize all access to the si4713 */ + rsdev->radio_dev.lock = &rsdev->lock; + video_set_drvdata(&rsdev->radio_dev, rsdev); + if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { dev_err(&pdev->dev, "Could not register video device.\n"); rval = -EIO; - goto free_vdev; + goto put_adapter; } dev_info(&pdev->dev, "New device successfully probed\n"); goto exit; -free_vdev: - video_device_release(rsdev->radio_dev); put_adapter: i2c_put_adapter(adapter); unregister_v4l2_dev: @@ -328,17 +218,16 @@ exit: } /* radio_si4713_pdriver_remove - remove the device */ -static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev) +static int radio_si4713_pdriver_remove(struct platform_device *pdev) { struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); - struct radio_si4713_device *rsdev = container_of(v4l2_dev, - struct radio_si4713_device, - v4l2_dev); struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, struct v4l2_subdev, list); struct i2c_client *client = v4l2_get_subdevdata(sd); + struct radio_si4713_device *rsdev; - video_unregister_device(rsdev->radio_dev); + rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev); + video_unregister_device(&rsdev->radio_dev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&rsdev->v4l2_dev); @@ -348,9 +237,10 @@ static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev) static struct platform_driver radio_si4713_pdriver = { .driver = { .name = "radio-si4713", + .owner = THIS_MODULE, }, .probe = radio_si4713_pdriver_probe, - .remove = __exit_p(radio_si4713_pdriver_remove), + .remove = radio_si4713_pdriver_remove, }; module_platform_driver(radio_si4713_pdriver); diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c new file mode 100644 index 0000000..9430c6a --- /dev/null +++ b/drivers/media/radio/radio-si476x.c @@ -0,0 +1,1599 @@ +/* + * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov <andrew.smirnov@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 of the License. + * + * 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/delay.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <linux/videodev2.h> +#include <linux/mutex.h> +#include <linux/debugfs.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-device.h> + +#include <media/si476x.h> +#include <linux/mfd/si476x-core.h> + +#define FM_FREQ_RANGE_LOW 64000000 +#define FM_FREQ_RANGE_HIGH 108000000 + +#define AM_FREQ_RANGE_LOW 520000 +#define AM_FREQ_RANGE_HIGH 30000000 + +#define PWRLINEFLTR (1 << 8) + +#define FREQ_MUL (10000000 / 625) + +#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0b10000000 & (status)) + +#define DRIVER_NAME "si476x-radio" +#define DRIVER_CARD "SI476x AM/FM Receiver" + +enum si476x_freq_bands { + SI476X_BAND_FM, + SI476X_BAND_AM, +}; + +static const struct v4l2_frequency_band si476x_bands[] = { + [SI476X_BAND_FM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_FM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 64 * FREQ_MUL, + .rangehigh = 108 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_FM, + }, + [SI476X_BAND_AM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_AM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 0.52 * FREQ_MUL, + .rangehigh = 30 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band) +{ + return freq >= si476x_bands[band].rangelow && + freq <= si476x_bands[band].rangehigh; +} + +static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high, + int band) +{ + return low >= si476x_bands[band].rangelow && + high <= si476x_bands[band].rangehigh; +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl); +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl); + +enum phase_diversity_modes_idx { + SI476X_IDX_PHDIV_DISABLED, + SI476X_IDX_PHDIV_PRIMARY_COMBINING, + SI476X_IDX_PHDIV_PRIMARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_COMBINING, +}; + +static const char * const phase_diversity_modes[] = { + [SI476X_IDX_PHDIV_DISABLED] = "Disabled", + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = "Primary with Secondary", + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = "Primary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = "Secondary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = "Secondary with Primary", +}; + +static inline enum phase_diversity_modes_idx +si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode) +{ + switch (mode) { + default: /* FALLTHROUGH */ + case SI476X_PHDIV_DISABLED: + return SI476X_IDX_PHDIV_DISABLED; + case SI476X_PHDIV_PRIMARY_COMBINING: + return SI476X_IDX_PHDIV_PRIMARY_COMBINING; + case SI476X_PHDIV_PRIMARY_ANTENNA: + return SI476X_IDX_PHDIV_PRIMARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_ANTENNA: + return SI476X_IDX_PHDIV_SECONDARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_COMBINING: + return SI476X_IDX_PHDIV_SECONDARY_COMBINING; + } +} + +static inline enum si476x_phase_diversity_mode +si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx) +{ + static const int idx_to_value[] = { + [SI476X_IDX_PHDIV_DISABLED] = SI476X_PHDIV_DISABLED, + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = SI476X_PHDIV_PRIMARY_COMBINING, + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = SI476X_PHDIV_PRIMARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = SI476X_PHDIV_SECONDARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = SI476X_PHDIV_SECONDARY_COMBINING, + }; + + return idx_to_value[idx]; +} + +static const struct v4l2_ctrl_ops si476x_ctrl_ops = { + .g_volatile_ctrl = si476x_radio_g_volatile_ctrl, + .s_ctrl = si476x_radio_s_ctrl, +}; + + +enum si476x_ctrl_idx { + SI476X_IDX_RSSI_THRESHOLD, + SI476X_IDX_SNR_THRESHOLD, + SI476X_IDX_MAX_TUNE_ERROR, + SI476X_IDX_HARMONICS_COUNT, + SI476X_IDX_DIVERSITY_MODE, + SI476X_IDX_INTERCHIP_LINK, +}; +static struct v4l2_ctrl_config si476x_ctrls[] = { + + /** + * SI476X during its station seeking(or tuning) process uses several + * parameters to detrmine if "the station" is valid: + * + * - Signal's SNR(in dBuV) must be lower than + * #V4L2_CID_SI476X_SNR_THRESHOLD + * - Signal's RSSI(in dBuV) must be greater than + * #V4L2_CID_SI476X_RSSI_THRESHOLD + * - Signal's frequency deviation(in units of 2ppm) must not be + * more than #V4L2_CID_SI476X_MAX_TUNE_ERROR + */ + [SI476X_IDX_RSSI_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_RSSI_THRESHOLD, + .name = "Valid RSSI Threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_SNR_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_SNR_THRESHOLD, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Valid SNR Threshold", + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_MAX_TUNE_ERROR] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_MAX_TUNE_ERROR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Max Tune Errors", + .min = 0, + .max = 126 * 2, + .step = 2, + }, + + /** + * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics + * built-in power-line noise supression filter is to reject + * during AM-mode operation. + */ + [SI476X_IDX_HARMONICS_COUNT] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_HARMONICS_COUNT, + .type = V4L2_CTRL_TYPE_INTEGER, + + .name = "Count of Harmonics to Reject", + .min = 0, + .max = 20, + .step = 1, + }, + + /** + * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which + * two tuners working in diversity mode are to work in. + * + * - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled + * - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is + * on, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is + * off, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is + * off, secondary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is + * on, secondary tuner's antenna is the main one. + */ + [SI476X_IDX_DIVERSITY_MODE] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_DIVERSITY_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Phase Diversity Mode", + .qmenu = phase_diversity_modes, + .min = 0, + .max = ARRAY_SIZE(phase_diversity_modes) - 1, + }, + + /** + * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in + * diversity mode indicator. Allows user to determine if two + * chips working in diversity mode have established a link + * between each other and if the system as a whole uses + * signals from both antennas to receive FM radio. + */ + [SI476X_IDX_INTERCHIP_LINK] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_INTERCHIP_LINK, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + .name = "Inter-Chip Link", + .min = 0, + .max = 1, + .step = 1, + }, +}; + +struct si476x_radio; + +/** + * struct si476x_radio_ops - vtable of tuner functions + * + * This table holds pointers to functions implementing particular + * operations depending on the mode in which the tuner chip was + * configured to start in. If the function is not supported + * corresponding element is set to #NULL. + * + * @tune_freq: Tune chip to a specific frequency + * @seek_start: Star station seeking + * @rsq_status: Get Recieved Signal Quality(RSQ) status + * @rds_blckcnt: Get recived RDS blocks count + * @phase_diversity: Change phase diversity mode of the tuner + * @phase_div_status: Get phase diversity mode status + * @acf_status: Get the status of Automatically Controlled + * Features(ACF) + * @agc_status: Get Automatic Gain Control(AGC) status + */ +struct si476x_radio_ops { + int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *); + int (*seek_start)(struct si476x_core *, bool, bool); + int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); + int (*rds_blckcnt)(struct si476x_core *, bool, + struct si476x_rds_blockcount_report *); + + int (*phase_diversity)(struct si476x_core *, + enum si476x_phase_diversity_mode); + int (*phase_div_status)(struct si476x_core *); + int (*acf_status)(struct si476x_core *, + struct si476x_acf_status_report *); + int (*agc_status)(struct si476x_core *, + struct si476x_agc_status_report *); +}; + +/** + * struct si476x_radio - radio device + * + * @core: Pointer to underlying core device + * @videodev: Pointer to video device created by V4L2 subsystem + * @ops: Vtable of functions. See struct si476x_radio_ops for details + * @kref: Reference counter + * @core_lock: An r/w semaphore to brebvent the deletion of underlying + * core structure is the radio device is being used + */ +struct si476x_radio { + struct v4l2_device v4l2dev; + struct video_device videodev; + struct v4l2_ctrl_handler ctrl_handler; + + struct si476x_core *core; + /* This field should not be accesses unless core lock is held */ + const struct si476x_radio_ops *ops; + + struct dentry *debugfs; + u32 audmode; +}; + +static inline struct si476x_radio * +v4l2_dev_to_radio(struct v4l2_device *d) +{ + return container_of(d, struct si476x_radio, v4l2dev); +} + +static inline struct si476x_radio * +v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d) +{ + return container_of(d, struct si476x_radio, ctrl_handler); +} + +/* + * si476x_vidioc_querycap - query device capabilities + */ +static int si476x_radio_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si476x_radio *radio = video_drvdata(file); + + strlcpy(capability->driver, radio->v4l2dev.name, + sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", radio->v4l2dev.name); + + capability->device_caps = V4L2_CAP_TUNER + | V4L2_CAP_RADIO + | V4L2_CAP_HW_FREQ_SEEK; + + si476x_core_lock(radio->core); + if (!si476x_core_is_a_secondary_tuner(radio->core)) + capability->device_caps |= V4L2_CAP_RDS_CAPTURE + | V4L2_CAP_READWRITE; + si476x_core_unlock(radio->core); + + capability->capabilities = capability->device_caps + | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int si476x_radio_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (band->tuner != 0) + return -EINVAL; + + switch (radio->core->chip_id) { + /* AM/FM tuners -- all bands are supported */ + case SI476X_CHIP_SI4761: + case SI476X_CHIP_SI4764: + if (band->index < ARRAY_SIZE(si476x_bands)) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + /* FM companion tuner chips -- only FM bands are + * supported */ + case SI476X_CHIP_SI4768: + if (band->index == SI476X_BAND_FM) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + + return err; +} + +static int si476x_radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + int err; + struct si476x_rsq_status_report report; + struct si476x_radio *radio = video_drvdata(file); + + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + if (tuner->index != 0) + return -EINVAL; + + tuner->type = V4L2_TUNER_RADIO; + tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies + * in multiples of + * 62.5 Hz */ + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_HWSEEK_BOUNDED + | V4L2_TUNER_CAP_HWSEEK_WRAP + | V4L2_TUNER_CAP_HWSEEK_PROG_LIM; + + si476x_core_lock(radio->core); + + if (si476x_core_is_a_secondary_tuner(radio->core)) { + strlcpy(tuner->name, "FM (secondary)", sizeof(tuner->name)); + tuner->rxsubchans = 0; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } else if (si476x_core_has_am(radio->core)) { + if (si476x_core_is_a_primary_tuner(radio->core)) + strlcpy(tuner->name, "AM/FM (primary)", + sizeof(tuner->name)); + else + strlcpy(tuner->name, "AM/FM", sizeof(tuner->name)); + + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO + | V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + + tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow; + } else { + strlcpy(tuner->name, "FM", sizeof(tuner->name)); + tuner->rxsubchans = V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } + + tuner->audmode = radio->audmode; + + tuner->afc = 1; + tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh; + + err = radio->ops->rsq_status(radio->core, + &args, &report); + if (err < 0) { + tuner->signal = 0; + } else { + /* + * tuner->signal value range: 0x0000 .. 0xFFFF, + * report.rssi: -128 .. 127 + */ + tuner->signal = (report.rssi + 128) * 257; + } + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *tuner) +{ + struct si476x_radio *radio = video_drvdata(file); + + if (tuner->index != 0) + return -EINVAL; + + if (tuner->audmode == V4L2_TUNER_MODE_MONO || + tuner->audmode == V4L2_TUNER_MODE_STEREO) + radio->audmode = tuner->audmode; + else + radio->audmode = V4L2_TUNER_MODE_STEREO; + + return 0; +} + +static int si476x_radio_init_vtable(struct si476x_radio *radio, + enum si476x_func func) +{ + static const struct si476x_radio_ops fm_ops = { + .tune_freq = si476x_core_cmd_fm_tune_freq, + .seek_start = si476x_core_cmd_fm_seek_start, + .rsq_status = si476x_core_cmd_fm_rsq_status, + .rds_blckcnt = si476x_core_cmd_fm_rds_blockcount, + .phase_diversity = si476x_core_cmd_fm_phase_diversity, + .phase_div_status = si476x_core_cmd_fm_phase_div_status, + .acf_status = si476x_core_cmd_fm_acf_status, + .agc_status = si476x_core_cmd_agc_status, + }; + + static const struct si476x_radio_ops am_ops = { + .tune_freq = si476x_core_cmd_am_tune_freq, + .seek_start = si476x_core_cmd_am_seek_start, + .rsq_status = si476x_core_cmd_am_rsq_status, + .rds_blckcnt = NULL, + .phase_diversity = NULL, + .phase_div_status = NULL, + .acf_status = si476x_core_cmd_am_acf_status, + .agc_status = NULL, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + radio->ops = &fm_ops; + return 0; + + case SI476X_FUNC_AM_RECEIVER: + radio->ops = &am_ops; + return 0; + default: + WARN(1, "Unexpected tuner function value\n"); + return -EINVAL; + } +} + +static int si476x_radio_pretune(struct si476x_radio *radio, + enum si476x_func func) +{ + int retval; + + struct si476x_tune_freq_args args = { + .zifsr = false, + .hd = false, + .injside = SI476X_INJSIDE_AUTO, + .tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE, + .smoothmetrics = SI476X_SM_INITIALIZE_AUDIO, + .antcap = 0, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 92 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + case SI476X_FUNC_AM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 0.6 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + default: + WARN(1, "Unexpected tuner function value\n"); + retval = -EINVAL; + } + + return retval; +} +static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + + /* regcache_mark_dirty(radio->core->regmap); */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE, + SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + SI476X_PROP_AUDIO_PWR_LINE_FILTER); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_INT_CTL_ENABLE, + SI476X_PROP_INT_CTL_ENABLE); + if (err < 0) + return err; + + /* + * Is there any point in restoring SNR and the like + * when switching between AM/FM? + */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + SI476X_PROP_VALID_MAX_TUNE_ERROR); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + SI476X_PROP_VALID_RSSI_THRESHOLD); + if (err < 0) + return err; + + if (func == SI476X_FUNC_FM_RECEIVER) { + if (si476x_core_has_diversity(radio->core)) { + err = si476x_core_cmd_fm_phase_diversity(radio->core, + radio->core->diversity_mode); + if (err < 0) + return err; + } + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_PROP_FM_RDS_CONFIG); + if (err < 0) + return err; + } + + return si476x_radio_init_vtable(radio, func); + +} + +static int si476x_radio_change_func(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + bool soft; + /* + * Since power/up down is a very time consuming operation, + * try to avoid doing it if the requested mode matches the one + * the tuner is in + */ + if (func == radio->core->power_up_parameters.func) + return 0; + + soft = true; + err = si476x_core_stop(radio->core, soft); + if (err < 0) { + /* + * OK, if the chip does not want to play nice let's + * try to reset it in more brutal way + */ + soft = false; + err = si476x_core_stop(radio->core, soft); + if (err < 0) + return err; + } + /* + Set the desired radio tuner function + */ + radio->core->power_up_parameters.func = func; + + err = si476x_core_start(radio->core, soft); + if (err < 0) + return err; + + /* + * No need to do the rest of manipulations for the bootlader + * mode + */ + if (func != SI476X_FUNC_FM_RECEIVER && + func != SI476X_FUNC_AM_RECEIVER) + return err; + + return si476x_radio_do_post_powerup_init(radio, func); +} + +static int si476x_radio_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (radio->ops->rsq_status) { + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = true, + .cancel = false, + .stcack = false, + }; + + err = radio->ops->rsq_status(radio->core, &args, &report); + if (!err) + f->frequency = si476x_to_v4l2(radio->core, + report.readfreq); + } else { + err = -EINVAL; + } + + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + int err; + u32 freq = f->frequency; + struct si476x_tune_freq_args args; + struct si476x_radio *radio = video_drvdata(file); + + const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh + + si476x_bands[SI476X_BAND_FM].rangelow) / 2; + const int band = (freq > midrange) ? + SI476X_BAND_FM : SI476X_BAND_AM; + const enum si476x_func func = (band == SI476X_BAND_AM) ? + SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER; + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + freq = clamp(freq, + si476x_bands[band].rangelow, + si476x_bands[band].rangehigh); + + if (si476x_radio_freq_is_inside_of_the_band(freq, + SI476X_BAND_AM) && + (!si476x_core_has_am(radio->core) || + si476x_core_is_a_secondary_tuner(radio->core))) { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + args.zifsr = false; + args.hd = false; + args.injside = SI476X_INJSIDE_AUTO; + args.freq = v4l2_to_si476x(radio->core, freq); + args.tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE; + args.smoothmetrics = SI476X_SM_INITIALIZE_AUDIO; + args.antcap = 0; + + err = radio->ops->tune_freq(radio->core, &args); + +unlock: + si476x_core_unlock(radio->core); + return err; +} + +static int si476x_radio_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *seek) +{ + int err; + enum si476x_func func; + u32 rangelow, rangehigh; + struct si476x_radio *radio = video_drvdata(file); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (seek->tuner != 0 || + seek->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (!seek->rangelow) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + &rangelow); + if (!err) + rangelow = si476x_to_v4l2(radio->core, rangelow); + else + goto unlock; + } + if (!seek->rangehigh) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + &rangehigh); + if (!err) + rangehigh = si476x_to_v4l2(radio->core, rangehigh); + else + goto unlock; + } + + if (rangelow > rangehigh) { + err = -EINVAL; + goto unlock; + } + + if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_FM)) { + func = SI476X_FUNC_FM_RECEIVER; + + } else if (si476x_core_has_am(radio->core) && + si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_AM)) { + func = SI476X_FUNC_AM_RECEIVER; + } else { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + if (seek->rangehigh) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + v4l2_to_si476x(radio->core, + seek->rangehigh)); + if (err) + goto unlock; + } + if (seek->rangelow) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + v4l2_to_si476x(radio->core, + seek->rangelow)); + if (err) + goto unlock; + } + if (seek->spacing) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_FREQUENCY_SPACING, + v4l2_to_si476x(radio->core, + seek->spacing)); + if (err) + goto unlock; + } + + err = radio->ops->seek_start(radio->core, + seek->seek_upward, + seek->wrap_around); +unlock: + si476x_core_unlock(radio->core); + + + + return err; +} + +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_INTERCHIP_LINK: + if (si476x_core_has_diversity(radio->core)) { + if (radio->ops->phase_diversity) { + retval = radio->ops->phase_div_status(radio->core); + if (retval < 0) + break; + + ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval); + retval = 0; + break; + } else { + retval = -ENOTTY; + break; + } + } + retval = -EINVAL; + break; + default: + retval = -EINVAL; + break; + } + si476x_core_unlock(radio->core); + return retval; + +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + enum si476x_phase_diversity_mode mode; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_HARMONICS_COUNT: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_HARMONICS_MASK, + ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (ctrl->val) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_ENABLE_MASK, + 0); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_50HZ); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_60HZ); + break; + default: + retval = -EINVAL; + break; + } + break; + case V4L2_CID_SI476X_RSSI_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_RSSI_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_SNR_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_MAX_TUNE_ERROR: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + ctrl->val); + break; + case V4L2_CID_RDS_RECEPTION: + /* + * It looks like RDS related properties are + * inaccesable when tuner is in AM mode, so cache the + * changes + */ + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, true); + + if (ctrl->val) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT, + radio->core->rds_fifo_depth); + if (retval < 0) + break; + + if (radio->core->client->irq) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_RDSRECV); + if (retval < 0) + break; + } + + /* Drain RDS FIFO before enabling RDS processing */ + retval = si476x_core_cmd_fm_rds_status(radio->core, + false, + true, + true, + NULL); + if (retval < 0) + break; + + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + SI476X_PROP_RDSEN); + } else { + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + !SI476X_PROP_RDSEN); + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, false); + break; + case V4L2_CID_TUNE_DEEMPHASIS: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + ctrl->val); + break; + + case V4L2_CID_SI476X_DIVERSITY_MODE: + mode = si476x_phase_diversity_idx_to_mode(ctrl->val); + + if (mode == radio->core->diversity_mode) { + retval = 0; + break; + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) { + /* + * Diversity cannot be configured while tuner + * is in AM mode so save the changes and carry on. + */ + radio->core->diversity_mode = mode; + retval = 0; + } else { + retval = radio->ops->phase_diversity(radio->core, mode); + if (!retval) + radio->core->diversity_mode = mode; + } + break; + + default: + retval = -EINVAL; + break; + } + + si476x_core_unlock(radio->core); + + return retval; +} + +static int si476x_radio_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + v4l2_chip_match_host(&chip->match)) + return 0; + return -EINVAL; +} + + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int si476x_radio_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + int err; + unsigned int value; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + reg->size = 2; + err = regmap_read(radio->core->regmap, + (unsigned int)reg->reg, &value); + reg->val = value; + si476x_core_unlock(radio->core); + + return err; +} +static int si476x_radio_s_register(struct file *file, void *fh, + const struct v4l2_dbg_register *reg) +{ + + int err; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + err = regmap_write(radio->core->regmap, + (unsigned int)reg->reg, + (unsigned int)reg->val); + si476x_core_unlock(radio->core); + + return err; +} +#endif + +static int si476x_radio_fops_open(struct file *file) +{ + struct si476x_radio *radio = video_drvdata(file); + int err; + + err = v4l2_fh_open(file); + if (err) + return err; + + if (v4l2_fh_is_singular_file(file)) { + si476x_core_lock(radio->core); + err = si476x_core_set_power_state(radio->core, + SI476X_POWER_UP_FULL); + if (err < 0) + goto done; + + err = si476x_radio_do_post_powerup_init(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + err = si476x_radio_pretune(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + si476x_core_unlock(radio->core); + /*Must be done after si476x_core_unlock to prevent a deadlock*/ + v4l2_ctrl_handler_setup(&radio->ctrl_handler); + } + + return err; + +power_down: + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); +done: + si476x_core_unlock(radio->core); + v4l2_fh_release(file); + + return err; +} + +static int si476x_radio_fops_release(struct file *file) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (v4l2_fh_is_singular_file(file) && + atomic_read(&radio->core->is_alive)) + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); + + err = v4l2_fh_release(file); + + return err; +} + +static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t rval; + size_t fifo_len; + unsigned int copied; + + struct si476x_radio *radio = video_drvdata(file); + + /* block if no new data available */ + if (kfifo_is_empty(&radio->core->rds_fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + rval = wait_event_interruptible(radio->core->rds_read_queue, + (!kfifo_is_empty(&radio->core->rds_fifo) || + !atomic_read(&radio->core->is_alive))); + if (rval < 0) + return -EINTR; + + if (!atomic_read(&radio->core->is_alive)) + return -ENODEV; + } + + fifo_len = kfifo_len(&radio->core->rds_fifo); + + if (kfifo_to_user(&radio->core->rds_fifo, buf, + min(fifo_len, count), + &copied) != 0) { + dev_warn(&radio->videodev.dev, + "Error during FIFO to userspace copy\n"); + rval = -EIO; + } else { + rval = (ssize_t)copied; + } + + return rval; +} + +static unsigned int si476x_radio_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct si476x_radio *radio = video_drvdata(file); + unsigned long req_events = poll_requested_events(pts); + unsigned int err = v4l2_ctrl_poll(file, pts); + + if (req_events & (POLLIN | POLLRDNORM)) { + if (atomic_read(&radio->core->is_alive)) + poll_wait(file, &radio->core->rds_read_queue, pts); + + if (!atomic_read(&radio->core->is_alive)) + err = POLLHUP; + + if (!kfifo_is_empty(&radio->core->rds_fifo)) + err = POLLIN | POLLRDNORM; + } + + return err; +} + +static const struct v4l2_file_operations si476x_fops = { + .owner = THIS_MODULE, + .read = si476x_radio_fops_read, + .poll = si476x_radio_fops_poll, + .unlocked_ioctl = video_ioctl2, + .open = si476x_radio_fops_open, + .release = si476x_radio_fops_release, +}; + + +static const struct v4l2_ioctl_ops si4761_ioctl_ops = { + .vidioc_querycap = si476x_radio_querycap, + .vidioc_g_tuner = si476x_radio_g_tuner, + .vidioc_s_tuner = si476x_radio_s_tuner, + + .vidioc_g_frequency = si476x_radio_g_frequency, + .vidioc_s_frequency = si476x_radio_s_frequency, + .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek, + .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_g_chip_ident = si476x_radio_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = si476x_radio_g_register, + .vidioc_s_register = si476x_radio_s_register, +#endif +}; + + +static const struct video_device si476x_viddev_template = { + .fops = &si476x_fops, + .name = DRIVER_NAME, + .release = video_device_release_empty, +}; + + + +static ssize_t si476x_radio_read_acf_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_acf_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->acf_status) + err = radio->ops->acf_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_acf_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_acf_blob, +}; + +static ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rds_blockcount_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rds_blckcnt(radio->core, true, + &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rds_blckcnt_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rds_blckcnt_blob, +}; + +static ssize_t si476x_radio_read_agc_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_agc_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->agc_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_agc_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_agc_blob, +}; + +static ssize_t si476x_radio_read_rsq_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_blob, +}; + +static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = true, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_primary_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_primary_blob, +}; + + +static int si476x_radio_init_debugfs(struct si476x_radio *radio) +{ + struct dentry *dentry; + int ret; + + dentry = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto exit; + } + radio->debugfs = dentry; + + dentry = debugfs_create_file("acf", S_IRUGO, + radio->debugfs, radio, &radio_acf_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rds_blckcnt", S_IRUGO, + radio->debugfs, radio, + &radio_rds_blckcnt_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("agc", S_IRUGO, + radio->debugfs, radio, &radio_agc_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq", S_IRUGO, + radio->debugfs, radio, &radio_rsq_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq_primary", S_IRUGO, + radio->debugfs, radio, + &radio_rsq_primary_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + return 0; +cleanup: + debugfs_remove_recursive(radio->debugfs); +exit: + return ret; +} + + +static int si476x_radio_add_new_custom(struct si476x_radio *radio, + enum si476x_ctrl_idx idx) +{ + int rval; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler, + &si476x_ctrls[idx], + NULL); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) + dev_err(radio->v4l2dev.dev, + "Could not initialize '%s' control %d\n", + si476x_ctrls[idx].name, rval); + + return rval; +} + +static int si476x_radio_probe(struct platform_device *pdev) +{ + int rval; + struct si476x_radio *radio; + struct v4l2_ctrl *ctrl; + + static atomic_t instance = ATOMIC_INIT(0); + + radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); + if (!radio) + return -ENOMEM; + + radio->core = i2c_mfd_cell_to_core(&pdev->dev); + + v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance); + + rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev); + if (rval) { + dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); + return rval; + } + + memcpy(&radio->videodev, &si476x_viddev_template, + sizeof(struct video_device)); + + radio->videodev.v4l2_dev = &radio->v4l2dev; + radio->videodev.ioctl_ops = &si4761_ioctl_ops; + + video_set_drvdata(&radio->videodev, radio); + platform_set_drvdata(pdev, radio); + + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); + + radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; + v4l2_ctrl_handler_init(&radio->ctrl_handler, + 1 + ARRAY_SIZE(si476x_ctrls)); + + if (si476x_core_has_am(radio->core)) { + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n", + rval); + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, + SI476X_IDX_HARMONICS_COUNT); + if (rval < 0) + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR); + if (rval < 0) + goto exit; + + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_TUNE_DEEMPHASIS, + V4L2_DEEMPHASIS_75_uS, 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n", + rval); + goto exit; + } + + ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops, + V4L2_CID_RDS_RECEPTION, + 0, 1, 1, 1); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n", + rval); + goto exit; + } + + if (si476x_core_has_diversity(radio->core)) { + si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def = + si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode); + si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE); + if (rval < 0) + goto exit; + + si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK); + if (rval < 0) + goto exit; + } + + /* register video device */ + rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1); + if (rval < 0) { + dev_err(&pdev->dev, "Could not register video device\n"); + goto exit; + } + + rval = si476x_radio_init_debugfs(radio); + if (rval < 0) { + dev_err(&pdev->dev, "Could not creat debugfs interface\n"); + goto exit; + } + + return 0; +exit: + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + return rval; +} + +static int si476x_radio_remove(struct platform_device *pdev) +{ + struct si476x_radio *radio = platform_get_drvdata(pdev); + + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + video_unregister_device(&radio->videodev); + v4l2_device_unregister(&radio->v4l2dev); + debugfs_remove_recursive(radio->debugfs); + + return 0; +} + +MODULE_ALIAS("platform:si476x-radio"); + +static struct platform_driver si476x_radio_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = si476x_radio_probe, + .remove = si476x_radio_remove, +}; +module_platform_driver(si476x_radio_driver); + +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); +MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 1978516..38d563d 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -339,7 +339,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct tea5764_device *radio = video_drvdata(file); @@ -351,7 +351,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct tea5764_device *radio = video_drvdata(file); diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c index 4b5190d..e245597 100644 --- a/drivers/media/radio/radio-tea5777.c +++ b/drivers/media/radio/radio-tea5777.c @@ -336,7 +336,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct radio_tea5777 *tea = video_drvdata(file); u32 orig_audmode = tea->audmode; @@ -344,10 +344,9 @@ static int vidioc_s_tuner(struct file *file, void *priv, if (v->index) return -EINVAL; - if (v->audmode > V4L2_TUNER_MODE_STEREO) - v->audmode = V4L2_TUNER_MODE_STEREO; - tea->audmode = v->audmode; + if (tea->audmode > V4L2_TUNER_MODE_STEREO) + tea->audmode = V4L2_TUNER_MODE_STEREO; if (tea->audmode != orig_audmode && tea->band == BAND_FM) return radio_tea5777_set_freq(tea); @@ -368,7 +367,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct radio_tea5777 *tea = video_drvdata(file); diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index b87effe..bb7b143 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -56,7 +56,7 @@ static int timbradio_vidioc_g_tuner(struct file *file, void *priv, } static int timbradio_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) + const struct v4l2_tuner *v) { struct timbradio *tr = video_drvdata(file); return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v); @@ -91,7 +91,7 @@ static int timbradio_vidioc_s_audio(struct file *file, void *priv, } static int timbradio_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct timbradio *tr = video_drvdata(file); return v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 02151e0..97c2c18 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1559,7 +1559,7 @@ out: } static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) + const struct v4l2_tuner *tuner) { struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); struct wl1273_core *core = radio->core; @@ -1640,7 +1640,7 @@ static int wl1273_fm_vidioc_g_frequency(struct file *file, void *priv, } static int wl1273_fm_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); struct wl1273_core *core = radio->core; diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 1898938..5c57e5b 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -636,7 +636,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, * si470x_vidioc_s_tuner - set tuner attributes */ static int si470x_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) + const struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); @@ -678,7 +678,7 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, * si470x_vidioc_s_frequency - set tuner or modulator radio frequency */ static int si470x_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); int retval; diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c index bd61b3b..fe16088 100644 --- a/drivers/media/radio/si4713-i2c.c +++ b/drivers/media/radio/si4713-i2c.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/mutex.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -53,8 +52,6 @@ static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = { #define DEFAULT_RDS_PI 0x00 #define DEFAULT_RDS_PTY 0x00 -#define DEFAULT_RDS_PS_NAME "" -#define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME #define DEFAULT_RDS_DEVIATION 0x00C8 #define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 #define DEFAULT_LIMITER_RTIME 0x1392 @@ -108,7 +105,6 @@ static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = { (status & SI4713_ERR)) /* mute definition */ #define set_mute(p) ((p & 1) | ((p & 1) << 1)); -#define get_mute(p) (p & 0x01) #ifdef DEBUG #define DBG_BUFFER(device, message, buffer, size) \ @@ -190,21 +186,6 @@ static int usecs_to_dev(unsigned long usecs, unsigned long const array[], return rval; } -static unsigned long dev_to_usecs(int value, unsigned long const array[], - int size) -{ - int i; - int rval = -EINVAL; - - for (i = 0; i < size / 2; i++) - if (array[i * 2] == value) { - rval = array[(i * 2) + 1]; - break; - } - - return rval; -} - /* si4713_handler: IRQ handler, just complete work */ static irqreturn_t si4713_handler(int irq, void *dev) { @@ -458,15 +439,13 @@ static int si4713_checkrev(struct si4713_device *sdev) int rval; u8 resp[SI4713_GETREV_NRESP]; - mutex_lock(&sdev->mutex); - rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (rval < 0) - goto unlock; + return rval; if (resp[1] == SI4713_PRODUCT_NUMBER) { v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", @@ -475,9 +454,6 @@ static int si4713_checkrev(struct si4713_device *sdev) v4l2_err(&sdev->sd, "Invalid product number\n"); rval = -EINVAL; } - -unlock: - mutex_unlock(&sdev->mutex); return rval; } @@ -778,17 +754,9 @@ static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, static int si4713_set_power_state(struct si4713_device *sdev, u8 value) { - int rval; - - mutex_lock(&sdev->mutex); - if (value) - rval = si4713_powerup(sdev); - else - rval = si4713_powerdown(sdev); - - mutex_unlock(&sdev->mutex); - return rval; + return si4713_powerup(sdev); + return si4713_powerdown(sdev); } static int si4713_set_mute(struct si4713_device *sdev, u16 mute) @@ -797,17 +765,10 @@ static int si4713_set_mute(struct si4713_device *sdev, u16 mute) mute = set_mute(mute); - mutex_lock(&sdev->mutex); - if (sdev->power_state) rval = si4713_write_property(sdev, SI4713_TX_LINE_INPUT_MUTE, mute); - if (rval >= 0) - sdev->mute = get_mute(mute); - - mutex_unlock(&sdev->mutex); - return rval; } @@ -820,15 +781,13 @@ static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) if (!strlen(ps_name)) memset(ps_name, 0, MAX_RDS_PS_NAME + 1); - mutex_lock(&sdev->mutex); - if (sdev->power_state) { /* Write the new ps name and clear the padding */ for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) { rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)), ps_name + i); if (rval < 0) - goto unlock; + return rval; } /* Setup the size to be sent */ @@ -841,19 +800,15 @@ static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) SI4713_TX_RDS_PS_MESSAGE_COUNT, rds_ps_nblocks(len)); if (rval < 0) - goto unlock; + return rval; rval = si4713_write_property(sdev, SI4713_TX_RDS_PS_REPEAT_COUNT, DEFAULT_RDS_PS_REPEAT_COUNT * 2); if (rval < 0) - goto unlock; + return rval; } - strncpy(sdev->rds_info.ps_name, ps_name, MAX_RDS_PS_NAME); - -unlock: - mutex_unlock(&sdev->mutex); return rval; } @@ -864,27 +819,24 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) u8 b_index = 0, cr_inserted = 0; s8 left; - mutex_lock(&sdev->mutex); - if (!sdev->power_state) - goto copy; + return rval; rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left); if (rval < 0) - goto unlock; + return rval; if (!strlen(rt)) - goto copy; + return rval; do { /* RDS spec says that if the last block isn't used, * then apply a carriage return */ - if (t_index < (RDS_RADIOTEXT_INDEX_MAX * - RDS_RADIOTEXT_BLK_SIZE)) { + if (t_index < (RDS_RADIOTEXT_INDEX_MAX * RDS_RADIOTEXT_BLK_SIZE)) { for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) { - if (!rt[t_index + i] || rt[t_index + i] == - RDS_CARRIAGE_RETURN) { + if (!rt[t_index + i] || + rt[t_index + i] == RDS_CARRIAGE_RETURN) { rt[t_index + i] = RDS_CARRIAGE_RETURN; cr_inserted = 1; break; @@ -898,7 +850,7 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) compose_u16(rt[t_index + 2], rt[t_index + 3]), &left); if (rval < 0) - goto unlock; + return rval; t_index += RDS_RADIOTEXT_BLK_SIZE; @@ -906,16 +858,38 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) break; } while (left > 0); -copy: - strncpy(sdev->rds_info.radio_text, rt, MAX_RDS_RADIO_TEXT); + return rval; +} -unlock: - mutex_unlock(&sdev->mutex); +/* + * si4713_update_tune_status - update properties from tx_tune_status + * command. Must be called with sdev->mutex held. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_update_tune_status(struct si4713_device *sdev) +{ + int rval; + u16 f = 0; + u8 p = 0, a = 0, n = 0; + + rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); + + if (rval < 0) + goto exit; + +/* TODO: check that power_level and antenna_capacitor really are not + changed by the hardware. If they are, then these controls should become + volatiles. + sdev->power_level = p; + sdev->antenna_capacitor = a;*/ + sdev->tune_rnl = n; + +exit: return rval; } static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, - u32 **shadow, s32 *bit, s32 *mask, u16 *property, int *mul, + s32 *bit, s32 *mask, u16 *property, int *mul, unsigned long **table, int *size) { s32 rval = 0; @@ -925,157 +899,71 @@ static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, case V4L2_CID_RDS_TX_PI: *property = SI4713_TX_RDS_PI; *mul = 1; - *shadow = &sdev->rds_info.pi; break; case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: *property = SI4713_TX_ACOMP_THRESHOLD; *mul = 1; - *shadow = &sdev->acomp_info.threshold; break; case V4L2_CID_AUDIO_COMPRESSION_GAIN: *property = SI4713_TX_ACOMP_GAIN; *mul = 1; - *shadow = &sdev->acomp_info.gain; break; case V4L2_CID_PILOT_TONE_FREQUENCY: *property = SI4713_TX_PILOT_FREQUENCY; *mul = 1; - *shadow = &sdev->pilot_info.frequency; break; case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: *property = SI4713_TX_ACOMP_ATTACK_TIME; *mul = ATTACK_TIME_UNIT; - *shadow = &sdev->acomp_info.attack_time; break; case V4L2_CID_PILOT_TONE_DEVIATION: *property = SI4713_TX_PILOT_DEVIATION; *mul = 10; - *shadow = &sdev->pilot_info.deviation; break; case V4L2_CID_AUDIO_LIMITER_DEVIATION: *property = SI4713_TX_AUDIO_DEVIATION; *mul = 10; - *shadow = &sdev->limiter_info.deviation; break; case V4L2_CID_RDS_TX_DEVIATION: *property = SI4713_TX_RDS_DEVIATION; *mul = 1; - *shadow = &sdev->rds_info.deviation; break; case V4L2_CID_RDS_TX_PTY: *property = SI4713_TX_RDS_PS_MISC; *bit = 5; *mask = 0x1F << 5; - *shadow = &sdev->rds_info.pty; break; case V4L2_CID_AUDIO_LIMITER_ENABLED: *property = SI4713_TX_ACOMP_ENABLE; *bit = 1; *mask = 1 << 1; - *shadow = &sdev->limiter_info.enabled; break; case V4L2_CID_AUDIO_COMPRESSION_ENABLED: *property = SI4713_TX_ACOMP_ENABLE; *bit = 0; *mask = 1 << 0; - *shadow = &sdev->acomp_info.enabled; break; case V4L2_CID_PILOT_TONE_ENABLED: *property = SI4713_TX_COMPONENT_ENABLE; *bit = 0; *mask = 1 << 0; - *shadow = &sdev->pilot_info.enabled; break; case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: *property = SI4713_TX_LIMITER_RELEASE_TIME; *table = limiter_times; *size = ARRAY_SIZE(limiter_times); - *shadow = &sdev->limiter_info.release_time; break; case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: *property = SI4713_TX_ACOMP_RELEASE_TIME; *table = acomp_rtimes; *size = ARRAY_SIZE(acomp_rtimes); - *shadow = &sdev->acomp_info.release_time; break; case V4L2_CID_TUNE_PREEMPHASIS: *property = SI4713_TX_PREEMPHASIS; *table = preemphasis_values; *size = ARRAY_SIZE(preemphasis_values); - *shadow = &sdev->preemphasis; - break; - - default: - rval = -EINVAL; - } - - return rval; -} - -static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); - -/* write string property */ -static int si4713_write_econtrol_string(struct si4713_device *sdev, - struct v4l2_ext_control *control) -{ - struct v4l2_queryctrl vqc; - int len; - s32 rval = 0; - - vqc.id = control->id; - rval = si4713_queryctrl(&sdev->sd, &vqc); - if (rval < 0) - goto exit; - - switch (control->id) { - case V4L2_CID_RDS_TX_PS_NAME: { - char ps_name[MAX_RDS_PS_NAME + 1]; - - len = control->size - 1; - if (len < 0 || len > MAX_RDS_PS_NAME) { - rval = -ERANGE; - goto exit; - } - rval = copy_from_user(ps_name, control->string, len); - if (rval) { - rval = -EFAULT; - goto exit; - } - ps_name[len] = '\0'; - - if (strlen(ps_name) % vqc.step) { - rval = -ERANGE; - goto exit; - } - - rval = si4713_set_rds_ps_name(sdev, ps_name); - } - break; - - case V4L2_CID_RDS_TX_RADIO_TEXT: { - char radio_text[MAX_RDS_RADIO_TEXT + 1]; - - len = control->size - 1; - if (len < 0 || len > MAX_RDS_RADIO_TEXT) { - rval = -ERANGE; - goto exit; - } - rval = copy_from_user(radio_text, control->string, len); - if (rval) { - rval = -EFAULT; - goto exit; - } - radio_text[len] = '\0'; - - if (strlen(radio_text) % vqc.step) { - rval = -ERANGE; - goto exit; - } - - rval = si4713_set_rds_radio_text(sdev, radio_text); - } break; default: @@ -1083,136 +971,10 @@ static int si4713_write_econtrol_string(struct si4713_device *sdev, break; } -exit: - return rval; -} - -static int validate_range(struct v4l2_subdev *sd, - struct v4l2_ext_control *control) -{ - struct v4l2_queryctrl vqc; - int rval; - - vqc.id = control->id; - rval = si4713_queryctrl(sd, &vqc); - if (rval < 0) - goto exit; - - if (control->value < vqc.minimum || control->value > vqc.maximum) - rval = -ERANGE; - -exit: - return rval; -} - -/* properties which use tx_tune_power*/ -static int si4713_write_econtrol_tune(struct si4713_device *sdev, - struct v4l2_ext_control *control) -{ - s32 rval = 0; - u8 power, antcap; - - rval = validate_range(&sdev->sd, control); - if (rval < 0) - goto exit; - - mutex_lock(&sdev->mutex); - - switch (control->id) { - case V4L2_CID_TUNE_POWER_LEVEL: - power = control->value; - antcap = sdev->antenna_capacitor; - break; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - power = sdev->power_level; - antcap = control->value; - break; - default: - rval = -EINVAL; - goto unlock; - } - - if (sdev->power_state) - rval = si4713_tx_tune_power(sdev, power, antcap); - - if (rval == 0) { - sdev->power_level = power; - sdev->antenna_capacitor = antcap; - } - -unlock: - mutex_unlock(&sdev->mutex); -exit: - return rval; -} - -static int si4713_write_econtrol_integers(struct si4713_device *sdev, - struct v4l2_ext_control *control) -{ - s32 rval; - u32 *shadow = NULL, val = 0; - s32 bit = 0, mask = 0; - u16 property = 0; - int mul = 0; - unsigned long *table = NULL; - int size = 0; - - rval = validate_range(&sdev->sd, control); - if (rval < 0) - goto exit; - - rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, - &mask, &property, &mul, &table, &size); - if (rval < 0) - goto exit; - - val = control->value; - if (mul) { - val = control->value / mul; - } else if (table) { - rval = usecs_to_dev(control->value, table, size); - if (rval < 0) - goto exit; - val = rval; - rval = 0; - } - - mutex_lock(&sdev->mutex); - - if (sdev->power_state) { - if (mask) { - rval = si4713_read_property(sdev, property, &val); - if (rval < 0) - goto unlock; - val = set_bits(val, control->value, bit, mask); - } - - rval = si4713_write_property(sdev, property, val); - if (rval < 0) - goto unlock; - if (mask) - val = control->value; - } - - if (mul) { - *shadow = val * mul; - } else if (table) { - rval = dev_to_usecs(val, table, size); - if (rval < 0) - goto unlock; - *shadow = rval; - rval = 0; - } else { - *shadow = val; - } - -unlock: - mutex_unlock(&sdev->mutex); -exit: return rval; } -static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f); +static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f); static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *); /* * si4713_setup - Sets the device up with current configuration. @@ -1220,111 +982,25 @@ static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulato */ static int si4713_setup(struct si4713_device *sdev) { - struct v4l2_ext_control ctrl; struct v4l2_frequency f; struct v4l2_modulator vm; - struct si4713_device *tmp; - int rval = 0; - - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - /* Get a local copy to avoid race */ - mutex_lock(&sdev->mutex); - memcpy(tmp, sdev, sizeof(*sdev)); - mutex_unlock(&sdev->mutex); - - ctrl.id = V4L2_CID_RDS_TX_PI; - ctrl.value = tmp->rds_info.pi; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_COMPRESSION_THRESHOLD; - ctrl.value = tmp->acomp_info.threshold; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_COMPRESSION_GAIN; - ctrl.value = tmp->acomp_info.gain; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_PILOT_TONE_FREQUENCY; - ctrl.value = tmp->pilot_info.frequency; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME; - ctrl.value = tmp->acomp_info.attack_time; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_PILOT_TONE_DEVIATION; - ctrl.value = tmp->pilot_info.deviation; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_LIMITER_DEVIATION; - ctrl.value = tmp->limiter_info.deviation; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_RDS_TX_DEVIATION; - ctrl.value = tmp->rds_info.deviation; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_RDS_TX_PTY; - ctrl.value = tmp->rds_info.pty; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_LIMITER_ENABLED; - ctrl.value = tmp->limiter_info.enabled; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ENABLED; - ctrl.value = tmp->acomp_info.enabled; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_PILOT_TONE_ENABLED; - ctrl.value = tmp->pilot_info.enabled; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_LIMITER_RELEASE_TIME; - ctrl.value = tmp->limiter_info.release_time; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME; - ctrl.value = tmp->acomp_info.release_time; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_TUNE_PREEMPHASIS; - ctrl.value = tmp->preemphasis; - rval |= si4713_write_econtrol_integers(sdev, &ctrl); - - ctrl.id = V4L2_CID_RDS_TX_PS_NAME; - rval |= si4713_set_rds_ps_name(sdev, tmp->rds_info.ps_name); - - ctrl.id = V4L2_CID_RDS_TX_RADIO_TEXT; - rval |= si4713_set_rds_radio_text(sdev, tmp->rds_info.radio_text); + int rval; /* Device procedure needs to set frequency first */ - f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY; + f.tuner = 0; + f.frequency = sdev->frequency ? sdev->frequency : DEFAULT_FREQUENCY; f.frequency = si4713_to_v4l2(f.frequency); - rval |= si4713_s_frequency(&sdev->sd, &f); - - ctrl.id = V4L2_CID_TUNE_POWER_LEVEL; - ctrl.value = tmp->power_level; - rval |= si4713_write_econtrol_tune(sdev, &ctrl); - - ctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR; - ctrl.value = tmp->antenna_capacitor; - rval |= si4713_write_econtrol_tune(sdev, &ctrl); + rval = si4713_s_frequency(&sdev->sd, &f); vm.index = 0; - if (tmp->stereo) + if (sdev->stereo) vm.txsubchans = V4L2_TUNER_SUB_STEREO; else vm.txsubchans = V4L2_TUNER_SUB_MONO; - if (tmp->rds_info.enabled) + if (sdev->rds_enabled) vm.txsubchans |= V4L2_TUNER_SUB_RDS; si4713_s_modulator(&sdev->sd, &vm); - kfree(tmp); - return rval; } @@ -1338,434 +1014,126 @@ static int si4713_initialize(struct si4713_device *sdev) rval = si4713_set_power_state(sdev, POWER_ON); if (rval < 0) - goto exit; + return rval; rval = si4713_checkrev(sdev); if (rval < 0) - goto exit; + return rval; rval = si4713_set_power_state(sdev, POWER_OFF); if (rval < 0) - goto exit; - - mutex_lock(&sdev->mutex); - - sdev->rds_info.pi = DEFAULT_RDS_PI; - sdev->rds_info.pty = DEFAULT_RDS_PTY; - sdev->rds_info.deviation = DEFAULT_RDS_DEVIATION; - strlcpy(sdev->rds_info.ps_name, DEFAULT_RDS_PS_NAME, MAX_RDS_PS_NAME); - strlcpy(sdev->rds_info.radio_text, DEFAULT_RDS_RADIO_TEXT, - MAX_RDS_RADIO_TEXT); - sdev->rds_info.enabled = 1; - - sdev->limiter_info.release_time = DEFAULT_LIMITER_RTIME; - sdev->limiter_info.deviation = DEFAULT_LIMITER_DEV; - sdev->limiter_info.enabled = 1; - - sdev->pilot_info.deviation = DEFAULT_PILOT_DEVIATION; - sdev->pilot_info.frequency = DEFAULT_PILOT_FREQUENCY; - sdev->pilot_info.enabled = 1; + return rval; - sdev->acomp_info.release_time = DEFAULT_ACOMP_RTIME; - sdev->acomp_info.attack_time = DEFAULT_ACOMP_ATIME; - sdev->acomp_info.threshold = DEFAULT_ACOMP_THRESHOLD; - sdev->acomp_info.gain = DEFAULT_ACOMP_GAIN; - sdev->acomp_info.enabled = 1; sdev->frequency = DEFAULT_FREQUENCY; - sdev->preemphasis = DEFAULT_PREEMPHASIS; - sdev->mute = DEFAULT_MUTE; - sdev->power_level = DEFAULT_POWER_LEVEL; - sdev->antenna_capacitor = 0; sdev->stereo = 1; sdev->tune_rnl = DEFAULT_TUNE_RNL; - - mutex_unlock(&sdev->mutex); - -exit: - return rval; -} - -/* read string property */ -static int si4713_read_econtrol_string(struct si4713_device *sdev, - struct v4l2_ext_control *control) -{ - s32 rval = 0; - - switch (control->id) { - case V4L2_CID_RDS_TX_PS_NAME: - if (strlen(sdev->rds_info.ps_name) + 1 > control->size) { - control->size = MAX_RDS_PS_NAME + 1; - rval = -ENOSPC; - goto exit; - } - rval = copy_to_user(control->string, sdev->rds_info.ps_name, - strlen(sdev->rds_info.ps_name) + 1); - if (rval) - rval = -EFAULT; - break; - - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (strlen(sdev->rds_info.radio_text) + 1 > control->size) { - control->size = MAX_RDS_RADIO_TEXT + 1; - rval = -ENOSPC; - goto exit; - } - rval = copy_to_user(control->string, sdev->rds_info.radio_text, - strlen(sdev->rds_info.radio_text) + 1); - if (rval) - rval = -EFAULT; - break; - - default: - rval = -EINVAL; - break; - } - -exit: - return rval; -} - -/* - * si4713_update_tune_status - update properties from tx_tune_status - * command. Must be called with sdev->mutex held. - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_update_tune_status(struct si4713_device *sdev) -{ - int rval; - u16 f = 0; - u8 p = 0, a = 0, n = 0; - - rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); - - if (rval < 0) - goto exit; - - sdev->power_level = p; - sdev->antenna_capacitor = a; - sdev->tune_rnl = n; - -exit: - return rval; -} - -/* properties which use tx_tune_status */ -static int si4713_read_econtrol_tune(struct si4713_device *sdev, - struct v4l2_ext_control *control) -{ - s32 rval = 0; - - mutex_lock(&sdev->mutex); - - if (sdev->power_state) { - rval = si4713_update_tune_status(sdev); - if (rval < 0) - goto unlock; - } - - switch (control->id) { - case V4L2_CID_TUNE_POWER_LEVEL: - control->value = sdev->power_level; - break; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - control->value = sdev->antenna_capacitor; - break; - default: - rval = -EINVAL; - } - -unlock: - mutex_unlock(&sdev->mutex); - return rval; + return 0; } -static int si4713_read_econtrol_integers(struct si4713_device *sdev, - struct v4l2_ext_control *control) +/* si4713_s_ctrl - set the value of a control */ +static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) { - s32 rval; - u32 *shadow = NULL, val = 0; + struct si4713_device *sdev = + container_of(ctrl->handler, struct si4713_device, ctrl_handler); + u32 val = 0; s32 bit = 0, mask = 0; u16 property = 0; int mul = 0; unsigned long *table = NULL; int size = 0; + bool force = false; + int c; + int ret = 0; - rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, - &mask, &property, &mul, &table, &size); - if (rval < 0) - goto exit; - - mutex_lock(&sdev->mutex); - - if (sdev->power_state) { - rval = si4713_read_property(sdev, property, &val); - if (rval < 0) - goto unlock; - - /* Keep negative values for threshold */ - if (control->id == V4L2_CID_AUDIO_COMPRESSION_THRESHOLD) - *shadow = (s16)val; - else if (mask) - *shadow = get_status_bit(val, bit, mask); - else if (mul) - *shadow = val * mul; - else - *shadow = dev_to_usecs(val, table, size); - } - - control->value = *shadow; - -unlock: - mutex_unlock(&sdev->mutex); -exit: - return rval; -} - -/* - * Video4Linux Subdev Interface - */ -/* si4713_s_ext_ctrls - set extended controls value */ -static int si4713_s_ext_ctrls(struct v4l2_subdev *sd, - struct v4l2_ext_controls *ctrls) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int i; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + if (ctrl->id != V4L2_CID_AUDIO_MUTE) return -EINVAL; - - for (i = 0; i < ctrls->count; i++) { - int err; - - switch ((ctrls->controls + i)->id) { - case V4L2_CID_RDS_TX_PS_NAME: - case V4L2_CID_RDS_TX_RADIO_TEXT: - err = si4713_write_econtrol_string(sdev, - ctrls->controls + i); - break; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - case V4L2_CID_TUNE_POWER_LEVEL: - err = si4713_write_econtrol_tune(sdev, - ctrls->controls + i); - break; - default: - err = si4713_write_econtrol_integers(sdev, - ctrls->controls + i); - } - - if (err < 0) { - ctrls->error_idx = i; - return err; + if (ctrl->is_new) { + if (ctrl->val) { + ret = si4713_set_mute(sdev, ctrl->val); + if (!ret) + ret = si4713_set_power_state(sdev, POWER_DOWN); + return ret; } + ret = si4713_set_power_state(sdev, POWER_UP); + if (!ret) + ret = si4713_set_mute(sdev, ctrl->val); + if (!ret) + ret = si4713_setup(sdev); + if (ret) + return ret; + force = true; } - return 0; -} + if (!sdev->power_state) + return 0; -/* si4713_g_ext_ctrls - get extended controls value */ -static int si4713_g_ext_ctrls(struct v4l2_subdev *sd, - struct v4l2_ext_controls *ctrls) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int i; + for (c = 1; !ret && c < ctrl->ncontrols; c++) { + ctrl = ctrl->cluster[c]; - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) - return -EINVAL; + if (!force && !ctrl->is_new) + continue; - for (i = 0; i < ctrls->count; i++) { - int err; - - switch ((ctrls->controls + i)->id) { + switch (ctrl->id) { case V4L2_CID_RDS_TX_PS_NAME: + ret = si4713_set_rds_ps_name(sdev, ctrl->string); + break; + case V4L2_CID_RDS_TX_RADIO_TEXT: - err = si4713_read_econtrol_string(sdev, - ctrls->controls + i); + ret = si4713_set_rds_radio_text(sdev, ctrl->string); break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + /* don't handle this control if we force setting all + * controls since in that case it will be handled by + * V4L2_CID_TUNE_POWER_LEVEL. */ + if (force) + break; + /* fall through */ case V4L2_CID_TUNE_POWER_LEVEL: - err = si4713_read_econtrol_tune(sdev, - ctrls->controls + i); + ret = si4713_tx_tune_power(sdev, + sdev->tune_pwr_level->val, sdev->tune_ant_cap->val); + if (!ret) { + /* Make sure we don't set this twice */ + sdev->tune_ant_cap->is_new = false; + sdev->tune_pwr_level->is_new = false; + } break; - default: - err = si4713_read_econtrol_integers(sdev, - ctrls->controls + i); - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -/* si4713_queryctrl - enumerate control items */ -static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int rval = 0; - - switch (qc->id) { - /* User class controls */ - case V4L2_CID_AUDIO_MUTE: - rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, DEFAULT_MUTE); - break; - /* FM_TX class controls */ - case V4L2_CID_RDS_TX_PI: - rval = v4l2_ctrl_query_fill(qc, 0, 0xFFFF, 1, DEFAULT_RDS_PI); - break; - case V4L2_CID_RDS_TX_PTY: - rval = v4l2_ctrl_query_fill(qc, 0, 31, 1, DEFAULT_RDS_PTY); - break; - case V4L2_CID_RDS_TX_DEVIATION: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_DEVIATION, - 10, DEFAULT_RDS_DEVIATION); - break; - case V4L2_CID_RDS_TX_PS_NAME: - /* - * Report step as 8. From RDS spec, psname - * should be 8. But there are receivers which scroll strings - * sized as 8xN. - */ - rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_PS_NAME, 8, 0); - break; - case V4L2_CID_RDS_TX_RADIO_TEXT: - /* - * Report step as 32 (2A block). From RDS spec, - * radio text should be 32 for 2A block. But there are receivers - * which scroll strings sized as 32xN. Setting default to 32. - */ - rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_RADIO_TEXT, 32, 0); - break; - - case V4L2_CID_AUDIO_LIMITER_ENABLED: - rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - break; - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: - rval = v4l2_ctrl_query_fill(qc, 250, MAX_LIMITER_RELEASE_TIME, - 50, DEFAULT_LIMITER_RTIME); - break; - case V4L2_CID_AUDIO_LIMITER_DEVIATION: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_LIMITER_DEVIATION, - 10, DEFAULT_LIMITER_DEV); - break; - - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: - rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - break; - case V4L2_CID_AUDIO_COMPRESSION_GAIN: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_GAIN, 1, - DEFAULT_ACOMP_GAIN); - break; - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: - rval = v4l2_ctrl_query_fill(qc, MIN_ACOMP_THRESHOLD, - MAX_ACOMP_THRESHOLD, 1, - DEFAULT_ACOMP_THRESHOLD); - break; - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_ATTACK_TIME, - 500, DEFAULT_ACOMP_ATIME); - break; - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: - rval = v4l2_ctrl_query_fill(qc, 100000, MAX_ACOMP_RELEASE_TIME, - 100000, DEFAULT_ACOMP_RTIME); - break; - - case V4L2_CID_PILOT_TONE_ENABLED: - rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - break; - case V4L2_CID_PILOT_TONE_DEVIATION: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_DEVIATION, - 10, DEFAULT_PILOT_DEVIATION); - break; - case V4L2_CID_PILOT_TONE_FREQUENCY: - rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_FREQUENCY, - 1, DEFAULT_PILOT_FREQUENCY); - break; - - case V4L2_CID_TUNE_PREEMPHASIS: - rval = v4l2_ctrl_query_fill(qc, V4L2_PREEMPHASIS_DISABLED, - V4L2_PREEMPHASIS_75_uS, 1, - V4L2_PREEMPHASIS_50_uS); - break; - case V4L2_CID_TUNE_POWER_LEVEL: - rval = v4l2_ctrl_query_fill(qc, 0, 120, 1, DEFAULT_POWER_LEVEL); - break; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - rval = v4l2_ctrl_query_fill(qc, 0, 191, 1, 0); - break; - default: - rval = -EINVAL; - break; - } - - return rval; -} - -/* si4713_g_ctrl - get the value of a control */ -static int si4713_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - - if (!sdev) - return -ENODEV; - - mutex_lock(&sdev->mutex); - - if (sdev->power_state) { - rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE, - &sdev->mute); - - if (rval < 0) - goto unlock; - } - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(sdev->mute); - break; - } - -unlock: - mutex_unlock(&sdev->mutex); - return rval; -} - -/* si4713_s_ctrl - set the value of a control */ -static int si4713_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - - if (!sdev) - return -ENODEV; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - rval = si4713_set_mute(sdev, ctrl->value); - if (rval < 0) - goto exit; - rval = si4713_set_power_state(sdev, POWER_DOWN); - } else { - rval = si4713_set_power_state(sdev, POWER_UP); - if (rval < 0) - goto exit; + default: + ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit, + &mask, &property, &mul, &table, &size); + if (ret < 0) + break; + + val = ctrl->val; + if (mul) { + val = val / mul; + } else if (table) { + ret = usecs_to_dev(val, table, size); + if (ret < 0) + break; + val = ret; + ret = 0; + } - rval = si4713_setup(sdev); - if (rval < 0) - goto exit; + if (mask) { + ret = si4713_read_property(sdev, property, &val); + if (ret < 0) + break; + val = set_bits(val, ctrl->val, bit, mask); + } - rval = si4713_set_mute(sdev, ctrl->value); + ret = si4713_write_property(sdev, property, val); + if (ret < 0) + break; + if (mask) + val = ctrl->val; + break; } - break; } -exit: - return rval; + return ret; } /* si4713_ioctl - deal with private ioctls (only rnl for now) */ @@ -1779,7 +1147,6 @@ static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) if (!arg) return -EINVAL; - mutex_lock(&sdev->mutex); switch (cmd) { case SI4713_IOC_MEASURE_RNL: frequency = v4l2_to_si4713(rnl->frequency); @@ -1788,11 +1155,11 @@ static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) /* Set desired measurement frequency */ rval = si4713_tx_tune_measure(sdev, frequency, 0); if (rval < 0) - goto unlock; + return rval; /* get results from tune status */ rval = si4713_update_tune_status(sdev); if (rval < 0) - goto unlock; + return rval; } rnl->rnl = sdev->tune_rnl; break; @@ -1802,35 +1169,20 @@ static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) rval = -ENOIOCTLCMD; } -unlock: - mutex_unlock(&sdev->mutex); return rval; } -static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { - .queryctrl = si4713_queryctrl, - .g_ext_ctrls = si4713_g_ext_ctrls, - .s_ext_ctrls = si4713_s_ext_ctrls, - .g_ctrl = si4713_g_ctrl, - .s_ctrl = si4713_s_ctrl, - .ioctl = si4713_ioctl, -}; - /* si4713_g_modulator - get modulator attributes */ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; - if (!sdev) { - rval = -ENODEV; - goto exit; - } + if (!sdev) + return -ENODEV; - if (vm->index > 0) { - rval = -EINVAL; - goto exit; - } + if (vm->index > 0) + return -EINVAL; strncpy(vm->name, "FM Modulator", 32); vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | @@ -1840,18 +1192,15 @@ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); - mutex_lock(&sdev->mutex); - if (sdev->power_state) { u32 comp_en = 0; rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, &comp_en); if (rval < 0) - goto unlock; + return rval; sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); - sdev->rds_info.enabled = get_status_bit(comp_en, 2, 1 << 2); } /* Report current audio mode: mono or stereo */ @@ -1861,14 +1210,11 @@ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) vm->txsubchans = V4L2_TUNER_SUB_MONO; /* Report rds feature status */ - if (sdev->rds_info.enabled) + if (sdev->rds_enabled) vm->txsubchans |= V4L2_TUNER_SUB_RDS; else vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; -unlock: - mutex_unlock(&sdev->mutex); -exit: return rval; } @@ -1896,13 +1242,11 @@ static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulato rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); - mutex_lock(&sdev->mutex); - if (sdev->power_state) { rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, &p); if (rval < 0) - goto unlock; + return rval; p = set_bits(p, stereo, 1, 1 << 1); p = set_bits(p, rds, 2, 1 << 2); @@ -1910,14 +1254,12 @@ static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulato rval = si4713_write_property(sdev, SI4713_TX_COMPONENT_ENABLE, p); if (rval < 0) - goto unlock; + return rval; } sdev->stereo = stereo; - sdev->rds_info.enabled = rds; + sdev->rds_enabled = rds; -unlock: - mutex_unlock(&sdev->mutex); return rval; } @@ -1927,9 +1269,8 @@ static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; - f->type = V4L2_TUNER_RADIO; - - mutex_lock(&sdev->mutex); + if (f->tuner) + return -EINVAL; if (sdev->power_state) { u16 freq; @@ -1937,46 +1278,49 @@ static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); if (rval < 0) - goto unlock; + return rval; sdev->frequency = freq; } f->frequency = si4713_to_v4l2(sdev->frequency); -unlock: - mutex_unlock(&sdev->mutex); return rval; } /* si4713_s_frequency - set tuner or modulator radio frequency */ -static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; u16 frequency = v4l2_to_si4713(f->frequency); - /* Check frequency range */ - if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) - return -EDOM; + if (f->tuner) + return -EINVAL; - mutex_lock(&sdev->mutex); + /* Check frequency range */ + frequency = clamp_t(u16, frequency, FREQ_RANGE_LOW, FREQ_RANGE_HIGH); if (sdev->power_state) { rval = si4713_tx_tune_freq(sdev, frequency); if (rval < 0) - goto unlock; + return rval; frequency = rval; rval = 0; } sdev->frequency = frequency; - f->frequency = si4713_to_v4l2(frequency); -unlock: - mutex_unlock(&sdev->mutex); return rval; } +static const struct v4l2_ctrl_ops si4713_ctrl_ops = { + .s_ctrl = si4713_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { + .ioctl = si4713_ioctl, +}; + static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = { .g_frequency = si4713_g_frequency, .s_frequency = si4713_s_frequency, @@ -1998,6 +1342,7 @@ static int si4713_probe(struct i2c_client *client, { struct si4713_device *sdev; struct si4713_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; int rval, i; sdev = kzalloc(sizeof *sdev, GFP_KERNEL); @@ -2031,9 +1376,84 @@ static int si4713_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops); - mutex_init(&sdev->mutex); init_completion(&sdev->work); + hdl = &sdev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 20); + sdev->mute = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, DEFAULT_MUTE); + + sdev->rds_pi = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, DEFAULT_RDS_PI); + sdev->rds_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PTY, 0, 31, 1, DEFAULT_RDS_PTY); + sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION, + 10, DEFAULT_RDS_DEVIATION); + /* + * Report step as 8. From RDS spec, psname + * should be 8. But there are receivers which scroll strings + * sized as 8xN. + */ + sdev->rds_ps_name = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PS_NAME, 0, MAX_RDS_PS_NAME, 8, 0); + /* + * Report step as 32 (2A block). From RDS spec, + * radio text should be 32 for 2A block. But there are receivers + * which scroll strings sized as 32xN. Setting default to 32. + */ + sdev->rds_radio_text = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_RADIO_TEXT, 0, MAX_RDS_RADIO_TEXT, 32, 0); + + sdev->limiter_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_ENABLED, 0, 1, 1, 1); + sdev->limiter_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, 250, + MAX_LIMITER_RELEASE_TIME, 10, DEFAULT_LIMITER_RTIME); + sdev->limiter_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_DEVIATION, 0, + MAX_LIMITER_DEVIATION, 10, DEFAULT_LIMITER_DEV); + + sdev->compression_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_ENABLED, 0, 1, 1, 1); + sdev->compression_gain = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_GAIN, 0, MAX_ACOMP_GAIN, 1, + DEFAULT_ACOMP_GAIN); + sdev->compression_threshold = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, MIN_ACOMP_THRESHOLD, + MAX_ACOMP_THRESHOLD, 1, + DEFAULT_ACOMP_THRESHOLD); + sdev->compression_attack_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, 0, + MAX_ACOMP_ATTACK_TIME, 500, DEFAULT_ACOMP_ATIME); + sdev->compression_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, 100000, + MAX_ACOMP_RELEASE_TIME, 100000, DEFAULT_ACOMP_RTIME); + + sdev->pilot_tone_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_ENABLED, 0, 1, 1, 1); + sdev->pilot_tone_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_DEVIATION, 0, MAX_PILOT_DEVIATION, + 10, DEFAULT_PILOT_DEVIATION); + sdev->pilot_tone_freq = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_FREQUENCY, 0, MAX_PILOT_FREQUENCY, + 1, DEFAULT_PILOT_FREQUENCY); + + sdev->tune_preemphasis = v4l2_ctrl_new_std_menu(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_PREEMPHASIS, + V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS); + sdev->tune_pwr_level = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_POWER_LEVEL, 0, 120, 1, DEFAULT_POWER_LEVEL); + sdev->tune_ant_cap = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, 191, 1, 0); + + if (hdl->error) { + rval = hdl->error; + goto free_ctrls; + } + v4l2_ctrl_cluster(20, &sdev->mute); + sdev->sd.ctrl_handler = hdl; + if (client->irq) { rval = request_irq(client->irq, si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, @@ -2058,6 +1478,8 @@ static int si4713_probe(struct i2c_client *client, free_irq: if (client->irq) free_irq(client->irq, sdev); +free_ctrls: + v4l2_ctrl_handler_free(hdl); put_reg: regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); free_gpio: @@ -2082,6 +1504,7 @@ static int si4713_remove(struct i2c_client *client) free_irq(client->irq, sdev); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); if (gpio_is_valid(sdev->gpio_reset)) gpio_free(sdev->gpio_reset); diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713-i2c.h index c6dfa7f..25cdea2 100644 --- a/drivers/media/radio/si4713-i2c.h +++ b/drivers/media/radio/si4713-i2c.h @@ -16,6 +16,7 @@ #define SI4713_I2C_H #include <media/v4l2-subdev.h> +#include <media/v4l2-ctrls.h> #include <media/si4713.h> #define SI4713_PRODUCT_NUMBER 0x0D @@ -160,56 +161,33 @@ #define POWER_UP 0x01 #define POWER_DOWN 0x00 -struct rds_info { - u32 pi; #define MAX_RDS_PTY 31 - u32 pty; #define MAX_RDS_DEVIATION 90000 - u32 deviation; + /* * PSNAME is known to be defined as 8 character sized (RDS Spec). * However, there is receivers which scroll PSNAME 8xN sized. */ #define MAX_RDS_PS_NAME 96 - u8 ps_name[MAX_RDS_PS_NAME + 1]; + /* * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) * character sized (RDS Spec). * However, there is receivers which scroll them as well. */ #define MAX_RDS_RADIO_TEXT 384 - u8 radio_text[MAX_RDS_RADIO_TEXT + 1]; - u32 enabled; -}; -struct limiter_info { #define MAX_LIMITER_RELEASE_TIME 102390 - u32 release_time; #define MAX_LIMITER_DEVIATION 90000 - u32 deviation; - u32 enabled; -}; -struct pilot_info { #define MAX_PILOT_DEVIATION 90000 - u32 deviation; #define MAX_PILOT_FREQUENCY 19000 - u32 frequency; - u32 enabled; -}; -struct acomp_info { #define MAX_ACOMP_RELEASE_TIME 1000000 - u32 release_time; #define MAX_ACOMP_ATTACK_TIME 5000 - u32 attack_time; #define MAX_ACOMP_THRESHOLD 0 #define MIN_ACOMP_THRESHOLD (-40) - s32 threshold; #define MAX_ACOMP_GAIN 20 - u32 gain; - u32 enabled; -}; #define SI4713_NUM_SUPPLIES 2 @@ -219,21 +197,41 @@ struct acomp_info { struct si4713_device { /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrl_handler; /* private data structures */ - struct mutex mutex; + struct { /* si4713 control cluster */ + /* This is one big cluster since the mute control + * powers off the device and after unmuting again all + * controls need to be set at once. The only way of doing + * that is by making it one big cluster. */ + struct v4l2_ctrl *mute; + struct v4l2_ctrl *rds_ps_name; + struct v4l2_ctrl *rds_radio_text; + struct v4l2_ctrl *rds_pi; + struct v4l2_ctrl *rds_deviation; + struct v4l2_ctrl *rds_pty; + struct v4l2_ctrl *compression_enabled; + struct v4l2_ctrl *compression_threshold; + struct v4l2_ctrl *compression_gain; + struct v4l2_ctrl *compression_attack_time; + struct v4l2_ctrl *compression_release_time; + struct v4l2_ctrl *pilot_tone_enabled; + struct v4l2_ctrl *pilot_tone_freq; + struct v4l2_ctrl *pilot_tone_deviation; + struct v4l2_ctrl *limiter_enabled; + struct v4l2_ctrl *limiter_deviation; + struct v4l2_ctrl *limiter_release_time; + struct v4l2_ctrl *tune_preemphasis; + struct v4l2_ctrl *tune_pwr_level; + struct v4l2_ctrl *tune_ant_cap; + }; struct completion work; - struct rds_info rds_info; - struct limiter_info limiter_info; - struct pilot_info pilot_info; - struct acomp_info acomp_info; struct regulator_bulk_data supplies[SI4713_NUM_SUPPLIES]; int gpio_reset; + u32 power_state; + u32 rds_enabled; u32 frequency; u32 preemphasis; - u32 mute; - u32 power_level; - u32 power_state; - u32 antenna_capacitor; u32 stereo; u32 tune_rnl; }; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index b18c2dc..82c6c94 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -96,12 +96,12 @@ static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) return 0; } -static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +static int tef6862_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) { return v->index ? -EINVAL : 0; } -static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +static int tef6862_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) { struct tef6862_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 0a8ee8f..5dec323 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -331,7 +331,7 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, * Should we set other tuner attributes, too? */ static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) + const struct v4l2_tuner *tuner) { struct fmdev *fmdev = video_drvdata(file); u16 aud_mode; @@ -388,7 +388,7 @@ static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv, /* Set tuner or modulator radio frequency */ static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct fmdev *fmdev = video_drvdata(file); @@ -396,9 +396,7 @@ static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency * in units of 62.5 Hz. */ - freq->frequency = (u32)(freq->frequency / 16); - - return fmc_set_freq(fmdev, freq->frequency); + return fmc_set_freq(fmdev, freq->frequency / 16); } /* Set hardware frequency seek. If current mode is NOT RX, set it RX. */ diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index ee6c984..ed184f6 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1098,6 +1098,7 @@ exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); exit_unregister_device: rc_unregister_device(rdev); + rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index dec203b..72e3fa6 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -112,6 +112,7 @@ struct imon_context { bool tx_control; unsigned char usb_rx_buf[8]; unsigned char usb_tx_buf[8]; + unsigned int send_packet_delay; struct tx_t { unsigned char data_buf[35]; /* user data buffer */ @@ -185,6 +186,10 @@ enum { IMON_KEY_PANEL = 2, }; +enum { + IMON_NEED_20MS_PKT_DELAY = 1 +}; + /* * USB Device ID for iMON USB Control Boards * @@ -215,7 +220,7 @@ static struct usb_device_id imon_usb_id_table[] = { /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */ { USB_DEVICE(0x15c2, 0x0035) }, /* SoundGraph iMON OEM VFD (IR & VFD) */ - { USB_DEVICE(0x15c2, 0x0036) }, + { USB_DEVICE(0x15c2, 0x0036), .driver_info = IMON_NEED_20MS_PKT_DELAY }, /* device specifics unknown */ { USB_DEVICE(0x15c2, 0x0037) }, /* SoundGraph iMON OEM LCD (IR & LCD) */ @@ -523,8 +528,10 @@ static int send_packet(struct imon_context *ictx) mutex_unlock(&ictx->lock); retval = wait_for_completion_interruptible( &ictx->tx.finished); - if (retval) + if (retval) { + usb_kill_urb(ictx->tx_urb); pr_err_ratelimited("task interrupted\n"); + } mutex_lock(&ictx->lock); retval = ictx->tx.status; @@ -535,12 +542,12 @@ static int send_packet(struct imon_context *ictx) kfree(control_req); /* - * Induce a mandatory 5ms delay before returning, as otherwise, + * Induce a mandatory delay before returning, as otherwise, * send_packet can get called so rapidly as to overwhelm the device, * particularly on faster systems and/or those with quirky usb. */ - timeout = msecs_to_jiffies(5); - set_current_state(TASK_UNINTERRUPTIBLE); + timeout = msecs_to_jiffies(ictx->send_packet_delay); + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(timeout); return retval; @@ -1568,11 +1575,6 @@ static void imon_incoming_packet(struct imon_context *ictx, if (press_type < 0) goto not_input_data; - spin_lock_irqsave(&ictx->kc_lock, flags); - if (ictx->kc == KEY_UNKNOWN) - goto unknown_key; - spin_unlock_irqrestore(&ictx->kc_lock, flags); - if (ktype != IMON_KEY_PANEL) { if (press_type == 0) rc_keyup(ictx->rdev); @@ -1615,12 +1617,6 @@ static void imon_incoming_packet(struct imon_context *ictx, return; -unknown_key: - spin_unlock_irqrestore(&ictx->kc_lock, flags); - dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__, - (long long)scancode); - return; - not_input_data: if (len != 8) { dev_warn(dev, "imon %s: invalid incoming packet " @@ -2099,7 +2095,8 @@ static bool imon_find_endpoints(struct imon_context *ictx, } -static struct imon_context *imon_init_intf0(struct usb_interface *intf) +static struct imon_context *imon_init_intf0(struct usb_interface *intf, + const struct usb_device_id *id) { struct imon_context *ictx; struct urb *rx_urb; @@ -2139,6 +2136,10 @@ 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); + /* 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; + ret = -ENODEV; iface_desc = intf->cur_altsetting; if (!imon_find_endpoints(ictx, iface_desc)) { @@ -2317,7 +2318,7 @@ static int imon_probe(struct usb_interface *interface, first_if_ctx = usb_get_intfdata(first_if); if (ifnum == 0) { - ictx = imon_init_intf0(interface); + ictx = imon_init_intf0(interface, id); if (!ictx) { pr_err("failed to initialize context!\n"); ret = -ENODEV; @@ -2325,7 +2326,14 @@ static int imon_probe(struct usb_interface *interface, } } else { - /* this is the secondary interface on the device */ + /* this is the secondary interface on the device */ + + /* fail early if first intf failed to register */ + if (!first_if_ctx) { + ret = -ENODEV; + goto fail; + } + ictx = imon_init_intf1(interface, first_if_ctx); if (!ictx) { pr_err("failed to attach to context!\n"); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 69edffb..3948138 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -47,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct jvc_dec *data = &dev->raw->jvc; - if (!(dev->raw->enabled_protocols & RC_BIT_JVC)) + if (!(dev->enabled_protocols & RC_BIT_JVC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 9945e5e..ff4d93d 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!(dev->raw->enabled_protocols & RC_BIT_LIRC)) + if (!(dev->enabled_protocols & RC_BIT_LIRC)) return 0; if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index 33fafa4..9f3c9b5 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -216,7 +216,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; unsigned long delay; - if (!(dev->raw->enabled_protocols & RC_BIT_MCE_KBD)) + if (!(dev->enabled_protocols & RC_BIT_MCE_KBD)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index a47ee36..9a90094 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -52,7 +52,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 address, not_address, command, not_command; bool send_32bits = false; - if (!(dev->raw->enabled_protocols & RC_BIT_NEC)) + if (!(dev->enabled_protocols & RC_BIT_NEC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 17c94be..5c42750 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -256,7 +256,7 @@ int ir_raw_event_register(struct rc_dev *dev) return -ENOMEM; dev->raw->dev = dev; - dev->raw->enabled_protocols = ~0; + dev->enabled_protocols = ~0; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, GFP_KERNEL); diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 5b4d1dd..4e53a31 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -52,7 +52,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle; u32 scancode; - if (!(dev->raw->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X))) + if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X))) return 0; if (!is_timing_event(ev)) { @@ -128,7 +128,7 @@ again: if (data->wanted_bits == RC5X_NBITS) { /* RC5X */ u8 xdata, command, system; - if (!(dev->raw->enabled_protocols & RC_BIT_RC5X)) { + if (!(dev->enabled_protocols & RC_BIT_RC5X)) { data->state = STATE_INACTIVE; return 0; } @@ -145,7 +145,7 @@ again: } else { /* RC5 */ u8 command, system; - if (!(dev->raw->enabled_protocols & RC_BIT_RC5)) { + if (!(dev->enabled_protocols & RC_BIT_RC5)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c index fd807a8..865fe84 100644 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ b/drivers/media/rc/ir-rc5-sz-decoder.c @@ -48,7 +48,7 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle, command, system; u32 scancode; - if (!(dev->raw->enabled_protocols & RC_BIT_RC5_SZ)) + if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index e19072f..7cba7d3 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -89,7 +89,7 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 toggle; - if (!(dev->raw->enabled_protocols & + if (!(dev->enabled_protocols & (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE))) return 0; diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 8ead492..31b955b 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -464,14 +464,14 @@ static int lirc_rx51_probe(struct platform_device *dev) return 0; } -static int __exit lirc_rx51_remove(struct platform_device *dev) +static int lirc_rx51_remove(struct platform_device *dev) { return lirc_unregister_driver(lirc_rx51_driver.minor); } struct platform_driver lirc_rx51_platform_driver = { .probe = lirc_rx51_probe, - .remove = __exit_p(lirc_rx51_remove), + .remove = lirc_rx51_remove, .suspend = lirc_rx51_suspend, .resume = lirc_rx51_resume, .driver = { diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index 7e69a3b..0a06205 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -58,7 +58,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 address, command, not_command; - if (!(dev->raw->enabled_protocols & RC_BIT_SANYO)) + if (!(dev->enabled_protocols & RC_BIT_SANYO)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index fb91434..29ab9c2 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -45,7 +45,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 device, subdevice, function; - if (!(dev->raw->enabled_protocols & + if (!(dev->enabled_protocols & (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20))) return 0; @@ -124,7 +124,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) switch (data->count) { case 12: - if (!(dev->raw->enabled_protocols & RC_BIT_SONY12)) { + if (!(dev->enabled_protocols & RC_BIT_SONY12)) { data->state = STATE_INACTIVE; return 0; } @@ -133,7 +133,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) function = bitrev8((data->bits >> 4) & 0xFE); break; case 15: - if (!(dev->raw->enabled_protocols & RC_BIT_SONY15)) { + if (!(dev->enabled_protocols & RC_BIT_SONY15)) { data->state = STATE_INACTIVE; return 0; } @@ -142,7 +142,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) function = bitrev8((data->bits >> 7) & 0xFE); break; case 20: - if (!(dev->raw->enabled_protocols & RC_BIT_SONY20)) { + if (!(dev->enabled_protocols & RC_BIT_SONY20)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index dd82373..63b4225 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1613,6 +1613,7 @@ exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); exit_unregister_device: rc_unregister_device(rdev); + rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 7786619..5ab94ea 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-hauppauge.o \ rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ + rc-reddo.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ rc-tbs-nec.o \ @@ -88,7 +89,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tevii-nec.o \ rc-tivo.o \ rc-total-media-in-hand.o \ - rc-total-media-in-hand-02.o \ + rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ rc-twinhan1027.o \ diff --git a/drivers/media/rc/keymaps/rc-reddo.c b/drivers/media/rc/keymaps/rc-reddo.c new file mode 100644 index 0000000..b80b336 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-reddo.c @@ -0,0 +1,86 @@ +/* + * MSI DIGIVOX mini III remote controller keytable + * + * Copyright (C) 2013 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + +/* + * Derived from MSI DIGIVOX mini III remote (rc-msi-digivox-iii.c) + * + * Differences between these remotes are: + * + * 1) scancode 0x61d601 is mapped to different button: + * MSI DIGIVOX mini III "Source" = KEY_VIDEO + * Reddo "EPG" = KEY_EPG + * + * 2) Reddo remote has less buttons. Missing buttons are: colored buttons, + * navigation buttons and main power button. + */ + +static struct rc_map_table reddo[] = { + { 0x61d601, KEY_EPG }, /* EPG */ + { 0x61d602, KEY_3 }, + { 0x61d604, KEY_1 }, + { 0x61d605, KEY_5 }, + { 0x61d606, KEY_6 }, + { 0x61d607, KEY_CHANNELDOWN }, /* CH- */ + { 0x61d608, KEY_2 }, + { 0x61d609, KEY_CHANNELUP }, /* CH+ */ + { 0x61d60a, KEY_9 }, + { 0x61d60b, KEY_ZOOM }, /* Zoom */ + { 0x61d60c, KEY_7 }, + { 0x61d60d, KEY_8 }, + { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */ + { 0x61d60f, KEY_4 }, + { 0x61d610, KEY_ESC }, /* [back up arrow] */ + { 0x61d611, KEY_0 }, + { 0x61d612, KEY_OK }, /* [enter arrow] */ + { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x61d614, KEY_RECORD }, /* Rec */ + { 0x61d615, KEY_STOP }, /* Stop */ + { 0x61d616, KEY_PLAY }, /* Play */ + { 0x61d617, KEY_MUTE }, /* Mute */ + { 0x61d643, KEY_POWER2 }, /* [red power button] */ +}; + +static struct rc_map_list reddo_map = { + .map = { + .scan = reddo, + .size = ARRAY_SIZE(reddo), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_REDDO, + } +}; + +static int __init init_rc_map_reddo(void) +{ + return rc_map_register(&reddo_map); +} + +static void __exit exit_rc_map_reddo(void) +{ + rc_map_unregister(&reddo_map); +} + +module_init(init_rc_map_reddo) +module_exit(exit_rc_map_reddo) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 5b5b6e6..3c76101 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -482,7 +482,7 @@ static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_RSP_EQIRRXPORTEN, 0x00}; */ -static int mceusb_cmdsize(u8 cmd, u8 subcmd) +static int mceusb_cmd_datasize(u8 cmd, u8 subcmd) { int datasize = 0; @@ -493,6 +493,9 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd) break; case MCE_CMD_PORT_SYS: switch (subcmd) { + case MCE_RSP_GETPORTSTATUS: + datasize = 5; + break; case MCE_RSP_EQWAKEVERSION: datasize = 4; break; @@ -500,6 +503,9 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd) datasize = 2; break; case MCE_RSP_EQWAKESUPPORT: + case MCE_RSP_GETWAKESOURCE: + case MCE_RSP_EQDEVDETAILS: + case MCE_RSP_EQEMVER: datasize = 1; break; } @@ -509,6 +515,7 @@ static int mceusb_cmdsize(u8 cmd, u8 subcmd) case MCE_RSP_EQIRCFS: case MCE_RSP_EQIRTIMEOUT: case MCE_RSP_EQIRRXCFCNT: + case MCE_RSP_EQIRNUMPORTS: datasize = 2; break; case MCE_CMD_SIG_END: @@ -968,7 +975,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) for (; i < buf_len; i++) { switch (ir->parser_state) { case SUBCMD: - ir->rem = mceusb_cmdsize(ir->cmd, ir->buf_in[i]); + ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]); mceusb_dev_printdata(ir, ir->buf_in, i - 1, ir->rem + 2, false); mceusb_handle_command(ir, i); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 40125d7..21ee0dc 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1107,6 +1107,7 @@ exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); exit_unregister_device: rc_unregister_device(rdev); + rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 5d87287..70a180b 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -39,7 +39,6 @@ struct ir_raw_event_ctrl { ktime_t last_event; /* when last event occurred */ enum raw_event_type last_type; /* last event type */ struct rc_dev *dev; /* pointer to the parent rc_dev */ - u64 enabled_protocols; /* enabled raw protocol decoders */ /* raw decoder state follows */ struct ir_raw_event prev_ev; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 759a40a..1cf382a 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -715,14 +715,14 @@ static void ir_close(struct input_dev *idev) } /* class for /sys/class/rc */ -static char *ir_devnode(struct device *dev, umode_t *mode) +static char *rc_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev)); } -static struct class ir_input_class = { +static struct class rc_class = { .name = "rc", - .devnode = ir_devnode, + .devnode = rc_devnode, }; /* @@ -783,13 +783,12 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); - if (dev->driver_type == RC_DRIVER_SCANCODE) { - enabled = dev->rc_map.rc_type; + enabled = dev->enabled_protocols; + if (dev->driver_type == RC_DRIVER_SCANCODE) allowed = dev->allowed_protos; - } else if (dev->raw) { - enabled = dev->raw->enabled_protocols; + else if (dev->raw) allowed = ir_raw_get_allowed_protocols(); - } else { + else { mutex_unlock(&dev->lock); return -ENODEV; } @@ -847,7 +846,6 @@ static ssize_t store_protocols(struct device *device, u64 type; u64 mask; int rc, i, count = 0; - unsigned long flags; ssize_t ret; /* Device is being removed */ @@ -856,15 +854,12 @@ static ssize_t store_protocols(struct device *device, mutex_lock(&dev->lock); - if (dev->driver_type == RC_DRIVER_SCANCODE) - type = dev->rc_map.rc_type; - else if (dev->raw) - type = dev->raw->enabled_protocols; - else { + if (dev->driver_type != RC_DRIVER_SCANCODE && !dev->raw) { IR_dprintk(1, "Protocol switching not supported\n"); ret = -EINVAL; goto out; } + type = dev->enabled_protocols; while ((tmp = strsep((char **) &data, " \n")) != NULL) { if (!*tmp) @@ -922,14 +917,7 @@ static ssize_t store_protocols(struct device *device, } } - if (dev->driver_type == RC_DRIVER_SCANCODE) { - spin_lock_irqsave(&dev->rc_map.lock, flags); - dev->rc_map.rc_type = type; - spin_unlock_irqrestore(&dev->rc_map.lock, flags); - } else { - dev->raw->enabled_protocols = type; - } - + dev->enabled_protocols = type; IR_dprintk(1, "Current protocol(s): 0x%llx\n", (long long)type); @@ -1016,7 +1004,7 @@ struct rc_dev *rc_allocate_device(void) setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev); dev->dev.type = &rc_dev_type; - dev->dev.class = &ir_input_class; + dev->dev.class = &rc_class; device_initialize(&dev->dev); __module_get(THIS_MODULE); @@ -1068,9 +1056,8 @@ int rc_register_device(struct rc_dev *dev) /* * Take the lock here, as the device sysfs node will appear * when device_add() is called, which may trigger an ir-keytable udev - * rule, which will in turn call show_protocols and access either - * dev->rc_map.rc_type or dev->raw->enabled_protocols before it has - * been initialized. + * rule, which will in turn call show_protocols and access + * dev->enabled_protocols before it has been initialized. */ mutex_lock(&dev->lock); @@ -1132,6 +1119,7 @@ int rc_register_device(struct rc_dev *dev) rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; + dev->enabled_protocols = rc_type; } mutex_unlock(&dev->lock); @@ -1190,7 +1178,7 @@ EXPORT_SYMBOL_GPL(rc_unregister_device); static int __init rc_core_init(void) { - int rc = class_register(&ir_input_class); + int rc = class_register(&rc_class); if (rc) { printk(KERN_ERR "rc_core: unable to register rc class\n"); return rc; @@ -1203,11 +1191,11 @@ static int __init rc_core_init(void) static void __exit rc_core_exit(void) { - class_unregister(&ir_input_class); + class_unregister(&rc_class); rc_map_unregister(&empty_map); } -module_init(rc_core_init); +subsys_initcall(rc_core_init); module_exit(rc_core_exit); int rc_core_debug; /* ir_debug level (0,1,2) */ diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 1b37fe2..12167a6 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -45,6 +45,7 @@ * */ +#include <asm/unaligned.h> #include <linux/device.h> #include <linux/module.h> #include <linux/slab.h> @@ -53,7 +54,6 @@ #include <media/rc-core.h> /* Driver Information */ -#define DRIVER_VERSION "0.70" #define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" #define DRIVER_AUTHOR2 "The Dweller, Stephen Cox" #define DRIVER_DESC "RedRat3 USB IR Transceiver Driver" @@ -129,25 +129,11 @@ static int debug; /* USB bulk-in IR data endpoint address */ #define RR3_BULK_IN_EP_ADDR 0x82 -/* Raw Modulated signal data value offsets */ -#define RR3_PAUSE_OFFSET 0 -#define RR3_FREQ_COUNT_OFFSET 4 -#define RR3_NUM_PERIOD_OFFSET 6 -#define RR3_MAX_LENGTHS_OFFSET 8 -#define RR3_NUM_LENGTHS_OFFSET 9 -#define RR3_MAX_SIGS_OFFSET 10 -#define RR3_NUM_SIGS_OFFSET 12 -#define RR3_REPEATS_OFFSET 14 - /* Size of the fixed-length portion of the signal */ -#define RR3_HEADER_LENGTH 15 #define RR3_DRIVER_MAXLENS 128 #define RR3_MAX_SIG_SIZE 512 -#define RR3_MAX_BUF_SIZE \ - ((2 * RR3_HEADER_LENGTH) + RR3_DRIVER_MAXLENS + RR3_MAX_SIG_SIZE) #define RR3_TIME_UNIT 50 #define RR3_END_OF_SIGNAL 0x7f -#define RR3_TX_HEADER_OFFSET 4 #define RR3_TX_TRAILER_LEN 2 #define RR3_RX_MIN_TIMEOUT 5 #define RR3_RX_MAX_TIMEOUT 2000 @@ -159,6 +145,32 @@ static int debug; #define USB_RR3USB_PRODUCT_ID 0x0001 #define USB_RR3IIUSB_PRODUCT_ID 0x0005 +struct redrat3_header { + __be16 length; + __be16 transfer_type; +} __packed; + +/* sending and receiving irdata */ +struct redrat3_irdata { + struct redrat3_header header; + __be32 pause; + __be16 mod_freq_count; + __be16 num_periods; + __u8 max_lengths; + __u8 no_lengths; + __be16 max_sig_size; + __be16 sig_size; + __u8 no_repeats; + __be16 lens[RR3_DRIVER_MAXLENS]; /* not aligned */ + __u8 sigdata[RR3_MAX_SIG_SIZE]; +} __packed; + +/* firmware errors */ +struct redrat3_error { + struct redrat3_header header; + __be16 fw_error; +} __packed; + /* table of devices that work with this driver */ static struct usb_device_id redrat3_dev_table[] = { /* Original version of the RedRat3 */ @@ -180,20 +192,15 @@ struct redrat3_dev { /* the receive endpoint */ struct usb_endpoint_descriptor *ep_in; /* the buffer to receive data */ - unsigned char *bulk_in_buf; + void *bulk_in_buf; /* urb used to read ir data */ struct urb *read_urb; /* the send endpoint */ struct usb_endpoint_descriptor *ep_out; - /* the buffer to send data */ - unsigned char *bulk_out_buf; - /* the urb used to send data */ - struct urb *write_urb; /* usb dma */ dma_addr_t dma_in; - dma_addr_t dma_out; /* rx signal timeout timer */ struct timer_list rx_timeout; @@ -205,72 +212,15 @@ struct redrat3_dev { bool transmitting; /* store for current packet */ - char pbuf[RR3_MAX_BUF_SIZE]; - u16 pktlen; - u16 pkttype; + struct redrat3_irdata irdata; u16 bytes_read; - /* indicate whether we are going to reprocess - * the USB callback with a bigger buffer */ - int buftoosmall; - char *datap; u32 carrier; - char name[128]; + char name[64]; char phys[64]; }; -/* All incoming data buffers adhere to a very specific data format */ -struct redrat3_signal_header { - u16 length; /* Length of data being transferred */ - u16 transfer_type; /* Type of data transferred */ - u32 pause; /* Pause between main and repeat signals */ - u16 mod_freq_count; /* Value of timer on mod. freq. measurement */ - u16 no_periods; /* No. of periods over which mod. freq. is measured */ - u8 max_lengths; /* Max no. of lengths (i.e. size of array) */ - u8 no_lengths; /* Actual no. of elements in lengths array */ - u16 max_sig_size; /* Max no. of values in signal data array */ - u16 sig_size; /* Acuto no. of values in signal data array */ - u8 no_repeats; /* No. of repeats of repeat signal section */ - /* Here forward is the lengths and signal data */ -}; - -static void redrat3_dump_signal_header(struct redrat3_signal_header *header) -{ - pr_info("%s:\n", __func__); - pr_info(" * length: %u, transfer_type: 0x%02x\n", - header->length, header->transfer_type); - pr_info(" * pause: %u, freq_count: %u, no_periods: %u\n", - header->pause, header->mod_freq_count, header->no_periods); - pr_info(" * lengths: %u (max: %u)\n", - header->no_lengths, header->max_lengths); - pr_info(" * sig_size: %u (max: %u)\n", - header->sig_size, header->max_sig_size); - pr_info(" * repeats: %u\n", header->no_repeats); -} - -static void redrat3_dump_signal_data(char *buffer, u16 len) -{ - int offset, i; - char *data_vals; - - pr_info("%s:", __func__); - - offset = RR3_TX_HEADER_OFFSET + RR3_HEADER_LENGTH - + (RR3_DRIVER_MAXLENS * sizeof(u16)); - - /* read RR3_DRIVER_MAXLENS from ctrl msg */ - data_vals = buffer + offset; - - for (i = 0; i < len; i++) { - if (i % 10 == 0) - pr_cont("\n * "); - pr_cont("%02x ", *data_vals++); - } - - pr_cont("\n"); -} - /* * redrat3_issue_async * @@ -283,7 +233,6 @@ static void redrat3_issue_async(struct redrat3_dev *rr3) rr3_ftr(rr3->dev, "Entering %s\n", __func__); - memset(rr3->bulk_in_buf, 0, rr3->ep_in->wMaxPacketSize); res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC); if (res) rr3_dbg(rr3->dev, "%s: receive request FAILED! " @@ -352,13 +301,14 @@ static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code) } } -static u32 redrat3_val_to_mod_freq(struct redrat3_signal_header *ph) +static u32 redrat3_val_to_mod_freq(struct redrat3_irdata *irdata) { u32 mod_freq = 0; + u16 mod_freq_count = be16_to_cpu(irdata->mod_freq_count); - if (ph->mod_freq_count != 0) - mod_freq = (RR3_CLK * ph->no_periods) / - (ph->mod_freq_count * RR3_CLK_PER_COUNT); + if (mod_freq_count != 0) + mod_freq = (RR3_CLK * be16_to_cpu(irdata->num_periods)) / + (mod_freq_count * RR3_CLK_PER_COUNT); return mod_freq; } @@ -396,7 +346,6 @@ static u32 redrat3_us_to_len(u32 microsec) /* don't allow zero lengths to go back, breaks lirc */ return result ? result : 1; - } /* timer callback to send reset event */ @@ -411,16 +360,11 @@ static void redrat3_rx_timeout(unsigned long data) static void redrat3_process_ir_data(struct redrat3_dev *rr3) { DEFINE_IR_RAW_EVENT(rawir); - struct redrat3_signal_header header; struct device *dev; - int i, trailer = 0; + unsigned i, trailer = 0; + unsigned sig_size, single_len, offset, val; unsigned long delay; - u32 mod_freq, single_len; - u16 *len_vals; - u8 *data_vals; - u32 tmp32; - u16 tmp16; - char *sig_data; + u32 mod_freq; if (!rr3) { pr_err("%s called with no context!\n", __func__); @@ -430,57 +374,20 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) rr3_ftr(rr3->dev, "Entered %s\n", __func__); dev = rr3->dev; - sig_data = rr3->pbuf; - - header.length = rr3->pktlen; - header.transfer_type = rr3->pkttype; - - /* Sanity check */ - if (!(header.length >= RR3_HEADER_LENGTH)) - dev_warn(dev, "read returned less than rr3 header len\n"); /* Make sure we reset the IR kfifo after a bit of inactivity */ delay = usecs_to_jiffies(rr3->hw_timeout); mod_timer(&rr3->rx_timeout, jiffies + delay); - memcpy(&tmp32, sig_data + RR3_PAUSE_OFFSET, sizeof(tmp32)); - header.pause = be32_to_cpu(tmp32); - - memcpy(&tmp16, sig_data + RR3_FREQ_COUNT_OFFSET, sizeof(tmp16)); - header.mod_freq_count = be16_to_cpu(tmp16); - - memcpy(&tmp16, sig_data + RR3_NUM_PERIOD_OFFSET, sizeof(tmp16)); - header.no_periods = be16_to_cpu(tmp16); - - header.max_lengths = sig_data[RR3_MAX_LENGTHS_OFFSET]; - header.no_lengths = sig_data[RR3_NUM_LENGTHS_OFFSET]; - - memcpy(&tmp16, sig_data + RR3_MAX_SIGS_OFFSET, sizeof(tmp16)); - header.max_sig_size = be16_to_cpu(tmp16); - - memcpy(&tmp16, sig_data + RR3_NUM_SIGS_OFFSET, sizeof(tmp16)); - header.sig_size = be16_to_cpu(tmp16); - - header.no_repeats= sig_data[RR3_REPEATS_OFFSET]; - - if (debug) { - redrat3_dump_signal_header(&header); - redrat3_dump_signal_data(sig_data, header.sig_size); - } - - mod_freq = redrat3_val_to_mod_freq(&header); + mod_freq = redrat3_val_to_mod_freq(&rr3->irdata); rr3_dbg(dev, "Got mod_freq of %u\n", mod_freq); - /* Here we pull out the 'length' values from the signal */ - len_vals = (u16 *)(sig_data + RR3_HEADER_LENGTH); - - data_vals = sig_data + RR3_HEADER_LENGTH + - (header.max_lengths * sizeof(u16)); - /* process each rr3 encoded byte into an int */ - for (i = 0; i < header.sig_size; i++) { - u16 val = len_vals[data_vals[i]]; - single_len = redrat3_len_to_us((u32)be16_to_cpu(val)); + sig_size = be16_to_cpu(rr3->irdata.sig_size); + for (i = 0; i < sig_size; i++) { + offset = rr3->irdata.sigdata[i]; + val = get_unaligned_be16(&rr3->irdata.lens[offset]); + single_len = redrat3_len_to_us(val); /* we should always get pulse/space/pulse/space samples */ if (i % 2) @@ -515,8 +422,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) rr3_dbg(dev, "calling ir_raw_event_handle\n"); ir_raw_event_handle(rr3->rc); - - return; } /* Util fn to send rr3 cmds */ @@ -540,7 +445,7 @@ static u8 redrat3_send_cmd(int cmd, struct redrat3_dev *rr3) __func__, res, *data); res = -EIO; } else - res = (u8)data[0]; + res = data[0]; kfree(data); @@ -598,22 +503,18 @@ static inline void redrat3_delete(struct redrat3_dev *rr3, { rr3_ftr(rr3->dev, "%s cleaning up\n", __func__); usb_kill_urb(rr3->read_urb); - usb_kill_urb(rr3->write_urb); usb_free_urb(rr3->read_urb); - usb_free_urb(rr3->write_urb); - usb_free_coherent(udev, rr3->ep_in->wMaxPacketSize, + usb_free_coherent(udev, le16_to_cpu(rr3->ep_in->wMaxPacketSize), rr3->bulk_in_buf, rr3->dma_in); - usb_free_coherent(udev, rr3->ep_out->wMaxPacketSize, - rr3->bulk_out_buf, rr3->dma_out); kfree(rr3); } static u32 redrat3_get_timeout(struct redrat3_dev *rr3) { - u32 *tmp; + __be32 *tmp; u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */ int len, ret, pipe; @@ -628,14 +529,16 @@ static u32 redrat3_get_timeout(struct redrat3_dev *rr3) ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5); - if (ret != len) { + if (ret != len) dev_warn(rr3->dev, "Failed to read timeout from hardware\n"); - return timeout; + else { + timeout = redrat3_len_to_us(be32_to_cpup(tmp)); + + rr3_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000); } - timeout = redrat3_len_to_us(be32_to_cpu(*tmp)); + kfree(tmp); - rr3_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000); return timeout; } @@ -652,7 +555,7 @@ static void redrat3_reset(struct redrat3_dev *rr3) rxpipe = usb_rcvctrlpipe(udev, 0); txpipe = usb_sndctrlpipe(udev, 0); - val = kzalloc(len, GFP_KERNEL); + val = kmalloc(len, GFP_KERNEL); if (!val) { dev_err(dev, "Memory allocation failure\n"); return; @@ -706,82 +609,74 @@ static void redrat3_get_firmware_rev(struct redrat3_dev *rr3) rr3_ftr(rr3->dev, "Exiting %s\n", __func__); } -static void redrat3_read_packet_start(struct redrat3_dev *rr3, int len) +static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len) { - u16 tx_error; - u16 hdrlen; + struct redrat3_header *header = rr3->bulk_in_buf; + unsigned pktlen, pkttype; rr3_ftr(rr3->dev, "Entering %s\n", __func__); /* grab the Length and type of transfer */ - memcpy(&(rr3->pktlen), (unsigned char *) rr3->bulk_in_buf, - sizeof(rr3->pktlen)); - memcpy(&(rr3->pkttype), ((unsigned char *) rr3->bulk_in_buf + - sizeof(rr3->pktlen)), - sizeof(rr3->pkttype)); + pktlen = be16_to_cpu(header->length); + pkttype = be16_to_cpu(header->transfer_type); - /*data needs conversion to know what its real values are*/ - rr3->pktlen = be16_to_cpu(rr3->pktlen); - rr3->pkttype = be16_to_cpu(rr3->pkttype); + if (pktlen > sizeof(rr3->irdata)) { + dev_warn(rr3->dev, "packet length %u too large\n", pktlen); + return; + } - switch (rr3->pkttype) { + switch (pkttype) { case RR3_ERROR: - memcpy(&tx_error, ((unsigned char *)rr3->bulk_in_buf - + (sizeof(rr3->pktlen) + sizeof(rr3->pkttype))), - sizeof(tx_error)); - tx_error = be16_to_cpu(tx_error); - redrat3_dump_fw_error(rr3, tx_error); + if (len >= sizeof(struct redrat3_error)) { + struct redrat3_error *error = rr3->bulk_in_buf; + unsigned fw_error = be16_to_cpu(error->fw_error); + redrat3_dump_fw_error(rr3, fw_error); + } break; case RR3_MOD_SIGNAL_IN: - hdrlen = sizeof(rr3->pktlen) + sizeof(rr3->pkttype); + memcpy(&rr3->irdata, rr3->bulk_in_buf, len); rr3->bytes_read = len; - rr3->bytes_read -= hdrlen; - rr3->datap = &(rr3->pbuf[0]); - - memcpy(rr3->datap, ((unsigned char *)rr3->bulk_in_buf + hdrlen), - rr3->bytes_read); - rr3->datap += rr3->bytes_read; rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", - rr3->bytes_read, rr3->pktlen); + rr3->bytes_read, pktlen); break; default: - rr3_dbg(rr3->dev, "ignoring packet with type 0x%02x, " - "len of %d, 0x%02x\n", rr3->pkttype, len, rr3->pktlen); + rr3_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n", + pkttype, len, pktlen); break; } } -static void redrat3_read_packet_continue(struct redrat3_dev *rr3, int len) +static void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len) { + void *irdata = &rr3->irdata; rr3_ftr(rr3->dev, "Entering %s\n", __func__); - memcpy(rr3->datap, (unsigned char *)rr3->bulk_in_buf, len); - rr3->datap += len; + if (len + rr3->bytes_read > sizeof(rr3->irdata)) { + dev_warn(rr3->dev, "too much data for packet\n"); + rr3->bytes_read = 0; + return; + } + + memcpy(irdata + rr3->bytes_read, rr3->bulk_in_buf, len); rr3->bytes_read += len; - rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", - rr3->bytes_read, rr3->pktlen); + rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read, + be16_to_cpu(rr3->irdata.header.length)); } /* gather IR data from incoming urb, process it when we have enough */ -static int redrat3_get_ir_data(struct redrat3_dev *rr3, int len) +static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len) { struct device *dev = rr3->dev; + unsigned pkttype; int ret = 0; rr3_ftr(dev, "Entering %s\n", __func__); - if (rr3->pktlen > RR3_MAX_BUF_SIZE) { - dev_err(rr3->dev, "error: packet larger than buffer\n"); - ret = -EINVAL; - goto out; - } - - if ((rr3->bytes_read == 0) && - (len >= (sizeof(rr3->pkttype) + sizeof(rr3->pktlen)))) { + if (rr3->bytes_read == 0 && len >= sizeof(struct redrat3_header)) { redrat3_read_packet_start(rr3, len); } else if (rr3->bytes_read != 0) { redrat3_read_packet_continue(rr3, len); @@ -791,31 +686,25 @@ static int redrat3_get_ir_data(struct redrat3_dev *rr3, int len) goto out; } - if (rr3->bytes_read > rr3->pktlen) { - dev_err(dev, "bytes_read (%d) greater than pktlen (%d)\n", - rr3->bytes_read, rr3->pktlen); - ret = -EINVAL; - goto out; - } else if (rr3->bytes_read < rr3->pktlen) + if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length)) /* we're still accumulating data */ return 0; /* if we get here, we've got IR data to decode */ - if (rr3->pkttype == RR3_MOD_SIGNAL_IN) + pkttype = be16_to_cpu(rr3->irdata.header.transfer_type); + if (pkttype == RR3_MOD_SIGNAL_IN) redrat3_process_ir_data(rr3); else - rr3_dbg(dev, "discarding non-signal data packet " - "(type 0x%02x)\n", rr3->pkttype); + rr3_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n", + pkttype); out: rr3->bytes_read = 0; - rr3->pktlen = 0; - rr3->pkttype = 0; return ret; } /* callback function from USB when async USB request has completed */ -static void redrat3_handle_async(struct urb *urb, struct pt_regs *regs) +static void redrat3_handle_async(struct urb *urb) { struct redrat3_dev *rr3; int ret; @@ -851,34 +740,16 @@ static void redrat3_handle_async(struct urb *urb, struct pt_regs *regs) default: dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status); rr3->bytes_read = 0; - rr3->pktlen = 0; - rr3->pkttype = 0; break; } } -static void redrat3_write_bulk_callback(struct urb *urb, struct pt_regs *regs) -{ - struct redrat3_dev *rr3; - int len; - - if (!urb) - return; - - rr3 = urb->context; - if (rr3) { - len = urb->actual_length; - rr3_ftr(rr3->dev, "%s: called (status=%d len=%d)\n", - __func__, urb->status, len); - } -} - static u16 mod_freq_to_val(unsigned int mod_freq) { int mult = 6000000; /* Clk used in mod. freq. generation is CLK24/4. */ - return (u16)(65536 - (mult / mod_freq)); + return 65536 - (mult / mod_freq); } static int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) @@ -900,17 +771,12 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, { struct redrat3_dev *rr3 = rcdev->priv; struct device *dev = rr3->dev; - struct redrat3_signal_header header; - int i, j, ret, ret_len, offset; + struct redrat3_irdata *irdata = NULL; + int ret, ret_len; int lencheck, cur_sample_len, pipe; - char *buffer = NULL, *sigdata = NULL; int *sample_lens = NULL; - u32 tmpi; - u16 tmps; - u8 *datap; u8 curlencheck = 0; - u16 *lengths_ptr; - int sendbuf_len; + unsigned i, sendbuf_len; rr3_ftr(dev, "Entering %s\n", __func__); @@ -931,8 +797,19 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, goto out; } + irdata = kzalloc(sizeof(*irdata), GFP_KERNEL); + if (!irdata) { + ret = -ENOMEM; + goto out; + } + for (i = 0; i < count; i++) { cur_sample_len = redrat3_us_to_len(txbuf[i]); + if (cur_sample_len > 0xffff) { + dev_warn(dev, "transmit period of %uus truncated to %uus\n", + txbuf[i], redrat3_len_to_us(0xffff)); + cur_sample_len = 0xffff; + } for (lencheck = 0; lencheck < curlencheck; lencheck++) { if (sample_lens[lencheck] == cur_sample_len) break; @@ -944,94 +821,41 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, /* now convert the value to a proper * rr3 value.. */ sample_lens[curlencheck] = cur_sample_len; + put_unaligned_be16(cur_sample_len, + &irdata->lens[curlencheck]); curlencheck++; } else { count = i - 1; break; } } + irdata->sigdata[i] = lencheck; } - sigdata = kzalloc((count + RR3_TX_TRAILER_LEN), GFP_KERNEL); - if (!sigdata) { - ret = -ENOMEM; - goto out; - } - - sigdata[count] = RR3_END_OF_SIGNAL; - sigdata[count + 1] = RR3_END_OF_SIGNAL; - for (i = 0; i < count; i++) { - for (j = 0; j < curlencheck; j++) { - if (sample_lens[j] == redrat3_us_to_len(txbuf[i])) - sigdata[i] = j; - } - } - - offset = RR3_TX_HEADER_OFFSET; - sendbuf_len = RR3_HEADER_LENGTH + (sizeof(u16) * RR3_DRIVER_MAXLENS) - + count + RR3_TX_TRAILER_LEN + offset; - - buffer = kzalloc(sendbuf_len, GFP_KERNEL); - if (!buffer) { - ret = -ENOMEM; - goto out; - } + irdata->sigdata[count] = RR3_END_OF_SIGNAL; + irdata->sigdata[count + 1] = RR3_END_OF_SIGNAL; + sendbuf_len = offsetof(struct redrat3_irdata, + sigdata[count + RR3_TX_TRAILER_LEN]); /* fill in our packet header */ - header.length = sendbuf_len - offset; - header.transfer_type = RR3_MOD_SIGNAL_OUT; - header.pause = redrat3_len_to_us(100); - header.mod_freq_count = mod_freq_to_val(rr3->carrier); - header.no_periods = 0; /* n/a to transmit */ - header.max_lengths = RR3_DRIVER_MAXLENS; - header.no_lengths = curlencheck; - header.max_sig_size = RR3_MAX_SIG_SIZE; - header.sig_size = count + RR3_TX_TRAILER_LEN; - /* we currently rely on repeat handling in the IR encoding source */ - header.no_repeats = 0; - - tmps = cpu_to_be16(header.length); - memcpy(buffer, &tmps, 2); - - tmps = cpu_to_be16(header.transfer_type); - memcpy(buffer + 2, &tmps, 2); - - tmpi = cpu_to_be32(header.pause); - memcpy(buffer + offset, &tmpi, sizeof(tmpi)); - - tmps = cpu_to_be16(header.mod_freq_count); - memcpy(buffer + offset + RR3_FREQ_COUNT_OFFSET, &tmps, 2); - - buffer[offset + RR3_NUM_LENGTHS_OFFSET] = header.no_lengths; - - tmps = cpu_to_be16(header.sig_size); - memcpy(buffer + offset + RR3_NUM_SIGS_OFFSET, &tmps, 2); - - buffer[offset + RR3_REPEATS_OFFSET] = header.no_repeats; - - lengths_ptr = (u16 *)(buffer + offset + RR3_HEADER_LENGTH); - for (i = 0; i < curlencheck; ++i) - lengths_ptr[i] = cpu_to_be16(sample_lens[i]); - - datap = (u8 *)(buffer + offset + RR3_HEADER_LENGTH + - (sizeof(u16) * RR3_DRIVER_MAXLENS)); - memcpy(datap, sigdata, (count + RR3_TX_TRAILER_LEN)); - - if (debug) { - redrat3_dump_signal_header(&header); - redrat3_dump_signal_data(buffer, header.sig_size); - } + irdata->header.length = cpu_to_be16(sendbuf_len - + sizeof(struct redrat3_header)); + irdata->header.transfer_type = cpu_to_be16(RR3_MOD_SIGNAL_OUT); + irdata->pause = cpu_to_be32(redrat3_len_to_us(100)); + irdata->mod_freq_count = cpu_to_be16(mod_freq_to_val(rr3->carrier)); + irdata->no_lengths = curlencheck; + irdata->sig_size = cpu_to_be16(count + RR3_TX_TRAILER_LEN); pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress); - tmps = usb_bulk_msg(rr3->udev, pipe, buffer, + ret = usb_bulk_msg(rr3->udev, pipe, irdata, sendbuf_len, &ret_len, 10 * HZ); - rr3_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, tmps); + rr3_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret); /* now tell the hardware to transmit what we sent it */ pipe = usb_rcvctrlpipe(rr3->udev, 0); ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, 0, buffer, 2, HZ * 10); + 0, 0, irdata, 2, HZ * 10); if (ret < 0) dev_err(dev, "Error: control msg send failed, rc %d\n", ret); @@ -1040,8 +864,7 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, out: kfree(sample_lens); - kfree(buffer); - kfree(sigdata); + kfree(irdata); rr3->transmitting = false; /* rr3 re-enables rc detector because it was enabled before */ @@ -1165,38 +988,18 @@ static int redrat3_dev_probe(struct usb_interface *intf, } rr3->ep_in = ep_in; - rr3->bulk_in_buf = usb_alloc_coherent(udev, ep_in->wMaxPacketSize, - GFP_ATOMIC, &rr3->dma_in); + rr3->bulk_in_buf = usb_alloc_coherent(udev, + le16_to_cpu(ep_in->wMaxPacketSize), GFP_ATOMIC, &rr3->dma_in); if (!rr3->bulk_in_buf) { dev_err(dev, "Read buffer allocation failure\n"); goto error; } pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress); - usb_fill_bulk_urb(rr3->read_urb, udev, pipe, - rr3->bulk_in_buf, ep_in->wMaxPacketSize, - (usb_complete_t)redrat3_handle_async, rr3); - - /* set up bulk-out endpoint*/ - rr3->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!rr3->write_urb) { - dev_err(dev, "Write urb allocation failure\n"); - goto error; - } + usb_fill_bulk_urb(rr3->read_urb, udev, pipe, rr3->bulk_in_buf, + le16_to_cpu(ep_in->wMaxPacketSize), redrat3_handle_async, rr3); rr3->ep_out = ep_out; - rr3->bulk_out_buf = usb_alloc_coherent(udev, ep_out->wMaxPacketSize, - GFP_ATOMIC, &rr3->dma_out); - if (!rr3->bulk_out_buf) { - dev_err(dev, "Write buffer allocation failure\n"); - goto error; - } - - pipe = usb_sndbulkpipe(udev, ep_out->bEndpointAddress); - usb_fill_bulk_urb(rr3->write_urb, udev, pipe, - rr3->bulk_out_buf, ep_out->wMaxPacketSize, - (usb_complete_t)redrat3_write_bulk_callback, rr3); - rr3->udev = udev; redrat3_reset(rr3); diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index cf0d47f..891762d 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -347,6 +347,7 @@ static int ttusbir_probe(struct usb_interface *intf, return 0; out3: rc_unregister_device(rc); + rc = NULL; out2: led_classdev_unregister(&tt->led); out: diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 535a18d..87af2d3 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1151,6 +1151,7 @@ exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); exit_unregister_device: rc_unregister_device(data->dev); + data->dev = NULL; exit_free_rc: rc_free_device(data->dev); exit_unregister_led: diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index e8fdf71..f6768ca 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -241,4 +241,18 @@ config MEDIA_TUNER_TUA9001 default m if !MEDIA_SUBDRV_AUTOSELECT help Infineon TUA 9001 silicon tuner driver. + +config MEDIA_TUNER_IT913X + tristate "ITE Tech IT913x silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + ITE Tech IT913x silicon tuner driver. + +config MEDIA_TUNER_R820T + tristate "Rafael Micro R820T silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Rafael Micro R820T silicon tuner driver. endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 5e569b1..308f108 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.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_R820T) += r820t.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h index 71b1935..3783a0b 100644 --- a/drivers/media/tuners/e4000.h +++ b/drivers/media/tuners/e4000.h @@ -21,6 +21,7 @@ #ifndef E4000_H #define E4000_H +#include <linux/kconfig.h> #include "dvb_frontend.h" struct e4000_config { @@ -36,8 +37,7 @@ struct e4000_config { u32 clock; }; -#if defined(CONFIG_MEDIA_TUNER_E4000) || \ - (defined(CONFIG_MEDIA_TUNER_E4000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000) extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct e4000_config *cfg); #else diff --git a/drivers/media/tuners/fc0011.h b/drivers/media/tuners/fc0011.h index 0ee581f..43ec893 100644 --- a/drivers/media/tuners/fc0011.h +++ b/drivers/media/tuners/fc0011.h @@ -1,6 +1,7 @@ #ifndef LINUX_FC0011_H_ #define LINUX_FC0011_H_ +#include <linux/kconfig.h> #include "dvb_frontend.h" @@ -22,8 +23,7 @@ enum fc0011_fe_callback_commands { FC0011_FE_CALLBACK_RESET, }; -#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ - defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_FC0011) struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0011_config *config); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 54508fc..1d08057 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -21,6 +21,7 @@ #ifndef _FC0012_H_ #define _FC0012_H_ +#include <linux/kconfig.h> #include "dvb_frontend.h" #include "fc001x-common.h" @@ -48,8 +49,7 @@ struct fc0012_config { bool clock_out; }; -#if defined(CONFIG_MEDIA_TUNER_FC0012) || \ - (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_FC0012) extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0012_config *cfg); diff --git a/drivers/media/tuners/fc0013.h b/drivers/media/tuners/fc0013.h index 594efd6..d65d5b3 100644 --- a/drivers/media/tuners/fc0013.h +++ b/drivers/media/tuners/fc0013.h @@ -22,11 +22,11 @@ #ifndef _FC0013_H_ #define _FC0013_H_ +#include <linux/kconfig.h> #include "dvb_frontend.h" #include "fc001x-common.h" -#if defined(CONFIG_MEDIA_TUNER_FC0013) || \ - (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_FC0013) extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_address, int dual_master, diff --git a/drivers/media/tuners/fc2580.h b/drivers/media/tuners/fc2580.h index 222601e..9c43c1c 100644 --- a/drivers/media/tuners/fc2580.h +++ b/drivers/media/tuners/fc2580.h @@ -21,6 +21,7 @@ #ifndef FC2580_H #define FC2580_H +#include <linux/kconfig.h> #include "dvb_frontend.h" struct fc2580_config { @@ -36,8 +37,7 @@ struct fc2580_config { u32 clock; }; -#if defined(CONFIG_MEDIA_TUNER_FC2580) || \ - (defined(CONFIG_MEDIA_TUNER_FC2580_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_FC2580) extern struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc2580_config *cfg); #else diff --git a/drivers/media/tuners/max2165.h b/drivers/media/tuners/max2165.h index c063c36..26e1dc6 100644 --- a/drivers/media/tuners/max2165.h +++ b/drivers/media/tuners/max2165.h @@ -22,6 +22,8 @@ #ifndef __MAX2165_H__ #define __MAX2165_H__ +#include <linux/kconfig.h> + struct dvb_frontend; struct i2c_adapter; @@ -30,8 +32,7 @@ struct max2165_config { u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ }; -#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ - (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MAX2165) extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct max2165_config *cfg); diff --git a/drivers/media/tuners/mc44s803.h b/drivers/media/tuners/mc44s803.h index 34f3892..9aae50a 100644 --- a/drivers/media/tuners/mc44s803.h +++ b/drivers/media/tuners/mc44s803.h @@ -22,6 +22,8 @@ #ifndef MC44S803_H #define MC44S803_H +#include <linux/kconfig.h> + struct dvb_frontend; struct i2c_adapter; @@ -30,8 +32,7 @@ struct mc44s803_config { u8 dig_out; }; -#if defined(CONFIG_MEDIA_TUNER_MC44S803) || \ - (defined(CONFIG_MEDIA_TUNER_MC44S803_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MC44S803) extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mc44s803_config *cfg); #else diff --git a/drivers/media/tuners/mxl5005s.h b/drivers/media/tuners/mxl5005s.h index fc8a1ff..ae8db88 100644 --- a/drivers/media/tuners/mxl5005s.h +++ b/drivers/media/tuners/mxl5005s.h @@ -23,6 +23,8 @@ #ifndef __MXL5005S_H #define __MXL5005S_H +#include <linux/kconfig.h> + #include <linux/i2c.h> #include "dvb_frontend.h" @@ -116,8 +118,7 @@ struct mxl5005s_config { u8 AgcMasterByte; }; -#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \ - (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MXL5005S) extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mxl5005s_config *config); diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c new file mode 100644 index 0000000..4835021 --- /dev/null +++ b/drivers/media/tuners/r820t.c @@ -0,0 +1,2355 @@ +/* + * Rafael Micro R820T driver + * + * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * This driver was written from scratch, based on an existing driver + * that it is part of rtl-sdr git tree, released under GPLv2: + * https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug + * https://github.com/n1gp/gr-baz + * + * From what I understood from the threads, the original driver was converted + * to userspace from a Realtek tree. I couldn't find the original tree. + * However, the original driver look awkward on my eyes. So, I decided to + * write a new version from it from the scratch, while trying to reproduce + * everything found there. + * + * TODO: + * After locking, the original driver seems to have some routines to + * improve reception. This was not implemented here yet. + * + * RF Gain set/get is not implemented. + * + * 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/videodev2.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/bitrev.h> + +#include "tuner-i2c.h" +#include "r820t.h" + +/* + * FIXME: I think that there are only 32 registers, but better safe than + * sorry. After finishing the driver, we may review it. + */ +#define REG_SHADOW_START 5 +#define NUM_REGS 27 +#define NUM_IMR 5 +#define IMR_TRIAL 9 + +#define VER_NUM 49 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +static int no_imr_cal; +module_param(no_imr_cal, int, 0444); +MODULE_PARM_DESC(no_imr_cal, "Disable IMR calibration at module init"); + + +/* + * enums and structures + */ + +enum xtal_cap_value { + XTAL_LOW_CAP_30P = 0, + XTAL_LOW_CAP_20P, + XTAL_LOW_CAP_10P, + XTAL_LOW_CAP_0P, + XTAL_HIGH_CAP_0P +}; + +struct r820t_sect_type { + u8 phase_y; + u8 gain_x; + u16 value; +}; + +struct r820t_priv { + struct list_head hybrid_tuner_instance_list; + const struct r820t_config *cfg; + struct tuner_i2c_props i2c_props; + struct mutex lock; + + u8 regs[NUM_REGS]; + u8 buf[NUM_REGS + 1]; + enum xtal_cap_value xtal_cap_sel; + u16 pll; /* kHz */ + u32 int_freq; + u8 fil_cal_code; + bool imr_done; + bool has_lock; + bool init_done; + struct r820t_sect_type imr_data[NUM_IMR]; + + /* Store current mode */ + u32 delsys; + enum v4l2_tuner_type type; + v4l2_std_id std; + u32 bw; /* in MHz */ +}; + +struct r820t_freq_range { + u32 freq; + u8 open_d; + u8 rf_mux_ploy; + u8 tf_c; + u8 xtal_cap20p; + u8 xtal_cap10p; + u8 xtal_cap0p; + u8 imr_mem; /* Not used, currently */ +}; + +#define VCO_POWER_REF 0x02 +#define DIP_FREQ 32000000 + +/* + * Static constants + */ + +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(r820t_list_mutex); + +/* Those initial values start from REG_SHADOW_START */ +static const u8 r820t_init_array[NUM_REGS] = { + 0x83, 0x32, 0x75, /* 05 to 07 */ + 0xc0, 0x40, 0xd6, 0x6c, /* 08 to 0b */ + 0xf5, 0x63, 0x75, 0x68, /* 0c to 0f */ + 0x6c, 0x83, 0x80, 0x00, /* 10 to 13 */ + 0x0f, 0x00, 0xc0, 0x30, /* 14 to 17 */ + 0x48, 0xcc, 0x60, 0x00, /* 18 to 1b */ + 0x54, 0xae, 0x4a, 0xc0 /* 1c to 1f */ +}; + +/* Tuner frequency ranges */ +static const struct r820t_freq_range freq_ranges[] = { + { + .freq = 0, + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xdf, /* R27[7:0] band2,band0 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 50, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xbe, /* R27[7:0] band4,band1 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 55, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x8b, /* R27[7:0] band7,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 60, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x7b, /* R27[7:0] band8,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 65, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x69, /* R27[7:0] band9,band6 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 70, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x58, /* R27[7:0] band10,band7 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 75, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 80, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 90, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 100, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 110, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 120, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 140, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x14, /* R27[7:0] band14,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 180, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 220, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 250, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x11, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 280, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 310, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 450, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 588, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 650, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 4, + } +}; + +static int r820t_xtal_capacitor[][2] = { + { 0x0b, XTAL_LOW_CAP_30P }, + { 0x02, XTAL_LOW_CAP_20P }, + { 0x01, XTAL_LOW_CAP_10P }, + { 0x00, XTAL_LOW_CAP_0P }, + { 0x10, XTAL_HIGH_CAP_0P }, +}; + +/* + * measured with a Racal 6103E GSM test set at 928 MHz with -60 dBm + * input power, for raw results see: + * http://steve-m.de/projects/rtl-sdr/gain_measurement/r820t/ + */ + +static const int r820t_lna_gain_steps[] = { + 0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13 +}; + +static const int r820t_mixer_gain_steps[] = { + 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 +}; + +/* + * I2C read/write code and shadow registers logic + */ +static void shadow_store(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int r = reg - REG_SHADOW_START; + + if (r < 0) { + len += r; + r = 0; + } + if (len <= 0) + return; + if (len > NUM_REGS) + len = NUM_REGS; + + tuner_dbg("%s: prev reg=%02x len=%d: %*ph\n", + __func__, r + REG_SHADOW_START, len, len, val); + + memcpy(&priv->regs[r], val, len); +} + +static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int rc, size, pos = 0; + + /* Store the shadow registers */ + shadow_store(priv, reg, val, len); + + do { + if (len > priv->cfg->max_i2c_msg_len - 1) + size = priv->cfg->max_i2c_msg_len - 1; + else + size = len; + + /* Fill I2C buffer */ + priv->buf[0] = reg; + memcpy(&priv->buf[1], &val[pos], size); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, priv->buf, size + 1); + if (rc != size + 1) { + tuner_info("%s: i2c wr failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, size, size, &priv->buf[1]); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + tuner_dbg("%s: i2c wr reg=%02x len=%d: %*ph\n", + __func__, reg, size, size, &priv->buf[1]); + + reg += size; + len -= size; + pos += size; + } while (len > 0); + + return 0; +} + +static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) +{ + return r820t_write(priv, reg, &val, 1); +} + +static int r820t_read_cache_reg(struct r820t_priv *priv, int reg) +{ + reg -= REG_SHADOW_START; + + if (reg >= 0 && reg < NUM_REGS) + return priv->regs[reg]; + else + return -EINVAL; +} + +static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, + u8 bit_mask) +{ + int rc = r820t_read_cache_reg(priv, reg); + + if (rc < 0) + return rc; + + val = (rc & ~bit_mask) | (val & bit_mask); + + return r820t_write(priv, reg, &val, 1); +} + +static int r820t_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) +{ + int rc, i; + u8 *p = &priv->buf[1]; + + priv->buf[0] = reg; + + rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, priv->buf, 1, p, len); + if (rc != len) { + tuner_info("%s: i2c rd failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, len, len, p); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + + /* Copy data to the output buffer */ + for (i = 0; i < len; i++) + val[i] = bitrev8(p[i]); + + tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", + __func__, reg, len, len, val); + + return 0; +} + +/* + * r820t tuning logic + */ + +static int r820t_set_mux(struct r820t_priv *priv, u32 freq) +{ + const struct r820t_freq_range *range; + int i, rc; + u8 val, reg08, reg09; + + /* Get the proper frequency range */ + freq = freq / 1000000; + for (i = 0; i < ARRAY_SIZE(freq_ranges) - 1; i++) { + if (freq < freq_ranges[i + 1].freq) + break; + } + range = &freq_ranges[i]; + + tuner_dbg("set r820t range#%d for frequency %d MHz\n", i, freq); + + /* Open Drain */ + rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); + if (rc < 0) + return rc; + + /* RF_MUX,Polymux */ + rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); + if (rc < 0) + return rc; + + /* TF BAND */ + rc = r820t_write_reg(priv, 0x1b, range->tf_c); + if (rc < 0) + return rc; + + /* XTAL CAP & Drive */ + switch (priv->xtal_cap_sel) { + case XTAL_LOW_CAP_30P: + case XTAL_LOW_CAP_20P: + val = range->xtal_cap20p | 0x08; + break; + case XTAL_LOW_CAP_10P: + val = range->xtal_cap10p | 0x08; + break; + case XTAL_HIGH_CAP_0P: + val = range->xtal_cap0p | 0x00; + break; + default: + case XTAL_LOW_CAP_0P: + val = range->xtal_cap0p | 0x08; + break; + } + rc = r820t_write_reg_mask(priv, 0x10, val, 0x0b); + if (rc < 0) + return rc; + + if (priv->imr_done) { + reg08 = priv->imr_data[range->imr_mem].gain_x; + reg09 = priv->imr_data[range->imr_mem].phase_y; + } else { + reg08 = 0; + reg09 = 0; + } + rc = r820t_write_reg_mask(priv, 0x08, reg08, 0x3f); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x09, reg09, 0x3f); + + return rc; +} + +static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, + u32 freq) +{ + u32 vco_freq; + int rc, i; + unsigned sleep_time = 10000; + u32 vco_fra; /* VCO contribution by SDM (kHz) */ + u32 vco_min = 1770000; + u32 vco_max = vco_min * 2; + u32 pll_ref; + u16 n_sdm = 2; + u16 sdm = 0; + u8 mix_div = 2; + u8 div_buf = 0; + u8 div_num = 0; + u8 refdiv2 = 0; + u8 ni, si, nint, vco_fine_tune, val; + u8 data[5]; + + /* Frequency in kHz */ + freq = freq / 1000; + pll_ref = priv->cfg->xtal / 1000; + +#if 0 + /* Doesn't exist on rtl-sdk, and on field tests, caused troubles */ + if ((priv->cfg->rafael_chip == CHIP_R620D) || + (priv->cfg->rafael_chip == CHIP_R828D) || + (priv->cfg->rafael_chip == CHIP_R828)) { + /* ref set refdiv2, reffreq = Xtal/2 on ATV application */ + if (type != V4L2_TUNER_DIGITAL_TV) { + pll_ref /= 2; + refdiv2 = 0x10; + sleep_time = 20000; + } + } else { + if (priv->cfg->xtal > 24000000) { + pll_ref /= 2; + refdiv2 = 0x10; + } + } +#endif + + rc = r820t_write_reg_mask(priv, 0x10, refdiv2, 0x10); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set VCO current = 100 */ + rc = r820t_write_reg_mask(priv, 0x12, 0x80, 0xe0); + if (rc < 0) + return rc; + + /* Calculate divider */ + while (mix_div <= 64) { + if (((freq * mix_div) >= vco_min) && + ((freq * mix_div) < vco_max)) { + div_buf = mix_div; + while (div_buf > 2) { + div_buf = div_buf >> 1; + div_num++; + } + break; + } + mix_div = mix_div << 1; + } + + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + vco_fine_tune = (data[4] & 0x30) >> 4; + + if (vco_fine_tune > VCO_POWER_REF) + div_num = div_num - 1; + else if (vco_fine_tune < VCO_POWER_REF) + div_num = div_num + 1; + + rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0); + if (rc < 0) + return rc; + + vco_freq = freq * mix_div; + nint = vco_freq / (2 * pll_ref); + vco_fra = vco_freq - 2 * pll_ref * nint; + + /* boundary spur prevention */ + if (vco_fra < pll_ref / 64) { + vco_fra = 0; + } else if (vco_fra > pll_ref * 127 / 64) { + vco_fra = 0; + nint++; + } else if ((vco_fra > pll_ref * 127 / 128) && (vco_fra < pll_ref)) { + vco_fra = pll_ref * 127 / 128; + } else if ((vco_fra > pll_ref) && (vco_fra < pll_ref * 129 / 128)) { + vco_fra = pll_ref * 129 / 128; + } + + if (nint > 63) { + tuner_info("No valid PLL values for %u kHz!\n", freq); + return -EINVAL; + } + + ni = (nint - 13) / 4; + si = nint - 4 * ni - 13; + + rc = r820t_write_reg(priv, 0x14, ni + (si << 6)); + if (rc < 0) + return rc; + + /* pw_sdm */ + if (!vco_fra) + val = 0x08; + else + val = 0x00; + + rc = r820t_write_reg_mask(priv, 0x12, val, 0x08); + if (rc < 0) + return rc; + + /* sdm calculator */ + while (vco_fra > 1) { + if (vco_fra > (2 * pll_ref / n_sdm)) { + sdm = sdm + 32768 / (n_sdm / 2); + vco_fra = vco_fra - 2 * pll_ref / n_sdm; + if (n_sdm >= 0x8000) + break; + } + n_sdm = n_sdm << 1; + } + + tuner_dbg("freq %d kHz, pll ref %d%s, sdm=0x%04x\n", + freq, pll_ref, refdiv2 ? " / 2" : "", sdm); + + rc = r820t_write_reg(priv, 0x16, sdm >> 8); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x15, sdm & 0xff); + if (rc < 0) + return rc; + + for (i = 0; i < 2; i++) { + usleep_range(sleep_time, sleep_time + 1000); + + /* Check if PLL has locked */ + rc = r820t_read(priv, 0x00, data, 3); + if (rc < 0) + return rc; + if (data[2] & 0x40) + break; + + if (!i) { + /* Didn't lock. Increase VCO current */ + rc = r820t_write_reg_mask(priv, 0x12, 0x60, 0xe0); + if (rc < 0) + return rc; + } + } + + if (!(data[2] & 0x40)) { + priv->has_lock = false; + return 0; + } + + priv->has_lock = true; + tuner_dbg("tuner has lock at frequency %d kHz\n", freq); + + /* set pll autotune = 8kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x08, 0x08); + + return rc; +} + +static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, + enum v4l2_tuner_type type, + v4l2_std_id std, + u32 delsys) +{ + int rc; + u8 mixer_top, lna_top, cp_cur, div_buf_cur, lna_vth_l, mixer_vth_l; + u8 air_cable1_in, cable2_in, pre_dect, lna_discharge, filter_cur; + + tuner_dbg("adjusting tuner parameters for the standard\n"); + + switch (delsys) { + case SYS_DVBT: + if ((freq == 506000000) || (freq == 666000000) || + (freq == 818000000)) { + mixer_top = 0x14; /* mixer top:14 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x28; /* 101, 0.2 */ + div_buf_cur = 0x20; /* 10, 200u */ + } else { + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + } + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + filter_cur = 0x40; /* 10, low */ + break; + case SYS_DVBT2: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + case SYS_ISDBT: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x75; /* lna vth 1.04 , vtl 0.84 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + default: /* DVB-T 8M */ + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + } + + if (priv->cfg->use_diplexer && + ((priv->cfg->rafael_chip == CHIP_R820T) || + (priv->cfg->rafael_chip == CHIP_R828S) || + (priv->cfg->rafael_chip == CHIP_R820C))) { + if (freq > DIP_FREQ) + air_cable1_in = 0x00; + else + air_cable1_in = 0x60; + cable2_in = 0x00; + } + + + if (priv->cfg->use_predetect) { + rc = r820t_write_reg_mask(priv, 0x06, pre_dect, 0x40); + if (rc < 0) + return rc; + } + + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0xc7); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0xf8); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0d, lna_vth_l); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0e, mixer_vth_l); + if (rc < 0) + return rc; + + /* Air-IN only for Astrometa */ + rc = r820t_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x06, cable2_in, 0x08); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x11, cp_cur, 0x38); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x17, div_buf_cur, 0x30); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x0a, filter_cur, 0x60); + if (rc < 0) + return rc; + /* + * Original driver initializes regs 0x05 and 0x06 with the + * same value again on this point. Probably, it is just an + * error there + */ + + /* + * Set LNA + */ + + tuner_dbg("adjusting LNA parameters\n"); + if (type != V4L2_TUNER_ANALOG_TV) { + /* LNA TOP: lowest */ + rc = r820t_write_reg_mask(priv, 0x1d, 0, 0x38); + if (rc < 0) + return rc; + + /* 0: normal mode */ + rc = r820t_write_reg_mask(priv, 0x1c, 0, 0x04); + if (rc < 0) + return rc; + + /* 0: PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* agc clk 250hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x30, 0x30); + if (rc < 0) + return rc; + + msleep(250); + + /* write LNA TOP = 3 */ + rc = r820t_write_reg_mask(priv, 0x1d, 0x18, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 60hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x20, 0x30); + if (rc < 0) + return rc; + } else { + /* PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* write LNA TOP */ + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 1Khz, external det1 cap 1u */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x30); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x04); + if (rc < 0) + return rc; + } + return 0; +} + +static int r820t_set_tv_standard(struct r820t_priv *priv, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) + +{ + int rc, i; + u32 if_khz, filt_cal_lo; + u8 data[5], val; + u8 filt_gain, img_r, filt_q, hp_cor, ext_enable, loop_through; + u8 lt_att, flt_ext_widest, polyfil_cur; + bool need_calibration; + + tuner_dbg("selecting the delivery system\n"); + + if (delsys == SYS_ISDBT) { + if_khz = 4063; + filt_cal_lo = 59000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ + ext_enable = 0x40; /* r30[6], ext enable; r30[5]:0 ext at lna max */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else { + if (bw <= 6) { + if_khz = 3570; + filt_cal_lo = 56000; /* 52000->56000 */ + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6b; /* 1.7m disable, +2cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else if (bw == 7) { +#if 0 + /* + * There are two 7 MHz tables defined on the original + * driver, but just the second one seems to be visible + * by rtl2832. Keep this one here commented, as it + * might be needed in the future + */ + + if_khz = 4070; + filt_cal_lo = 60000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2b; /* 1.7m disable, +1cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ +#endif + /* 7 MHz, second table */ + if_khz = 4570; + filt_cal_lo = 63000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2a; /* 1.7m disable, +1cap, 1.25mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else { + if_khz = 4570; + filt_cal_lo = 68500; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } + } + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* Init Flag & Xtal_check Result */ + if (priv->imr_done) + val = 1 | priv->xtal_cap_sel << 1; + else + val = 0; + rc = r820t_write_reg_mask(priv, 0x0c, val, 0x0f); + if (rc < 0) + return rc; + + /* version */ + rc = r820t_write_reg_mask(priv, 0x13, VER_NUM, 0x3f); + if (rc < 0) + return rc; + + /* for LT Gain test */ + if (type != V4L2_TUNER_ANALOG_TV) { + rc = r820t_write_reg_mask(priv, 0x1d, 0x00, 0x38); + if (rc < 0) + return rc; + usleep_range(1000, 2000); + } + priv->int_freq = if_khz * 1000; + + /* Check if standard changed. If so, filter calibration is needed */ + if (type != priv->type) + need_calibration = true; + else if ((type == V4L2_TUNER_ANALOG_TV) && (std != priv->std)) + need_calibration = true; + else if ((type == V4L2_TUNER_DIGITAL_TV) && + ((delsys != priv->delsys) || bw != priv->bw)) + need_calibration = true; + else + need_calibration = false; + + if (need_calibration) { + tuner_dbg("calibrating the tuner\n"); + for (i = 0; i < 2; i++) { + /* Set filt_cap */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x60); + if (rc < 0) + return rc; + + /* set cali clk =on */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); + if (rc < 0) + return rc; + + /* X'tal cap 0pF for PLL */ + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); + if (rc < 0) + return rc; + + rc = r820t_set_pll(priv, type, filt_cal_lo * 1000); + if (rc < 0 || !priv->has_lock) + return rc; + + /* Start Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); + if (rc < 0) + return rc; + + usleep_range(1000, 2000); + + /* Stop Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); + if (rc < 0) + return rc; + + /* set cali clk =off */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); + if (rc < 0) + return rc; + + /* Check if calibration worked */ + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + priv->fil_cal_code = data[4] & 0x0f; + if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) + break; + } + /* narrowest */ + if (priv->fil_cal_code == 0x0f) + priv->fil_cal_code = 0; + } + + rc = r820t_write_reg_mask(priv, 0x0a, + filt_q | priv->fil_cal_code, 0x1f); + if (rc < 0) + return rc; + + /* Set BW, Filter_gain, & HP corner */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0xef); + if (rc < 0) + return rc; + + + /* Set Img_R */ + rc = r820t_write_reg_mask(priv, 0x07, img_r, 0x80); + if (rc < 0) + return rc; + + /* Set filt_3dB, V6MHz */ + rc = r820t_write_reg_mask(priv, 0x06, filt_gain, 0x30); + if (rc < 0) + return rc; + + /* channel filter extension */ + rc = r820t_write_reg_mask(priv, 0x1e, ext_enable, 0x60); + if (rc < 0) + return rc; + + /* Loop through */ + rc = r820t_write_reg_mask(priv, 0x05, loop_through, 0x80); + if (rc < 0) + return rc; + + /* Loop through attenuation */ + rc = r820t_write_reg_mask(priv, 0x1f, lt_att, 0x80); + if (rc < 0) + return rc; + + /* filter extension widest */ + rc = r820t_write_reg_mask(priv, 0x0f, flt_ext_widest, 0x80); + if (rc < 0) + return rc; + + /* RF poly filter current */ + rc = r820t_write_reg_mask(priv, 0x19, polyfil_cur, 0x60); + if (rc < 0) + return rc; + + /* Store current standard. If it changes, re-calibrate the tuner */ + priv->delsys = delsys; + priv->type = type; + priv->std = std; + priv->bw = bw; + + return 0; +} + +static int r820t_read_gain(struct r820t_priv *priv) +{ + u8 data[4]; + int rc; + + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + return ((data[3] & 0x0f) << 1) + ((data[3] & 0xf0) >> 4); +} + +#if 0 +/* FIXME: This routine requires more testing */ +static int r820t_set_gain_mode(struct r820t_priv *priv, + bool set_manual_gain, + int gain) +{ + int rc; + + if (set_manual_gain) { + int i, total_gain = 0; + uint8_t mix_index = 0, lna_index = 0; + u8 data[4]; + + /* LNA auto off */ + rc = r820t_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + /* Mixer auto off */ + rc = r820t_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (16.3 dB) */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x08, 0x9f); + if (rc < 0) + return rc; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r820t_lna_gain_steps[++lna_index]; + + if (total_gain >= gain) + break; + + total_gain += r820t_mixer_gain_steps[++mix_index]; + } + + /* set LNA gain */ + rc = r820t_write_reg_mask(priv, 0x05, lna_index, 0x0f); + if (rc < 0) + return rc; + + /* set Mixer gain */ + rc = r820t_write_reg_mask(priv, 0x07, mix_index, 0x0f); + if (rc < 0) + return rc; + } else { + /* LNA */ + rc = r820t_write_reg_mask(priv, 0x05, 0, 0x10); + if (rc < 0) + return rc; + + /* Mixer */ + rc = r820t_write_reg_mask(priv, 0x07, 0x10, 0x10); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (26.5 dB) */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x0b, 0x9f); + if (rc < 0) + return rc; + } + + return 0; +} +#endif + +static int generic_set_freq(struct dvb_frontend *fe, + u32 freq /* in HZ */, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc = -EINVAL; + u32 lo_freq; + + tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", + freq / 1000, bw); + + rc = r820t_set_tv_standard(priv, bw, type, std, delsys); + if (rc < 0) + goto err; + + if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) + lo_freq = freq - priv->int_freq; + else + lo_freq = freq + priv->int_freq; + + rc = r820t_set_mux(priv, lo_freq); + if (rc < 0) + goto err; + + rc = r820t_set_pll(priv, type, lo_freq); + if (rc < 0 || !priv->has_lock) + goto err; + + rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); + if (rc < 0) + goto err; + + tuner_dbg("%s: PLL locked on frequency %d Hz, gain=%d\n", + __func__, freq, r820t_read_gain(priv)); + +err: + + if (rc < 0) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +/* + * r820t standby logic + */ + +static int r820t_standby(struct r820t_priv *priv) +{ + int rc; + + /* If device was not initialized yet, don't need to standby */ + if (!priv->init_done) + return 0; + + rc = r820t_write_reg(priv, 0x06, 0xb1); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x05, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x07, 0x3a); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x08, 0x40); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x09, 0xc0); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0a, 0x36); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0c, 0x35); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0f, 0x68); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x11, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x17, 0xf4); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x19, 0x0c); + + /* Force initial calibration */ + priv->type = -1; + + return rc; +} + +/* + * r820t device init logic + */ + +static int r820t_xtal_check(struct r820t_priv *priv) +{ + int rc, i; + u8 data[3], val; + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* cap 30pF & Drive Low */ + rc = r820t_write_reg_mask(priv, 0x10, 0x0b, 0x0b); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set manual initial reg = 111111; */ + rc = r820t_write_reg_mask(priv, 0x13, 0x7f, 0x7f); + if (rc < 0) + return rc; + + /* set auto */ + rc = r820t_write_reg_mask(priv, 0x13, 0x00, 0x40); + if (rc < 0) + return rc; + + /* Try several xtal capacitor alternatives */ + for (i = 0; i < ARRAY_SIZE(r820t_xtal_capacitor); i++) { + rc = r820t_write_reg_mask(priv, 0x10, + r820t_xtal_capacitor[i][0], 0x1b); + if (rc < 0) + return rc; + + usleep_range(5000, 6000); + + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + if (!(data[2] & 0x40)) + continue; + + val = data[2] & 0x3f; + + if (priv->cfg->xtal == 16000000 && (val > 29 || val < 23)) + break; + + if (val != 0x3f) + break; + } + + if (i == ARRAY_SIZE(r820t_xtal_capacitor)) + return -EINVAL; + + return r820t_xtal_capacitor[i][1]; +} + +static int r820t_imr_prepare(struct r820t_priv *priv) +{ + int rc; + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* lna off (air-in off) */ + rc = r820t_write_reg_mask(priv, 0x05, 0x20, 0x20); + if (rc < 0) + return rc; + + /* mixer gain mode = manual */ + rc = r820t_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + /* filter corner = lowest */ + rc = r820t_write_reg_mask(priv, 0x0a, 0x0f, 0x0f); + if (rc < 0) + return rc; + + /* filter bw=+2cap, hp=5M */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x60, 0x6f); + if (rc < 0) + return rc; + + /* adc=on, vga code mode, gain = 26.5dB */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x0b, 0x9f); + if (rc < 0) + return rc; + + /* ring clk = on */ + rc = r820t_write_reg_mask(priv, 0x0f, 0, 0x08); + if (rc < 0) + return rc; + + /* ring power = on */ + rc = r820t_write_reg_mask(priv, 0x18, 0x10, 0x10); + if (rc < 0) + return rc; + + /* from ring = ring pll in */ + rc = r820t_write_reg_mask(priv, 0x1c, 0x02, 0x02); + if (rc < 0) + return rc; + + /* sw_pdect = det3 */ + rc = r820t_write_reg_mask(priv, 0x1e, 0x80, 0x80); + if (rc < 0) + return rc; + + /* Set filt_3dB */ + rc = r820t_write_reg_mask(priv, 0x06, 0x20, 0x20); + + return rc; +} + +static int r820t_multi_read(struct r820t_priv *priv) +{ + int rc, i; + u8 data[2], min = 0, max = 255, sum = 0; + + usleep_range(5000, 6000); + + for (i = 0; i < 6; i++) { + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + sum += data[1]; + + if (data[1] < min) + min = data[1]; + + if (data[1] > max) + max = data[1]; + } + rc = sum - max - min; + + return rc; +} + +static int r820t_imr_cross(struct r820t_priv *priv, + struct r820t_sect_type iq_point[3], + u8 *x_direct) +{ + struct r820t_sect_type cross[5]; /* (0,0)(0,Q-1)(0,I-1)(Q-1,0)(I-1,0) */ + struct r820t_sect_type tmp; + int i, rc; + u8 reg08, reg09; + + reg08 = r820t_read_cache_reg(priv, 8) & 0xc0; + reg09 = r820t_read_cache_reg(priv, 9) & 0xc0; + + tmp.gain_x = 0; + tmp.phase_y = 0; + tmp.value = 255; + + for (i = 0; i < 5; i++) { + switch (i) { + case 0: + cross[i].gain_x = reg08; + cross[i].phase_y = reg09; + break; + case 1: + cross[i].gain_x = reg08; /* 0 */ + cross[i].phase_y = reg09 + 1; /* Q-1 */ + break; + case 2: + cross[i].gain_x = reg08; /* 0 */ + cross[i].phase_y = (reg09 | 0x20) + 1; /* I-1 */ + break; + case 3: + cross[i].gain_x = reg08 + 1; /* Q-1 */ + cross[i].phase_y = reg09; + break; + default: + cross[i].gain_x = (reg08 | 0x20) + 1; /* I-1 */ + cross[i].phase_y = reg09; + } + + rc = r820t_write_reg(priv, 0x08, cross[i].gain_x); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, 0x09, cross[i].phase_y); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + + cross[i].value = rc; + + if (cross[i].value < tmp.value) + memcpy(&tmp, &cross[i], sizeof(tmp)); + } + + if ((tmp.phase_y & 0x1f) == 1) { /* y-direction */ + *x_direct = 0; + + iq_point[0] = cross[0]; + iq_point[1] = cross[1]; + iq_point[2] = cross[2]; + } else { /* (0,0) or x-direction */ + *x_direct = 1; + + iq_point[0] = cross[0]; + iq_point[1] = cross[3]; + iq_point[2] = cross[4]; + } + return 0; +} + +static void r820t_compre_cor(struct r820t_sect_type iq[3]) +{ + int i; + + for (i = 3; i > 0; i--) { + if (iq[0].value > iq[i - 1].value) + swap(iq[0], iq[i - 1]); + } +} + +static int r820t_compre_step(struct r820t_priv *priv, + struct r820t_sect_type iq[3], u8 reg) +{ + int rc; + struct r820t_sect_type tmp; + + /* + * Purpose: if (Gain<9 or Phase<9), Gain+1 or Phase+1 and compare + * with min value: + * new < min => update to min and continue + * new > min => Exit + */ + + /* min value already saved in iq[0] */ + tmp.phase_y = iq[0].phase_y; + tmp.gain_x = iq[0].gain_x; + + while (((tmp.gain_x & 0x1f) < IMR_TRIAL) && + ((tmp.phase_y & 0x1f) < IMR_TRIAL)) { + if (reg == 0x08) + tmp.gain_x++; + else + tmp.phase_y++; + + rc = r820t_write_reg(priv, 0x08, tmp.gain_x); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, 0x09, tmp.phase_y); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + tmp.value = rc; + + if (tmp.value <= iq[0].value) { + iq[0].gain_x = tmp.gain_x; + iq[0].phase_y = tmp.phase_y; + iq[0].value = tmp.value; + } else { + return 0; + } + + } + + return 0; +} + +static int r820t_iq_tree(struct r820t_priv *priv, + struct r820t_sect_type iq[3], + u8 fix_val, u8 var_val, u8 fix_reg) +{ + int rc, i; + u8 tmp, var_reg; + + /* + * record IMC results by input gain/phase location then adjust + * gain or phase positive 1 step and negtive 1 step, + * both record results + */ + + if (fix_reg == 0x08) + var_reg = 0x09; + else + var_reg = 0x08; + + for (i = 0; i < 3; i++) { + rc = r820t_write_reg(priv, fix_reg, fix_val); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, var_reg, var_val); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + iq[i].value = rc; + + if (fix_reg == 0x08) { + iq[i].gain_x = fix_val; + iq[i].phase_y = var_val; + } else { + iq[i].phase_y = fix_val; + iq[i].gain_x = var_val; + } + + if (i == 0) { /* try right-side point */ + var_val++; + } else if (i == 1) { /* try left-side point */ + /* if absolute location is 1, change I/Q direction */ + if ((var_val & 0x1f) < 0x02) { + tmp = 2 - (var_val & 0x1f); + + /* b[5]:I/Q selection. 0:Q-path, 1:I-path */ + if (var_val & 0x20) { + var_val &= 0xc0; + var_val |= tmp; + } else { + var_val |= 0x20 | tmp; + } + } else { + var_val -= 2; + } + } + } + + return 0; +} + +static int r820t_section(struct r820t_priv *priv, + struct r820t_sect_type *iq_point) +{ + int rc; + struct r820t_sect_type compare_iq[3], compare_bet[3]; + + /* Try X-1 column and save min result to compare_bet[0] */ + if (!(iq_point->gain_x & 0x1f)) + compare_iq[0].gain_x = ((iq_point->gain_x) & 0xdf) + 1; /* Q-path, Gain=1 */ + else + compare_iq[0].gain_x = iq_point->gain_x - 1; /* left point */ + compare_iq[0].phase_y = iq_point->phase_y; + + /* y-direction */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[0] = compare_iq[0]; + + /* Try X column and save min result to compare_bet[1] */ + compare_iq[0].gain_x = iq_point->gain_x; + compare_iq[0].phase_y = iq_point->phase_y; + + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[1] = compare_iq[0]; + + /* Try X+1 column and save min result to compare_bet[2] */ + if ((iq_point->gain_x & 0x1f) == 0x00) + compare_iq[0].gain_x = ((iq_point->gain_x) | 0x20) + 1; /* I-path, Gain=1 */ + else + compare_iq[0].gain_x = iq_point->gain_x + 1; + compare_iq[0].phase_y = iq_point->phase_y; + + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[2] = compare_iq[0]; + + r820t_compre_cor(compare_bet); + + *iq_point = compare_bet[0]; + + return 0; +} + +static int r820t_vga_adjust(struct r820t_priv *priv) +{ + int rc; + u8 vga_count; + + /* increase vga power to let image significant */ + for (vga_count = 12; vga_count < 16; vga_count++) { + rc = r820t_write_reg_mask(priv, 0x0c, vga_count, 0x0f); + if (rc < 0) + return rc; + + usleep_range(10000, 11000); + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + + if (rc > 40 * 4) + break; + } + + return 0; +} + +static int r820t_iq(struct r820t_priv *priv, struct r820t_sect_type *iq_pont) +{ + struct r820t_sect_type compare_iq[3]; + int rc; + u8 x_direction = 0; /* 1:x, 0:y */ + u8 dir_reg, other_reg; + + r820t_vga_adjust(priv); + + rc = r820t_imr_cross(priv, compare_iq, &x_direction); + if (rc < 0) + return rc; + + if (x_direction == 1) { + dir_reg = 0x08; + other_reg = 0x09; + } else { + dir_reg = 0x09; + other_reg = 0x08; + } + + /* compare and find min of 3 points. determine i/q direction */ + r820t_compre_cor(compare_iq); + + /* increase step to find min value of this direction */ + rc = r820t_compre_step(priv, compare_iq, dir_reg); + if (rc < 0) + return rc; + + /* the other direction */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, dir_reg); + if (rc < 0) + return rc; + + /* compare and find min of 3 points. determine i/q direction */ + r820t_compre_cor(compare_iq); + + /* increase step to find min value on this direction */ + rc = r820t_compre_step(priv, compare_iq, other_reg); + if (rc < 0) + return rc; + + /* check 3 points again */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, other_reg); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + /* section-9 check */ + rc = r820t_section(priv, compare_iq); + + *iq_pont = compare_iq[0]; + + /* reset gain/phase control setting */ + rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); + + return rc; +} + +static int r820t_f_imr(struct r820t_priv *priv, struct r820t_sect_type *iq_pont) +{ + int rc; + + r820t_vga_adjust(priv); + + /* + * search surrounding points from previous point + * try (x-1), (x), (x+1) columns, and find min IMR result point + */ + rc = r820t_section(priv, iq_pont); + if (rc < 0) + return rc; + + return 0; +} + +static int r820t_imr(struct r820t_priv *priv, unsigned imr_mem, bool im_flag) +{ + struct r820t_sect_type imr_point; + int rc; + u32 ring_vco, ring_freq, ring_ref; + u8 n_ring, n; + int reg18, reg19, reg1f; + + if (priv->cfg->xtal > 24000000) + ring_ref = priv->cfg->xtal / 2; + else + ring_ref = priv->cfg->xtal; + + n_ring = 15; + for (n = 0; n < 16; n++) { + if ((16 + n) * 8 * ring_ref >= 3100000) { + n_ring = n; + break; + } + } + + reg18 = r820t_read_cache_reg(priv, 0x18); + reg19 = r820t_read_cache_reg(priv, 0x19); + reg1f = r820t_read_cache_reg(priv, 0x1f); + + reg18 &= 0xf0; /* set ring[3:0] */ + reg18 |= n_ring; + + ring_vco = (16 + n_ring) * 8 * ring_ref; + + reg18 &= 0xdf; /* clear ring_se23 */ + reg19 &= 0xfc; /* clear ring_seldiv */ + reg1f &= 0xfc; /* clear ring_att */ + + switch (imr_mem) { + case 0: + ring_freq = ring_vco / 48; + reg18 |= 0x20; /* ring_se23 = 1 */ + reg19 |= 0x03; /* ring_seldiv = 3 */ + reg1f |= 0x02; /* ring_att 10 */ + break; + case 1: + ring_freq = ring_vco / 16; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x02; /* ring_seldiv = 2 */ + reg1f |= 0x00; /* pw_ring 00 */ + break; + case 2: + ring_freq = ring_vco / 8; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x01; /* ring_seldiv = 1 */ + reg1f |= 0x03; /* pw_ring 11 */ + break; + case 3: + ring_freq = ring_vco / 6; + reg18 |= 0x20; /* ring_se23 = 1 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x03; /* pw_ring 11 */ + break; + case 4: + ring_freq = ring_vco / 4; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x01; /* pw_ring 01 */ + break; + default: + ring_freq = ring_vco / 4; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x01; /* pw_ring 01 */ + break; + } + + + /* write pw_ring, n_ring, ringdiv2 registers */ + + /* n_ring, ring_se23 */ + rc = r820t_write_reg(priv, 0x18, reg18); + if (rc < 0) + return rc; + + /* ring_sediv */ + rc = r820t_write_reg(priv, 0x19, reg19); + if (rc < 0) + return rc; + + /* pw_ring */ + rc = r820t_write_reg(priv, 0x1f, reg1f); + if (rc < 0) + return rc; + + /* mux input freq ~ rf_in freq */ + rc = r820t_set_mux(priv, (ring_freq - 5300) * 1000); + if (rc < 0) + return rc; + + rc = r820t_set_pll(priv, V4L2_TUNER_DIGITAL_TV, + (ring_freq - 5300) * 1000); + if (!priv->has_lock) + rc = -EINVAL; + if (rc < 0) + return rc; + + if (im_flag) { + rc = r820t_iq(priv, &imr_point); + } else { + imr_point.gain_x = priv->imr_data[3].gain_x; + imr_point.phase_y = priv->imr_data[3].phase_y; + imr_point.value = priv->imr_data[3].value; + + rc = r820t_f_imr(priv, &imr_point); + } + if (rc < 0) + return rc; + + /* save IMR value */ + switch (imr_mem) { + case 0: + priv->imr_data[0].gain_x = imr_point.gain_x; + priv->imr_data[0].phase_y = imr_point.phase_y; + priv->imr_data[0].value = imr_point.value; + break; + case 1: + priv->imr_data[1].gain_x = imr_point.gain_x; + priv->imr_data[1].phase_y = imr_point.phase_y; + priv->imr_data[1].value = imr_point.value; + break; + case 2: + priv->imr_data[2].gain_x = imr_point.gain_x; + priv->imr_data[2].phase_y = imr_point.phase_y; + priv->imr_data[2].value = imr_point.value; + break; + case 3: + priv->imr_data[3].gain_x = imr_point.gain_x; + priv->imr_data[3].phase_y = imr_point.phase_y; + priv->imr_data[3].value = imr_point.value; + break; + case 4: + priv->imr_data[4].gain_x = imr_point.gain_x; + priv->imr_data[4].phase_y = imr_point.phase_y; + priv->imr_data[4].value = imr_point.value; + break; + default: + priv->imr_data[4].gain_x = imr_point.gain_x; + priv->imr_data[4].phase_y = imr_point.phase_y; + priv->imr_data[4].value = imr_point.value; + break; + } + + return 0; +} + +static int r820t_imr_callibrate(struct r820t_priv *priv) +{ + int rc, i; + int xtal_cap = 0; + + if (priv->init_done) + return 0; + + /* Detect Xtal capacitance */ + if ((priv->cfg->rafael_chip == CHIP_R820T) || + (priv->cfg->rafael_chip == CHIP_R828S) || + (priv->cfg->rafael_chip == CHIP_R820C)) { + priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; + } else { + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + if (rc < 0) + return rc; + for (i = 0; i < 3; i++) { + rc = r820t_xtal_check(priv); + if (rc < 0) + return rc; + if (!i || rc > xtal_cap) + xtal_cap = rc; + } + priv->xtal_cap_sel = xtal_cap; + } + + /* + * Disables IMR callibration. That emulates the same behaviour + * as what is done by rtl-sdr userspace library. Useful for testing + */ + if (no_imr_cal) { + priv->init_done = true; + + return 0; + } + + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + if (rc < 0) + return rc; + + rc = r820t_imr_prepare(priv); + if (rc < 0) + return rc; + + rc = r820t_imr(priv, 3, true); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 1, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 0, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 2, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 4, false); + if (rc < 0) + return rc; + + priv->init_done = true; + priv->imr_done = true; + + return 0; +} + +#if 0 +/* Not used, for now */ +static int r820t_gpio(struct r820t_priv *priv, bool enable) +{ + return r820t_write_reg_mask(priv, 0x0f, enable ? 1 : 0, 0x01); +} +#endif + +/* + * r820t frontend operations and tuner attach code + * + * All driver locks and i2c control are only in this part of the code + */ + +static int r820t_init(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = r820t_imr_callibrate(priv); + if (rc < 0) + goto err; + + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + if (rc < 0) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_sleep(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = r820t_standby(priv); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_set_analog_freq(struct dvb_frontend *fe, + struct analog_parameters *p) +{ + struct r820t_priv *priv = fe->tuner_priv; + unsigned bw; + int rc; + + tuner_dbg("%s called\n", __func__); + + /* if std is not defined, choose one */ + if (!p->std) + p->std = V4L2_STD_MN; + + if ((p->std == V4L2_STD_PAL_M) || (p->std == V4L2_STD_NTSC)) + bw = 6; + else + bw = 8; + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = generic_set_freq(fe, 62500l * p->frequency, bw, + V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + return rc; +} + +static int r820t_set_params(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + unsigned bw; + + tuner_dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, c->bandwidth_hz); + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + bw = (c->bandwidth_hz + 500000) / 1000000; + if (!bw) + bw = 8; + + rc = generic_set_freq(fe, c->frequency, bw, + V4L2_TUNER_DIGITAL_TV, 0, c->delivery_system); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + if (rc) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_signal(struct dvb_frontend *fe, u16 *strength) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc = 0; + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (priv->has_lock) { + rc = r820t_read_gain(priv); + if (rc < 0) + goto err; + + /* A higher gain at LNA means a lower signal strength */ + *strength = (45 - rc) << 4 | 0xff; + if (*strength == 0xff) + *strength = 0; + } else { + *strength = 0; + } + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + tuner_dbg("%s: %s, gain=%d strength=%d\n", + __func__, + priv->has_lock ? "PLL locked" : "no signal", + rc, *strength); + + return 0; +} + +static int r820t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + *frequency = priv->int_freq; + + return 0; +} + +static int r820t_release(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&r820t_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&r820t_list_mutex); + + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops r820t_tuner_ops = { + .info = { + .name = "Rafael Micro R820T", + .frequency_min = 42000000, + .frequency_max = 1002000000, + }, + .init = r820t_init, + .release = r820t_release, + .sleep = r820t_sleep, + .set_params = r820t_set_params, + .set_analog_params = r820t_set_analog_freq, + .get_if_frequency = r820t_get_if_frequency, + .get_rf_strength = r820t_signal, +}; + +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + struct r820t_priv *priv; + int rc = -ENODEV; + u8 data[5]; + int instance; + + mutex_lock(&r820t_list_mutex); + + instance = hybrid_tuner_request_state(struct r820t_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_addr, + "r820t"); + switch (instance) { + case 0: + /* memory allocation failure */ + goto err_no_gate; + break; + case 1: + /* new tuner instance */ + priv->cfg = cfg; + + mutex_init(&priv->lock); + + fe->tuner_priv = priv; + break; + case 2: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } + + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, sizeof(r820t_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* check if the tuner is there */ + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + goto err; + + rc = r820t_sleep(fe); + if (rc < 0) + goto err; + + tuner_info("Rafael Micro r820t successfully identified\n"); + + fe->tuner_priv = priv; + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mutex_unlock(&r820t_list_mutex); + + return fe; +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + +err_no_gate: + mutex_unlock(&r820t_list_mutex); + + tuner_info("%s: failed=%d\n", __func__, rc); + r820t_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(r820t_attach); + +MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h new file mode 100644 index 0000000..48af354 --- /dev/null +++ b/drivers/media/tuners/r820t.h @@ -0,0 +1,59 @@ +/* + * Elonics R820T silicon tuner driver + * + * Copyright (C) 2012 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. + * + * 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 R820T_H +#define R820T_H + +#include <linux/kconfig.h> +#include "dvb_frontend.h" + +enum r820t_chip { + CHIP_R820T, + CHIP_R620D, + CHIP_R828D, + CHIP_R828, + CHIP_R828S, + CHIP_R820C, +}; + +struct r820t_config { + u8 i2c_addr; /* 0x34 */ + u32 xtal; + enum r820t_chip rafael_chip; + unsigned max_i2c_msg_len; + bool use_diplexer; + bool use_predetect; +}; + +#if IS_ENABLED(CONFIG_MEDIA_TUNER_R820T) +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg); +#else +static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/tuners/tda18212.h b/drivers/media/tuners/tda18212.h index 9bd5da4..7e0d503 100644 --- a/drivers/media/tuners/tda18212.h +++ b/drivers/media/tuners/tda18212.h @@ -21,6 +21,7 @@ #ifndef TDA18212_H #define TDA18212_H +#include <linux/kconfig.h> #include "dvb_frontend.h" struct tda18212_config { @@ -36,8 +37,7 @@ struct tda18212_config { u16 if_dvbc; }; -#if defined(CONFIG_MEDIA_TUNER_TDA18212) || \ - (defined(CONFIG_MEDIA_TUNER_TDA18212_MODULE) && defined(MODULE)) +#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 diff --git a/drivers/media/tuners/tda18218.h b/drivers/media/tuners/tda18218.h index b4180d1..366410e 100644 --- a/drivers/media/tuners/tda18218.h +++ b/drivers/media/tuners/tda18218.h @@ -21,6 +21,7 @@ #ifndef TDA18218_H #define TDA18218_H +#include <linux/kconfig.h> #include "dvb_frontend.h" struct tda18218_config { @@ -29,8 +30,7 @@ struct tda18218_config { u8 loop_through:1; }; -#if defined(CONFIG_MEDIA_TUNER_TDA18218) || \ - (defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18218) extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tda18218_config *cfg); #else diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index e778686..4995b89 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -21,6 +21,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include "tda18271-priv.h" +#include "tda8290.h" int tda18271_debug; module_param_named(debug, tda18271_debug, int, 0644); @@ -867,12 +868,12 @@ static int tda18271_agc(struct dvb_frontend *fe) int ret = 0; switch (priv->config) { - case 0: + case TDA8290_LNA_OFF: /* no external agc configuration required */ if (tda18271_debug & DBG_ADV) tda_dbg("no agc configuration provided\n"); break; - case 3: + case TDA8290_LNA_ON_BRIDGE: /* switch with GPIO of saa713x */ tda_dbg("invoking callback\n"); if (fe->callback) @@ -881,8 +882,8 @@ static int tda18271_agc(struct dvb_frontend *fe) TDA18271_CALLBACK_CMD_AGC_ENABLE, priv->mode); break; - case 1: - case 2: + case TDA8290_LNA_GP0_HIGH_ON: + case TDA8290_LNA_GP0_HIGH_OFF: default: /* n/a - currently not supported */ tda_err("unsupported configuration: %d\n", priv->config); diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index a0d1762..73453a2 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -479,10 +479,10 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, dprintk("setting LNA to low gain\n"); } switch (priv->cfg->config) { - case 0: /* no LNA */ + case TDA8290_LNA_OFF: /* no LNA */ break; - case 1: /* switch is GPIO 0 of tda8290 */ - case 2: + case TDA8290_LNA_GP0_HIGH_ON: /* switch is GPIO 0 of tda8290 */ + case TDA8290_LNA_GP0_HIGH_OFF: if (params == NULL) { gp_func = 0; arg = 0; @@ -499,11 +499,11 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, DVB_FRONTEND_COMPONENT_TUNER, gp_func, arg); buf[1] = high ? 0 : 1; - if (priv->cfg->config == 2) + if (priv->cfg->config == TDA8290_LNA_GP0_HIGH_OFF) buf[1] = high ? 1 : 0; tuner_transfer(fe, &msg, 1); break; - case 3: /* switch with GPIO of saa713x */ + case TDA8290_LNA_ON_BRIDGE: /* switch with GPIO of saa713x */ if (fe->callback) fe->callback(priv->i2c_adap->algo_data, DVB_FRONTEND_COMPONENT_TUNER, 0, high); diff --git a/drivers/media/tuners/tda827x.h b/drivers/media/tuners/tda827x.h index 9432b5b..b642921 100644 --- a/drivers/media/tuners/tda827x.h +++ b/drivers/media/tuners/tda827x.h @@ -26,6 +26,7 @@ #include <linux/i2c.h> #include "dvb_frontend.h" +#include "tda8290.h" struct tda827x_config { @@ -34,7 +35,7 @@ struct tda827x_config int (*sleep) (struct dvb_frontend *fe); /* interface to tda829x driver */ - unsigned int config; + enum tda8290_lna config; int switch_addr; void (*agcf)(struct dvb_frontend *fe); diff --git a/drivers/media/tuners/tda8290.c b/drivers/media/tuners/tda8290.c index 8c48521..ab4106c 100644 --- a/drivers/media/tuners/tda8290.c +++ b/drivers/media/tuners/tda8290.c @@ -54,6 +54,7 @@ struct tda8290_priv { #define TDA18271 16 struct tda827x_config cfg; + struct tda18271_std_map *tda18271_std_map; }; /*---------------------------------------------------------------------*/ @@ -233,7 +234,8 @@ static void tda8290_set_params(struct dvb_frontend *fe, } - tda8290_i2c_bridge(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); if (fe->ops.tuner_ops.set_analog_params) fe->ops.tuner_ops.set_analog_params(fe, params); @@ -302,7 +304,8 @@ static void tda8290_set_params(struct dvb_frontend *fe, } } - tda8290_i2c_bridge(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2); } @@ -388,7 +391,7 @@ static void tda8295_agc2_out(struct dvb_frontend *fe, int enable) tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2); } -static int tda8295_has_signal(struct dvb_frontend *fe) +static int tda8295_has_signal(struct dvb_frontend *fe, u16 *signal) { struct tda8290_priv *priv = fe->analog_demod_priv; @@ -396,7 +399,8 @@ static int tda8295_has_signal(struct dvb_frontend *fe) unsigned char ret; tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1); - return (ret & 0x01) ? 65535 : 0; + *signal = (ret & 0x01) ? 65535 : 0; + return 0; } /*---------------------------------------------------------------------*/ @@ -405,7 +409,7 @@ static void tda8295_set_params(struct dvb_frontend *fe, struct analog_parameters *params) { struct tda8290_priv *priv = fe->analog_demod_priv; - + u16 signal = 0; unsigned char blanking_mode[] = { 0x1d, 0x00 }; set_audio(fe, params); @@ -424,7 +428,8 @@ static void tda8295_set_params(struct dvb_frontend *fe, tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2); msleep(20); - tda8295_i2c_bridge(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); if (fe->ops.tuner_ops.set_analog_params) fe->ops.tuner_ops.set_analog_params(fe, params); @@ -432,17 +437,19 @@ static void tda8295_set_params(struct dvb_frontend *fe, if (priv->cfg.agcf) priv->cfg.agcf(fe); - if (tda8295_has_signal(fe)) + tda8295_has_signal(fe, &signal); + if (signal) tuner_dbg("tda8295 is locked\n"); else tuner_dbg("tda8295 not locked, no signal?\n"); - tda8295_i2c_bridge(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); } /*---------------------------------------------------------------------*/ -static int tda8290_has_signal(struct dvb_frontend *fe) +static int tda8290_has_signal(struct dvb_frontend *fe, u16 *signal) { struct tda8290_priv *priv = fe->analog_demod_priv; @@ -451,7 +458,8 @@ static int tda8290_has_signal(struct dvb_frontend *fe) tuner_i2c_xfer_send_recv(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1); - return (afc & 0x80)? 65535:0; + *signal = (afc & 0x80) ? 65535 : 0; + return 0; } /*---------------------------------------------------------------------*/ @@ -465,11 +473,13 @@ static void tda8290_standby(struct dvb_frontend *fe) unsigned char tda8290_agc_tri[] = { 0x02, 0x20 }; struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; - tda8290_i2c_bridge(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); if (priv->ver & TDA8275A) cb1[1] = 0x90; i2c_transfer(priv->i2c_props.adap, &msg, 1); - tda8290_i2c_bridge(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2); tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2); } @@ -489,7 +499,8 @@ static void tda8290_init_if(struct dvb_frontend *fe) unsigned char set_GP00_CF[] = { 0x20, 0x01 }; unsigned char set_GP01_CF[] = { 0x20, 0x0B }; - if ((priv->cfg.config == 1) || (priv->cfg.config == 2)) + if ((priv->cfg.config == TDA8290_LNA_GP0_HIGH_ON) || + (priv->cfg.config == TDA8290_LNA_GP0_HIGH_OFF)) tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2); else tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2); @@ -537,9 +548,11 @@ static void tda8290_init_tuner(struct dvb_frontend *fe) if (priv->ver & TDA8275A) msg.buf = tda8275a_init; - tda8290_i2c_bridge(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); i2c_transfer(priv->i2c_props.adap, &msg, 1); - tda8290_i2c_bridge(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); } /*---------------------------------------------------------------------*/ @@ -565,19 +578,13 @@ static struct tda18271_config tda829x_tda18271_config = { static int tda829x_find_tuner(struct dvb_frontend *fe) { struct tda8290_priv *priv = fe->analog_demod_priv; - struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; int i, ret, tuners_found; u32 tuner_addrs; u8 data; struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 }; - if (!analog_ops->i2c_gate_ctrl) { - printk(KERN_ERR "tda8290: no gate control were provided!\n"); - - return -EINVAL; - } - - analog_ops->i2c_gate_ctrl(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); /* probe for tuner chip */ tuners_found = 0; @@ -595,7 +602,8 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) give a response now */ - analog_ops->i2c_gate_ctrl(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); if (tuners_found > 1) for (i = 0; i < tuners_found; i++) { @@ -618,18 +626,21 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) priv->tda827x_addr = tuner_addrs; msg.addr = tuner_addrs; - analog_ops->i2c_gate_ctrl(fe, 1); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 1); ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); if (ret != 1) { tuner_warn("tuner access failed!\n"); - analog_ops->i2c_gate_ctrl(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); return -EREMOTEIO; } if ((data == 0x83) || (data == 0x84)) { priv->ver |= TDA18271; tda829x_tda18271_config.config = priv->cfg.config; + tda829x_tda18271_config.std_map = priv->tda18271_std_map; dvb_attach(tda18271_attach, fe, priv->tda827x_addr, priv->i2c_props.adap, &tda829x_tda18271_config); } else { @@ -648,7 +659,8 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) if (fe->ops.tuner_ops.sleep) fe->ops.tuner_ops.sleep(fe); - analog_ops->i2c_gate_ctrl(fe, 0); + if (fe->ops.analog_ops.i2c_gate_ctrl) + fe->ops.analog_ops.i2c_gate_ctrl(fe, 0); return 0; } @@ -740,8 +752,10 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; priv->i2c_props.name = "tda829x"; - if (cfg) - priv->cfg.config = cfg->lna_cfg; + if (cfg) { + priv->cfg.config = cfg->lna_cfg; + priv->tda18271_std_map = cfg->tda18271_std_map; + } if (tda8290_probe(&priv->i2c_props) == 0) { priv->ver = TDA8290; @@ -755,6 +769,9 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, sizeof(struct analog_demod_ops)); } + if (cfg && cfg->no_i2c_gate) + fe->ops.analog_ops.i2c_gate_ctrl = NULL; + if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { tda8295_power(fe, 1); if (tda829x_find_tuner(fe) < 0) diff --git a/drivers/media/tuners/tda8290.h b/drivers/media/tuners/tda8290.h index e12ecba..cf96e58 100644 --- a/drivers/media/tuners/tda8290.h +++ b/drivers/media/tuners/tda8290.h @@ -19,13 +19,23 @@ #include <linux/i2c.h> #include "dvb_frontend.h" +#include "tda18271.h" + +enum tda8290_lna { + TDA8290_LNA_OFF = 0, + TDA8290_LNA_GP0_HIGH_ON = 1, + TDA8290_LNA_GP0_HIGH_OFF = 2, + TDA8290_LNA_ON_BRIDGE = 3, +}; struct tda829x_config { - unsigned int lna_cfg; + enum tda8290_lna lna_cfg; unsigned int probe_tuner:1; #define TDA829X_PROBE_TUNER 0 #define TDA829X_DONT_PROBE 1 + unsigned int no_i2c_gate:1; + struct tda18271_std_map *tda18271_std_map; }; #if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA8290) diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index cdb645d..300005c 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -596,22 +596,22 @@ static void tda9887_tuner_status(struct dvb_frontend *fe) priv->data[1], priv->data[2], priv->data[3]); } -static int tda9887_get_afc(struct dvb_frontend *fe) +static int tda9887_get_afc(struct dvb_frontend *fe, s32 *afc) { struct tda9887_priv *priv = fe->analog_demod_priv; - static int AFC_BITS_2_kHz[] = { + static const int AFC_BITS_2_kHz[] = { -12500, -37500, -62500, -97500, -112500, -137500, -162500, -187500, 187500, 162500, 137500, 112500, 97500 , 62500, 37500 , 12500 }; - int afc=0; __u8 reg = 0; - if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,®,1)) - afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; - - return afc; + if (priv->mode != V4L2_TUNER_RADIO) + return 0; + if (1 == tuner_i2c_xfer_recv(&priv->i2c_props, ®, 1)) + *afc = AFC_BITS_2_kHz[(reg >> 1) & 0x0f]; + return 0; } static void tda9887_standby(struct dvb_frontend *fe) diff --git a/drivers/media/tuners/tua9001.h b/drivers/media/tuners/tua9001.h index cf5b815..26358da 100644 --- a/drivers/media/tuners/tua9001.h +++ b/drivers/media/tuners/tua9001.h @@ -21,6 +21,7 @@ #ifndef TUA9001_H #define TUA9001_H +#include <linux/kconfig.h> #include "dvb_frontend.h" struct tua9001_config { @@ -50,8 +51,7 @@ struct tua9001_config { #define TUA9001_CMD_RESETN 1 #define TUA9001_CMD_RXEN 2 -#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \ - (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TUA9001) extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tua9001_config *cfg); #else diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index 39e7e58..ca274c2 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -115,6 +115,7 @@ struct tuner_simple_priv { u32 frequency; u32 bandwidth; + bool radio_mode; }; /* ---------------------------------------------------------------------- */ @@ -189,7 +190,7 @@ static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) struct tuner_simple_priv *priv = fe->tuner_priv; int signal; - if (priv->i2c_props.adap == NULL) + if (priv->i2c_props.adap == NULL || !priv->radio_mode) return -EINVAL; signal = tuner_signal(tuner_read_status(fe)); @@ -776,11 +777,13 @@ static int simple_set_params(struct dvb_frontend *fe, switch (params->mode) { case V4L2_TUNER_RADIO: + priv->radio_mode = true; ret = simple_set_radio_freq(fe, params); priv->frequency = params->frequency * 125 / 2; break; case V4L2_TUNER_ANALOG_TV: case V4L2_TUNER_DIGITAL_TV: + priv->radio_mode = false; ret = simple_set_tv_freq(fe, params); priv->frequency = params->frequency * 62500; break; diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index 2da4440..98bc15a 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1381,6 +1381,58 @@ static struct tuner_params tuner_philips_fq1236_mk5_params[] = { }, }; +/* --------- Sony BTF-PG472Z PAL/SECAM ------- */ + +static struct tuner_range tuner_sony_btf_pg472z_ranges[] = { + { 16 * 144.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 427.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg472z_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_sony_btf_pg472z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg472z_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_invert_for_secam_lc = 1, + }, +}; + +/* 90-99 */ +/* --------- Sony BTF-PG467Z NTSC-M-JP ------- */ + +static struct tuner_range tuner_sony_btf_pg467z_ranges[] = { + { 16 * 220.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 467.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg467z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pg467z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg467z_ranges), + }, +}; + +/* --------- Sony BTF-PG463Z NTSC-M ------- */ + +static struct tuner_range tuner_sony_btf_pg463z_ranges[] = { + { 16 * 130.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 364.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg463z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pg463z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg463z_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1872,6 +1924,23 @@ struct tunertype tuners[] = { .name = "Xceive 5000C tuner", /* see xc5000.c for details */ }, + [TUNER_SONY_BTF_PG472Z] = { + .name = "Sony BTF-PG472Z PAL/SECAM", + .params = tuner_sony_btf_pg472z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg472z_params), + }, + + /* 90-99 */ + [TUNER_SONY_BTF_PK467Z] = { + .name = "Sony BTF-PK467Z NTSC-M-JP", + .params = tuner_sony_btf_pg467z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg467z_params), + }, + [TUNER_SONY_BTF_PB463Z] = { + .name = "Sony BTF-PB463Z NTSC-M", + .params = tuner_sony_btf_pg463z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg463z_params), + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 0945173..878d2c4 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1378,8 +1378,7 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) * For the firmware name, keep a local copy of the string, * in order to avoid troubles during device release. */ - if (priv->ctrl.fname) - kfree(priv->ctrl.fname); + kfree(priv->ctrl.fname); memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); if (p->fname) { priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); diff --git a/drivers/media/tuners/tuner_it913x.c b/drivers/media/tuners/tuner_it913x.c new file mode 100644 index 0000000..6f30d7e --- /dev/null +++ b/drivers/media/tuners/tuner_it913x.c @@ -0,0 +1,447 @@ +/* + * 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; + + /* 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; + + 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.h b/drivers/media/tuners/tuner_it913x.h new file mode 100644 index 0000000..12dd36b --- /dev/null +++ b/drivers/media/tuners/tuner_it913x.h @@ -0,0 +1,45 @@ +/* + * 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_H +#define IT913X_H + +#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 + +#endif diff --git a/drivers/media/tuners/tuner_it913x_priv.h b/drivers/media/tuners/tuner_it913x_priv.h new file mode 100644 index 0000000..ce65210 --- /dev/null +++ b/drivers/media/tuners/tuner_it913x_priv.h @@ -0,0 +1,78 @@ +/* + * 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/xc5000.c b/drivers/media/tuners/xc5000.c index d6be1b6..5cd09a6 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -422,13 +422,19 @@ static int xc_initialize(struct xc5000_priv *priv) } static int xc_SetTVStandard(struct xc5000_priv *priv, - u16 VideoMode, u16 AudioMode) + u16 VideoMode, u16 AudioMode, u8 RadioMode) { int ret; dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode); - dprintk(1, "%s() Standard = %s\n", - __func__, - XC5000_Standard[priv->video_standard].Name); + if (RadioMode) { + dprintk(1, "%s() Standard = %s\n", + __func__, + XC5000_Standard[RadioMode].Name); + } else { + dprintk(1, "%s() Standard = %s\n", + __func__, + XC5000_Standard[priv->video_standard].Name); + } ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode); if (ret == XC_RESULT_SUCCESS) @@ -824,7 +830,7 @@ static int xc5000_set_params(struct dvb_frontend *fe) ret = xc_SetTVStandard(priv, XC5000_Standard[priv->video_standard].VideoMode, - XC5000_Standard[priv->video_standard].AudioMode); + XC5000_Standard[priv->video_standard].AudioMode, 0); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); return -EREMOTEIO; @@ -940,7 +946,7 @@ tune_channel: ret = xc_SetTVStandard(priv, XC5000_Standard[priv->video_standard].VideoMode, - XC5000_Standard[priv->video_standard].AudioMode); + XC5000_Standard[priv->video_standard].AudioMode, 0); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); return -EREMOTEIO; @@ -1003,7 +1009,7 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, priv->rf_mode = XC_RF_MODE_AIR; ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, - XC5000_Standard[radio_input].AudioMode); + XC5000_Standard[radio_input].AudioMode, radio_input); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); diff --git a/drivers/media/tuners/xc5000.h b/drivers/media/tuners/xc5000.h index b1a5474..7245cae 100644 --- a/drivers/media/tuners/xc5000.h +++ b/drivers/media/tuners/xc5000.h @@ -22,6 +22,7 @@ #ifndef __XC5000_H__ #define __XC5000_H__ +#include <linux/kconfig.h> #include <linux/firmware.h> struct dvb_frontend; @@ -56,8 +57,7 @@ struct xc5000_config { * it's passed back to a bridge during tuner_callback(). */ -#if defined(CONFIG_MEDIA_TUNER_XC5000) || \ - (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_XC5000) extern struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct xc5000_config *cfg); diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 1e6f40e..bd9d19a 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -125,6 +125,26 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, return status; } +static void au0828_usb_release(struct au0828_dev *dev) +{ + /* I2C */ + au0828_i2c_unregister(dev); + + kfree(dev); +} + +#ifdef CONFIG_VIDEO_AU0828_V4L2 +static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) +{ + struct au0828_dev *dev = + container_of(v4l2_dev, struct au0828_dev, v4l2_dev); + + v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl); + v4l2_device_unregister(&dev->v4l2_dev); + au0828_usb_release(dev); +} +#endif + static void au0828_usb_disconnect(struct usb_interface *interface) { struct au0828_dev *dev = usb_get_intfdata(interface); @@ -134,26 +154,19 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); -#ifdef CONFIG_VIDEO_AU0828_V4L2 - if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) - au0828_analog_unregister(dev); -#endif - - /* I2C */ - au0828_i2c_unregister(dev); - -#ifdef CONFIG_VIDEO_AU0828_V4L2 - v4l2_device_unregister(&dev->v4l2_dev); -#endif - usb_set_intfdata(interface, NULL); - mutex_lock(&dev->mutex); dev->usbdev = NULL; mutex_unlock(&dev->mutex); - - kfree(dev); - +#ifdef CONFIG_VIDEO_AU0828_V4L2 + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { + au0828_analog_unregister(dev); + v4l2_device_disconnect(&dev->v4l2_dev); + v4l2_device_put(&dev->v4l2_dev); + return; + } +#endif + au0828_usb_release(dev); } static int au0828_usb_probe(struct usb_interface *interface, @@ -202,15 +215,27 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->boardnr = id->driver_info; #ifdef CONFIG_VIDEO_AU0828_V4L2 + dev->v4l2_dev.release = au0828_usb_v4l2_release; + /* Create the v4l2_device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { - printk(KERN_ERR "%s() v4l2_device_register failed\n", + pr_err("%s() v4l2_device_register failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; + } + /* This control handler will inherit the controls from au8522 */ + retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4); + if (retval) { + pr_err("%s() v4l2_ctrl_handler_init failed\n", __func__); mutex_unlock(&dev->lock); kfree(dev); - return -EIO; + return retval; } + dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl; #endif /* Power Up the bridge */ diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 8b9e826..75ac994 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -35,6 +35,7 @@ #include <linux/suspend.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> #include <media/tuner.h> #include "au0828.h" @@ -58,6 +59,12 @@ do {\ } \ } while (0) +static inline void i2c_gate_ctrl(struct au0828_dev *dev, int val) +{ + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, val); +} + static inline void print_err_status(struct au0828_dev *dev, int packet, int status) { @@ -988,20 +995,22 @@ static int au0828_v4l2_open(struct file *filp) fh->type = type; fh->dev = dev; + v4l2_fh_init(&fh->fh, vdev); filp->private_data = fh; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + if (mutex_lock_interruptible(&dev->lock)) { + kfree(fh); + return -ERESTARTSYS; + } + if (dev->users == 0) { /* set au0828 interface0 to AS5 here again */ ret = usb_set_interface(dev->usbdev, 0, 5); if (ret < 0) { + mutex_unlock(&dev->lock); printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); + kfree(fh); return -EBUSY; } - dev->width = NTSC_STD_W; - dev->height = NTSC_STD_H; - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->width * dev->height; - dev->bytesperline = dev->width * 2; au0828_analog_stream_enable(dev); au0828_analog_stream_reset(dev); @@ -1014,6 +1023,7 @@ static int au0828_v4l2_open(struct file *filp) } dev->users++; + mutex_unlock(&dev->lock); videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, NULL, &dev->slock, @@ -1023,14 +1033,13 @@ static int au0828_v4l2_open(struct file *filp) &dev->lock); /* VBI Setup */ - dev->vbi_width = 720; - dev->vbi_height = 1; videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, NULL, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct au0828_buffer), fh, &dev->lock); + v4l2_fh_add(&fh->fh); return ret; } @@ -1040,6 +1049,9 @@ static int au0828_v4l2_close(struct file *filp) struct au0828_fh *fh = filp->private_data; struct au0828_dev *dev = fh->dev; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + mutex_lock(&dev->lock); if (res_check(fh, AU0828_RESOURCE_VIDEO)) { /* Cancel timeout thread in case they didn't call streamoff */ dev->vid_timeout_running = 0; @@ -1058,19 +1070,14 @@ static int au0828_v4l2_close(struct file *filp) res_free(fh, AU0828_RESOURCE_VBI); } - if (dev->users == 1) { - if (dev->dev_state & DEV_DISCONNECTED) { - au0828_analog_unregister(dev); - kfree(dev); - return 0; - } - + if (dev->users == 1 && video_is_registered(video_devdata(filp))) { au0828_analog_stream_disable(dev); au0828_uninit_isoc(dev); /* Save some power by putting tuner to sleep */ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + dev->std_set_in_tuner_core = 0; /* When close the device, set the usb intf0 into alt0 to free USB bandwidth */ @@ -1078,6 +1085,7 @@ static int au0828_v4l2_close(struct file *filp) if (ret < 0) printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); } + mutex_unlock(&dev->lock); videobuf_mmap_free(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vbiq); @@ -1087,6 +1095,26 @@ static int au0828_v4l2_close(struct file *filp) return 0; } +/* Must be called with dev->lock held */ +static void au0828_init_tuner(struct au0828_dev *dev) +{ + struct v4l2_frequency f = { + .frequency = dev->ctrl_freq, + .type = V4L2_TUNER_ANALOG_TV, + }; + + if (dev->std_set_in_tuner_core) + return; + dev->std_set_in_tuner_core = 1; + i2c_gate_ctrl(dev, 1); + /* If we've never sent the standard in tuner core, do so now. + We don't do this at device probe because we don't want to + incur the cost of a firmware load */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->std); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + i2c_gate_ctrl(dev, 0); +} + static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { @@ -1098,6 +1126,11 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, if (rc < 0) return rc; + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + au0828_init_tuner(dev); + mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (res_locked(dev, AU0828_RESOURCE_VIDEO)) return -EBUSY; @@ -1128,23 +1161,32 @@ static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) { struct au0828_fh *fh = filp->private_data; struct au0828_dev *dev = fh->dev; - int rc; + unsigned long req_events = poll_requested_events(wait); + unsigned int res; - rc = check_dev(dev); - if (rc < 0) - return rc; + if (check_dev(dev) < 0) + return POLLERR; + + res = v4l2_ctrl_poll(filp, wait); + if (!(req_events & (POLLIN | POLLRDNORM))) + return res; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + au0828_init_tuner(dev); + mutex_unlock(&dev->lock); if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (!res_get(fh, AU0828_RESOURCE_VIDEO)) return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + return res | videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { if (!res_get(fh, AU0828_RESOURCE_VBI)) return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; + return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); } + return POLLERR; } static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) @@ -1172,9 +1214,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, int width = format->fmt.pix.width; int height = format->fmt.pix.height; - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - /* If they are demanding a format other than the one we support, bail out (tvtime asks for UYVY and then retries with YUYV) */ if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) @@ -1193,6 +1232,7 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, format->fmt.pix.sizeimage = width * height * 2; format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; format->fmt.pix.field = V4L2_FIELD_INTERLACED; + format->fmt.pix.priv = 0; if (cmd == VIDIOC_TRY_FMT) return 0; @@ -1226,35 +1266,28 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - if (qc->type) - return 0; - else - return -EINVAL; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct au0828_fh *fh = priv; + struct video_device *vdev = video_devdata(file); + struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; strlcpy(cap->driver, "au0828", sizeof(cap->driver)); strlcpy(cap->card, dev->board.name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + usb_make_path(dev->usbdev, cap->bus_info, sizeof(cap->bus_info)); - /*set the device capabilities */ - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_AUDIO | + /* set the device capabilities */ + cap->device_caps = V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + else + cap->device_caps |= V4L2_CAP_VBI_CAPTURE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE; return 0; } @@ -1286,6 +1319,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = dev->frame_size; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */ f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.priv = 0; return 0; } @@ -1320,24 +1354,34 @@ out: return rc; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); + dev->std = norm; + + au0828_init_tuner(dev); + + i2c_gate_ctrl(dev, 1); /* FIXME: when we support something other than NTSC, we are going to have to make the au0828 bridge adjust the size of its capture buffer, which is currently hardcoded at 720x480 */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm); - dev->std_set_in_tuner_core = 1; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm); - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + i2c_gate_ctrl(dev, 0); + + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + *norm = dev->std; return 0; } @@ -1368,10 +1412,13 @@ static int vidioc_enum_input(struct file *file, void *priv, input->index = tmp; strcpy(input->name, inames[AUVI_INPUT(tmp).type]); if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) || - (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) + (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) { input->type |= V4L2_INPUT_TYPE_TUNER; - else + input->audioset = 1; + } else { input->type |= V4L2_INPUT_TYPE_CAMERA; + input->audioset = 2; + } input->std = dev->vdev->tvnorms; @@ -1386,32 +1433,25 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int vidioc_s_input(struct file *file, void *priv, unsigned int index) +static void au0828_s_input(struct au0828_dev *dev, int index) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; int i; - dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, - index); - if (index >= AU0828_MAX_INPUT) - return -EINVAL; - if (AUVI_INPUT(index).type == 0) - return -EINVAL; - dev->ctrl_input = index; - switch (AUVI_INPUT(index).type) { case AU0828_VMUX_SVIDEO: dev->input_type = AU0828_VMUX_SVIDEO; + dev->ctrl_ainput = 1; break; case AU0828_VMUX_COMPOSITE: dev->input_type = AU0828_VMUX_COMPOSITE; + dev->ctrl_ainput = 1; break; case AU0828_VMUX_TELEVISION: dev->input_type = AU0828_VMUX_TELEVISION; + dev->ctrl_ainput = 0; break; default: - dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n", + dprintk(1, "unknown input type set [%d]\n", AUVI_INPUT(index).type); break; } @@ -1442,55 +1482,60 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index) v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, AUVI_INPUT(index).amux, 0, 0); - return 0; } -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_input(struct file *file, void *priv, unsigned int index) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; - unsigned int index = a->index; + dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, + index); + if (index >= AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(index).type == 0) + return -EINVAL; + dev->ctrl_input = index; + au0828_s_input(dev, index); + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a) +{ if (a->index > 1) return -EINVAL; - index = dev->ctrl_ainput; - if (index == 0) + if (a->index == 0) strcpy(a->name, "Television"); else strcpy(a->name, "Line in"); a->capability = V4L2_AUDCAP_STEREO; - a->index = index; return 0; } -static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; - if (a->index != dev->ctrl_ainput) - return -EINVAL; - return 0; -} -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + a->index = dev->ctrl_ainput; + if (a->index == 0) + strcpy(a->name, "Television"); + else + strcpy(a->name, "Line in"); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); + a->capability = V4L2_AUDCAP_STEREO; return 0; - } -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + + if (a->index != dev->ctrl_ainput) + return -EINVAL; return 0; } @@ -1503,12 +1548,16 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return -EINVAL; strcpy(t->name, "Auvitek tuner"); + + au0828_init_tuner(dev); + i2c_gate_ctrl(dev, 1); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + i2c_gate_ctrl(dev, 0); return 0; } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; @@ -1516,15 +1565,10 @@ static int vidioc_s_tuner(struct file *file, void *priv, if (t->index != 0) return -EINVAL; - t->type = V4L2_TUNER_ANALOG_TV; - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); - + au0828_init_tuner(dev); + i2c_gate_ctrl(dev, 1); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + i2c_gate_ctrl(dev, 0); dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, t->afc); @@ -1539,40 +1583,31 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; - freq->type = V4L2_TUNER_ANALOG_TV; + if (freq->tuner != 0) + return -EINVAL; freq->frequency = dev->ctrl_freq; return 0; } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; + struct v4l2_frequency new_freq = *freq; if (freq->tuner != 0) return -EINVAL; - if (freq->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - - dev->ctrl_freq = freq->frequency; - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); - - if (dev->std_set_in_tuner_core == 0) { - /* If we've never sent the standard in tuner core, do so now. We - don't do this at device probe because we don't want to incur - the cost of a firmware load */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, - dev->vdev->tvnorms); - dev->std_set_in_tuner_core = 1; - } + au0828_init_tuner(dev); + i2c_gate_ctrl(dev, 1); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); + /* Get the actual set (and possibly clamped) frequency */ + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq); + dev->ctrl_freq = new_freq.frequency; - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + i2c_gate_ctrl(dev, 0); au0828_analog_stream_reset(dev); @@ -1598,6 +1633,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.count[1] = dev->vbi_height; format->fmt.vbi.start[0] = 21; format->fmt.vbi.start[1] = 284; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); return 0; } @@ -1664,6 +1700,7 @@ static int vidioc_streamon(struct file *file, void *priv, if (unlikely(!res_get(fh, get_ressource(fh)))) return -EBUSY; + au0828_init_tuner(dev); if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { au0828_analog_stream_enable(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); @@ -1756,7 +1793,7 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; @@ -1773,6 +1810,15 @@ static int vidioc_s_register(struct file *file, void *priv, } #endif +static int vidioc_log_status(struct file *file, void *fh) +{ + struct video_device *vdev = video_devdata(file); + + v4l2_ctrl_log_status(file, fh); + v4l2_device_call_all(vdev->v4l2_dev, 0, core, log_status); + return 0; +} + static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { @@ -1872,7 +1918,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_enumaudio = vidioc_enumaudio, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -1881,12 +1929,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .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_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, @@ -1898,6 +1944,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, + .vidioc_log_status = vidioc_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct video_device au0828_video_template = { @@ -1905,7 +1954,6 @@ static const struct video_device au0828_video_template = { .release = video_device_release, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_NTSC_M, - .current_norm = V4L2_STD_NTSC_M, }; /**************************************************************************/ @@ -1972,7 +2020,12 @@ int au0828_analog_register(struct au0828_dev *dev, dev->field_size = dev->width * dev->height; dev->frame_size = dev->field_size << 1; dev->bytesperline = dev->width << 1; + dev->vbi_width = 720; + dev->vbi_height = 1; dev->ctrl_ainput = 0; + dev->ctrl_freq = 960; + dev->std = V4L2_STD_NTSC_M; + au0828_s_input(dev, 0); /* allocate and fill v4l2 video struct */ dev->vdev = video_device_alloc(); @@ -1991,14 +2044,16 @@ int au0828_analog_register(struct au0828_dev *dev, /* Fill the video capture device struct */ *dev->vdev = au0828_video_template; - dev->vdev->parent = &dev->usbdev->dev; + dev->vdev->v4l2_dev = &dev->v4l2_dev; dev->vdev->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev->flags); strcpy(dev->vdev->name, "au0828a video"); /* Setup the VBI device */ *dev->vbi_dev = au0828_video_template; - dev->vbi_dev->parent = &dev->usbdev->dev; + dev->vbi_dev->v4l2_dev = &dev->v4l2_dev; dev->vbi_dev->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vbi_dev->flags); strcpy(dev->vbi_dev->name, "au0828a vbi"); /* Register the v4l2 device */ diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index e579ff6..ef1f57f 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -28,6 +28,8 @@ #include <linux/videodev2.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> /* DVB */ #include "demux.h" @@ -118,6 +120,9 @@ enum au0828_dev_state { }; struct au0828_fh { + /* must be the first field of this struct! */ + struct v4l2_fh fh; + struct au0828_dev *dev; unsigned int resources; @@ -202,6 +207,7 @@ struct au0828_dev { #ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler v4l2_ctrl_hdl; #endif int users; unsigned int resources; /* resources in use */ @@ -216,6 +222,7 @@ struct au0828_dev { int vbi_width; int vbi_height; u32 vbi_read; + v4l2_std_id std; u32 field_size; u32 frame_size; u32 bytesperline; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 28688db..f548db8 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -34,11 +34,12 @@ #include <linux/vmalloc.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/cx2341x.h> +#include <media/tuner.h> #include <linux/usb.h> #include "cx231xx.h" -/*#include "cx23885-ioctl.h"*/ #define CX231xx_FIRM_IMAGE_SIZE 376836 #define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" @@ -75,9 +76,11 @@ static unsigned int mpegbufs = 8; module_param(mpegbufs, int, 0644); MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); + static unsigned int mpeglines = 128; module_param(mpeglines, int, 0644); MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); + static unsigned int mpeglinesize = 512; module_param(mpeglinesize, int, 0644); MODULE_PARM_DESC(mpeglinesize, @@ -86,10 +89,10 @@ MODULE_PARM_DESC(mpeglinesize, static unsigned int v4l_debug = 1; module_param(v4l_debug, int, 0644); MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); -struct cx231xx_dmaqueue *dma_qq; + #define dprintk(level, fmt, arg...)\ do { if (v4l_debug >= level) \ - printk(KERN_INFO "%s: " fmt, \ + pr_info("%s: " fmt, \ (dev) ? dev->name : "cx231xx[?]", ## arg); \ } while (0) @@ -131,11 +134,13 @@ static struct cx231xx_tvnorm cx231xx_tvnorms[] = { }; /* ------------------------------------------------------------------ */ + enum cx231xx_capture_type { CX231xx_MPEG_CAPTURE, CX231xx_RAW_CAPTURE, CX231xx_RAW_PASSTHRU_CAPTURE }; + enum cx231xx_capture_bits { CX231xx_RAW_BITS_NONE = 0x00, CX231xx_RAW_BITS_YUV_CAPTURE = 0x01, @@ -144,33 +149,40 @@ enum cx231xx_capture_bits { CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08, CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10 }; + enum cx231xx_capture_end { CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */ CX231xx_END_NOW, /* stop immediately, no irq */ }; + enum cx231xx_framerate { CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */ CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */ }; + enum cx231xx_stream_port { CX231xx_OUTPUT_PORT_MEMORY, CX231xx_OUTPUT_PORT_STREAMING, CX231xx_OUTPUT_PORT_SERIAL }; + enum cx231xx_data_xfer_status { CX231xx_MORE_BUFFERS_FOLLOW, CX231xx_LAST_BUFFER, }; + enum cx231xx_picture_mask { CX231xx_PICTURE_MASK_NONE, CX231xx_PICTURE_MASK_I_FRAMES, CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3, CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7, }; + enum cx231xx_vbi_mode_bits { CX231xx_VBI_BITS_SLICED, CX231xx_VBI_BITS_RAW, }; + enum cx231xx_vbi_insertion_bits { CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, @@ -178,56 +190,69 @@ enum cx231xx_vbi_insertion_bits { CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, }; + enum cx231xx_dma_unit { CX231xx_DMA_BYTES, CX231xx_DMA_FRAMES, }; + enum cx231xx_dma_transfer_status_bits { CX231xx_DMA_TRANSFER_BITS_DONE = 0x01, CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04, CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10, }; + enum cx231xx_pause { CX231xx_PAUSE_ENCODING, CX231xx_RESUME_ENCODING, }; + enum cx231xx_copyright { CX231xx_COPYRIGHT_OFF, CX231xx_COPYRIGHT_ON, }; + enum cx231xx_notification_type { CX231xx_NOTIFICATION_REFRESH, }; + enum cx231xx_notification_status { CX231xx_NOTIFICATION_OFF, CX231xx_NOTIFICATION_ON, }; + enum cx231xx_notification_mailbox { CX231xx_NOTIFICATION_NO_MAILBOX = -1, }; + enum cx231xx_field1_lines { CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */ CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */ CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */ }; + enum cx231xx_field2_lines { CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */ CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */ CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */ }; + enum cx231xx_custom_data_type { CX231xx_CUSTOM_EXTENSION_USR_DATA, CX231xx_CUSTOM_PRIVATE_PACKET, }; + enum cx231xx_mute { CX231xx_UNMUTE, CX231xx_MUTE, }; + enum cx231xx_mute_video_mask { CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00, CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000, CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000, }; + enum cx231xx_mute_video_shift { CX231xx_MUTE_VIDEO_V_SHIFT = 8, CX231xx_MUTE_VIDEO_U_SHIFT = 16, @@ -296,41 +321,43 @@ enum cx231xx_mute_video_shift { #define CX23417_GPIO_MASK 0xFC0003FF -static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value) + +static int set_itvc_reg(struct cx231xx *dev, u32 gpio_direction, u32 value) { int status = 0; u32 _gpio_direction = 0; _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; - _gpio_direction = _gpio_direction|gpio_direction; + _gpio_direction = _gpio_direction | gpio_direction; status = cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0); return status; } -static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue) + +static int get_itvc_reg(struct cx231xx *dev, u32 gpio_direction, u32 *val_ptr) { int status = 0; u32 _gpio_direction = 0; _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; - _gpio_direction = _gpio_direction|gpio_direction; + _gpio_direction = _gpio_direction | gpio_direction; status = cx231xx_send_gpio_cmd(dev, _gpio_direction, - (u8 *)pValue, 4, 0, 1); + (u8 *)val_ptr, 4, 0, 1); return status; } -static int waitForMciComplete(struct cx231xx *dev) +static int wait_for_mci_complete(struct cx231xx *dev) { u32 gpio; - u32 gpio_driection = 0; + u32 gpio_direction = 0; u8 count = 0; - getITVCReg(dev, gpio_driection, &gpio); + get_itvc_reg(dev, gpio_direction, &gpio); while (!(gpio&0x020000)) { msleep(10); - getITVCReg(dev, gpio_driection, &gpio); + get_itvc_reg(dev, gpio_direction, &gpio); if (count++ > 100) { dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio); @@ -345,57 +372,57 @@ static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value) u32 temp; int status = 0; - temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8); - temp = temp<<10; - status = setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_DATA_BYTE0 | ((value & 0x000000FF) << 8); + temp = temp << 10; + status = set_itvc_reg(dev, ITVC_WRITE_DIR, temp); if (status < 0) return status; - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 1;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_DATA_BYTE1 | (value & 0x0000FF00); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 2;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_DATA_BYTE2 | ((value & 0x00FF0000) >> 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 3;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_DATA_BYTE3 | ((value & 0xFF000000) >> 16); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write address byte 0;*/ - temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x000000FF) << 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write address byte 1;*/ - temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0x0000FF00); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*Write that the mode is write.*/ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE; - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); - return waitForMciComplete(dev); + return wait_for_mci_complete(dev); } static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value) @@ -407,70 +434,68 @@ static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value) temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write address byte 1;*/ temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00); temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write that the mode is read;*/ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ; temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*wait for the MIRDY line to be asserted , signalling that the read is done;*/ - ret = waitForMciComplete(dev); + ret = wait_for_mci_complete(dev); /*switch the DATA- GPIO to input mode;*/ /*Read data byte 0;*/ temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); return_value |= ((temp & 0x03FC0000) >> 18); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /* Read data byte 1;*/ temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); return_value |= ((temp & 0x03FC0000) >> 10); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /*Read data byte 2;*/ temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); return_value |= ((temp & 0x03FC0000) >> 2); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /*Read data byte 3;*/ temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); return_value |= ((temp & 0x03FC0000) << 6); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); *value = return_value; - - return ret; } @@ -481,59 +506,59 @@ static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value) u32 temp; int ret = 0; - temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8); + temp = 0x82 | MCI_MEMORY_DATA_BYTE0 | ((value & 0x000000FF) << 8); temp = temp << 10; - ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); + ret = set_itvc_reg(dev, ITVC_WRITE_DIR, temp); if (ret < 0) return ret; - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 1;*/ temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00); temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 2;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_DATA_BYTE2 | ((value & 0x00FF0000) >> 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write data byte 3;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_DATA_BYTE3 | ((value & 0xFF000000) >> 16); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /* write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | - ((address & 0x003F0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | + ((address & 0x003F0000) >> 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /* write address byte 1;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /* write address byte 0;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*wait for MIRDY line;*/ - waitForMciComplete(dev); + wait_for_mci_complete(dev); return 0; } @@ -545,68 +570,68 @@ static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value) int ret = 0; /*write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ | - ((address & 0x003F0000)>>8); - temp = temp<<10; - ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ | + ((address & 0x003F0000) >> 8); + temp = temp << 10; + ret = set_itvc_reg(dev, ITVC_WRITE_DIR, temp); if (ret < 0) return ret; - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write address byte 1*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*write address byte 0*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); + temp = temp << 10; + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); + temp = temp | (0x05 << 10); + set_itvc_reg(dev, ITVC_WRITE_DIR, temp); /*Wait for MIRDY line*/ - ret = waitForMciComplete(dev); + ret = wait_for_mci_complete(dev); /*Read data byte 3;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)<<6); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + temp = (0x82 | MCI_MEMORY_DATA_BYTE3) << 10; + set_itvc_reg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_MEMORY_DATA_BYTE3) << 10); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) << 6); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /*Read data byte 2;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>2); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + temp = (0x82 | MCI_MEMORY_DATA_BYTE2) << 10; + set_itvc_reg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_MEMORY_DATA_BYTE2) << 10); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) >> 2); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /* Read data byte 1;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>10); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + temp = (0x82 | MCI_MEMORY_DATA_BYTE1) << 10; + set_itvc_reg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_MEMORY_DATA_BYTE1) << 10); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) >> 10); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); /*Read data byte 0;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>18); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + temp = (0x82 | MCI_MEMORY_DATA_BYTE0) << 10; + set_itvc_reg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_MEMORY_DATA_BYTE0) << 10); + set_itvc_reg(dev, ITVC_READ_DIR, temp); + get_itvc_reg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) >> 18); + set_itvc_reg(dev, ITVC_READ_DIR, (0x87 << 10)); *value = return_value; return ret; @@ -619,94 +644,91 @@ static char *cmd_to_str(int cmd) { switch (cmd) { case CX2341X_ENC_PING_FW: - return "PING_FW"; + return "PING_FW"; case CX2341X_ENC_START_CAPTURE: - return "START_CAPTURE"; + return "START_CAPTURE"; case CX2341X_ENC_STOP_CAPTURE: - return "STOP_CAPTURE"; + return "STOP_CAPTURE"; case CX2341X_ENC_SET_AUDIO_ID: - return "SET_AUDIO_ID"; + return "SET_AUDIO_ID"; case CX2341X_ENC_SET_VIDEO_ID: - return "SET_VIDEO_ID"; + return "SET_VIDEO_ID"; case CX2341X_ENC_SET_PCR_ID: - return "SET_PCR_PID"; + return "SET_PCR_PID"; case CX2341X_ENC_SET_FRAME_RATE: - return "SET_FRAME_RATE"; + return "SET_FRAME_RATE"; case CX2341X_ENC_SET_FRAME_SIZE: - return "SET_FRAME_SIZE"; + return "SET_FRAME_SIZE"; case CX2341X_ENC_SET_BIT_RATE: - return "SET_BIT_RATE"; + return "SET_BIT_RATE"; case CX2341X_ENC_SET_GOP_PROPERTIES: - return "SET_GOP_PROPERTIES"; + return "SET_GOP_PROPERTIES"; case CX2341X_ENC_SET_ASPECT_RATIO: - return "SET_ASPECT_RATIO"; + return "SET_ASPECT_RATIO"; case CX2341X_ENC_SET_DNR_FILTER_MODE: - return "SET_DNR_FILTER_PROPS"; + return "SET_DNR_FILTER_PROPS"; case CX2341X_ENC_SET_DNR_FILTER_PROPS: - return "SET_DNR_FILTER_PROPS"; + return "SET_DNR_FILTER_PROPS"; case CX2341X_ENC_SET_CORING_LEVELS: - return "SET_CORING_LEVELS"; + return "SET_CORING_LEVELS"; case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: - return "SET_SPATIAL_FILTER_TYPE"; + return "SET_SPATIAL_FILTER_TYPE"; case CX2341X_ENC_SET_VBI_LINE: - return "SET_VBI_LINE"; + return "SET_VBI_LINE"; case CX2341X_ENC_SET_STREAM_TYPE: - return "SET_STREAM_TYPE"; + return "SET_STREAM_TYPE"; case CX2341X_ENC_SET_OUTPUT_PORT: - return "SET_OUTPUT_PORT"; + return "SET_OUTPUT_PORT"; case CX2341X_ENC_SET_AUDIO_PROPERTIES: - return "SET_AUDIO_PROPERTIES"; + return "SET_AUDIO_PROPERTIES"; case CX2341X_ENC_HALT_FW: - return "HALT_FW"; + return "HALT_FW"; case CX2341X_ENC_GET_VERSION: - return "GET_VERSION"; + return "GET_VERSION"; case CX2341X_ENC_SET_GOP_CLOSURE: - return "SET_GOP_CLOSURE"; + return "SET_GOP_CLOSURE"; case CX2341X_ENC_GET_SEQ_END: - return "GET_SEQ_END"; + return "GET_SEQ_END"; case CX2341X_ENC_SET_PGM_INDEX_INFO: - return "SET_PGM_INDEX_INFO"; + return "SET_PGM_INDEX_INFO"; case CX2341X_ENC_SET_VBI_CONFIG: - return "SET_VBI_CONFIG"; + return "SET_VBI_CONFIG"; case CX2341X_ENC_SET_DMA_BLOCK_SIZE: - return "SET_DMA_BLOCK_SIZE"; + return "SET_DMA_BLOCK_SIZE"; case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: - return "GET_PREV_DMA_INFO_MB_10"; + return "GET_PREV_DMA_INFO_MB_10"; case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: - return "GET_PREV_DMA_INFO_MB_9"; + return "GET_PREV_DMA_INFO_MB_9"; case CX2341X_ENC_SCHED_DMA_TO_HOST: - return "SCHED_DMA_TO_HOST"; + return "SCHED_DMA_TO_HOST"; case CX2341X_ENC_INITIALIZE_INPUT: - return "INITIALIZE_INPUT"; + return "INITIALIZE_INPUT"; case CX2341X_ENC_SET_FRAME_DROP_RATE: - return "SET_FRAME_DROP_RATE"; + return "SET_FRAME_DROP_RATE"; case CX2341X_ENC_PAUSE_ENCODER: - return "PAUSE_ENCODER"; + return "PAUSE_ENCODER"; case CX2341X_ENC_REFRESH_INPUT: - return "REFRESH_INPUT"; + return "REFRESH_INPUT"; case CX2341X_ENC_SET_COPYRIGHT: - return "SET_COPYRIGHT"; + return "SET_COPYRIGHT"; case CX2341X_ENC_SET_EVENT_NOTIFICATION: - return "SET_EVENT_NOTIFICATION"; + return "SET_EVENT_NOTIFICATION"; case CX2341X_ENC_SET_NUM_VSYNC_LINES: - return "SET_NUM_VSYNC_LINES"; + return "SET_NUM_VSYNC_LINES"; case CX2341X_ENC_SET_PLACEHOLDER: - return "SET_PLACEHOLDER"; + return "SET_PLACEHOLDER"; case CX2341X_ENC_MUTE_VIDEO: - return "MUTE_VIDEO"; + return "MUTE_VIDEO"; case CX2341X_ENC_MUTE_AUDIO: - return "MUTE_AUDIO"; + return "MUTE_AUDIO"; case CX2341X_ENC_MISC: - return "MISC"; + return "MISC"; default: return "UNKNOWN"; } } -static int cx231xx_mbox_func(void *priv, - u32 command, - int in, - int out, +static int cx231xx_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) { struct cx231xx *dev = priv; @@ -721,11 +743,9 @@ static int cx231xx_mbox_func(void *priv, without side effects */ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); if (value != 0x12345678) { - dprintk(3, - "Firmware and/or mailbox pointer not initialized " - "or corrupted, signature = 0x%x, cmd = %s\n", value, - cmd_to_str(command)); - return -1; + dprintk(3, "Firmware and/or mailbox pointer not initialized or corrupted, signature = 0x%x, cmd = %s\n", + value, cmd_to_str(command)); + return -EIO; } /* This read looks at 32 bits, but flag is only 8 bits. @@ -733,9 +753,9 @@ static int cx231xx_mbox_func(void *priv, */ mc417_memory_read(dev, dev->cx23417_mailbox, &flag); if (flag) { - dprintk(3, "ERROR: Mailbox appears to be in use " - "(%x), cmd = %s\n", flag, cmd_to_str(command)); - return -1; + dprintk(3, "ERROR: Mailbox appears to be in use (%x), cmd = %s\n", + flag, cmd_to_str(command)); + return -EBUSY; } flag |= 1; /* tell 'em we're working on it */ @@ -764,7 +784,7 @@ static int cx231xx_mbox_func(void *priv, break; if (time_after(jiffies, timeout)) { dprintk(3, "ERROR: API Mailbox timeout\n"); - return -1; + return -EIO; } udelay(10); } @@ -781,17 +801,14 @@ static int cx231xx_mbox_func(void *priv, flag = 0; mc417_memory_write(dev, dev->cx23417_mailbox, flag); - return retval; + return 0; } /* We don't need to call the API often, so using just one * mailbox will probably suffice */ -static int cx231xx_api_cmd(struct cx231xx *dev, - u32 command, - u32 inputcnt, - u32 outputcnt, - ...) +static int cx231xx_api_cmd(struct cx231xx *dev, u32 command, + u32 inputcnt, u32 outputcnt, ...) { u32 data[CX2341X_MBOX_MAX_DATA]; va_list vargs; @@ -813,6 +830,7 @@ static int cx231xx_api_cmd(struct cx231xx *dev, return err; } + static int cx231xx_find_mailbox(struct cx231xx *dev) { u32 signature[4] = { @@ -834,81 +852,80 @@ static int cx231xx_find_mailbox(struct cx231xx *dev) else signaturecnt = 0; if (4 == signaturecnt) { - dprintk(1, "Mailbox signature found at 0x%x\n", i+1); - return i+1; + dprintk(1, "Mailbox signature found at 0x%x\n", i + 1); + return i + 1; } } dprintk(3, "Mailbox signature values not found!\n"); return -1; } -static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value, +static void mci_write_memory_to_gpio(struct cx231xx *dev, u32 address, u32 value, u32 *p_fw_image) { - u32 temp = 0; int i = 0; - temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_DATA_BYTE0 | ((value & 0x000000FF) << 8); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /*write data byte 1;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /*write data byte 2;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_DATA_BYTE2 | ((value & 0x00FF0000) >> 8); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /*write data byte 3;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_DATA_BYTE3 | ((value & 0xFF000000) >> 16); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /* write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | - ((address & 0x003F0000)>>8); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | + ((address & 0x003F0000) >> 8); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /* write address byte 1;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; /* write address byte 0;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); - temp = temp<<10; + temp = 0x82 | MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); + temp = temp << 10; *p_fw_image = temp; p_fw_image++; - temp = temp|((0x05)<<10); + temp = temp | (0x05 << 10); *p_fw_image = temp; p_fw_image++; @@ -971,8 +988,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) IVTV_REG_APU, 0); if (retval != 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", - __func__); + pr_err("%s: Error with mc417_register_write\n", __func__); return -1; } @@ -980,25 +996,21 @@ static int cx231xx_load_firmware(struct cx231xx *dev) &dev->udev->dev); if (retval != 0) { - printk(KERN_ERR - "ERROR: Hotplug firmware request failed (%s).\n", + pr_err("ERROR: Hotplug firmware request failed (%s).\n", CX231xx_FIRM_IMAGE_NAME); - printk(KERN_ERR "Please fix your hotplug setup, the board will " - "not work without firmware loaded!\n"); + pr_err("Please fix your hotplug setup, the board will not work without firmware loaded!\n"); return -1; } if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) { - printk(KERN_ERR "ERROR: Firmware size mismatch " - "(have %zd, expected %d)\n", + pr_err("ERROR: Firmware size mismatch (have %zd, expected %d)\n", firmware->size, CX231xx_FIRM_IMAGE_SIZE); release_firmware(firmware); return -1; } if (0 != memcmp(firmware->data, magic, 8)) { - printk(KERN_ERR - "ERROR: Firmware magic mismatch, wrong file?\n"); + pr_err("ERROR: Firmware magic mismatch, wrong file?\n"); release_firmware(firmware); return -1; } @@ -1013,7 +1025,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) transfer_size += 4) { fw_data = *p_fw_data; - mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw); + mci_write_memory_to_gpio(dev, address, fw_data, p_current_fw); address = address + 1; p_current_fw += 20; p_fw_data += 1; @@ -1045,7 +1057,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); if (retval < 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", + pr_err("%s: Error with mc417_register_write\n", __func__); return retval; } @@ -1057,7 +1069,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); if (retval < 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", + pr_err("%s: Error with mc417_register_write\n", __func__); return retval; } @@ -1082,10 +1094,10 @@ static void cx231xx_codec_settings(struct cx231xx *dev) cx231xx_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_ctrl_handler.width = dev->ts1.width; + dev->mpeg_ctrl_handler.height = dev->ts1.height; - cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params); + cx2341x_handler_setup(&dev->mpeg_ctrl_handler); cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); @@ -1105,27 +1117,25 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) dprintk(2, "%s() PING OK\n", __func__); retval = cx231xx_load_firmware(dev); if (retval < 0) { - printk(KERN_ERR "%s() f/w load failed\n", __func__); + pr_err("%s() f/w load failed\n", __func__); return retval; } retval = cx231xx_find_mailbox(dev); if (retval < 0) { - printk(KERN_ERR "%s() mailbox < 0, error\n", + pr_err("%s() mailbox < 0, error\n", __func__); return -1; } dev->cx23417_mailbox = retval; retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); if (retval < 0) { - printk(KERN_ERR - "ERROR: cx23417 firmware ping failed!\n"); + pr_err("ERROR: cx23417 firmware ping failed!\n"); return -1; } retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version); if (retval < 0) { - printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" - "version failed!\n"); + pr_err("ERROR: cx23417 firmware get encoder: version failed!\n"); return -1; } dprintk(1, "cx23417 firmware version is 0x%08x\n", version); @@ -1134,7 +1144,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) for (i = 0; i < 1; i++) { retval = mc417_register_read(dev, 0x20f8, &val); - dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n", + dprintk(3, "***before enable656() VIM Capture Lines = %d ***\n", val); if (retval < 0) return retval; @@ -1202,7 +1212,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) for (i = 0; i < 1; i++) { mc417_register_read(dev, 0x20f8, &val); - dprintk(3, "***VIM Capture Lines =%d ***\n", val); + dprintk(3, "***VIM Capture Lines =%d ***\n", val); } return 0; @@ -1223,6 +1233,7 @@ static int bb_buf_setup(struct videobuf_queue *q, return 0; } + static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) { struct cx231xx_fh *fh = vq->priv_data; @@ -1249,91 +1260,85 @@ static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb, struct cx231xx_dmaqueue *dma_q) { - void *vbuf; - struct cx231xx_buffer *buf; - u32 tail_data = 0; - char *p_data; - - if (dma_q->mpeg_buffer_done == 0) { - if (list_empty(&dma_q->active)) - return; - - buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); - dev->video_mode.isoc_ctl.buf = buf; - dma_q->mpeg_buffer_done = 1; - } - /* Fill buffer */ - buf = dev->video_mode.isoc_ctl.buf; - vbuf = videobuf_to_vmalloc(&buf->vb); - - if ((dma_q->mpeg_buffer_completed+len) < - mpeglines*mpeglinesize) { - if (dma_q->add_ps_package_head == - CX231XX_NEED_ADD_PS_PACKAGE_HEAD) { - memcpy(vbuf+dma_q->mpeg_buffer_completed, - dma_q->ps_head, 3); - dma_q->mpeg_buffer_completed = - dma_q->mpeg_buffer_completed + 3; - dma_q->add_ps_package_head = - CX231XX_NONEED_PS_PACKAGE_HEAD; - } - memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len); - dma_q->mpeg_buffer_completed = - dma_q->mpeg_buffer_completed + len; - } else { - dma_q->mpeg_buffer_done = 0; - - tail_data = - mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed; - memcpy(vbuf+dma_q->mpeg_buffer_completed, - data, tail_data); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - dma_q->mpeg_buffer_completed = 0; - - if (len - tail_data > 0) { - p_data = data + tail_data; - dma_q->left_data_count = len - tail_data; - memcpy(dma_q->p_left_data, - p_data, len - tail_data); - } - - } - - return; -} - -static void buffer_filled(char *data, int len, struct urb *urb, - struct cx231xx_dmaqueue *dma_q) -{ - void *vbuf; - struct cx231xx_buffer *buf; + void *vbuf; + struct cx231xx_buffer *buf; + u32 tail_data = 0; + char *p_data; + if (dma_q->mpeg_buffer_done == 0) { if (list_empty(&dma_q->active)) return; - buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); + struct cx231xx_buffer, vb.queue); + dev->video_mode.isoc_ctl.buf = buf; + dma_q->mpeg_buffer_done = 1; + } + /* Fill buffer */ + buf = dev->video_mode.isoc_ctl.buf; + vbuf = videobuf_to_vmalloc(&buf->vb); + + if ((dma_q->mpeg_buffer_completed+len) < + mpeglines*mpeglinesize) { + if (dma_q->add_ps_package_head == + CX231XX_NEED_ADD_PS_PACKAGE_HEAD) { + memcpy(vbuf+dma_q->mpeg_buffer_completed, + dma_q->ps_head, 3); + dma_q->mpeg_buffer_completed = + dma_q->mpeg_buffer_completed + 3; + dma_q->add_ps_package_head = + CX231XX_NONEED_PS_PACKAGE_HEAD; + } + memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len); + dma_q->mpeg_buffer_completed = + dma_q->mpeg_buffer_completed + len; + } else { + dma_q->mpeg_buffer_done = 0; + tail_data = + mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed; + memcpy(vbuf+dma_q->mpeg_buffer_completed, + data, tail_data); - /* Fill buffer */ - vbuf = videobuf_to_vmalloc(&buf->vb); - memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); + dma_q->mpeg_buffer_completed = 0; - return; + if (len - tail_data > 0) { + p_data = data + tail_data; + dma_q->left_data_count = len - tail_data; + memcpy(dma_q->p_left_data, + p_data, len - tail_data); + } + } } -static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) + +static void buffer_filled(char *data, int len, struct urb *urb, + struct cx231xx_dmaqueue *dma_q) +{ + void *vbuf; + struct cx231xx_buffer *buf; + + if (list_empty(&dma_q->active)) + return; + + buf = list_entry(dma_q->active.next, + struct cx231xx_buffer, vb.queue); + + /* Fill buffer */ + vbuf = videobuf_to_vmalloc(&buf->vb); + memcpy(vbuf, data, len); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + v4l2_get_timestamp(&buf->vb.ts); + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) { struct cx231xx_dmaqueue *dma_q = urb->context; unsigned char *p_buffer; @@ -1358,11 +1363,9 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) return 0; } -static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) -{ - /*char *outp;*/ - /*struct cx231xx_buffer *buf;*/ +static int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) +{ struct cx231xx_dmaqueue *dma_q = urb->context; unsigned char *p_buffer, *buffer; u32 buffer_size = 0; @@ -1393,8 +1396,6 @@ static int bb_buf_prepare(struct videobuf_queue *q, int rc = 0, urb_init = 0; int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - dma_qq = &dev->video_mode.vidq; - if (0 != buf->vb.baddr && buf->vb.bsize < size) return -EINVAL; buf->vb.width = fh->dev->ts1.ts_packet_size; @@ -1482,36 +1483,6 @@ static struct videobuf_queue_ops cx231xx_qops = { /* ------------------------------------------------------------------ */ -static const u32 *ctrl_classes[] = { - cx2341x_mpeg_ctrls, - NULL -}; - -static int cx231xx_queryctrl(struct cx231xx *dev, - struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - - /* MPEG V4L2 controls */ - if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - - return 0; -} - -static int cx231xx_querymenu(struct cx231xx *dev, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; - cx231xx_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 *fh0, v4l2_std_id *norm) { struct cx231xx_fh *fh = file->private_data; @@ -1520,14 +1491,15 @@ static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm) *norm = dev->encodernorm.id; return 0; } -static int vidioc_s_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 cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; unsigned int i; for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++) - if (*id & cx231xx_tvnorms[i].id) + if (id & cx231xx_tvnorms[i].id) break; if (i == ARRAY_SIZE(cx231xx_tvnorms)) return -EINVAL; @@ -1537,12 +1509,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) dprintk(3, "encodernorm set to NTSC\n"); dev->norm = V4L2_STD_NTSC; dev->ts1.height = 480; - dev->mpeg_params.is_50hz = 0; + cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, false); } else { dprintk(3, "encodernorm set to PAL\n"); dev->norm = V4L2_STD_PAL_B; dev->ts1.height = 576; - dev->mpeg_params.is_50hz = 1; + cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, true); } call_all(dev, core, s_std, dev->norm); /* do mode control overrides */ @@ -1551,161 +1523,23 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) dprintk(3, "exit vidioc_s_std() i=0x%x\n", i); return 0; } -static int vidioc_g_audio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - struct v4l2_audio *vin = a; - - int ret = -EINVAL; - if (vin->index > 0) - return ret; - strncpy(vin->name, "VideoGrabber Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; -return 0; -} -static int vidioc_enumaudio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - struct v4l2_audio *vin = a; - - int ret = -EINVAL; - - if (vin->index > 0) - return ret; - strncpy(vin->name, "VideoGrabber Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; - - -return 0; -} -static const char *iname[] = { - [CX231XX_VMUX_COMPOSITE1] = "Composite1", - [CX231XX_VMUX_SVIDEO] = "S-Video", - [CX231XX_VMUX_TELEVISION] = "Television", - [CX231XX_VMUX_CABLE] = "Cable TV", - [CX231XX_VMUX_DVB] = "DVB", - [CX231XX_VMUX_DEBUG] = "for debug only", -}; -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - struct cx231xx_input *input; - int n; - dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index); - - if (i->index >= 4) - return -EINVAL; - - - input = &cx231xx_boards[dev->model].input[i->index]; - - if (input->type == 0) - return -EINVAL; - - /* FIXME - * strcpy(i->name, input->name); */ - - n = i->index; - strcpy(i->name, iname[INPUT(n)->type]); - - if (input->type == CX231XX_VMUX_TELEVISION || - input->type == CX231XX_VMUX_CABLE) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; - - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(3, "enter vidioc_s_input() i=%d\n", i); - - mutex_lock(&dev->lock); - - video_mux(dev, i); - - mutex_unlock(&dev->lock); - - if (i >= 4) - return -EINVAL; - dev->input = i; - dprintk(3, "exit vidioc_s_input()\n"); - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - - - return 0; -} static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) { struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_s_ctrl()\n"); /* Update the A/V core */ call_all(dev, core, s_ctrl, ctl); dprintk(3, "exit vidioc_s_ctrl()\n"); return 0; } -static struct v4l2_capability pvr_capability = { - .driver = "cx231xx", - .card = "VideoGrabber", - .bus_info = "usb", - .version = 1, - .capabilities = (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), -}; -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - - - - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - return 0; -} static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) return -EINVAL; @@ -1720,17 +1554,18 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_g_fmt_vid_cap()\n"); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + 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.width = dev->ts1.width; - f->fmt.pix.height = dev->ts1.height; - f->fmt.pix.field = fh->vidq.field; - dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->vidq.field); + f->fmt.pix.sizeimage = mpeglines * mpeglinesize; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.width = dev->ts1.width; + f->fmt.pix.height = dev->ts1.height; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.priv = 0; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_g_fmt_vid_cap()\n"); return 0; } @@ -1740,25 +1575,20 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_try_fmt_vid_cap()\n"); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + 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->vidq.field); + f->fmt.pix.sizeimage = mpeglines * mpeglinesize; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_try_fmt_vid_cap()\n"); return 0; } -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - - return 0; -} - static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { @@ -1795,22 +1625,22 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_streamon()\n"); - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - if (dev->USE_ISO) - cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_isoc_copy); - else { - cx231xx_init_bulk(dev, 320, - 5, - dev->ts1_mode.max_pkt_size, - cx231xx_bulk_copy); - } + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (dev->USE_ISO) + cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + else { + cx231xx_init_bulk(dev, 320, + 5, + dev->ts1_mode.max_pkt_size, + cx231xx_bulk_copy); + } dprintk(3, "exit vidioc_streamon()\n"); return videobuf_streamon(&fh->vidq); } @@ -1822,117 +1652,25 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return videobuf_streamoff(&fh->vidq); } -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_g_ext_ctrls()\n"); - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - dprintk(3, "exit vidioc_g_ext_ctrls()\n"); - 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 cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - dprintk(3, "enter vidioc_s_ext_ctrls()\n"); - 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); - if (err == 0) { - err = cx2341x_update(dev, cx231xx_mbox_func, - &dev->mpeg_params, &p); - dev->mpeg_params = p; - } - - return err; - - -return 0; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - dprintk(3, "enter vidioc_try_ext_ctrls()\n"); - 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); - dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err); - return err; -} - static int vidioc_log_status(struct file *file, void *priv) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; - char name[32 + 2]; - snprintf(name, sizeof(name), "%s/2", dev->name); - dprintk(3, - "%s/2: ============ START LOG STATUS ============\n", - dev->name); call_all(dev, core, log_status); - cx2341x_log_status(&dev->mpeg_params, name); - dprintk(3, - "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_querymenu()\n"); - dprintk(3, "exit vidioc_querymenu()\n"); - return cx231xx_querymenu(dev, a); -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_queryctrl()\n"); - dprintk(3, "exit vidioc_queryctrl()\n"); - return cx231xx_queryctrl(dev, c); + return v4l2_ctrl_log_status(file, priv); } static int mpeg_open(struct file *file) { - int minor = video_devdata(file)->minor; - struct cx231xx *h, *dev = NULL; - /*struct list_head *list;*/ + struct video_device *vdev = video_devdata(file); + struct cx231xx *dev = video_drvdata(file); struct cx231xx_fh *fh; - /*u32 value = 0;*/ dprintk(2, "%s()\n", __func__); - list_for_each_entry(h, &cx231xx_devlist, devlist) { - if (h->v4l_device->minor == minor) - dev = h; - } - - if (dev == NULL) - return -ENODEV; - - mutex_lock(&dev->lock); + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh), GFP_KERNEL); @@ -1942,29 +1680,30 @@ static int mpeg_open(struct file *file) } file->private_data = fh; - fh->dev = dev; + v4l2_fh_init(&fh->fh, vdev); + fh->dev = dev; videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops, NULL, &dev->video_mode.slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), fh, NULL); + sizeof(struct cx231xx_buffer), fh, &dev->lock); /* videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops, &dev->udev->dev, &dev->ts1.slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct cx231xx_buffer), - fh, NULL); + fh, &dev->lock); */ - cx231xx_set_alt_setting(dev, INDEX_VANC, 1); cx231xx_set_gpio_value(dev, 2, 0); cx231xx_initialize_codec(dev); mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); cx231xx_start_TS1(dev); return 0; @@ -1977,25 +1716,20 @@ static int mpeg_release(struct file *file) dprintk(3, "mpeg_release()! dev=0x%p\n", dev); - if (!dev) { - dprintk(3, "abort!!!\n"); - return 0; - } - mutex_lock(&dev->lock); cx231xx_stop_TS1(dev); - /* do this before setting alternate! */ - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - cx231xx_set_mode(dev, CX231XX_SUSPEND); + /* do this before setting alternate! */ + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); - cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, - CX231xx_RAW_BITS_NONE); + cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, + CX231xx_RAW_BITS_NONE); /* FIXME: Review this crap */ /* Shut device down on last close */ @@ -2015,7 +1749,8 @@ static int mpeg_release(struct file *file) videobuf_read_stop(&fh->vidq); videobuf_mmap_free(&fh->vidq); - file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); mutex_unlock(&dev->lock); return 0; @@ -2027,7 +1762,6 @@ static ssize_t mpeg_read(struct file *file, char __user *data, struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; - /* 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) { @@ -2044,12 +1778,23 @@ static ssize_t mpeg_read(struct file *file, char __user *data, static unsigned int mpeg_poll(struct file *file, struct poll_table_struct *wait) { + unsigned long req_events = poll_requested_events(wait); struct cx231xx_fh *fh = file->private_data; - /*struct cx231xx *dev = fh->dev;*/ + struct cx231xx *dev = fh->dev; + unsigned int res = 0; + + if (v4l2_event_pending(&fh->fh)) + res |= POLLPRI; + else + poll_wait(file, &fh->fh.wait, wait); - /*dprintk(2, "%s\n", __func__);*/ + if (!(req_events & (POLLIN | POLLRDNORM))) + return res; - return videobuf_poll_stream(file, &fh->vidq, wait); + mutex_lock(&dev->lock); + res |= videobuf_poll_stream(file, &fh->vidq, wait); + mutex_unlock(&dev->lock); + return res; } static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) @@ -2069,44 +1814,39 @@ static struct v4l2_file_operations mpeg_fops = { .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx231xx_g_tuner, + .vidioc_s_tuner = cx231xx_s_tuner, + .vidioc_g_frequency = cx231xx_g_frequency, + .vidioc_s_frequency = cx231xx_s_frequency, + .vidioc_enum_input = cx231xx_enum_input, + .vidioc_g_input = cx231xx_g_input, + .vidioc_s_input = cx231xx_s_input, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_querycap = vidioc_querycap, + .vidioc_querycap = cx231xx_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_s_fmt_vid_cap = vidioc_try_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_log_status = vidioc_log_status, - .vidioc_querymenu = vidioc_querymenu, - .vidioc_queryctrl = vidioc_queryctrl, -/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/ + .vidioc_g_chip_ident = cx231xx_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG -/* .vidioc_g_register = cx231xx_g_register,*/ -/* .vidioc_s_register = cx231xx_s_register,*/ + .vidioc_g_register = cx231xx_g_register, + .vidioc_s_register = cx231xx_s_register, #endif + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx231xx_mpeg_template = { @@ -2114,8 +1854,7 @@ static struct video_device cx231xx_mpeg_template = { .fops = &mpeg_fops, .ioctl_ops = &mpeg_ioctl_ops, .minor = -1, - .tvnorms = CX231xx_NORMS, - .current_norm = V4L2_STD_NTSC_M, + .tvnorms = V4L2_STD_ALL, }; void cx231xx_417_unregister(struct cx231xx *dev) @@ -2128,10 +1867,44 @@ void cx231xx_417_unregister(struct cx231xx *dev) video_unregister_device(dev->v4l_device); else video_device_release(dev->v4l_device); + v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl); dev->v4l_device = NULL; } } +static int cx231xx_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) +{ + struct cx231xx *dev = container_of(cxhdl, struct cx231xx, mpeg_ctrl_handler); + int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_mbus_framefmt fmt; + + /* fix videodecoder resolution */ + fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); + fmt.height = cxhdl->height; + fmt.code = V4L2_MBUS_FMT_FIXED; + v4l2_subdev_call(dev->sd_cx25840, video, s_mbus_fmt, &fmt); + return 0; +} + +static int cx231xx_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) +{ + static const u32 freqs[3] = { 44100, 48000, 32000 }; + struct cx231xx *dev = container_of(cxhdl, struct cx231xx, mpeg_ctrl_handler); + + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (idx < ARRAY_SIZE(freqs)) + call_all(dev, audio, s_clock_freq, freqs[idx]); + return 0; +} + +static struct cx2341x_handler_ops cx231xx_ops = { + /* needed for the video clock freq */ + .s_audio_sampling_freq = cx231xx_s_audio_sampling_freq, + /* needed for setting up the video resolution */ + .s_video_encoding = cx231xx_s_video_encoding, +}; + static struct video_device *cx231xx_video_dev_alloc( struct cx231xx *dev, struct usb_device *usbdev, @@ -2145,12 +1918,21 @@ static struct video_device *cx231xx_video_dev_alloc( if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx231xx_boards[dev->model].name); vfd->v4l2_dev = &dev->v4l2_dev; + vfd->lock = &dev->lock; vfd->release = video_device_release; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + vfd->ctrl_handler = &dev->mpeg_ctrl_handler.hdl; + video_set_drvdata(vfd, dev); + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); + v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); + } return vfd; @@ -2173,10 +1955,27 @@ int cx231xx_417_register(struct cx231xx *dev) tsport->height = 576; tsport->width = 720; - cx2341x_fill_defaults(&dev->mpeg_params); + err = cx2341x_handler_init(&dev->mpeg_ctrl_handler, 50); + if (err) { + dprintk(3, "%s: can't init cx2341x controls\n", dev->name); + return err; + } + dev->mpeg_ctrl_handler.func = cx231xx_mbox_func; + dev->mpeg_ctrl_handler.priv = dev; + dev->mpeg_ctrl_handler.ops = &cx231xx_ops; + if (dev->sd_cx25840) + v4l2_ctrl_add_handler(&dev->mpeg_ctrl_handler.hdl, + dev->sd_cx25840->ctrl_handler, NULL); + if (dev->mpeg_ctrl_handler.hdl.error) { + err = dev->mpeg_ctrl_handler.hdl.error; + dprintk(3, "%s: can't add cx25840 controls\n", dev->name); + v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl); + return err; + } dev->norm = V4L2_STD_NTSC; - dev->mpeg_params.port = CX2341X_PORT_SERIAL; + dev->mpeg_ctrl_handler.port = CX2341X_PORT_SERIAL; + cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, false); /* Allocate and initialize V4L video device */ dev->v4l_device = cx231xx_video_dev_alloc(dev, @@ -2185,6 +1984,7 @@ int cx231xx_417_register(struct cx231xx *dev) VFL_TYPE_GRABBER, -1); if (err < 0) { dprintk(3, "%s: can't register mpeg device\n", dev->name); + v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl); return err; } diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index b4c99c7..81a1d97 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -449,9 +449,6 @@ static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) return -ENODEV; } - /* Sets volume, mute, etc */ - dev->mute = 0; - /* set alternate setting for audio interface */ /* 1 - 48000 samples per sec */ mutex_lock(&dev->lock); @@ -503,7 +500,6 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) return ret; } - dev->mute = 1; dev->adev.users--; mutex_unlock(&dev->lock); @@ -708,8 +704,8 @@ static int cx231xx_audio_init(struct cx231xx *dev) audio_index + 1]; adev->end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); + uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress; adev->num_alt = uif->num_altsetting; cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 7222079..235ba65 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -357,6 +357,7 @@ int cx231xx_afe_update_power_control(struct cx231xx *dev, case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: + case CX231XX_BOARD_OTG102: if (avmode == POLARIS_AVMODE_ANALOGT_TV) { while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | FLD_PWRDN_ENABLE_PLL)) { @@ -1720,6 +1721,7 @@ int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) case CX231XX_BOARD_CNXT_RDU_250: case CX231XX_BOARD_CNXT_VIDEO_GRABBER: case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_OTG102: func_mode = 0x03; break; case CX231XX_BOARD_CNXT_RDE_253S: @@ -2133,7 +2135,7 @@ int cx231xx_tuner_post_channel_change(struct cx231xx *dev) status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); - return status; + return status == sizeof(dwval) ? 0 : -EIO; } /****************************************************************************** @@ -2221,7 +2223,7 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) if (status < 0) return status; - tmp = *((u32 *) value); + tmp = le32_to_cpu(*((u32 *) value)); switch (mode) { case POLARIS_AVMODE_ENXTERNAL_AV: @@ -2442,7 +2444,7 @@ int cx231xx_power_suspend(struct cx231xx *dev) if (status > 0) return status; - tmp = *((u32 *) value); + tmp = le32_to_cpu(*((u32 *) value)); tmp &= (~PWR_MODE_MASK); value[0] = (u8) tmp; @@ -2470,7 +2472,7 @@ int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) if (status < 0) return status; - tmp = *((u32 *) value); + tmp = le32_to_cpu(*((u32 *) value)); tmp |= ep_mask; value[0] = (u8) tmp; value[1] = (u8) (tmp >> 8); @@ -2495,7 +2497,7 @@ int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) if (status < 0) return status; - tmp = *((u32 *) value); + tmp = le32_to_cpu(*((u32 *) value)); tmp &= (~ep_mask); value[0] = (u8) tmp; value[1] = (u8) (tmp >> 8); @@ -2638,20 +2640,23 @@ EXPORT_SYMBOL_GPL(cx231xx_capture_start); /***************************************************************************** * G P I O B I T control functions * ******************************************************************************/ -int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) +static int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 gpio_val) { int status = 0; - status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0); + gpio_val = cpu_to_le32(gpio_val); + status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&gpio_val, 4, 0, 0); return status; } -int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) +static int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 *gpio_val) { + u32 tmp; int status = 0; - status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1); + status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&tmp, 4, 0, 1); + *gpio_val = le32_to_cpu(tmp); return status; } @@ -2683,7 +2688,7 @@ int cx231xx_set_gpio_direction(struct cx231xx *dev, else value = dev->gpio_dir | (1 << pin_number); - status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, value, dev->gpio_val); /* cache the value for future */ dev->gpio_dir = value; @@ -2717,7 +2722,7 @@ int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) value = dev->gpio_dir | (1 << pin_number); dev->gpio_dir = value; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *) &dev->gpio_val); + dev->gpio_val); value = 0; } @@ -2730,7 +2735,7 @@ int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) dev->gpio_val = value; /* toggle bit0 of GP_IO */ - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); return status; } @@ -2748,7 +2753,7 @@ int cx231xx_gpio_i2c_start(struct cx231xx *dev) dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2756,7 +2761,7 @@ int cx231xx_gpio_i2c_start(struct cx231xx *dev) dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2764,7 +2769,7 @@ int cx231xx_gpio_i2c_start(struct cx231xx *dev) dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2782,7 +2787,7 @@ int cx231xx_gpio_i2c_end(struct cx231xx *dev) dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2790,7 +2795,7 @@ int cx231xx_gpio_i2c_end(struct cx231xx *dev) dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2800,7 +2805,7 @@ int cx231xx_gpio_i2c_end(struct cx231xx *dev) dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); status = - cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); if (status < 0) return -EINVAL; @@ -2822,33 +2827,33 @@ int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data) dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* set SCL to output 1; set SDA to output 0 */ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* set SCL to output 0; set SDA to output 0 */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); } else { /* set SCL to output 0; set SDA to output 1 */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* set SCL to output 1; set SDA to output 1 */ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* set SCL to output 0; set SDA to output 1 */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); } } return status; @@ -2867,17 +2872,17 @@ int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf) /* set SCL to output 0; set SDA to input */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* set SCL to output 1; set SDA to input */ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + dev->gpio_val); /* get SDA data bit */ gpio_logic_value = dev->gpio_val; status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + &dev->gpio_val); if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0) value |= (1 << (8 - i - 1)); @@ -2888,7 +2893,7 @@ int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf) !!!set SDA to input, never to modify SDA direction at the same times */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* store the value */ *buf = value & 0xff; @@ -2909,12 +2914,12 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); gpio_logic_value = dev->gpio_val; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); do { msleep(2); status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); + &dev->gpio_val); nCnt--; } while (((dev->gpio_val & (1 << dev->board.tuner_scl_gpio)) == 0) && @@ -2929,7 +2934,7 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) * through clock stretch, slave has given a SCL signal, * so the SDA data can be directly read. */ - status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, &dev->gpio_val); if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { dev->gpio_val = gpio_logic_value; @@ -2945,7 +2950,7 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) dev->gpio_val = gpio_logic_value; dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio); dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); return status; } @@ -2956,24 +2961,24 @@ int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev) /* set SDA to ouput */ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set SCL = 0 (output); set SDA = 0 (output) */ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set SCL = 1 (output); set SDA = 0 (output) */ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set SCL = 0 (output); set SDA = 0 (output) */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set SDA to input,and then the slave will read data from SDA. */ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); return status; } @@ -2985,15 +2990,15 @@ int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev) /* set scl to output ; set sda to input */ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set scl to output 0; set sda to input */ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); /* set scl to output 1; set sda to input */ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); return status; } diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 8d52956..13249e5 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -263,7 +263,11 @@ struct cx231xx_board cx231xx_boards[] = { .norm = V4L2_STD_PAL, .no_alt_vanc = 1, .external_av = 1, - .has_417 = 1, + .dont_use_port_3 = 1, + /* Actually, it has a 417, but it isn't working correctly. + * So set to 0 for now until someone can manage to get this + * to work reliably. */ + .has_417 = 0, .input = {{ .type = CX231XX_VMUX_COMPOSITE1, @@ -630,6 +634,39 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_OTG102] = { + .name = "Geniatech OTG102", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + /* According with PV CxPlrCAP.inf file */ + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + /*.has_417 = 1, */ + /* This board is believed to have a hardware encoding chip + * supporting mpeg1/2/4, but as the 417 is apparently not + * working for the reference board it is not here either. */ + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -671,6 +708,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_ICONBIT_U100}, {USB_DEVICE(0x0fd9, 0x0037), .driver_info = CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2}, + {USB_DEVICE(0x1f4d, 0x0102), + .driver_info = CX231XX_BOARD_OTG102}, {}, }; @@ -846,8 +885,6 @@ void cx231xx_card_setup(struct cx231xx *dev) int cx231xx_config(struct cx231xx *dev) { /* TBD need to add cx231xx specific code */ - dev->mute = 1; /* maybe not the right place... */ - dev->volume = 0x1f; return 0; } @@ -1187,8 +1224,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, uif = udev->actconfig->interface[dev->current_pcb_config. hs_config_info[0].interface_info.video_index + 1]; - dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0]. - endpoint[isoc_pipe].desc.bEndpointAddress); + dev->video_mode.end_point_addr = uif->altsetting[0]. + endpoint[isoc_pipe].desc.bEndpointAddress; dev->video_mode.num_alt = uif->num_altsetting; cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", @@ -1221,8 +1258,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, vanc_index + 1]; dev->vbi_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); + uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress; dev->vbi_mode.num_alt = uif->num_altsetting; cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", @@ -1256,8 +1293,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, hanc_index + 1]; dev->sliced_cc_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); + uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress; dev->sliced_cc_mode.num_alt = uif->num_altsetting; cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", @@ -1292,8 +1329,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ts1_index + 1]; dev->ts1_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe]. - desc.bEndpointAddress); + uif->altsetting[0].endpoint[isoc_pipe]. + desc.bEndpointAddress; dev->ts1_mode.num_alt = uif->num_altsetting; cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 05358d4..4ba3ce0 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1488,7 +1488,7 @@ int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) if (status < 0) return status; - tmp = *((u32 *) value); + tmp = le32_to_cpu(*((u32 *) 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 7c4e360..14e2610 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -89,8 +89,8 @@ static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = { }; static struct tda18271_std_map mb86a20s_tda18271_config = { - .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, - .if_lvl = 7, .rfagc_top = 0x37, }, + .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, + .if_lvl = 0, .rfagc_top = 0x37, }, }; static struct tda18271_config cnxt_rde253s_tunerconfig = { diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c index 7473c33..d7308ab 100644 --- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c @@ -672,7 +672,7 @@ u32 initialize_cx231xx(struct cx231xx *dev) pcb config it is related to */ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); - config_info = *((u32 *) data); + config_info = le32_to_cpu(*((u32 *) data)); usb_speed = (u8) (config_info & 0x1); /* Verify this device belongs to Bus power or Self power device */ diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h index f5e46e8..b3c6190 100644 --- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h @@ -68,11 +68,6 @@ enum USB_SPEED{ HIGH_SPEED = 0x1 /* 1: high speed */ }; -enum _true_false{ - FALSE = 0, - TRUE = 1 -}; - #define TS_MASK 0x6 enum TS_PORT{ NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid, diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index 46e3892..1340ff2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -70,10 +70,10 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status) break; } if (packet < 0) { - cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status, + cx231xx_err("URB status %d [%s].\n", status, errmsg); } else { - cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n", + cx231xx_err("URB packet %d, status %d [%s].\n", packet, status, errmsg); } } @@ -317,7 +317,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - cx231xx_err(DRIVER_NAME "urb completition error %d.\n", + cx231xx_err("urb completition error %d.\n", urb->status); break; } @@ -332,7 +332,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { - cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n", + cx231xx_err("urb resubmit failed (error=%i)\n", urb->status); } } @@ -345,7 +345,7 @@ void cx231xx_uninit_vbi_isoc(struct cx231xx *dev) struct urb *urb; int i; - cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n"); + cx231xx_info("called cx231xx_uninit_vbi_isoc\n"); dev->vbi_mode.bulk_ctl.nfields = -1; for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { @@ -394,7 +394,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, struct urb *urb; int rc; - cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n"); + cx231xx_info("called cx231xx_vbi_isoc\n"); /* De-allocates all pending stuff */ cx231xx_uninit_vbi_isoc(dev); @@ -442,8 +442,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { - cx231xx_err(DRIVER_NAME - ": cannot alloc bulk_ctl.urb %i\n", i); + cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i); cx231xx_uninit_vbi_isoc(dev); return -ENOMEM; } @@ -453,8 +452,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, dev->vbi_mode.bulk_ctl.transfer_buffer[i] = kzalloc(sb_size, GFP_KERNEL); if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { - cx231xx_err(DRIVER_NAME - ": unable to allocate %i bytes for transfer" + cx231xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); cx231xx_uninit_vbi_isoc(dev); @@ -473,8 +471,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC); if (rc) { - cx231xx_err(DRIVER_NAME - ": submit of urb %i failed (error=%i)\n", i, + cx231xx_err("submit of urb %i failed (error=%i)\n", i, rc); cx231xx_uninit_vbi_isoc(dev); return rc; @@ -526,7 +523,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, struct cx231xx_buffer *buf) { /* Advice that buffer was filled */ - /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */ + /* cx231xx_info("[%p/%d] wakeup\n", buf, buf->vb.i); */ buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; @@ -618,7 +615,7 @@ static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, char *outp; if (list_empty(&dma_q->active)) { - cx231xx_err(DRIVER_NAME ": No active queue to serve\n"); + cx231xx_err("No active queue to serve\n"); dev->vbi_mode.bulk_ctl.buf = NULL; *buf = NULL; return; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 06376d9..cd22147 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -35,6 +35,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -100,125 +101,6 @@ static struct cx231xx_fmt format[] = { }, }; -/* supported controls */ -/* Common to all boards */ - -/* ------------------------------------------------------------------- */ - -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static struct cx231xx_ctrl cx231xx_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 = 0xff, - .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 = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .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 = 0xff, - .step = 1, - .default_value = 0x7f, - .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 = 0x3f, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = PATH1_VOL_CTL, - .mask = 0xff, - .shift = 0, - } -}; -static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls); - -static const u32 cx231xx_user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, -#if 0 - V4L2_CID_AUDIO_BALANCE, -#endif - V4L2_CID_AUDIO_MUTE, - 0 -}; - -static const u32 *ctrl_classes[] = { - cx231xx_user_ctrls, - NULL -}; /* ------------------------------------------------------------------ Video buffer and parser functions @@ -1005,6 +887,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.priv = 0; return 0; } @@ -1045,10 +928,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = width; f->fmt.pix.height = height; f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.priv = 0; return 0; } @@ -1103,39 +987,39 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; struct v4l2_mbus_framefmt mbus_fmt; - struct v4l2_format f; int rc; rc = check_dev(dev); if (rc < 0) return rc; - cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm); + if (dev->norm == norm) + return 0; - dev->norm = *norm; + if (videobuf_queue_is_busy(&fh->vb_vidq)) + return -EBUSY; + + dev->norm = norm; /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; - vidioc_try_fmt_vid_cap(file, priv, &f); + dev->width = 720; + dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480; call_all(dev, core, s_std, dev->norm); /* We need to reset basic properties in the decoder related to resolution (since a standard change effects things like the number of lines in VACT, etc) */ - v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED); + memset(&mbus_fmt, 0, sizeof(mbus_fmt)); + mbus_fmt.code = V4L2_MBUS_FMT_FIXED; + mbus_fmt.width = dev->width; + mbus_fmt.height = dev->height; call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt); - - /* set new image size */ - dev->width = f.fmt.pix.width; - dev->height = f.fmt.pix.height; /* do mode control overrides */ cx231xx_do_mode_ctrl_overrides(dev); @@ -1152,7 +1036,7 @@ static const char *iname[] = { [CX231XX_VMUX_DEBUG] = "for debug only", }; -static int vidioc_enum_input(struct file *file, void *priv, +int cx231xx_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct cx231xx_fh *fh = priv; @@ -1192,7 +1076,7 @@ static int vidioc_enum_input(struct file *file, void *priv, return 0; } -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +int cx231xx_g_input(struct file *file, void *priv, unsigned int *i) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1202,7 +1086,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +int cx231xx_s_input(struct file *file, void *priv, unsigned int i) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1231,117 +1115,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - switch (a->index) { - case CX231XX_AMUX_VIDEO: - strcpy(a->name, "Television"); - break; - case CX231XX_AMUX_LINE_IN: - strcpy(a->name, "Line In"); - break; - default: - return -EINVAL; - } - - a->index = dev->ctl_ainput; - a->capability = V4L2_AUDCAP_STEREO; - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int status = 0; - - /* Doesn't allow manual routing */ - if (a->index != dev->ctl_ainput) - return -EINVAL; - - dev->ctl_ainput = INPUT(a->index)->amux; - status = cx231xx_set_audio_input(dev, dev->ctl_ainput); - - return status; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int id = qc->id; - int i; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); - if (unlikely(qc->id == 0)) - return -EINVAL; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < CX231XX_CTLS; i++) - if (cx231xx_ctls[i].v.id == qc->id) - break; - - if (i == CX231XX_CTLS) { - *qc = no_ctl; - return 0; - } - *qc = cx231xx_ctls[i].v; - - call_all(dev, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, core, g_ctrl, ctrl); - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, core, s_ctrl, ctrl); - return rc; -} - -static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1360,11 +1134,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; t->signal = 0xffff; /* LOCKED */ + call_all(dev, tuner, g_tuner, t); return 0; } -static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1382,25 +1157,26 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -static int vidioc_g_frequency(struct file *file, void *priv, +int cx231xx_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; + if (f->tuner) + return -EINVAL; - call_all(dev, tuner, g_frequency, f); + f->frequency = dev->ctl_freq; return 0; } -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +int cx231xx_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; + struct v4l2_frequency new_freq = *f; int rc; u32 if_frequency = 5400000; @@ -1415,16 +1191,12 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - /* set pre channel change settings in DIF first */ rc = cx231xx_tuner_pre_channel_change(dev); - dev->ctl_freq = f->frequency; call_all(dev, tuner, s_frequency, f); + call_all(dev, tuner, g_frequency, &new_freq); + dev->ctl_freq = new_freq.frequency; /* set post channel change settings in DIF first */ rc = cx231xx_tuner_post_channel_change(dev); @@ -1456,6 +1228,19 @@ static int vidioc_s_frequency(struct file *file, void *priv, return rc; } +int cx231xx_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_CX23100; + return 0; + } + return -EINVAL; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG /* @@ -1471,7 +1256,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if type == i2caddr, then <chip> is the 7-bit I2C address */ -static int vidioc_g_register(struct file *file, void *priv, +int cx231xx_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { struct cx231xx_fh *fh = priv; @@ -1618,8 +1403,8 @@ static int vidioc_g_register(struct file *file, void *priv, return ret; } -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) +int cx231xx_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1837,9 +1622,6 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; if (type != fh->type) return -EINVAL; @@ -1851,9 +1633,10 @@ static int vidioc_streamoff(struct file *file, void *priv, return 0; } -static int vidioc_querycap(struct file *file, void *priv, +int cx231xx_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -1861,17 +1644,22 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VBI_CAPTURE | -#if 0 - V4L2_CAP_SLICED_VBI_CAPTURE | -#endif - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - + if (vdev->vfl_type == VFL_TYPE_RADIO) + cap->device_caps = V4L2_CAP_RADIO; + else { + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (vdev->vfl_type == VFL_TYPE_VBI) + cap->device_caps |= V4L2_CAP_VBI_CAPTURE; + else + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + } if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_READWRITE | + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (dev->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1888,47 +1676,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - - call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, @@ -1936,6 +1683,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; + f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -1947,6 +1695,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); return 0; @@ -1958,12 +1707,6 @@ static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; - if (dev->vbi_stream_on && !fh->stream_on) { - cx231xx_errdev("%s device in use by another fh\n", __func__); - return -EBUSY; - } - - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -1976,11 +1719,25 @@ static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); return 0; } +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (dev->vbi_stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + return -EBUSY; + } + return vidioc_try_fmt_vbi_cap(file, priv, f); +} + static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { @@ -2038,58 +1795,24 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - - strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); - strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - if (unlikely(t->index > 0)) + if (t->index) return -EINVAL; strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - call_all(dev, tuner, s_tuner, t); - - return 0; -} -static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; + call_all(dev, tuner, g_tuner, t); return 0; } - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - if (0 != t->index) + if (t->index) return -EINVAL; call_all(dev, tuner, s_tuner, t); @@ -2097,36 +1820,6 @@ static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -static int radio_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - int i; - - if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX231XX_CTLS; i++) { - if (cx231xx_ctls[i].v.id == c->id) - break; - } - if (i == CX231XX_CTLS) - return -EINVAL; - *c = cx231xx_ctls[i].v; - } else - *c = no_ctl; - return 0; -} - /* * cx231xx_v4l2_open() * inits the device and starts isoc transfer @@ -2174,14 +1867,11 @@ static int cx231xx_v4l2_open(struct file *filp) return -ERESTARTSYS; } fh->dev = dev; - fh->radio = radio; fh->type = fh_type; filp->private_data = fh; + v4l2_fh_init(&fh->fh, vdev); if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { - dev->width = norm_maxw(dev); - dev->height = norm_maxh(dev); - /* Power up in Analog TV mode */ if (dev->board.external_av) cx231xx_set_power_mode(dev, @@ -2204,7 +1894,7 @@ static int cx231xx_v4l2_open(struct file *filp) dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; } - if (fh->radio) { + if (radio) { cx231xx_videodbg("video_open: setting radio device\n"); /* cx231xx_start_radio(dev); */ @@ -2232,6 +1922,7 @@ static int cx231xx_v4l2_open(struct file *filp) fh, &dev->lock); } mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); return errCode; } @@ -2275,6 +1966,8 @@ void cx231xx_release_analog_resources(struct cx231xx *dev) video_device_release(dev->vdev); dev->vdev = NULL; } + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); } /* @@ -2324,12 +2017,15 @@ static int cx231xx_close(struct file *filp) else cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); return 0; } + v4l2_fh_del(&fh->fh); dev->users--; if (!dev->users) { videobuf_stop(&fh->vb_vidq); @@ -2356,6 +2052,7 @@ static int cx231xx_close(struct file *filp) /* set alternate 0 */ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); } + v4l2_fh_exit(&fh->fh); kfree(fh); wake_up_interruptible_nr(&dev->open, 1); return 0; @@ -2412,29 +2109,37 @@ cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct cx231xx_fh *fh = filp->private_data; struct cx231xx *dev = fh->dev; + unsigned res = 0; int rc; rc = check_dev(dev); if (rc < 0) - return rc; + return POLLERR; rc = res_get(fh); if (unlikely(rc < 0)) return POLLERR; + if (v4l2_event_pending(&fh->fh)) + res |= POLLPRI; + else + poll_wait(filp, &fh->fh.wait, wait); + + if (!(req_events & (POLLIN | POLLRDNORM))) + return res; + if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) { - unsigned int res; - mutex_lock(&dev->lock); - res = videobuf_poll_stream(filp, &fh->vb_vidq, wait); + res |= videobuf_poll_stream(filp, &fh->vb_vidq, wait); mutex_unlock(&dev->lock); return res; } - return POLLERR; + return res | POLLERR; } /* @@ -2479,41 +2184,37 @@ static const struct v4l2_file_operations cx231xx_v4l_fops = { }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, + .vidioc_querycap = cx231xx_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_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .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_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_enum_input = cx231xx_enum_input, + .vidioc_g_input = cx231xx_g_input, + .vidioc_s_input = cx231xx_s_input, .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, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx231xx_g_tuner, + .vidioc_s_tuner = cx231xx_s_tuner, + .vidioc_g_frequency = cx231xx_g_frequency, + .vidioc_s_frequency = cx231xx_s_frequency, + .vidioc_g_chip_ident = cx231xx_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx231xx_g_register, + .vidioc_s_register = cx231xx_s_register, #endif + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx231xx_vbi_template; @@ -2523,33 +2224,29 @@ static const struct video_device cx231xx_video_template = { .release = video_device_release, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, }; static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = cx231xx_v4l2_open, .release = cx231xx_v4l2_close, - .ioctl = video_ioctl2, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = cx231xx_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_frequency = cx231xx_g_frequency, + .vidioc_s_frequency = cx231xx_s_frequency, + .vidioc_g_chip_ident = cx231xx_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx231xx_g_register, + .vidioc_s_register = cx231xx_s_register, #endif + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx231xx_radio_template = { @@ -2575,10 +2272,17 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); video_set_drvdata(vfd, dev); + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); + v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); + } return vfd; } @@ -2590,7 +2294,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->name, CX231XX_VERSION); /* set default norm */ - /*dev->norm = cx231xx_video_template.current_norm; */ + dev->norm = V4L2_STD_PAL; dev->width = norm_maxw(dev); dev->height = norm_maxh(dev); dev->interlaced = 0; @@ -2601,9 +2305,23 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) /* Set the initial input */ video_mux(dev, dev->video_input); - /* Audio defaults */ - dev->mute = 1; - dev->volume = 0x1f; + call_all(dev, core, s_std, dev->norm); + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 10); + v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5); + + if (dev->sd_cx25840) { + v4l2_ctrl_add_handler(&dev->ctrl_handler, + dev->sd_cx25840->ctrl_handler, NULL); + v4l2_ctrl_add_handler(&dev->radio_ctrl_handler, + dev->sd_cx25840->ctrl_handler, + v4l2_ctrl_radio_filter); + } + + if (dev->ctrl_handler.error) + return dev->ctrl_handler.error; + if (dev->radio_ctrl_handler.error) + return dev->radio_ctrl_handler.error; /* enable vbi capturing */ /* write code here... */ @@ -2615,6 +2333,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) return -ENODEV; } + dev->vdev->ctrl_handler = &dev->ctrl_handler; /* register v4l2 video video_device */ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); @@ -2634,6 +2353,11 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) /* Allocate and fill vbi video_device struct */ dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi"); + if (!dev->vbi_dev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + dev->vbi_dev->ctrl_handler = &dev->ctrl_handler; /* register v4l2 vbi video_device */ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); @@ -2652,6 +2376,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) cx231xx_errdev("cannot allocate video_device.\n"); return -ENODEV; } + dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler; ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 3e11462..5ad9fd6 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -33,6 +33,8 @@ #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> #include <media/rc-core.h> #include <media/ir-kbd-i2c.h> #include <media/videobuf-dvb.h> @@ -69,6 +71,7 @@ #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 #define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 +#define CX231XX_BOARD_OTG102 17 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 @@ -428,27 +431,12 @@ struct cx231xx_audio { struct cx231xx; struct cx231xx_fh { + struct v4l2_fh fh; struct cx231xx *dev; unsigned int stream_on:1; /* Locks streams */ - int radio; - - struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - - -/*following is copyed from cx23885.h*/ - u32 resources; - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - - /* video capture */ - struct cx23417_fmt *fmt; - unsigned int width, height; + struct videobuf_queue vb_vidq; /* vbi capture */ struct videobuf_queue vidq; @@ -516,14 +504,6 @@ struct cx231xx_tvnorm { u32 cxoformat; }; -struct cx231xx_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 mask; - u32 shift; -}; - enum TRANSFER_TYPE { Raw_Video = 0, Audio, @@ -631,6 +611,9 @@ struct cx231xx { struct v4l2_device v4l2_dev; struct v4l2_subdev *sd_cx25840; struct v4l2_subdev *sd_tuner; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; + struct cx2341x_handler mpeg_ctrl_handler; struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ atomic_t stream_started; /* stream should be running if true */ @@ -653,8 +636,6 @@ struct cx231xx { v4l2_std_id norm; /* selected tv norm */ int ctl_freq; /* selected frequency */ unsigned int ctl_ainput; /* selected audio input */ - int mute; - int volume; /* frame properties */ int width; /* current frame width */ @@ -736,7 +717,6 @@ struct cx231xx { u8 USE_ISO; struct cx231xx_tvnorm encodernorm; struct cx231xx_tsport ts1, ts2; - struct cx2341x_mpeg_params mpeg_params; struct video_device *v4l_device; atomic_t v4l_reader_count; u32 freq; @@ -866,8 +846,6 @@ int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, /* Gpio related functions */ int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, u8 len, u8 request, u8 direction); -int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); -int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value); int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number, int pin_value); @@ -955,6 +933,23 @@ int cx231xx_register_extension(struct cx231xx_ops *dev); void cx231xx_unregister_extension(struct cx231xx_ops *dev); void cx231xx_init_extension(struct cx231xx *dev); void cx231xx_close_extension(struct cx231xx *dev); +int cx231xx_querycap(struct file *file, void *priv, + struct v4l2_capability *cap); +int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t); +int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t); +int cx231xx_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +int cx231xx_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f); +int cx231xx_enum_input(struct file *file, void *priv, + struct v4l2_input *i); +int cx231xx_g_input(struct file *file, void *priv, unsigned int *i); +int cx231xx_s_input(struct file *file, void *priv, unsigned int i); +int cx231xx_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip); +int cx231xx_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg); +int cx231xx_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg); /* Provided by cx231xx-cards.c */ extern void cx231xx_pre_card_setup(struct cx231xx *dev); diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 692224d..a3c8ecf 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -13,10 +13,6 @@ config DVB_USB_V2 Say Y if you own a USB DVB device. -config DVB_USB_CYPRESS_FIRMWARE - tristate "Cypress firmware helper routines" - depends on DVB_USB_V2 - config DVB_USB_AF9015 tristate "Afatech AF9015 DVB-T USB2.0 support" depends on DVB_USB_V2 @@ -41,6 +37,7 @@ config DVB_USB_AF9035 select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_IT913X if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. @@ -72,7 +69,7 @@ config DVB_USB_AU6610 config DVB_USB_AZ6007 tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support" depends on DVB_USB_V2 - select DVB_USB_CYPRESS_FIRMWARE + select CYPRESS_FIRMWARE select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT help @@ -146,6 +143,7 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index b76f58e..2c06714 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -1,9 +1,6 @@ dvb_usb_v2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o obj-$(CONFIG_DVB_USB_V2) += dvb_usb_v2.o -dvb_usb_cypress_firmware-objs := cypress_firmware.o -obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o - dvb-usb-af9015-objs := af9015.o obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o @@ -46,4 +43,4 @@ obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ccflags-y += -I$(srctree)/drivers/media/tuners - +ccflags-y += -I$(srctree)/drivers/media/common diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index b86d0f2..d556042 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -30,22 +30,22 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) { -#define BUF_LEN 63 #define REQ_HDR_LEN 8 /* send header size */ #define ACK_HDR_LEN 2 /* rece header size */ struct af9015_state *state = d_to_priv(d); int ret, wlen, rlen; - u8 buf[BUF_LEN]; u8 write = 1; - buf[0] = req->cmd; - buf[1] = state->seq++; - buf[2] = req->i2c_addr; - buf[3] = req->addr >> 8; - buf[4] = req->addr & 0xff; - buf[5] = req->mbox; - buf[6] = req->addr_len; - buf[7] = req->data_len; + mutex_lock(&d->usb_mutex); + + state->buf[0] = req->cmd; + state->buf[1] = state->seq++; + state->buf[2] = req->i2c_addr; + state->buf[3] = req->addr >> 8; + state->buf[4] = req->addr & 0xff; + state->buf[5] = req->mbox; + state->buf[6] = req->addr_len; + state->buf[7] = req->data_len; switch (req->cmd) { case GET_CONFIG: @@ -55,14 +55,14 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) break; case READ_I2C: write = 0; - buf[2] |= 0x01; /* set I2C direction */ + state->buf[2] |= 0x01; /* set I2C direction */ case WRITE_I2C: - buf[0] = READ_WRITE_I2C; + state->buf[0] = READ_WRITE_I2C; break; case WRITE_MEMORY: if (((req->addr & 0xff00) == 0xff00) || ((req->addr & 0xff00) == 0xae00)) - buf[0] = WRITE_VIRTUAL_MEMORY; + state->buf[0] = WRITE_VIRTUAL_MEMORY; case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: case DOWNLOAD_FIRMWARE: @@ -90,7 +90,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) rlen = ACK_HDR_LEN; if (write) { wlen += req->data_len; - memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len); + memcpy(&state->buf[REQ_HDR_LEN], req->data, req->data_len); } else { rlen += req->data_len; } @@ -99,22 +99,25 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) rlen = 0; - ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen); + ret = dvb_usbv2_generic_rw_locked(d, + state->buf, wlen, state->buf, rlen); if (ret) goto error; /* check status */ - if (rlen && buf[1]) { + if (rlen && state->buf[1]) { dev_err(&d->udev->dev, "%s: command failed=%d\n", - KBUILD_MODNAME, buf[1]); + KBUILD_MODNAME, state->buf[1]); ret = -EIO; goto error; } /* read request, copy returned data to return buf */ if (!write) - memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); + memcpy(req->data, &state->buf[ACK_HDR_LEN], req->data_len); error: + mutex_unlock(&d->usb_mutex); + return ret; } @@ -1317,6 +1320,43 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) #define af9015_get_rc_config NULL #endif +static int af9015_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + char manufacturer[sizeof("ITE Technologies, Inc.")]; + + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + /* + * There is two devices having same ID but different chipset. One uses + * AF9015 and the other IT9135 chipset. Only difference seen on lsusb + * is iManufacturer string. + * + * idVendor 0x0ccd TerraTec Electronic GmbH + * idProduct 0x0099 + * bcdDevice 2.00 + * iManufacturer 1 Afatech + * iProduct 2 DVB-T 2 + * + * idVendor 0x0ccd TerraTec Electronic GmbH + * idProduct 0x0099 + * bcdDevice 2.00 + * iManufacturer 1 ITE Technologies, Inc. + * iProduct 2 DVB-T TV Stick + */ + if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) && + (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { + if (!strcmp("ITE Technologies, Inc.", manufacturer)) { + dev_dbg(&udev->dev, "%s: rejecting device\n", __func__); + return -ENODEV; + } + } + + return dvb_usbv2_probe(intf, id); +} + /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ static struct dvb_usb_device_properties af9015_props = { @@ -1425,6 +1465,7 @@ static const struct usb_device_id af9015_id_table[] = { &af9015_props, "AverMedia AVerTV Volar M (A815Mac)", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC, &af9015_props, "TerraTec Cinergy T Stick RC", RC_MAP_TERRATEC_SLIM_2) }, + /* XXX: that same ID [0ccd:0099] is used by af9035 driver too */ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, &af9015_props, "TerraTec Cinergy T Stick Dual RC", RC_MAP_TERRATEC_SLIM) }, { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T, @@ -1441,7 +1482,7 @@ MODULE_DEVICE_TABLE(usb, af9015_id_table); static struct usb_driver af9015_usb_driver = { .name = KBUILD_MODNAME, .id_table = af9015_id_table, - .probe = dvb_usbv2_probe, + .probe = af9015_probe, .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h index 533637d..3a6f3ad 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.h +++ b/drivers/media/usb/dvb-usb-v2/af9015.h @@ -115,7 +115,9 @@ enum af9015_ir_mode { AF9015_IR_MODE_POLLING, /* just guess */ }; +#define BUF_LEN 63 struct af9015_state { + u8 buf[BUF_LEN]; /* bulk USB control message */ u8 ir_mode; u8 rc_repeat; u32 rc_keycode; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index f11cc42..b638fc1 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -41,79 +41,84 @@ static u16 af9035_checksum(const u8 *buf, size_t len) static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) { -#define BUF_LEN 64 #define REQ_HDR_LEN 4 /* send header size */ #define ACK_HDR_LEN 3 /* rece header size */ #define CHECKSUM_LEN 2 #define USB_TIMEOUT 2000 struct state *state = d_to_priv(d); int ret, wlen, rlen; - u8 buf[BUF_LEN]; u16 checksum, tmp_checksum; + mutex_lock(&d->usb_mutex); + /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n", __func__, req->wlen, req->rlen); - return -EINVAL; + ret = -EINVAL; + goto exit; } - buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; - buf[1] = req->mbox; - buf[2] = req->cmd; - buf[3] = state->seq++; - memcpy(&buf[REQ_HDR_LEN], req->wbuf, req->wlen); + state->buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; + state->buf[1] = req->mbox; + state->buf[2] = req->cmd; + state->buf[3] = state->seq++; + memcpy(&state->buf[REQ_HDR_LEN], req->wbuf, req->wlen); wlen = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN; rlen = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; /* calc and add checksum */ - checksum = af9035_checksum(buf, buf[0] - 1); - buf[buf[0] - 1] = (checksum >> 8); - buf[buf[0] - 0] = (checksum & 0xff); + checksum = af9035_checksum(state->buf, state->buf[0] - 1); + state->buf[state->buf[0] - 1] = (checksum >> 8); + state->buf[state->buf[0] - 0] = (checksum & 0xff); /* no ack for these packets */ if (req->cmd == CMD_FW_DL) rlen = 0; - ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen); + ret = dvb_usbv2_generic_rw_locked(d, + state->buf, wlen, state->buf, rlen); if (ret) - goto err; + goto exit; /* no ack for those packets */ if (req->cmd == CMD_FW_DL) goto exit; /* verify checksum */ - checksum = af9035_checksum(buf, rlen - 2); - tmp_checksum = (buf[rlen - 2] << 8) | buf[rlen - 1]; + checksum = af9035_checksum(state->buf, rlen - 2); + tmp_checksum = (state->buf[rlen - 2] << 8) | state->buf[rlen - 1]; if (tmp_checksum != checksum) { dev_err(&d->udev->dev, "%s: command=%02x checksum mismatch " \ "(%04x != %04x)\n", KBUILD_MODNAME, req->cmd, tmp_checksum, checksum); ret = -EIO; - goto err; + goto exit; } /* check status */ - if (buf[2]) { + if (state->buf[2]) { + /* fw returns status 1 when IR code was not received */ + if (req->cmd == CMD_IR_GET || state->buf[2] == 1) { + ret = 1; + goto exit; + } + dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n", - __func__, req->cmd, buf[2]); + __func__, req->cmd, state->buf[2]); ret = -EIO; - goto err; + goto exit; } /* read request, copy returned data to return buf */ if (req->rlen) - memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen); - + memcpy(req->rbuf, &state->buf[ACK_HDR_LEN], req->rlen); exit: - return 0; - -err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - + mutex_unlock(&d->usb_mutex); + if (ret < 0) + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -292,12 +297,40 @@ static struct i2c_algorithm af9035_i2c_algo = { static int af9035_identify_state(struct dvb_usb_device *d, const char **name) { + struct state *state = d_to_priv(d); int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, sizeof(rbuf), rbuf }; + ret = af9035_rd_regs(d, 0x1222, rbuf, 3); + if (ret < 0) + goto err; + + state->chip_version = rbuf[0]; + state->chip_type = rbuf[2] << 8 | rbuf[1] << 0; + + ret = af9035_rd_reg(d, 0x384f, &state->prechip_version); + if (ret < 0) + goto err; + + dev_info(&d->udev->dev, + "%s: prechip_version=%02x chip_version=%02x chip_type=%04x\n", + __func__, state->prechip_version, state->chip_version, + state->chip_type); + + if (state->chip_type == 0x9135) { + if (state->chip_version == 0x02) + *name = AF9035_FIRMWARE_IT9135_V2; + else + *name = AF9035_FIRMWARE_IT9135_V1; + state->eeprom_addr = EEPROM_BASE_IT9135; + } else { + *name = AF9035_FIRMWARE_AF9035; + state->eeprom_addr = EEPROM_BASE_AF9035; + } + ret = af9035_ctrl_msg(d, &req); if (ret < 0) goto err; @@ -316,66 +349,19 @@ err: return ret; } -static int af9035_download_firmware(struct dvb_usb_device *d, +static int af9035_download_firmware_old(struct dvb_usb_device *d, const struct firmware *fw) { int ret, i, j, len; u8 wbuf[1]; - u8 rbuf[4]; struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; - struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; - u8 hdr_core, tmp; + u8 hdr_core; u16 hdr_addr, hdr_data_len, hdr_checksum; #define MAX_DATA 58 #define HDR_SIZE 7 /* - * In case of dual tuner configuration we need to do some extra - * initialization in order to download firmware to slave demod too, - * which is done by master demod. - * Master feeds also clock and controls power via GPIO. - */ - ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); - if (ret < 0) - goto err; - - if (tmp) { - /* configure gpioh1, reset & power slave demod */ - ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01); - if (ret < 0) - goto err; - - ret = af9035_wr_reg_mask(d, 0x00d8b1, 0x01, 0x01); - if (ret < 0) - goto err; - - ret = af9035_wr_reg_mask(d, 0x00d8af, 0x00, 0x01); - if (ret < 0) - goto err; - - usleep_range(10000, 50000); - - ret = af9035_wr_reg_mask(d, 0x00d8af, 0x01, 0x01); - if (ret < 0) - goto err; - - /* tell the slave I2C address */ - ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00417f, tmp); - if (ret < 0) - goto err; - - /* enable clock out */ - ret = af9035_wr_reg_mask(d, 0x00d81a, 0x01, 0x01); - if (ret < 0) - goto err; - } - - /* * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info! * * byte 0: MCS 51 core @@ -441,28 +427,6 @@ static int af9035_download_firmware(struct dvb_usb_device *d, if (i) dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); - /* firmware loaded, request boot */ - req.cmd = CMD_FW_BOOT; - ret = af9035_ctrl_msg(d, &req); - if (ret < 0) - goto err; - - /* ensure firmware starts */ - wbuf[0] = 1; - ret = af9035_ctrl_msg(d, &req_fw_ver); - if (ret < 0) - goto err; - - if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - dev_err(&d->udev->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); - ret = -ENODEV; - goto err; - } - - dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d", - KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); - return 0; err: @@ -471,15 +435,11 @@ err: return ret; } -static int af9035_download_firmware_it9135(struct dvb_usb_device *d, +static int af9035_download_firmware_new(struct dvb_usb_device *d, const struct firmware *fw) { int ret, i, i_prev; - u8 wbuf[1]; - u8 rbuf[4]; - struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; - struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; #define HDR_SIZE 7 /* @@ -494,7 +454,6 @@ static int af9035_download_firmware_it9135(struct dvb_usb_device *d, * 5: addr LSB * 6: count of data bytes ? */ - for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) { if (i == fw->size || (fw->data[i + 0] == 0x03 && @@ -513,6 +472,86 @@ static int af9035_download_firmware_it9135(struct dvb_usb_device *d, } } + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + struct state *state = d_to_priv(d); + int ret; + u8 wbuf[1]; + u8 rbuf[4]; + 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__); + + /* + * In case of dual tuner configuration we need to do some extra + * initialization in order to download firmware to slave demod too, + * which is done by master demod. + * Master feeds also clock and controls power via GPIO. + */ + ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* configure gpioh1, reset & power slave demod */ + ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8b1, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x01, 0x01); + if (ret < 0) + goto err; + + /* tell the slave I2C address */ + ret = af9035_rd_reg(d, + state->eeprom_addr + EEPROM_2ND_DEMOD_ADDR, + &tmp); + if (ret < 0) + goto err; + + if (state->chip_type == 0x9135) { + ret = af9035_wr_reg(d, 0x004bfb, tmp); + if (ret < 0) + goto err; + } else { + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + /* enable clock out */ + ret = af9035_wr_reg_mask(d, 0x00d81a, 0x01, 0x01); + if (ret < 0) + goto err; + } + } + + if (fw->data[0] == 0x01) + ret = af9035_download_firmware_old(d, fw); + else + ret = af9035_download_firmware_new(d, fw); + if (ret < 0) + goto err; + /* firmware loaded, request boot */ req.cmd = CMD_FW_BOOT; ret = af9035_ctrl_msg(d, &req); @@ -546,15 +585,42 @@ err: static int af9035_read_config(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); - int ret, i, eeprom_shift = 0; + int ret, i; u8 tmp; - u16 tmp16; + u16 tmp16, addr; /* demod I2C "address" */ state->af9033_config[0].i2c_addr = 0x38; + 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) { + 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; + } else { + state->af9033_config[0].tuner = AF9033_TUNER_IT9135_38; + state->af9033_config[1].tuner = AF9033_TUNER_IT9135_38; + tmp16 = 0x00461b; + } + + /* check if eeprom exists */ + ret = af9035_rd_reg(d, tmp16, &tmp); + if (ret < 0) + goto err; + + if (tmp == 0x00) { + dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__); + goto skip_eeprom; + } + } /* check if there is dual tuners */ - ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_DUAL_MODE, &tmp); if (ret < 0) goto err; @@ -564,7 +630,9 @@ static int af9035_read_config(struct dvb_usb_device *d) if (state->dual_mode) { /* read 2nd demodulator I2C address */ - ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); + ret = af9035_rd_reg(d, + state->eeprom_addr + EEPROM_2ND_DEMOD_ADDR, + &tmp); if (ret < 0) goto err; @@ -573,17 +641,25 @@ static int af9035_read_config(struct dvb_usb_device *d) __func__, tmp); } + addr = state->eeprom_addr; + for (i = 0; i < state->dual_mode + 1; i++) { /* tuner */ - ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); + ret = af9035_rd_reg(d, addr + EEPROM_1_TUNER_ID, &tmp); if (ret < 0) goto err; - state->af9033_config[i].tuner = tmp; + if (tmp == 0x00) + dev_dbg(&d->udev->dev, + "%s: [%d]tuner not set, using default\n", + __func__, i); + else + state->af9033_config[i].tuner = tmp; + dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n", - __func__, i, tmp); + __func__, i, state->af9033_config[i].tuner); - switch (tmp) { + switch (state->af9033_config[i].tuner) { case AF9033_TUNER_TUA9001: case AF9033_TUNER_FC0011: case AF9033_TUNER_MXL5007T: @@ -592,32 +668,46 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_FC0012: state->af9033_config[i].spec_inv = 1; break; + 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: + break; default: - dev_warn(&d->udev->dev, "%s: tuner id=%02x not " \ - "supported, please report!", + dev_warn(&d->udev->dev, + "%s: tuner id=%02x not supported, please report!", KBUILD_MODNAME, tmp); } /* disable dual mode if driver does not support it */ if (i == 1) - switch (tmp) { + switch (state->af9033_config[i].tuner) { case AF9033_TUNER_FC0012: + 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: + case AF9033_TUNER_MXL5007T: break; default: state->dual_mode = false; - dev_info(&d->udev->dev, "%s: driver does not " \ - "support 2nd tuner and will " \ - "disable it", KBUILD_MODNAME); + dev_info(&d->udev->dev, + "%s: driver does not support 2nd tuner and will disable it", + KBUILD_MODNAME); } /* tuner IF frequency */ - ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); + ret = af9035_rd_reg(d, addr + EEPROM_1_IF_L, &tmp); if (ret < 0) goto err; tmp16 = tmp; - ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp); + ret = af9035_rd_reg(d, addr + EEPROM_1_IF_H, &tmp); if (ret < 0) goto err; @@ -625,9 +715,10 @@ static int af9035_read_config(struct dvb_usb_device *d) dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16); - eeprom_shift = 0x10; /* shift for the 2nd tuner params */ + addr += 0x10; /* shift for the 2nd tuner params */ } +skip_eeprom: /* get demod clock */ ret = af9035_rd_reg(d, 0x00d800, &tmp); if (ret < 0) @@ -635,34 +726,12 @@ static int af9035_read_config(struct dvb_usb_device *d) tmp = (tmp >> 0) & 0x0f; - for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++) - state->af9033_config[i].clock = clock_lut[tmp]; - - return 0; - -err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - - return ret; -} - -static int af9035_read_config_it9135(struct dvb_usb_device *d) -{ - struct state *state = d_to_priv(d); - int ret, i; - u8 tmp; - - state->dual_mode = false; - - /* get demod clock */ - ret = af9035_rd_reg(d, 0x00d800, &tmp); - if (ret < 0) - goto err; - - tmp = (tmp >> 0) & 0x0f; - - for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++) - state->af9033_config[i].clock = clock_lut_it9135[tmp]; + for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++) { + if (state->chip_type == 0x9135) + state->af9033_config[i].clock = clock_lut_it9135[tmp]; + else + state->af9033_config[i].clock = clock_lut_af9035[tmp]; + } return 0; @@ -821,7 +890,12 @@ 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; + + /* disable 2nd adapter as we don't have PID filters implemented */ + if (d->udev->speed == USB_SPEED_FULL) + return 1; + else + return state->dual_mode + 1; } static int af9035_frontend_attach(struct dvb_usb_adapter *adap) @@ -829,6 +903,7 @@ 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__); if (!state->af9033_config[adap->id].tuner) { /* unsupported tuner */ @@ -836,20 +911,6 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) goto err; } - if (adap->id == 0) { - state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB; - state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL; - - ret = af9035_wr_reg(d, 0x00417f, - state->af9033_config[1].i2c_addr); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00d81a, state->dual_mode); - if (ret < 0) - goto err; - } - /* attach demodulator */ adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id], &d->i2c_adap); @@ -928,6 +989,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__); + /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] * to carry info about used I2C bus for dual tuner configuration. @@ -1082,6 +1145,17 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, &af9035_fc0012_config[adap->id]); break; + 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: + /* 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); + break; default: fe = NULL; } @@ -1103,8 +1177,8 @@ static int af9035_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); int ret, i; - u16 frame_size = 87 * 188 / 4; - u8 packet_size = 512 / 4; + u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4; + u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; struct reg_val_mask tab[] = { { 0x80f99d, 0x01, 0x01 }, { 0x80f9a4, 0x01, 0x01 }, @@ -1149,40 +1223,49 @@ err: #if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { - unsigned int key; - unsigned char b[4]; int ret; - struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b }; + u32 key; + u8 buf[4]; + struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, buf }; ret = af9035_ctrl_msg(d, &req); - if (ret < 0) + if (ret == 1) + return 0; + else if (ret < 0) goto err; - if ((b[2] + b[3]) == 0xff) { - if ((b[0] + b[1]) == 0xff) { - /* NEC */ - key = b[0] << 8 | b[2]; + if ((buf[2] + buf[3]) == 0xff) { + if ((buf[0] + buf[1]) == 0xff) { + /* NEC standard 16bit */ + key = buf[0] << 8 | buf[2]; } else { - /* ext. NEC */ - key = b[0] << 16 | b[1] << 8 | b[2]; + /* NEC extended 24bit */ + key = buf[0] << 16 | buf[1] << 8 | buf[2]; } } else { - key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + /* NEC full code 32bit */ + key = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; } + dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf); + rc_keydown(d->rc_dev, key, 0); -err: - /* ignore errors */ return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; } static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { + struct state *state = d_to_priv(d); int ret; u8 tmp; - ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp); + ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_IR_MODE, &tmp); if (ret < 0) goto err; @@ -1190,7 +1273,8 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) /* don't activate rc if in HID mode or if not available */ if (tmp == 5) { - ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp); + ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_IR_TYPE, + &tmp); if (ret < 0) goto err; @@ -1225,6 +1309,109 @@ err: #define af9035_get_rc_config NULL #endif +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) + stream->u.bulk.buffersize = 5 * 188; + + return 0; +} + +/* + * FIXME: PID filter is property of demodulator and should be moved to the + * correct driver. Also we support only adapter #0 PID filter and will + * disable adapter #1 if USB1.1 is used. + */ +static int af9035_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + + ret = af9035_wr_reg_mask(d, 0x80f993, onoff, 0x01); + if (ret < 0) + goto err; + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff}; + + dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n", + __func__, index, pid, onoff); + + ret = af9035_wr_regs(d, 0x80f996, wbuf, 2); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x80f994, onoff); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x80f995, index); + if (ret < 0) + goto err; + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + char manufacturer[sizeof("Afatech")]; + + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + /* + * There is two devices having same ID but different chipset. One uses + * AF9015 and the other IT9135 chipset. Only difference seen on lsusb + * is iManufacturer string. + * + * idVendor 0x0ccd TerraTec Electronic GmbH + * idProduct 0x0099 + * bcdDevice 2.00 + * iManufacturer 1 Afatech + * iProduct 2 DVB-T 2 + * + * idVendor 0x0ccd TerraTec Electronic GmbH + * idProduct 0x0099 + * bcdDevice 2.00 + * iManufacturer 1 ITE Technologies, Inc. + * iProduct 2 DVB-T TV Stick + */ + if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) && + (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { + if (!strcmp("Afatech", manufacturer)) { + dev_dbg(&udev->dev, "%s: rejecting device\n", __func__); + return -ENODEV; + } + } + + return dvb_usbv2_probe(intf, id); +} + /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ static const struct dvb_usb_device_properties af9035_props = { @@ -1237,7 +1424,6 @@ static const struct dvb_usb_device_properties af9035_props = { .generic_bulk_ctrl_endpoint_response = 0x81, .identify_state = af9035_identify_state, - .firmware = AF9035_FIRMWARE_AF9035, .download_firmware = af9035_download_firmware, .i2c_algo = &af9035_i2c_algo, @@ -1246,40 +1432,18 @@ static const struct dvb_usb_device_properties af9035_props = { .tuner_attach = af9035_tuner_attach, .init = af9035_init, .get_rc_config = af9035_get_rc_config, + .get_stream_config = af9035_get_stream_config, .get_adapter_count = af9035_get_adapter_count, .adapter = { { - .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), - }, { - .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188), - }, - }, -}; - -static const struct dvb_usb_device_properties it9135_props = { - .driver_name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .adapter_nr = adapter_nr, - .size_of_priv = sizeof(struct state), + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - .generic_bulk_ctrl_endpoint = 0x02, - .generic_bulk_ctrl_endpoint_response = 0x81, - - .identify_state = af9035_identify_state, - .firmware = AF9035_FIRMWARE_IT9135, - .download_firmware = af9035_download_firmware_it9135, + .pid_filter_count = 32, + .pid_filter_ctrl = af9035_pid_filter_ctrl, + .pid_filter = af9035_pid_filter, - .i2c_algo = &af9035_i2c_algo, - .read_config = af9035_read_config_it9135, - .frontend_attach = af9035_frontend_attach, - .tuner_attach = af9035_tuner_attach, - .init = af9035_init, - .get_rc_config = af9035_get_rc_config, - - .num_adapters = 1, - .adapter = { - { .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), }, { .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188), @@ -1288,6 +1452,7 @@ static const struct dvb_usb_device_properties it9135_props = { }; static const struct usb_device_id af9035_id_table[] = { + /* AF9035 devices */ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035, &af9035_props, "Afatech AF9035 reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000, @@ -1312,6 +1477,18 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "AVerMedia Twinstar (A825)", NULL) }, { DVB_USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100MINI_PLUS, &af9035_props, "Asus U3100Mini Plus", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa, + &af9035_props, "TerraTec Cinergy T Stick (rev. 2)", NULL) }, + /* IT9135 devices */ +#if 0 + { DVB_USB_DEVICE(0x048d, 0x9135, + &af9035_props, "IT9135 reference design", NULL) }, + { DVB_USB_DEVICE(0x048d, 0x9006, + &af9035_props, "IT9135 reference design", NULL) }, +#endif + /* 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) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); @@ -1319,7 +1496,7 @@ MODULE_DEVICE_TABLE(usb, af9035_id_table); static struct usb_driver af9035_usb_driver = { .name = KBUILD_MODNAME, .id_table = af9035_id_table, - .probe = dvb_usbv2_probe, + .probe = af9035_probe, .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, @@ -1334,4 +1511,5 @@ MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Afatech AF9035 driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(AF9035_FIRMWARE_AF9035); -MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135); +MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V1); +MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V2); diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 29f3eec..b5827ca 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -30,6 +30,7 @@ #include "mxl5007t.h" #include "tda18218.h" #include "fc2580.h" +#include "tuner_it913x.h" struct reg_val { u32 reg; @@ -52,12 +53,18 @@ struct usb_req { }; struct state { +#define BUF_LEN 64 + u8 buf[BUF_LEN]; u8 seq; /* packet sequence number */ - bool dual_mode; + u8 prechip_version; + u8 chip_version; + u16 chip_type; + u8 dual_mode:1; + u16 eeprom_addr; struct af9033_config af9033_config[2]; }; -u32 clock_lut[] = { +static const u32 clock_lut_af9035[] = { 20480000, /* FPGA */ 16384000, /* 16.38 MHz */ 20480000, /* 20.48 MHz */ @@ -72,7 +79,7 @@ u32 clock_lut[] = { 12000000, /* 12.00 MHz */ }; -u32 clock_lut_it9135[] = { +static const u32 clock_lut_it9135[] = { 12000000, /* 12.00 MHz */ 20480000, /* 20.48 MHz */ 36000000, /* 36.00 MHz */ @@ -86,19 +93,31 @@ u32 clock_lut_it9135[] = { }; #define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw" -#define AF9035_FIRMWARE_IT9135 "dvb-usb-it9135-01.fw" +#define AF9035_FIRMWARE_IT9135_V1 "dvb-usb-it9135-01.fw" +#define AF9035_FIRMWARE_IT9135_V2 "dvb-usb-it9135-02.fw" -/* EEPROM locations */ -#define EEPROM_IR_MODE 0x430d -#define EEPROM_DUAL_MODE 0x4326 -#define EEPROM_2ND_DEMOD_ADDR 0x4327 -#define EEPROM_IR_TYPE 0x4329 -#define EEPROM_1_IFFREQ_L 0x432d -#define EEPROM_1_IFFREQ_H 0x432e -#define EEPROM_1_TUNER_ID 0x4331 -#define EEPROM_2_IFFREQ_L 0x433d -#define EEPROM_2_IFFREQ_H 0x433e -#define EEPROM_2_TUNER_ID 0x4341 +/* + * eeprom is memory mapped as read only. Writing that memory mapped address + * will not corrupt eeprom. + * + * eeprom has value 0x00 single mode and 0x03 for dual mode as far as I have + * seen to this day. + */ + +#define EEPROM_BASE_AF9035 0x42fd +#define EEPROM_BASE_IT9135 0x499c +#define EEPROM_SHIFT 0x10 + +#define EEPROM_IR_MODE 0x10 +#define EEPROM_DUAL_MODE 0x29 +#define EEPROM_2ND_DEMOD_ADDR 0x2a +#define EEPROM_IR_TYPE 0x2c +#define EEPROM_1_IF_L 0x30 +#define EEPROM_1_IF_H 0x31 +#define EEPROM_1_TUNER_ID 0x34 +#define EEPROM_2_IF_L 0x40 +#define EEPROM_2_IF_H 0x41 +#define EEPROM_2_TUNER_ID 0x44 /* USB commands */ #define CMD_MEM_RD 0x00 diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index a20d691..90cfa35 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -45,25 +45,24 @@ #include "cxd2820r.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -static DEFINE_MUTEX(anysee_usb_mutex); -static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, - u8 *rbuf, u8 rlen) +static int anysee_ctrl_msg(struct dvb_usb_device *d, + u8 *sbuf, u8 slen, u8 *rbuf, u8 rlen) { struct anysee_state *state = d_to_priv(d); int act_len, ret, i; - u8 buf[64]; - memcpy(&buf[0], sbuf, slen); - buf[60] = state->seq++; + mutex_lock(&d->usb_mutex); - mutex_lock(&anysee_usb_mutex); + memcpy(&state->buf[0], sbuf, slen); + state->buf[60] = state->seq++; - dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, slen, buf); + dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, slen, state->buf); /* We need receive one message more after dvb_usb_generic_rw due to weird transaction flow, which is 1 x send + 2 x receive. */ - ret = dvb_usbv2_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf)); + ret = dvb_usbv2_generic_rw_locked(d, state->buf, sizeof(state->buf), + state->buf, sizeof(state->buf)); if (ret) goto error_unlock; @@ -82,20 +81,19 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, for (i = 0; i < 3; i++) { /* receive 2nd answer */ ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, - d->props->generic_bulk_ctrl_endpoint), buf, sizeof(buf), - &act_len, 2000); - + d->props->generic_bulk_ctrl_endpoint), + state->buf, sizeof(state->buf), &act_len, 2000); if (ret) { - dev_dbg(&d->udev->dev, "%s: recv bulk message " \ - "failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, + "%s: recv bulk message failed=%d\n", + __func__, ret); } else { dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__, - rlen, buf); - - if (buf[63] != 0x4f) - dev_dbg(&d->udev->dev, "%s: cmd failed\n", - __func__); + rlen, state->buf); + if (state->buf[63] != 0x4f) + dev_dbg(&d->udev->dev, + "%s: cmd failed\n", __func__); break; } } @@ -109,11 +107,10 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, /* read request, copy returned data to return buf */ if (rbuf && rlen) - memcpy(rbuf, buf, rlen); + memcpy(rbuf, state->buf, rlen); error_unlock: - mutex_unlock(&anysee_usb_mutex); - + mutex_unlock(&d->usb_mutex); return ret; } @@ -638,7 +635,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) { struct anysee_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret; + int ret = 0; u8 tmp; struct i2c_msg msg[2] = { { @@ -884,9 +881,8 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) if (!adap->fe[0]) { /* we have no frontend :-( */ ret = -ENODEV; - dev_err(&d->udev->dev, "%s: Unsupported Anysee version. " \ - "Please report the " \ - "<linux-media@vger.kernel.org>.\n", + dev_err(&d->udev->dev, + "%s: Unsupported Anysee version. Please report to <linux-media@vger.kernel.org>.\n", KBUILD_MODNAME); } error: diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h index c1a4273..8f426d9 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.h +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -52,8 +52,9 @@ enum cmd { }; struct anysee_state { - u8 hw; /* PCB ID */ + u8 buf[64]; u8 seq; + u8 hw; /* PCB ID */ u8 fe_id:1; /* frondend ID */ u8 has_ci:1; u8 ci_attached:1; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 70ec80d..44c64ef3 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -842,7 +842,7 @@ static int az6007_download_firmware(struct dvb_usb_device *d, { pr_debug("Loading az6007 firmware\n"); - return usbv2_cypress_load_firmware(d->udev, fw, CYPRESS_FX2); + return cypress_load_firmware(d->udev, fw, CYPRESS_FX2); } /* DVB USB Driver stuff */ diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 3cac8bd..658c6d4 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -329,13 +329,16 @@ struct dvb_usb_adapter { u8 feed_count; u8 max_feed_count; s8 active_fe; +#define ADAP_INIT 0 +#define ADAP_SLEEP 1 +#define ADAP_STREAMING 2 + unsigned long state_bits; /* dvb */ struct dvb_adapter dvb_adap; struct dmxdev dmxdev; struct dvb_demux demux; struct dvb_net dvb_net; - struct mutex sync_mutex; struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP]; int (*fe_init[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *); @@ -400,5 +403,9 @@ extern int dvb_usbv2_reset_resume(struct usb_interface *); /* the generic read/write method for device control */ extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); +/* caller must hold lock when locked versions are called */ +extern int dvb_usbv2_generic_rw_locked(struct dvb_usb_device *, + u8 *, u16, u8 *, u16); +extern int dvb_usbv2_generic_write_locked(struct dvb_usb_device *, u8 *, u16); #endif 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 0867920..19f6737 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -28,10 +28,11 @@ MODULE_PARM_DESC(disable_rc_polling, static int dvb_usb_force_pid_filter_usage; module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444); -MODULE_PARM_DESC(force_pid_filter_usage, "force all DVB USB devices to use a " \ - "PID filter, if any (default: 0)"); +MODULE_PARM_DESC(force_pid_filter_usage, + "force all DVB USB devices to use a PID filter, if any (default: 0)"); -static int dvb_usbv2_download_firmware(struct dvb_usb_device *d, const char *name) +static int dvb_usbv2_download_firmware(struct dvb_usb_device *d, + const char *name) { int ret; const struct firmware *fw; @@ -44,10 +45,9 @@ static int dvb_usbv2_download_firmware(struct dvb_usb_device *d, const char *nam ret = request_firmware(&fw, name, &d->udev->dev); if (ret < 0) { - dev_err(&d->udev->dev, "%s: Did not find the firmware file "\ - "'%s'. Please see linux/Documentation/dvb/ " \ - "for more details on firmware-problems. " \ - "Status %d\n", KBUILD_MODNAME, name, ret); + dev_err(&d->udev->dev, + "%s: Did not find the firmware file '%s'. Please see linux/Documentation/dvb/ for more details on firmware-problems. Status %d\n", + KBUILD_MODNAME, name, ret); goto err; } @@ -181,9 +181,9 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) /* initialize a work queue for handling polling */ INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); - dev_info(&d->udev->dev, "%s: schedule remote query interval " \ - "to %d msecs\n", KBUILD_MODNAME, - d->rc.interval); + dev_info(&d->udev->dev, + "%s: schedule remote query interval to %d msecs\n", + KBUILD_MODNAME, d->rc.interval); schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); d->rc_polling_active = true; @@ -253,128 +253,159 @@ static int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap) return usb_urb_exitv2(&adap->stream); } -static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, - int count) +static int wait_schedule(void *ptr) +{ + schedule(); + + return 0; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; struct dvb_usb_device *d = adap_to_d(adap); - int ret; - dev_dbg(&d->udev->dev, "%s: adap=%d active_fe=%d feed_type=%d " \ - "setting pid [%s]: %04x (%04d) at index %d '%s'\n", + int ret = 0; + struct usb_data_stream_properties stream_props; + dev_dbg(&d->udev->dev, + "%s: adap=%d active_fe=%d feed_type=%d setting pid [%s]: %04x (%04d) at index %d\n", __func__, adap->id, adap->active_fe, dvbdmxfeed->type, adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid, - dvbdmxfeed->pid, dvbdmxfeed->index, - (count == 1) ? "on" : "off"); + dvbdmxfeed->pid, dvbdmxfeed->index); + + /* wait init is done */ + wait_on_bit(&adap->state_bits, ADAP_INIT, wait_schedule, + TASK_UNINTERRUPTIBLE); if (adap->active_fe == -1) return -EINVAL; - adap->feed_count += count; - - /* stop feeding if it is last pid */ - if (adap->feed_count == 0) { - dev_dbg(&d->udev->dev, "%s: stop feeding\n", __func__); - - if (d->props->streaming_ctrl) { - ret = d->props->streaming_ctrl( - adap->fe[adap->active_fe], 0); - if (ret < 0) { - dev_err(&d->udev->dev, "%s: streaming_ctrl() " \ - "failed=%d\n", KBUILD_MODNAME, - ret); - usb_urb_killv2(&adap->stream); - goto err_mutex_unlock; - } - } - usb_urb_killv2(&adap->stream); - mutex_unlock(&adap->sync_mutex); - } + /* skip feed setup if we are already feeding */ + if (adap->feed_count++ > 0) + goto skip_feed_start; - /* activate the pid on the device pid filter */ - if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->pid_filtering && adap->props->pid_filter) { - ret = adap->props->pid_filter(adap, dvbdmxfeed->index, - dvbdmxfeed->pid, (count == 1) ? 1 : 0); - if (ret < 0) - dev_err(&d->udev->dev, "%s: pid_filter() failed=%d\n", + /* set 'streaming' status bit */ + set_bit(ADAP_STREAMING, &adap->state_bits); + + /* resolve input and output streaming parameters */ + if (d->props->get_stream_config) { + memcpy(&stream_props, &adap->props->stream, + sizeof(struct usb_data_stream_properties)); + ret = d->props->get_stream_config(adap->fe[adap->active_fe], + &adap->ts_type, &stream_props); + if (ret) + dev_err(&d->udev->dev, + "%s: get_stream_config() failed=%d\n", KBUILD_MODNAME, ret); + } else { + stream_props = adap->props->stream; } - /* start feeding if it is first pid */ - if (adap->feed_count == 1 && count == 1) { - struct usb_data_stream_properties stream_props; - mutex_lock(&adap->sync_mutex); - dev_dbg(&d->udev->dev, "%s: start feeding\n", __func__); + switch (adap->ts_type) { + case DVB_USB_FE_TS_TYPE_204: + adap->stream.complete = dvb_usb_data_complete_204; + break; + case DVB_USB_FE_TS_TYPE_RAW: + adap->stream.complete = dvb_usb_data_complete_raw; + break; + case DVB_USB_FE_TS_TYPE_188: + default: + adap->stream.complete = dvb_usb_data_complete; + break; + } - /* resolve input and output streaming paramters */ - if (d->props->get_stream_config) { - memcpy(&stream_props, &adap->props->stream, - sizeof(struct usb_data_stream_properties)); - ret = d->props->get_stream_config( - adap->fe[adap->active_fe], - &adap->ts_type, &stream_props); - if (ret < 0) - goto err_mutex_unlock; - } else { - stream_props = adap->props->stream; - } + /* submit USB streaming packets */ + usb_urb_submitv2(&adap->stream, &stream_props); - switch (adap->ts_type) { - case DVB_USB_FE_TS_TYPE_204: - adap->stream.complete = dvb_usb_data_complete_204; - break; - case DVB_USB_FE_TS_TYPE_RAW: - adap->stream.complete = dvb_usb_data_complete_raw; - break; - case DVB_USB_FE_TS_TYPE_188: - default: - adap->stream.complete = dvb_usb_data_complete; - break; - } + /* enable HW PID filter */ + if (adap->pid_filtering && adap->props->pid_filter_ctrl) { + ret = adap->props->pid_filter_ctrl(adap, 1); + if (ret) + dev_err(&d->udev->dev, + "%s: pid_filter_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + } - usb_urb_submitv2(&adap->stream, &stream_props); - - if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->props->caps & - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && - adap->props->pid_filter_ctrl) { - ret = adap->props->pid_filter_ctrl(adap, - adap->pid_filtering); - if (ret < 0) { - dev_err(&d->udev->dev, "%s: " \ - "pid_filter_ctrl() failed=%d\n", - KBUILD_MODNAME, ret); - goto err_mutex_unlock; - } - } + /* ask device to start streaming */ + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl(adap->fe[adap->active_fe], 1); + if (ret) + dev_err(&d->udev->dev, + "%s: streaming_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + } +skip_feed_start: - if (d->props->streaming_ctrl) { - ret = d->props->streaming_ctrl( - adap->fe[adap->active_fe], 1); - if (ret < 0) { - dev_err(&d->udev->dev, "%s: streaming_ctrl() " \ - "failed=%d\n", KBUILD_MODNAME, - ret); - goto err_mutex_unlock; - } - } + /* add PID to device HW PID filter */ + if (adap->pid_filtering && adap->props->pid_filter) { + ret = adap->props->pid_filter(adap, dvbdmxfeed->index, + dvbdmxfeed->pid, 1); + if (ret) + dev_err(&d->udev->dev, "%s: pid_filter() failed=%d\n", + KBUILD_MODNAME, ret); } - return 0; -err_mutex_unlock: - mutex_unlock(&adap->sync_mutex); - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + if (ret) + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } -static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - return dvb_usb_ctrl_feed(dvbdmxfeed, 1); -} - static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { - return dvb_usb_ctrl_feed(dvbdmxfeed, -1); + struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; + struct dvb_usb_device *d = adap_to_d(adap); + int ret = 0; + dev_dbg(&d->udev->dev, + "%s: adap=%d active_fe=%d feed_type=%d setting pid [%s]: %04x (%04d) at index %d\n", + __func__, adap->id, adap->active_fe, dvbdmxfeed->type, + adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid, + dvbdmxfeed->pid, dvbdmxfeed->index); + + if (adap->active_fe == -1) + return -EINVAL; + + /* remove PID from device HW PID filter */ + if (adap->pid_filtering && adap->props->pid_filter) { + ret = adap->props->pid_filter(adap, dvbdmxfeed->index, + dvbdmxfeed->pid, 0); + if (ret) + dev_err(&d->udev->dev, "%s: pid_filter() failed=%d\n", + KBUILD_MODNAME, ret); + } + + /* we cannot stop streaming until last PID is removed */ + if (--adap->feed_count > 0) + goto skip_feed_stop; + + /* ask device to stop streaming */ + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl(adap->fe[adap->active_fe], 0); + if (ret) + dev_err(&d->udev->dev, + "%s: streaming_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + } + + /* disable HW PID filter */ + if (adap->pid_filtering && adap->props->pid_filter_ctrl) { + ret = adap->props->pid_filter_ctrl(adap, 0); + if (ret) + dev_err(&d->udev->dev, + "%s: pid_filter_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + } + + /* kill USB streaming packets */ + usb_urb_killv2(&adap->stream); + + /* clear 'streaming' status bit */ + clear_bit(ADAP_STREAMING, &adap->state_bits); + smp_mb__after_clear_bit(); + wake_up_bit(&adap->state_bits, ADAP_STREAMING); +skip_feed_stop: + + if (ret) + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) @@ -435,8 +466,6 @@ static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) goto err_dvb_net_init; } - mutex_init(&adap->sync_mutex); - return 0; err_dvb_net_init: dvb_dmxdev_release(&adap->dmxdev); @@ -500,7 +529,7 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) if (!adap->suspend_resume_active) { adap->active_fe = fe->id; - mutex_lock(&adap->sync_mutex); + set_bit(ADAP_INIT, &adap->state_bits); } ret = dvb_usbv2_device_power_ctrl(d, 1); @@ -519,8 +548,11 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) goto err; } err: - if (!adap->suspend_resume_active) - mutex_unlock(&adap->sync_mutex); + if (!adap->suspend_resume_active) { + clear_bit(ADAP_INIT, &adap->state_bits); + smp_mb__after_clear_bit(); + wake_up_bit(&adap->state_bits, ADAP_INIT); + } dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret); return ret; @@ -534,8 +566,11 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); - if (!adap->suspend_resume_active) - mutex_lock(&adap->sync_mutex); + if (!adap->suspend_resume_active) { + set_bit(ADAP_SLEEP, &adap->state_bits); + wait_on_bit(&adap->state_bits, ADAP_STREAMING, wait_schedule, + TASK_UNINTERRUPTIBLE); + } if (adap->fe_sleep[fe->id]) { ret = adap->fe_sleep[fe->id](fe); @@ -555,7 +590,9 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) err: if (!adap->suspend_resume_active) { adap->active_fe = -1; - mutex_unlock(&adap->sync_mutex); + clear_bit(ADAP_SLEEP, &adap->state_bits); + smp_mb__after_clear_bit(); + wake_up_bit(&adap->state_bits, ADAP_SLEEP); } dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret); @@ -574,8 +611,9 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) if (d->props->frontend_attach) { ret = d->props->frontend_attach(adap); if (ret < 0) { - dev_dbg(&d->udev->dev, "%s: frontend_attach() " \ - "failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, + "%s: frontend_attach() failed=%d\n", + __func__, ret); goto err_dvb_frontend_detach; } } else { @@ -595,8 +633,9 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); if (ret < 0) { - dev_err(&d->udev->dev, "%s: frontend%d registration " \ - "failed\n", KBUILD_MODNAME, i); + dev_err(&d->udev->dev, + "%s: frontend%d registration failed\n", + KBUILD_MODNAME, i); goto err_dvb_unregister_frontend; } @@ -670,33 +709,33 @@ static int dvb_usbv2_adapter_init(struct dvb_usb_device *d) /* speed - when running at FULL speed we need a HW PID filter */ if (d->udev->speed == USB_SPEED_FULL && !(adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { - dev_err(&d->udev->dev, "%s: this USB2.0 device " \ - "cannot be run on a USB1.1 port (it " \ - "lacks a hardware PID filter)\n", + dev_err(&d->udev->dev, + "%s: this USB2.0 device cannot be run on a USB1.1 port (it lacks a hardware PID filter)\n", KBUILD_MODNAME); ret = -ENODEV; goto err; } else if ((d->udev->speed == USB_SPEED_FULL && adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || (adap->props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - dev_info(&d->udev->dev, "%s: will use the device's " \ - "hardware PID filter " \ - "(table count: %d)\n", KBUILD_MODNAME, + dev_info(&d->udev->dev, + "%s: will use the device's hardware PID filter (table count: %d)\n", + KBUILD_MODNAME, adap->props->pid_filter_count); adap->pid_filtering = 1; adap->max_feed_count = adap->props->pid_filter_count; } else { - dev_info(&d->udev->dev, "%s: will pass the complete " \ - "MPEG2 transport stream to the " \ - "software demuxer\n", KBUILD_MODNAME); + dev_info(&d->udev->dev, + "%s: will pass the complete MPEG2 transport stream to the software demuxer\n", + KBUILD_MODNAME); adap->pid_filtering = 0; adap->max_feed_count = 255; } if (!adap->pid_filtering && dvb_usb_force_pid_filter_usage && adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { - dev_info(&d->udev->dev, "%s: PID filter enabled by " \ - "module option\n", KBUILD_MODNAME); + dev_info(&d->udev->dev, + "%s: PID filter enabled by module option\n", + KBUILD_MODNAME); adap->pid_filtering = 1; adap->max_feed_count = adap->props->pid_filter_count; } @@ -825,8 +864,9 @@ static void dvb_usbv2_init_work(struct work_struct *work) if (ret == 0) { ; } else if (ret == COLD) { - dev_info(&d->udev->dev, "%s: found a '%s' in cold " \ - "state\n", KBUILD_MODNAME, d->name); + dev_info(&d->udev->dev, + "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, d->name); if (!name) name = d->props->firmware; @@ -868,8 +908,9 @@ static void dvb_usbv2_init_work(struct work_struct *work) if (ret < 0) goto err_usb_driver_release_interface; - dev_info(&d->udev->dev, "%s: '%s' successfully initialized and " \ - "connected\n", KBUILD_MODNAME, d->name); + dev_info(&d->udev->dev, + "%s: '%s' successfully initialized and connected\n", + KBUILD_MODNAME, d->name); return; err_usb_driver_release_interface: 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 5716662..33ff97e 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -21,8 +21,8 @@ #include "dvb_usb_common.h" -int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, - u16 rlen) +static int dvb_usb_v2_generic_io(struct dvb_usb_device *d, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { int ret, actual_length; @@ -32,8 +32,6 @@ int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, return -EINVAL; } - mutex_lock(&d->usb_mutex); - dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, wlen, wbuf); ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, @@ -56,20 +54,51 @@ int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, d->props->generic_bulk_ctrl_endpoint_response), rbuf, rlen, &actual_length, 2000); if (ret) - dev_err(&d->udev->dev, "%s: 2nd usb_bulk_msg() " \ - "failed=%d\n", KBUILD_MODNAME, ret); + dev_err(&d->udev->dev, + "%s: 2nd usb_bulk_msg() failed=%d\n", + KBUILD_MODNAME, ret); dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__, actual_length, rbuf); } + return ret; +} + +int dvb_usbv2_generic_rw(struct dvb_usb_device *d, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + int ret; + + mutex_lock(&d->usb_mutex); + ret = dvb_usb_v2_generic_io(d, wbuf, wlen, rbuf, rlen); mutex_unlock(&d->usb_mutex); + return ret; } EXPORT_SYMBOL(dvb_usbv2_generic_rw); int dvb_usbv2_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len) { - return dvb_usbv2_generic_rw(d, buf, len, NULL, 0); + int ret; + + mutex_lock(&d->usb_mutex); + ret = dvb_usb_v2_generic_io(d, buf, len, NULL, 0); + mutex_unlock(&d->usb_mutex); + + return ret; } EXPORT_SYMBOL(dvb_usbv2_generic_write); + +int dvb_usbv2_generic_rw_locked(struct dvb_usb_device *d, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + return dvb_usb_v2_generic_io(d, wbuf, wlen, rbuf, rlen); +} +EXPORT_SYMBOL(dvb_usbv2_generic_rw_locked); + +int dvb_usbv2_generic_write_locked(struct dvb_usb_device *d, u8 *buf, u16 len) +{ + return dvb_usb_v2_generic_io(d, buf, len, NULL, 0); +} +EXPORT_SYMBOL(dvb_usbv2_generic_write_locked); diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 8338479..e48cdeb 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -218,6 +218,7 @@ static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) deb_info(1, "PID_C (%02x)", onoff); + st->pid_filter_onoff = adap->pid_filtering; ret = it913x_wr_reg(d, pro, PID_EN, st->pid_filter_onoff); mutex_unlock(&d->i2c_mutex); diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index f30c58c..b3fd0ff 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -1241,10 +1241,13 @@ static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, struct usb_data_stream_properties *stream) { struct dvb_usb_adapter *adap = fe_to_adap(fe); - struct dvb_usb_device *d = adap_to_d(adap); + struct dvb_usb_device *d; if (adap == NULL) return 0; + + d = adap_to_d(adap); + /* Turn PID filter on the fly by module option */ if (pid_filter == 2) { adap->pid_filtering = 1; @@ -1299,8 +1302,7 @@ static void lme2510_exit(struct dvb_usb_device *d) if (d != NULL) { usb_buffer = lme2510_exit_int(d); - if (usb_buffer != NULL) - kfree(usb_buffer); + kfree(usb_buffer); } } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h index 432706a..3f3f8bf 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h @@ -21,6 +21,7 @@ #ifndef __MXL111SF_DEMOD_H__ #define __MXL111SF_DEMOD_H__ +#include <linux/kconfig.h> #include "dvb_frontend.h" #include "mxl111sf.h" @@ -31,8 +32,7 @@ struct mxl111sf_demod_config { struct mxl111sf_reg_ctrl_info *ctrl_reg_info); }; -#if defined(CONFIG_DVB_USB_MXL111SF) || \ - (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF) extern struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, struct mxl111sf_demod_config *cfg); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h index ff33396..90f583e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h @@ -21,8 +21,8 @@ #ifndef __MXL111SF_TUNER_H__ #define __MXL111SF_TUNER_H__ +#include <linux/kconfig.h> #include "dvb_frontend.h" - #include "mxl111sf.h" enum mxl_if_freq { @@ -60,8 +60,7 @@ struct mxl111sf_tuner_config { /* ------------------------------------------------------------------------ */ -#if defined(CONFIG_DVB_USB_MXL111SF) || \ - (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF) extern struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, struct mxl111sf_state *mxl_state, diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index d98387a..22015fe 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -33,6 +33,7 @@ #include "e4000.h" #include "fc2580.h" #include "tua9001.h" +#include "r820t.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -375,6 +376,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; + struct rtl28xxu_req req_r820t = {0x0034, CMD_I2C_RD, 5, buf}; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -479,6 +481,14 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) goto found; } + /* check R820T by reading tuner stats at I2C addr 0x1a */ + ret = rtl28xxu_ctrl_msg(d, &req_r820t); + if (ret == 0) { + priv->tuner = TUNER_RTL2832_R820T; + priv->tuner_name = "R820T"; + goto found; + } + found: dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); @@ -589,6 +599,12 @@ static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = { .tuner = TUNER_RTL2832_E4000, }; +static struct rtl2832_config rtl28xxu_rtl2832_r820t_config = { + .i2c_addr = 0x10, + .xtal = 28800000, + .tuner = TUNER_RTL2832_R820T, +}; + static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -728,6 +744,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) case TUNER_RTL2832_E4000: rtl2832_config = &rtl28xxu_rtl2832_e4000_config; break; + case TUNER_RTL2832_R820T: + rtl2832_config = &rtl28xxu_rtl2832_r820t_config; + break; default: dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", KBUILD_MODNAME, priv->tuner_name); @@ -840,6 +859,13 @@ static const struct fc0012_config rtl2832u_fc0012_config = { .xtal_freq = FC_XTAL_28_8_MHZ, }; +static const struct r820t_config rtl2832u_r820t_config = { + .i2c_addr = 0x1a, + .xtal = 28800000, + .max_i2c_msg_len = 2, + .rafael_chip = CHIP_R820T, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -889,6 +915,14 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, &rtl2832u_tua9001_config); break; + case TUNER_RTL2832_R820T: + fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap, + &rtl2832u_r820t_config); + + /* Use tuner to get the signal strength */ + adap->fe[0]->ops.read_signal_strength = + adap->fe[0]->ops.tuner_ops.get_rf_strength; + break; default: fe = NULL; dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, @@ -1372,6 +1406,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Digivox Micro Hd", NULL) }, { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620, &rtl2832u_props, "Compro VideoMate U620F", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394, + &rtl2832u_props, "MaxMedia HU394-T", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 2f3af2d..533a331 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -82,6 +82,7 @@ enum rtl28xxu_tuner { TUNER_RTL2832_E4000, TUNER_RTL2832_TDA18272, TUNER_RTL2832_FC0013, + TUNER_RTL2832_R820T, }; struct rtl28xxu_req { diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c index 7346f85..ca8f3c2 100644 --- a/drivers/media/usb/dvb-usb-v2/usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -22,8 +22,8 @@ static void usb_urb_complete(struct urb *urb) int i; u8 *b; - dev_dbg_ratelimited(&stream->udev->dev, "%s: %s urb completed " \ - "status=%d length=%d/%d pack_num=%d errors=%d\n", + dev_dbg_ratelimited(&stream->udev->dev, + "%s: %s urb completed status=%d length=%d/%d pack_num=%d errors=%d\n", __func__, ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", urb->status, urb->actual_length, urb->transfer_buffer_length, @@ -49,8 +49,8 @@ static void usb_urb_complete(struct urb *urb) case PIPE_ISOCHRONOUS: for (i = 0; i < urb->number_of_packets; i++) { if (urb->iso_frame_desc[i].status != 0) - dev_dbg(&stream->udev->dev, "%s: iso frame " \ - "descriptor has an error=%d\n", + dev_dbg(&stream->udev->dev, + "%s: iso frame descriptor has an error=%d\n", __func__, urb->iso_frame_desc[i].status); else if (urb->iso_frame_desc[i].actual_length > 0) @@ -67,8 +67,9 @@ static void usb_urb_complete(struct urb *urb) stream->complete(stream, b, urb->actual_length); break; default: - dev_err(&stream->udev->dev, "%s: unknown endpoint type in " \ - "completition handler\n", KBUILD_MODNAME); + dev_err(&stream->udev->dev, + "%s: unknown endpoint type in completition handler\n", + KBUILD_MODNAME); return; } usb_submit_urb(urb, GFP_ATOMIC); @@ -101,8 +102,8 @@ int usb_urb_submitv2(struct usb_data_stream *stream, dev_dbg(&stream->udev->dev, "%s: submit urb=%d\n", __func__, i); ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC); if (ret) { - dev_err(&stream->udev->dev, "%s: could not submit " \ - "urb no. %d - get them all back\n", + dev_err(&stream->udev->dev, + "%s: could not submit urb no. %d - get them all back\n", KBUILD_MODNAME, i); usb_urb_killv2(stream); return ret; @@ -229,8 +230,9 @@ static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num, stream->buf_num = 0; stream->buf_size = size; - dev_dbg(&stream->udev->dev, "%s: all in all I will use %lu bytes for " \ - "streaming\n", __func__, num * size); + dev_dbg(&stream->udev->dev, + "%s: all in all I will use %lu bytes for streaming\n", + __func__, num * size); for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { stream->buf_list[stream->buf_num] = usb_alloc_coherent( @@ -274,8 +276,8 @@ int usb_urb_reconfig(struct usb_data_stream *stream, } if (stream->buf_num < props->count || stream->buf_size < buf_size) { - dev_err(&stream->udev->dev, "%s: cannot reconfigure as " \ - "allocated buffers are too small\n", + dev_err(&stream->udev->dev, + "%s: cannot reconfigure as allocated buffers are too small\n", KBUILD_MODNAME); return -EINVAL; } @@ -321,8 +323,9 @@ int usb_urb_initv2(struct usb_data_stream *stream, memcpy(&stream->props, props, sizeof(*props)); if (!stream->complete) { - dev_err(&stream->udev->dev, "%s: there is no data callback - " \ - "this doesn't make sense\n", KBUILD_MODNAME); + dev_err(&stream->udev->dev, + "%s: there is no data callback - this doesn't make sense\n", + KBUILD_MODNAME); return -EINVAL; } @@ -343,8 +346,9 @@ int usb_urb_initv2(struct usb_data_stream *stream, return usb_urb_alloc_isoc_urbs(stream); default: - dev_err(&stream->udev->dev, "%s: unknown urb-type for data " \ - "transfer\n", KBUILD_MODNAME); + dev_err(&stream->udev->dev, + "%s: unknown urb-type for data transfer\n", + KBUILD_MODNAME); return -EINVAL; } } diff --git a/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c index 1efc028..c890fe4 100644 --- a/drivers/media/usb/dvb-usb/cinergyT2-fe.c +++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c @@ -300,8 +300,7 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) static void cinergyt2_fe_release(struct dvb_frontend *fe) { struct cinergyt2_fe_state *state = fe->demodulator_priv; - if (state != NULL) - kfree(state); + kfree(state); } static struct dvb_frontend_ops cinergyt2_fe_ops; diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 1179842..f081360 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1431,13 +1431,22 @@ static int dib8090_get_adc_power(struct dvb_frontend *fe) return dib8000_get_adc_power(fe, 1); } +static void dib8090_agc_control(struct dvb_frontend *fe, u8 restart) +{ + deb_info("AGC control callback: %i\n", restart); + dib0090_dcc_freq(fe, restart); + + if (restart == 0) /* before AGC startup */ + dib0090_set_dc_servo(fe, 1); +} + static struct dib8000_config dib809x_dib8000_config[2] = { { .output_mpeg2_in_188_bytes = 1, .agc_config_count = 2, .agc = dib8090_agc_config, - .agc_control = dib0090_dcc_freq, + .agc_control = dib8090_agc_control, .pll = &dib8090_pll_config_12mhz, .tuner_is_baseband = 1, @@ -1456,7 +1465,7 @@ static struct dib8000_config dib809x_dib8000_config[2] = { .agc_config_count = 2, .agc = dib8090_agc_config, - .agc_control = dib0090_dcc_freq, + .agc_control = dib8090_agc_control, .pll = &dib8090_pll_config_12mhz, .tuner_is_baseband = 1, @@ -1504,28 +1513,89 @@ static struct dib0090_config dib809x_dib0090_config = { .fref_clock_ratio = 6, }; +static u8 dib8090_compute_pll_parameters(struct dvb_frontend *fe) +{ + u8 optimal_pll_ratio = 20; + u32 freq_adc, ratio, rest, max = 0; + u8 pll_ratio; + + for (pll_ratio = 17; pll_ratio <= 20; pll_ratio++) { + freq_adc = 12 * pll_ratio * (1 << 8) / 16; + ratio = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) / freq_adc; + rest = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) - ratio * freq_adc; + + if (rest > freq_adc / 2) + rest = freq_adc - rest; + deb_info("PLL ratio=%i rest=%i\n", pll_ratio, rest); + if ((rest > max) && (rest > 717)) { + optimal_pll_ratio = pll_ratio; + max = rest; + } + } + deb_info("optimal PLL ratio=%i\n", optimal_pll_ratio); + + return optimal_pll_ratio; +} + static int dib8096_set_param_override(struct dvb_frontend *fe) { - struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dib0700_adapter_state *state = adap->priv; - u8 band = BAND_OF_FREQUENCY(p->frequency/1000); - u16 target; + u8 pll_ratio, band = BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000); + u16 target, ltgain, rf_gain_limit; + u32 timf; int ret = 0; enum frontend_tune_state tune_state = CT_SHUTDOWN; - u16 ltgain, rf_gain_limit; + + switch (band) { + default: + deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency); + case BAND_VHF: + dib8000_set_gpio(fe, 3, 0, 1); + break; + case BAND_UHF: + dib8000_set_gpio(fe, 3, 0, 0); + break; + } ret = state->set_param_save(fe); if (ret < 0) return ret; - target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; - dib8000_set_wbd_ref(fe, target); + if (fe->dtv_property_cache.bandwidth_hz != 6000000) { + deb_info("only 6MHz bandwidth is supported\n"); + return -EINVAL; + } + + /** Update PLL if needed ratio **/ + dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, 0); + + /** Get optimize PLL ratio to remove spurious **/ + pll_ratio = dib8090_compute_pll_parameters(fe); + if (pll_ratio == 17) + timf = 21387946; + else if (pll_ratio == 18) + timf = 20199727; + else if (pll_ratio == 19) + timf = 19136583; + else + timf = 18179756; + + /** Update ratio **/ + dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, pll_ratio); + dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, timf); + + if (band != BAND_CBAND) { + /* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */ + target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; + dib8000_set_wbd_ref(fe, target); + } if (band == BAND_CBAND) { deb_info("tuning in CBAND - soft-AGC startup\n"); dib0090_set_tune_state(fe, CT_AGC_START); + do { ret = dib0090_gain_control(fe); msleep(ret); @@ -1534,14 +1604,17 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) dib8000_set_gpio(fe, 6, 0, 1); else if (tune_state == CT_AGC_STEP_1) { dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); - if (rf_gain_limit == 0) + if (rf_gain_limit < 2000) /* activate the external attenuator in case of very high input power */ dib8000_set_gpio(fe, 6, 0, 0); } } while (tune_state < CT_AGC_STOP); + + deb_info("switching to PWM AGC\n"); dib0090_pwm_gain_reset(fe); dib8000_pwm_agc_reset(fe); dib8000_set_tune_state(fe, CT_DEMOD_START); } else { + /* for everything else than CBAND we are using standard AGC */ deb_info("not tuning in CBAND - standard AGC startup\n"); dib0090_pwm_gain_reset(fe); } @@ -1814,21 +1887,92 @@ struct dibx090p_adc { u32 pll_prediv; /* New loopdiv */ }; -struct dibx090p_adc dib8090p_adc_tab[] = { - { 50000, 17043521, 16, 3}, /* 64 MHz */ - {878000, 20199729, 9, 1}, /* 60 MHz */ - {0xffffffff, 0, 0, 0}, /* 60 MHz */ +struct dibx090p_best_adc { + u32 timf; + u32 pll_loopdiv; + u32 pll_prediv; }; +static int dib8096p_get_best_sampling(struct dvb_frontend *fe, struct dibx090p_best_adc *adc) +{ + u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; + u16 xtal = 12000; + u16 fcp_min = 1900; /* PLL, Minimum Frequency of phase comparator (KHz) */ + u16 fcp_max = 20000; /* PLL, Maximum Frequency of phase comparator (KHz) */ + u32 fmem_max = 140000; /* 140MHz max SDRAM freq */ + u32 fdem_min = 66000; + u32 fcp = 0, fs = 0, fdem = 0, fmem = 0; + u32 harmonic_id = 0; + + adc->timf = 0; + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + + deb_info("bandwidth = %d", fe->dtv_property_cache.bandwidth_hz); + + /* Find Min and Max prediv */ + while ((xtal / max_prediv) >= fcp_min) + max_prediv++; + + max_prediv--; + min_prediv = max_prediv; + while ((xtal / min_prediv) <= fcp_max) { + min_prediv--; + if (min_prediv == 1) + break; + } + deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); + + min_prediv = 1; + + for (prediv = min_prediv; prediv < max_prediv; prediv++) { + fcp = xtal / prediv; + if (fcp > fcp_min && fcp < fcp_max) { + for (loopdiv = 1; loopdiv < 64; loopdiv++) { + fmem = ((xtal/prediv) * loopdiv); + fdem = fmem / 2; + fs = fdem / 4; + + /* test min/max system restrictions */ + if ((fdem >= fdem_min) && (fmem <= fmem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz / 1000)) { + spur = 0; + /* test fs harmonics positions */ + for (harmonic_id = (fe->dtv_property_cache.frequency / (1000 * fs)); harmonic_id <= ((fe->dtv_property_cache.frequency / (1000 * fs)) + 1); harmonic_id++) { + if (((fs * harmonic_id) >= (fe->dtv_property_cache.frequency / 1000 - (fe->dtv_property_cache.bandwidth_hz / 2000))) && ((fs * harmonic_id) <= (fe->dtv_property_cache.frequency / 1000 + (fe->dtv_property_cache.bandwidth_hz / 2000)))) { + spur = 1; + break; + } + } + + if (!spur) { + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + adc->timf = (4260880253U / fdem) * (1 << 8); + adc->timf += ((4260880253U % fdem) << 8) / fdem; + + deb_info("RF %6d; BW %6d; Xtal %6d; Fmem %6d; Fdem %6d; Fs %6d; Prediv %2d; Loopdiv %2d; Timf %8d;", fe->dtv_property_cache.frequency, fe->dtv_property_cache.bandwidth_hz, xtal, fmem, fdem, fs, prediv, loopdiv, adc->timf); + break; + } + } + } + } + if (!spur) + break; + } + + if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0) + return -EINVAL; + return 0; +} + static int dib8096p_agc_startup(struct dvb_frontend *fe) { - struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dib0700_adapter_state *state = adap->priv; struct dibx000_bandwidth_config pll; + struct dibx090p_best_adc adc; u16 target; - int better_sampling_freq = 0, ret; - struct dibx090p_adc *adc_table = &dib8090p_adc_tab[0]; + int ret; ret = state->set_param_save(fe); if (ret < 0) @@ -1841,23 +1985,27 @@ static int dib8096p_agc_startup(struct dvb_frontend *fe) target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; dib8000_set_wbd_ref(fe, target); + if (dib8096p_get_best_sampling(fe, &adc) == 0) { + pll.pll_ratio = adc.pll_loopdiv; + pll.pll_prediv = adc.pll_prediv; - while (p->frequency / 1000 > adc_table->freq) { - better_sampling_freq = 1; - adc_table++; - } - - if ((adc_table->freq != 0xffffffff) && better_sampling_freq) { - pll.pll_ratio = adc_table->pll_loopdiv; - pll.pll_prediv = adc_table->pll_prediv; - dib8000_update_pll(fe, &pll); - dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc_table->timf); + dib0700_set_i2c_speed(adap->dev, 200); + dib8000_update_pll(fe, &pll, fe->dtv_property_cache.bandwidth_hz / 1000, 0); + dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); + dib0700_set_i2c_speed(adap->dev, 1000); } return 0; } static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_state *st = adap->dev->priv; + u32 fw_version; + + dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); + if (fw_version >= 0x10200) + st->fw_use_new_i2c_api = 1; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -2242,13 +2390,7 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) } /* NIM7090 */ -struct dib7090p_best_adc { - u32 timf; - u32 pll_loopdiv; - u32 pll_prediv; -}; - -static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc) +static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dibx090p_best_adc *adc) { u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; @@ -2327,7 +2469,7 @@ static int dib7090_agc_startup(struct dvb_frontend *fe) struct dib0700_adapter_state *state = adap->priv; struct dibx000_bandwidth_config pll; u16 target; - struct dib7090p_best_adc adc; + struct dibx090p_best_adc adc; int ret; ret = state->set_param_save(fe); @@ -2357,36 +2499,16 @@ static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) return 0; } -static int dib7090e_update_lna(struct dvb_frontend *fe, u16 agc_global) +static int tfe7790p_update_lna(struct dvb_frontend *fe, u16 agc_global) { - u16 agc1 = 0, agc2, wbd = 0, wbd_target, wbd_offset, threshold_agc1; - s16 wbd_delta; + deb_info("update LNA: agc global=%i", agc_global); - if ((fe->dtv_property_cache.frequency) < 400000000) - threshold_agc1 = 25000; - else - threshold_agc1 = 30000; - - wbd_target = (dib0090_get_wbd_target(fe)*8+1)/2; - wbd_offset = dib0090_get_wbd_offset(fe); - dib7000p_get_agc_values(fe, NULL, &agc1, &agc2, &wbd); - wbd_delta = (s16)wbd - (((s16)wbd_offset+10)*4) ; - - deb_info("update lna, agc_global=%d agc1=%d agc2=%d", - agc_global, agc1, agc2); - deb_info("update lna, wbd=%d wbd target=%d wbd offset=%d wbd delta=%d", - wbd, wbd_target, wbd_offset, wbd_delta); - - if ((agc1 < threshold_agc1) && (wbd_delta > 0)) { - dib0090_set_switch(fe, 1, 1, 1); - dib0090_set_vga(fe, 0); - dib0090_update_rframp_7090(fe, 0); - dib0090_update_tuning_table_7090(fe, 0); + if (agc_global < 25000) { + dib7000p_set_gpio(fe, 8, 0, 0); + dib7000p_set_agc1_min(fe, 0); } else { - dib0090_set_vga(fe, 1); - dib0090_update_rframp_7090(fe, 1); - dib0090_update_tuning_table_7090(fe, 1); - dib0090_set_switch(fe, 0, 0, 0); + dib7000p_set_gpio(fe, 8, 0, 1); + dib7000p_set_agc1_min(fe, 32768); } return 0; @@ -2400,15 +2522,6 @@ static struct dib0090_wbd_slope dib7090_wbd_table[] = { { 0xFFFF, 0, 0, 0, 0, 0}, }; -static struct dib0090_wbd_slope dib7090e_wbd_table[] = { - { 380, 81, 850, 64, 540, 4}, - { 700, 51, 866, 21, 320, 4}, - { 860, 48, 666, 18, 330, 6}, - {1700, 0, 250, 0, 100, 6}, - {2600, 0, 250, 0, 100, 6}, - { 0xFFFF, 0, 0, 0, 0, 0}, -}; - static struct dibx000_agc_config dib7090_agc_config[2] = { { .band_caps = BAND_UHF, @@ -2428,7 +2541,7 @@ static struct dibx000_agc_config dib7090_agc_config[2] = { .wbd_alpha = 5, .agc1_max = 65535, - .agc1_min = 0, + .agc1_min = 32768, .agc2_max = 65535, .agc2_min = 0, @@ -2505,7 +2618,7 @@ static struct dib7000p_config nim7090_dib7000p_config = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = NULL, + .update_lna = tfe7790p_update_lna, /* GPIO used is the same as TFE7790 */ .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2529,12 +2642,26 @@ static struct dib7000p_config nim7090_dib7000p_config = { .enMpegOutput = 1, }; +static int tfe7090p_pvr_update_lna(struct dvb_frontend *fe, u16 agc_global) +{ + deb_info("TFE7090P-PVR update LNA: agc global=%i", agc_global); + if (agc_global < 25000) { + dib7000p_set_gpio(fe, 5, 0, 0); + dib7000p_set_agc1_min(fe, 0); + } else { + dib7000p_set_gpio(fe, 5, 0, 1); + dib7000p_set_agc1_min(fe, 32768); + } + + return 0; +} + static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = NULL, + .update_lna = tfe7090p_pvr_update_lna, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2561,7 +2688,7 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = NULL, + .update_lna = tfe7090p_pvr_update_lna, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2587,34 +2714,6 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { } }; -static struct dib7000p_config tfe7090e_dib7000p_config = { - .output_mpeg2_in_188_bytes = 1, - .hostbus_diversity = 1, - .tuner_is_baseband = 1, - .update_lna = dib7090e_update_lna, - - .agc_config_count = 2, - .agc = dib7090_agc_config, - - .bw = &dib7090_clock_config_12_mhz, - - .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, - .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, - .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, - - .pwm_freq_div = 0, - - .agc_control = dib7090_agc_restart, - - .spur_protect = 0, - .disable_sample_and_hold = 0, - .enable_current_mirror = 0, - .diversity_delay = 0, - - .output_mode = OUTMODE_MPEG2_FIFO, - .enMpegOutput = 1, -}; - static const struct dib0090_config nim7090_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, @@ -2649,47 +2748,11 @@ static const struct dib0090_config nim7090_dib0090_config = { .in_soc = 1, }; -static const struct dib0090_config tfe7090e_dib0090_config = { - .io.clock_khz = 12000, - .io.pll_bypass = 0, - .io.pll_range = 0, - .io.pll_prediv = 3, - .io.pll_loopdiv = 6, - .io.adc_clock_ratio = 0, - .io.pll_int_loop_filt = 0, - .reset = dib7090_tuner_sleep, - .sleep = dib7090_tuner_sleep, - - .freq_offset_khz_uhf = 0, - .freq_offset_khz_vhf = 0, - - .get_adc_power = dib7090_get_adc_power, - - .clkouttobamse = 1, - .analog_output = 0, - - .wbd_vhf_offset = 0, - .wbd_cband_offset = 0, - .use_pwm_agc = 1, - .clkoutdrive = 0, - - .fref_clock_ratio = 0, - - .wbd = dib7090e_wbd_table, - - .ls_cfg_pad_drv = 0, - .data_tx_drv = 0, - .low_if = NULL, - .in_soc = 1, - .force_cband_input = 1, - .is_dib7090e = 1, -}; - -static struct dib7000p_config tfe7790e_dib7000p_config = { +static struct dib7000p_config tfe7790p_dib7000p_config = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = dib7090e_update_lna, + .update_lna = tfe7790p_update_lna, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2713,7 +2776,7 @@ static struct dib7000p_config tfe7790e_dib7000p_config = { .enMpegOutput = 1, }; -static const struct dib0090_config tfe7790e_dib0090_config = { +static const struct dib0090_config tfe7790p_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -2739,14 +2802,14 @@ static const struct dib0090_config tfe7790e_dib0090_config = { .fref_clock_ratio = 0, - .wbd = dib7090e_wbd_table, + .wbd = dib7090_wbd_table, .ls_cfg_pad_drv = 0, .data_tx_drv = 0, .low_if = NULL, .in_soc = 1, - .force_cband_input = 1, - .is_dib7090e = 1, + .force_cband_input = 0, + .is_dib7090e = 0, .force_crystal_mode = 1, }; @@ -2942,37 +3005,11 @@ static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) return 0; } -static int tfe7090e_frontend_attach(struct dvb_usb_adapter *adap) -{ - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); - msleep(20); - dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); - - msleep(20); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(20); - dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, - 1, 0x10, &tfe7090e_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", - __func__); - return -ENODEV; - } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, - 0x80, &tfe7090e_dib7000p_config); - - return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; -} - -static int tfe7790e_frontend_attach(struct dvb_usb_adapter *adap) +static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; - /* The TFE7790E requires the dib0700 to not be in master mode */ + /* The TFE7790P requires the dib0700 to not be in master mode */ st->disable_streaming_master_mode = 1; dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); @@ -2988,42 +3025,25 @@ static int tfe7790e_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, - 1, 0x10, &tfe7790e_dib7000p_config) != 0) { + 1, 0x10, &tfe7790p_dib7000p_config) != 0) { err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); return -ENODEV; } adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, - 0x80, &tfe7790e_dib7000p_config); + 0x80, &tfe7790p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } -static int tfe7790e_tuner_attach(struct dvb_usb_adapter *adap) -{ - struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = - dib7090_get_i2c_tuner(adap->fe_adap[0].fe); - - if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, - &tfe7790e_dib0090_config) == NULL) - return -ENODEV; - - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); - - st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; - adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; - return 0; -} - -static int tfe7090e_tuner_attach(struct dvb_usb_adapter *adap) +static int tfe7790p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, - &tfe7090e_dib0090_config) == NULL) + &tfe7790p_dib0090_config) == NULL) return -ENODEV; dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); @@ -3566,10 +3586,9 @@ struct usb_device_id dib0700_usb_id_table[] = { /* 75 */{ USB_DEVICE(USB_VID_MEDION, USB_PID_CREATIX_CTX1921) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E_SE) }, - { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, - { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, -/* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, - { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790P) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, +/* 80 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3880,7 +3899,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, { "Elgato EyeTV DTT rev. 2", - { &dib0700_usb_id_table[81], NULL }, + { &dib0700_usb_id_table[80], NULL }, { NULL }, }, }, @@ -4697,48 +4716,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, - .frontend_attach = tfe7090e_frontend_attach, - .tuner_attach = tfe7090e_tuner_attach, - - DIB0700_DEFAULT_STREAMING_CONFIG(0x02), - } }, - - .size_of_priv = - sizeof(struct dib0700_adapter_state), - }, - }, - - .num_device_descs = 1, - .devices = { - { "DiBcom TFE7090E reference design", - { &dib0700_usb_id_table[78], NULL }, - { NULL }, - }, - }, - - .rc.core = { - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_codes = RC_MAP_DIB0700_RC5_TABLE, - .module_name = "dib0700", - .rc_query = dib0700_rc_query_old_firmware, - .allowed_protos = RC_BIT_RC5 | - RC_BIT_RC6_MCE | - RC_BIT_NEC, - .change_protocol = dib0700_change_protocol, - }, - }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, - .num_adapters = 1, - .adapter = { - { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - .pid_filter_count = 32, - .pid_filter = stk70x0p_pid_filter, - .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, - .frontend_attach = tfe7790e_frontend_attach, - .tuner_attach = tfe7790e_tuner_attach, + .frontend_attach = tfe7790p_frontend_attach, + .tuner_attach = tfe7790p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), } }, @@ -4750,8 +4729,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { - { "DiBcom TFE7790E reference design", - { &dib0700_usb_id_table[79], NULL }, + { "DiBcom TFE7790P reference design", + { &dib0700_usb_id_table[78], NULL }, { NULL }, }, }, @@ -4792,7 +4771,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { { "DiBcom TFE8096P reference design", - { &dib0700_usb_id_table[80], NULL }, + { &dib0700_usb_id_table[79], NULL }, { NULL }, }, }, diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index af0d432..c2dded9 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -8,6 +8,8 @@ * * see Documentation/dvb/README.dvb-usb for more information */ + +#include <linux/kconfig.h> #include "dibusb.h" static int debug; @@ -232,8 +234,7 @@ static struct dibx000_agc_config dib3000p_panasonic_agc_config = { .agc2_slope2 = 0x1e, }; -#if defined(CONFIG_DVB_DIB3000MC) || \ - (defined(CONFIG_DVB_DIB3000MC_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB3000MC) static struct dib3000mc_config mod3000p_dib3000p_config = { &dib3000p_panasonic_agc_config, diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 9578a67..6e237b6 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -29,7 +29,6 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "m88rs2000.h" -#include "ts2020.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -79,6 +78,10 @@ #define USB_PID_TEVII_S632 0xd632 #endif +#ifndef USB_PID_GOTVIEW_SAT_HD +#define USB_PID_GOTVIEW_SAT_HD 0x5456 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -1548,6 +1551,8 @@ enum dw2102_table_entry { X3M_SPC1400HD, TEVII_S421, TEVII_S632, + TERRATEC_CINERGY_S2_R2, + GOTVIEW_SAT_HD, }; static struct usb_device_id dw2102_table[] = { @@ -1568,6 +1573,8 @@ static struct usb_device_id dw2102_table[] = { [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)}, [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)}, + [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00b0)}, + [GOTVIEW_SAT_HD] = {USB_DEVICE(0x1FE1, USB_PID_GOTVIEW_SAT_HD)}, { } }; @@ -1968,7 +1975,7 @@ static struct dvb_usb_device_properties su3000_properties = { }}, } }, - .num_device_descs = 3, + .num_device_descs = 5, .devices = { { "SU3000HD DVB-S USB2.0", { &dw2102_table[GENIATECH_SU3000], NULL }, @@ -1982,6 +1989,14 @@ static struct dvb_usb_device_properties su3000_properties = { { &dw2102_table[X3M_SPC1400HD], NULL }, { NULL }, }, + { "Terratec Cinergy S2 USB HD Rev.2", + { &dw2102_table[TERRATEC_CINERGY_S2_R2], NULL }, + { NULL }, + }, + { "GOTVIEW Satellite HD", + { &dw2102_table[GOTVIEW_SAT_HD], NULL }, + { NULL }, + }, } }; diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 92afeb2..c2b635d 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -68,20 +68,20 @@ static inline int m920x_write_seq(struct usb_device *udev, u8 request, struct m920x_inits *seq) { int ret; - while (seq->address) { + do { ret = m920x_write(udev, request, seq->data, seq->address); if (ret != 0) return ret; seq++; - } + } while (seq->address); - return ret; + return 0; } static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) { - int ret = 0, i, epi, flags = 0; + int ret, i, epi, flags = 0; int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ @@ -124,7 +124,7 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) } } - return ret; + return 0; } static int m920x_init_ep(struct usb_interface *intf) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index c754a80..ca5ee6a 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -46,6 +46,7 @@ config VIDEO_EM28XX_DVB select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index 634fb92..ad6d485 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -1,5 +1,5 @@ em28xx-y += em28xx-video.o em28xx-i2c.o em28xx-cards.o -em28xx-y += em28xx-core.o em28xx-vbi.o +em28xx-y += em28xx-core.o em28xx-vbi.o em28xx-camera.o em28xx-alsa-objs := em28xx-audio.o em28xx-rc-objs := em28xx-input.o diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c new file mode 100644 index 0000000..73cc50a --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -0,0 +1,434 @@ +/* + em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices + + Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org> + Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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 <linux/i2c.h> +#include <media/soc_camera.h> +#include <media/mt9v011.h> +#include <media/v4l2-common.h> + +#include "em28xx.h" + + +/* Possible i2c addresses of Micron sensors */ +static unsigned short micron_sensor_addrs[] = { + 0xb8 >> 1, /* MT9V111, MT9V403 */ + 0xba >> 1, /* MT9M001/011/111/112, MT9V011/012/112, MT9D011 */ + 0x90 >> 1, /* MT9V012/112, MT9D011 (alternative address) */ + I2C_CLIENT_END +}; + +/* Possible i2c addresses of Omnivision sensors */ +static unsigned short omnivision_sensor_addrs[] = { + 0x42 >> 1, /* OV7725, OV7670/60/48 */ + 0x60 >> 1, /* OV2640, OV9650/53/55 */ + I2C_CLIENT_END +}; + + +static struct soc_camera_link camlink = { + .bus_id = 0, + .flags = 0, + .module_name = "em28xx", +}; + + +/* FIXME: Should be replaced by a proper mt9m111 driver */ +static int em28xx_initialize_mt9m111(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ + { 0x0d, 0x00, 0x00, }, + { 0x0a, 0x00, 0x21, }, + { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + ®s[i][0], 3); + + return 0; +} + + +/* FIXME: Should be replaced by a proper mt9m001 driver */ +static int em28xx_initialize_mt9m001(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, + { 0x0d, 0x00, 0x00, }, + { 0x04, 0x05, 0x00, }, /* hres = 1280 */ + { 0x03, 0x04, 0x00, }, /* vres = 1024 */ + { 0x20, 0x11, 0x00, }, + { 0x06, 0x00, 0x10, }, + { 0x2b, 0x00, 0x24, }, + { 0x2e, 0x00, 0x24, }, + { 0x35, 0x00, 0x24, }, + { 0x2d, 0x00, 0x20, }, + { 0x2c, 0x00, 0x20, }, + { 0x09, 0x0a, 0xd4, }, + { 0x35, 0x00, 0x57, }, + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + ®s[i][0], 3); + + return 0; +} + + +/* + * Probes Micron sensors with 8 bit address and 16 bit register width + */ +static int em28xx_probe_sensor_micron(struct em28xx *dev) +{ + int ret, i; + char *name; + u8 reg; + __be16 id_be; + u16 id; + + struct i2c_client client = dev->i2c_client[dev->def_i2c_bus]; + + dev->em28xx_sensor = EM28XX_NOSENSOR; + for (i = 0; micron_sensor_addrs[i] != I2C_CLIENT_END; i++) { + client.addr = micron_sensor_addrs[i]; + /* NOTE: i2c_smbus_read_word_data() doesn't work with BE data */ + /* Read chip ID from register 0x00 */ + reg = 0x00; + ret = i2c_master_send(&client, ®, 1); + if (ret < 0) { + if (ret != -ENODEV) + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + ret = i2c_master_recv(&client, (u8 *)&id_be, 2); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + id = be16_to_cpu(id_be); + /* Read chip ID from register 0xff */ + reg = 0xff; + ret = i2c_master_send(&client, ®, 1); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + ret = i2c_master_recv(&client, (u8 *)&id_be, 2); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + /* Validate chip ID to be sure we have a Micron device */ + if (id != be16_to_cpu(id_be)) + continue; + /* Check chip ID */ + id = be16_to_cpu(id_be); + switch (id) { + case 0x1222: + name = "MT9V012"; /* MI370 */ /* 640x480 */ + break; + case 0x1229: + name = "MT9V112"; /* 640x480 */ + break; + case 0x1433: + name = "MT9M011"; /* 1280x1024 */ + break; + case 0x143a: /* found in the ECS G200 */ + name = "MT9M111"; /* MI1310 */ /* 1280x1024 */ + dev->em28xx_sensor = EM28XX_MT9M111; + break; + case 0x148c: + name = "MT9M112"; /* MI1320 */ /* 1280x1024 */ + break; + case 0x1511: + name = "MT9D011"; /* MI2010 */ /* 1600x1200 */ + break; + case 0x8232: + case 0x8243: /* rev B */ + name = "MT9V011"; /* MI360 */ /* 640x480 */ + dev->em28xx_sensor = EM28XX_MT9V011; + break; + case 0x8431: + name = "MT9M001"; /* 1280x1024 */ + dev->em28xx_sensor = EM28XX_MT9M001; + break; + default: + em28xx_info("unknown Micron sensor detected: 0x%04x\n", + id); + return 0; + } + + if (dev->em28xx_sensor == EM28XX_NOSENSOR) + em28xx_info("unsupported sensor detected: %s\n", name); + else + em28xx_info("sensor %s detected\n", name); + + dev->i2c_client[dev->def_i2c_bus].addr = client.addr; + return 0; + } + + return -ENODEV; +} + +/* + * Probes Omnivision sensors with 8 bit address and register width + */ +static int em28xx_probe_sensor_omnivision(struct em28xx *dev) +{ + int ret, i; + char *name; + u8 reg; + u16 id; + struct i2c_client client = dev->i2c_client[dev->def_i2c_bus]; + + dev->em28xx_sensor = EM28XX_NOSENSOR; + /* NOTE: these devices have the register auto incrementation disabled + * by default, so we have to use single byte reads ! */ + for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) { + client.addr = omnivision_sensor_addrs[i]; + /* Read manufacturer ID from registers 0x1c-0x1d (BE) */ + reg = 0x1c; + ret = i2c_smbus_read_byte_data(&client, reg); + if (ret < 0) { + if (ret != -ENODEV) + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + id = ret << 8; + reg = 0x1d; + ret = i2c_smbus_read_byte_data(&client, reg); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + id += ret; + /* Check manufacturer ID */ + if (id != 0x7fa2) + continue; + /* Read product ID from registers 0x0a-0x0b (BE) */ + reg = 0x0a; + ret = i2c_smbus_read_byte_data(&client, reg); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + id = ret << 8; + reg = 0x0b; + ret = i2c_smbus_read_byte_data(&client, reg); + if (ret < 0) { + em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n", + client.addr << 1, ret); + continue; + } + id += ret; + /* Check product ID */ + switch (id) { + case 0x2642: + name = "OV2640"; + dev->em28xx_sensor = EM28XX_OV2640; + break; + case 0x7648: + name = "OV7648"; + break; + case 0x7660: + name = "OV7660"; + break; + case 0x7673: + name = "OV7670"; + break; + case 0x7720: + name = "OV7720"; + break; + case 0x7721: + name = "OV7725"; + break; + case 0x9648: /* Rev 2 */ + case 0x9649: /* Rev 3 */ + name = "OV9640"; + break; + case 0x9650: + case 0x9652: /* OV9653 */ + name = "OV9650"; + break; + case 0x9656: /* Rev 4 */ + case 0x9657: /* Rev 5 */ + name = "OV9655"; + break; + default: + em28xx_info("unknown OmniVision sensor detected: 0x%04x\n", + id); + return 0; + } + + if (dev->em28xx_sensor == EM28XX_NOSENSOR) + em28xx_info("unsupported sensor detected: %s\n", name); + else + em28xx_info("sensor %s detected\n", name); + + dev->i2c_client[dev->def_i2c_bus].addr = client.addr; + return 0; + } + + return -ENODEV; +} + +int em28xx_detect_sensor(struct em28xx *dev) +{ + int ret; + + ret = em28xx_probe_sensor_micron(dev); + + if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) + ret = em28xx_probe_sensor_omnivision(dev); + + /* + * NOTE: the Windows driver also probes i2c addresses + * 0x22 (Samsung ?) and 0x66 (Kodak ?) + */ + + if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) { + em28xx_info("No sensor detected\n"); + return -ENODEV; + } + + return 0; +} + +int em28xx_init_camera(struct em28xx *dev) +{ + switch (dev->em28xx_sensor) { + case EM28XX_MT9V011: + { + struct mt9v011_platform_data pdata; + struct i2c_board_info mt9v011_info = { + .type = "mt9v011", + .addr = dev->i2c_client[dev->def_i2c_bus].addr, + .platform_data = &pdata, + }; + + dev->sensor_xres = 640; + dev->sensor_yres = 480; + + /* + * FIXME: mt9v011 uses I2S speed as xtal clk - at least with + * the Silvercrest cam I have here for testing - for higher + * resolutions, a high clock cause horizontal artifacts, so we + * need to use a lower xclk frequency. + * Yet, it would be possible to adjust xclk depending on the + * desired resolution, since this affects directly the + * frame rate. + */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); + dev->sensor_xtal = 4300000; + pdata.xtal = dev->sensor_xtal; + if (NULL == + v4l2_i2c_new_subdev_board(&dev->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + &mt9v011_info, NULL)) + return -ENODEV; + /* probably means GRGB 16 bit bayer */ + dev->vinmode = 0x0d; + dev->vinctl = 0x00; + + break; + } + case EM28XX_MT9M001: + dev->sensor_xres = 1280; + dev->sensor_yres = 1024; + + em28xx_initialize_mt9m001(dev); + + /* probably means BGGR 16 bit bayer */ + dev->vinmode = 0x0c; + dev->vinctl = 0x00; + + break; + case EM28XX_MT9M111: + dev->sensor_xres = 640; + dev->sensor_yres = 512; + + dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); + em28xx_initialize_mt9m111(dev); + + dev->vinmode = 0x0a; + dev->vinctl = 0x00; + + break; + case EM28XX_OV2640: + { + struct v4l2_subdev *subdev; + struct i2c_board_info ov2640_info = { + .type = "ov2640", + .flags = I2C_CLIENT_SCCB, + .addr = dev->i2c_client[dev->def_i2c_bus].addr, + .platform_data = &camlink, + }; + struct v4l2_mbus_framefmt fmt; + + /* + * FIXME: sensor supports resolutions up to 1600x1200, but + * resolution setting/switching needs to be modified to + * - switch sensor output resolution (including further + * configuration changes) + * - adjust bridge xclk + * - disable 16 bit (12 bit) output formats on high resolutions + */ + dev->sensor_xres = 640; + dev->sensor_yres = 480; + + subdev = + v4l2_i2c_new_subdev_board(&dev->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + &ov2640_info, NULL); + + fmt.code = V4L2_MBUS_FMT_YUYV8_2X8; + fmt.width = 640; + fmt.height = 480; + v4l2_subdev_call(subdev, video, s_mbus_fmt, &fmt); + + /* NOTE: for UXGA=1600x1200 switch to 12MHz */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ; + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); + dev->vinmode = 0x08; + dev->vinctl = 0x00; + + break; + } + case EM28XX_NOSENSOR: + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 54a03b20..83bfbe4 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -34,7 +34,6 @@ #include <media/saa7115.h> #include <media/tvp5150.h> #include <media/tvaudio.h> -#include <media/mt9v011.h> #include <media/i2c-addr.h> #include <media/tveeprom.h> #include <media/v4l2-common.h> @@ -345,6 +344,18 @@ static struct em28xx_reg_seq pctv_460e[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq c3tech_digital_duo_digital[] = { + {EM2874_R80_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xfd, 0xff, 10}, /* xc5000 reset */ + {EM2874_R80_GPIO, 0xf9, 0xff, 35}, + {EM2874_R80_GPIO, 0xfd, 0xff, 10}, + {EM2874_R80_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xfe, 0xff, 10}, + {EM2874_R80_GPIO, 0xbe, 0xff, 10}, + {EM2874_R80_GPIO, 0xfe, 0xff, 20}, + { -1, -1, -1, -1}, +}; + #if 0 static struct em28xx_reg_seq hauppauge_930c_gpio[] = { {EM2874_R80_GPIO, 0x6f, 0xff, 10}, @@ -958,8 +969,8 @@ struct em28xx_board em28xx_boards[] = { #else .tuner_type = TUNER_ABSENT, #endif - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = { @@ -974,17 +985,27 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, #endif .ir_codes = RC_MAP_HAUPPAUGE, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, + [EM2884_BOARD_C3TECH_DIGITAL_DUO] = { + .name = "C3 Tech Digital Duo HDTV/SDTV USB", + .has_dvb = 1, + /* FIXME: Add analog support - need a saa7136 driver */ + .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ + .ir_codes = RC_MAP_EMPTY, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, + .dvb_gpio = c3tech_digital_duo_digital, + }, [EM2884_BOARD_CINERGY_HTC_STICK] = { .name = "Terratec Cinergy HTC Stick", .has_dvb = 1, .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, .tuner_type = TUNER_ABSENT, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { @@ -1404,8 +1425,8 @@ struct em28xx_board em28xx_boards[] = { }, [EM2874_BOARD_LEADERSHIP_ISDBT] = { - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, .xclk = EM28XX_XCLK_FREQUENCY_10MHZ, .name = "EM2874 Leadership ISDBT", @@ -1917,8 +1938,8 @@ struct em28xx_board em28xx_boards[] = { * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */ [EM28174_BOARD_PCTV_290E] = { .name = "PCTV nanoStick T2 290e", - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_290e, .has_dvb = 1, @@ -1927,8 +1948,8 @@ struct em28xx_board em28xx_boards[] = { /* 2013:024f PCTV DVB-S2 Stick 460e * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */ [EM28174_BOARD_PCTV_460E] = { - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, .name = "PCTV DVB-S2 Stick (460e)", .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_460e, @@ -1958,8 +1979,9 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .tuner_gpio = maxmedia_ub425_tc, .has_dvb = 1, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .ir_codes = RC_MAP_REDDO, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, /* 2304:0242 PCTV QuatroStick (510e) @@ -1970,8 +1992,8 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = pctv_510e, .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, /* 2013:0251 PCTV QuatroStick nano (520e) @@ -1982,8 +2004,8 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = pctv_520e, .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, [EM2884_BOARD_TERRATEC_HTC_USB_XS] = { @@ -1991,8 +2013,8 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, .tuner_type = TUNER_ABSENT, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, }; @@ -2144,6 +2166,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_PCTV_460E }, { USB_DEVICE(0x2040, 0x1605), .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C }, + { USB_DEVICE(0x1b80, 0xe755), + .driver_info = EM2884_BOARD_C3TECH_DIGITAL_DUO }, { USB_DEVICE(0xeb1a, 0x5006), .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ @@ -2183,6 +2207,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, }; +/* NOTE: introduce a separate hash table for devices with 16 bit eeproms */ /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ static unsigned short saa711x_addrs[] = { @@ -2204,8 +2229,9 @@ static unsigned short msp3400_addrs[] = { int em28xx_tuner_callback(void *ptr, int component, int command, int arg) { + struct em28xx_i2c_bus *i2c_bus = ptr; + struct em28xx *dev = i2c_bus->dev; int rc = 0; - struct em28xx *dev = ptr; if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000) return 0; @@ -2233,145 +2259,9 @@ static inline void em28xx_set_model(struct em28xx *dev) if (!dev->board.i2c_speed) dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ; -} - - -/* FIXME: Should be replaced by a proper mt9m111 driver */ -static int em28xx_initialize_mt9m111(struct em28xx *dev) -{ - int i; - unsigned char regs[][3] = { - { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ - { 0x0d, 0x00, 0x00, }, - { 0x0a, 0x00, 0x21, }, - { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ - }; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, ®s[i][0], 3); - - return 0; -} - - -/* FIXME: Should be replaced by a proper mt9m001 driver */ -static int em28xx_initialize_mt9m001(struct em28xx *dev) -{ - int i; - unsigned char regs[][3] = { - { 0x0d, 0x00, 0x01, }, - { 0x0d, 0x00, 0x00, }, - { 0x04, 0x05, 0x00, }, /* hres = 1280 */ - { 0x03, 0x04, 0x00, }, /* vres = 1024 */ - { 0x20, 0x11, 0x00, }, - { 0x06, 0x00, 0x10, }, - { 0x2b, 0x00, 0x24, }, - { 0x2e, 0x00, 0x24, }, - { 0x35, 0x00, 0x24, }, - { 0x2d, 0x00, 0x20, }, - { 0x2c, 0x00, 0x20, }, - { 0x09, 0x0a, 0xd4, }, - { 0x35, 0x00, 0x57, }, - }; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, ®s[i][0], 3); - - return 0; -} - -/* HINT method: webcam I2C chips - * - * This method works for webcams with Micron sensors - */ -static int em28xx_hint_sensor(struct em28xx *dev) -{ - int rc; - char *sensor_name; - unsigned char cmd; - __be16 version_be; - u16 version; - - /* Micron sensor detection */ - dev->i2c_client.addr = 0xba >> 1; - cmd = 0; - i2c_master_send(&dev->i2c_client, &cmd, 1); - rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2); - if (rc != 2) - return -EINVAL; - - version = be16_to_cpu(version_be); - switch (version) { - case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ - case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ - dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; - em28xx_set_model(dev); - - sensor_name = "mt9v011"; - dev->em28xx_sensor = EM28XX_MT9V011; - dev->sensor_xres = 640; - dev->sensor_yres = 480; - /* - * FIXME: mt9v011 uses I2S speed as xtal clk - at least with - * the Silvercrest cam I have here for testing - for higher - * resolutions, a high clock cause horizontal artifacts, so we - * need to use a lower xclk frequency. - * Yet, it would be possible to adjust xclk depending on the - * desired resolution, since this affects directly the - * frame rate. - */ - dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; - dev->sensor_xtal = 4300000; - - /* probably means GRGB 16 bit bayer */ - dev->vinmode = 0x0d; - dev->vinctl = 0x00; - - break; - - case 0x143a: /* MT9M111 as found in the ECS G200 */ - dev->model = EM2750_BOARD_UNKNOWN; - em28xx_set_model(dev); - - sensor_name = "mt9m111"; - dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; - dev->em28xx_sensor = EM28XX_MT9M111; - em28xx_initialize_mt9m111(dev); - dev->sensor_xres = 640; - dev->sensor_yres = 512; - - dev->vinmode = 0x0a; - dev->vinctl = 0x00; - - break; - - case 0x8431: - dev->model = EM2750_BOARD_UNKNOWN; - em28xx_set_model(dev); - - sensor_name = "mt9m001"; - dev->em28xx_sensor = EM28XX_MT9M001; - em28xx_initialize_mt9m001(dev); - dev->sensor_xres = 1280; - dev->sensor_yres = 1024; - - /* probably means BGGR 16 bit bayer */ - dev->vinmode = 0x0c; - dev->vinctl = 0x00; - - break; - default: - printk("Unknown Micron Sensor 0x%04x\n", version); - return -EINVAL; - } - - /* Setup webcam defaults */ - em28xx_pre_card_setup(dev); - em28xx_errdev("Sensor is %s, using model %s entry.\n", - sensor_name, em28xx_boards[dev->model].name); - - return 0; + /* Should be initialized early, for I2C to work */ + dev->def_i2c_bus = dev->board.def_i2c_bus; } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -2599,6 +2489,18 @@ static int em28xx_hint_board(struct em28xx *dev) { int i; + if (dev->board.is_webcam) { + if (dev->em28xx_sensor == EM28XX_MT9V011) { + dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; + } else if (dev->em28xx_sensor == EM28XX_MT9M001 || + dev->em28xx_sensor == EM28XX_MT9M111) { + dev->model = EM2750_BOARD_UNKNOWN; + } + /* FIXME: IMPROVE ! */ + + return 0; + } + /* HINT method: EEPROM * * This method works only for boards with eeprom. @@ -2638,7 +2540,7 @@ static int em28xx_hint_board(struct em28xx *dev) /* user did not request i2c scanning => do it now */ if (!dev->i2c_hash) - em28xx_do_i2c_scan(dev); + em28xx_do_i2c_scan(dev, dev->def_i2c_bus); for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) { if (dev->i2c_hash == em28xx_i2c_hash[i].hash) { @@ -2684,16 +2586,16 @@ static void em28xx_card_setup(struct em28xx *dev) * If sensor is not found, then it isn't a webcam. */ if (dev->board.is_webcam) { - if (em28xx_hint_sensor(dev) < 0) + if (em28xx_detect_sensor(dev) < 0) dev->board.is_webcam = 0; else dev->progressive = 1; } - if (!dev->board.is_webcam) { - switch (dev->model) { - case EM2820_BOARD_UNKNOWN: - case EM2800_BOARD_UNKNOWN: + switch (dev->model) { + case EM2750_BOARD_UNKNOWN: + case EM2820_BOARD_UNKNOWN: + case EM2800_BOARD_UNKNOWN: /* * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. * @@ -2714,9 +2616,8 @@ static void em28xx_card_setup(struct em28xx *dev) em28xx_pre_card_setup(dev); } break; - default: - em28xx_set_model(dev); - } + default: + em28xx_set_model(dev); } em28xx_info("Identified as %s (card=%d)\n", @@ -2736,15 +2637,19 @@ static void em28xx_card_setup(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: { struct tveeprom tv; + + if (dev->eedata == NULL) + break; #if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); #endif /* Call first TVeeprom */ - dev->i2c_client.addr = 0xa0 >> 1; - tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata); + dev->i2c_client[dev->def_i2c_bus].addr = 0xa0 >> 1; + tveeprom_hauppauge_analog(&dev->i2c_client[dev->def_i2c_bus], &tv, dev->eedata); dev->tuner_type = tv.tuner_type; @@ -2791,7 +2696,7 @@ static void em28xx_card_setup(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; -/* + /* * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR. * * This occurs because they share identical USB vendor and @@ -2826,51 +2731,41 @@ static void em28xx_card_setup(struct em28xx *dev) "addresses)\n\n"); } + /* Free eeprom data memory */ + kfree(dev->eedata); + dev->eedata = NULL; + /* Allow override tuner type by a module parameter */ if (tuner >= 0) dev->tuner_type = tuner; /* request some modules */ if (dev->board.has_msp34xx) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "msp3400", 0, msp3400_addrs); if (dev->board.decoder == EM28XX_SAA711X) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "saa7115_auto", 0, saa711x_addrs); if (dev->board.decoder == EM28XX_TVP5150) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tvp5150", 0, tvp5150_addrs); - if (dev->em28xx_sensor == EM28XX_MT9V011) { - struct mt9v011_platform_data pdata; - struct i2c_board_info mt9v011_info = { - .type = "mt9v011", - .addr = 0xba >> 1, - .platform_data = &pdata, - }; - - pdata.xtal = dev->sensor_xtal; - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap, - &mt9v011_info, NULL); - } - - if (dev->board.adecoder == EM28XX_TVAUDIO) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tvaudio", dev->board.tvaudio_addr, NULL); if (dev->board.tuner_type != TUNER_ABSENT) { int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); if (dev->board.radio.type) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tuner", dev->board.radio_addr, NULL); if (has_demod) v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "tuner", + &dev->i2c_adap[dev->def_i2c_bus], "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (dev->tuner_addr == 0) { enum v4l2_i2c_tuner_type type = @@ -2878,18 +2773,20 @@ static void em28xx_card_setup(struct em28xx *dev) struct v4l2_subdev *sd; sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "tuner", + &dev->i2c_adap[dev->def_i2c_bus], "tuner", 0, v4l2_i2c_tuner_addrs(type)); if (sd) dev->tuner_addr = v4l2_i2c_subdev_addr(sd); } else { - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tuner", dev->tuner_addr, NULL); } } em28xx_tuner_setup(dev); + + em28xx_init_camera(dev); } @@ -2914,7 +2811,8 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); - if ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir) + if (dev->board.has_snapshot_button || + ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir)) request_module("em28xx-rc"); #endif /* CONFIG_MODULES */ } @@ -2941,7 +2839,9 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_release_analog_resources(dev); - em28xx_i2c_unregister(dev); + if (dev->def_i2c_bus) + em28xx_i2c_unregister(dev, 1); + em28xx_i2c_unregister(dev, 0); v4l2_ctrl_handler_free(&dev->ctrl_handler); @@ -3002,8 +2902,23 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, case CHIP_ID_EM2750: chip_name = "em2750"; break; + case CHIP_ID_EM2765: + chip_name = "em2765"; + dev->wait_after_write = 0; + dev->is_em25xx = 1; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2820: chip_name = "em2710/2820"; + if (le16_to_cpu(dev->udev->descriptor.idVendor) + == 0xeb1a) { + __le16 idProd = dev->udev->descriptor.idProduct; + if (le16_to_cpu(idProd) == 0x2710) + chip_name = "em2710"; + else if (le16_to_cpu(idProd) == 0x2820) + chip_name = "em2820"; + } + /* NOTE: the em2820 is used in webcams, too ! */ break; case CHIP_ID_EM2840: chip_name = "em2840"; @@ -3019,11 +2934,13 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, chip_name = "em2874"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; break; case CHIP_ID_EM28174: chip_name = "em28174"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; break; case CHIP_ID_EM2883: chip_name = "em2882/3"; @@ -3033,6 +2950,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, chip_name = "em2884"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; break; default: printk(KERN_INFO DRIVER_NAME @@ -3066,14 +2984,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); - if (dev->chip_id == CHIP_ID_EM2820) { - if (dev->board.is_webcam) - chip_name = "em2710"; - else - chip_name = "em2820"; - snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); - } - if (!dev->board.is_em2800) { /* Resets I2C speed */ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); @@ -3091,17 +3001,37 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return retval; } - v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_handler_init(hdl, 8); dev->v4l2_dev.ctrl_handler = hdl; - /* register i2c bus */ - retval = em28xx_i2c_register(dev); + rt_mutex_init(&dev->i2c_bus_lock); + + /* register i2c bus 0 */ + if (dev->board.is_em2800) + retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM2800); + else + retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM28XX); if (retval < 0) { - em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n", + em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n", __func__, retval); goto unregister_dev; } + /* register i2c bus 1 */ + if (dev->def_i2c_bus) { + if (dev->is_em25xx) + retval = em28xx_i2c_register(dev, 1, + EM28XX_I2C_ALGO_EM25XX_BUS_B); + else + retval = em28xx_i2c_register(dev, 1, + EM28XX_I2C_ALGO_EM28XX); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2c_register bus 1 - error [%d]!\n", + __func__, retval); + goto unregister_dev; + } + } + /* * Default format, used for tvp5150 or saa711x output formats */ @@ -3160,11 +3090,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, msleep(3); } - v4l2_ctrl_handler_setup(&dev->ctrl_handler); - retval = dev->ctrl_handler.error; - if (retval) - goto fail; - retval = em28xx_register_analog_devices(dev); if (retval < 0) { goto fail; @@ -3176,7 +3101,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return 0; fail: - em28xx_i2c_unregister(dev); + if (dev->def_i2c_bus) + em28xx_i2c_unregister(dev, 1); + em28xx_i2c_unregister(dev, 0); v4l2_ctrl_handler_free(&dev->ctrl_handler); unregister_dev: @@ -3292,14 +3219,15 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->analog_ep_bulk = e->bEndpointAddress; } else { - has_dvb = true; if (usb_endpoint_xfer_isoc(e)) { - dev->dvb_ep_isoc = e->bEndpointAddress; if (size > dev->dvb_max_pkt_size_isoc) { + has_dvb = true; /* see NOTE (~) */ + dev->dvb_ep_isoc = e->bEndpointAddress; dev->dvb_max_pkt_size_isoc = size; dev->dvb_alt_isoc = i; } } else { + has_dvb = true; dev->dvb_ep_bulk = e->bEndpointAddress; } } @@ -3326,6 +3254,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, * so far. But there might be devices for which this * logic is not sufficient... */ + /* + * NOTE (~): some manufacturers (e.g. Terratec) disable + * endpoints by setting wMaxPacketSize to 0 bytes for + * all alt settings. So far, we've seen this for + * DVB isoc endpoints only. + */ } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index aaedd11..a802128 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -607,12 +607,12 @@ EXPORT_SYMBOL_GPL(em28xx_audio_setup); int em28xx_colorlevels_set_default(struct em28xx *dev) { - em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */ - em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */ - em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */ - em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00); + em28xx_write_reg(dev, EM28XX_R20_YGAIN, CONTRAST_DEFAULT); + em28xx_write_reg(dev, EM28XX_R21_YOFFSET, BRIGHTNESS_DEFAULT); + em28xx_write_reg(dev, EM28XX_R22_UVGAIN, SATURATION_DEFAULT); + em28xx_write_reg(dev, EM28XX_R23_UOFFSET, BLUE_BALANCE_DEFAULT); + em28xx_write_reg(dev, EM28XX_R24_VOFFSET, RED_BALANCE_DEFAULT); + em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, SHARPNESS_DEFAULT); em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); @@ -681,6 +681,11 @@ int em28xx_vbi_supported(struct em28xx *dev) if (disable_vbi == 1) return 0; + if (dev->board.is_webcam) + return 0; + + /* FIXME: check subdevices for VBI support */ + if (dev->chip_id == CHIP_ID_EM2860 || dev->chip_id == CHIP_ID_EM2883) return 1; @@ -692,12 +697,23 @@ int em28xx_vbi_supported(struct em28xx *dev) int em28xx_set_outfmt(struct em28xx *dev) { int ret; - u8 vinctrl; - - ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, - dev->format->reg | 0x20, 0xff); + u8 fmt, vinctrl; + + fmt = dev->format->reg; + if (!dev->is_em25xx) + fmt |= 0x20; + /* + * NOTE: it's not clear if this is really needed ! + * The datasheets say bit 5 is a reserved bit and devices seem to work + * fine without it. But the Windows driver sets it for em2710/50+em28xx + * devices and we've always been setting it, too. + * + * em2765 (em25xx, em276x/7x/8x) devices do NOT work with this bit set, + * it's likely used for an additional (compressed ?) format there. + */ + ret = em28xx_write_reg(dev, EM28XX_R27_OUTFMT, fmt); if (ret < 0) - return ret; + return ret; ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); if (ret < 0) @@ -751,6 +767,13 @@ static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); + + /* FIXME: function/meaning of these registers ? */ + /* FIXME: align width+height to multiples of 4 ?! */ + if (dev->is_em25xx) { + em28xx_write_reg(dev, 0x34, width >> 4); + em28xx_write_reg(dev, 0x35, height >> 4); + } } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a81ec2e..b22f8fe 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -50,6 +50,7 @@ #include "tda10071.h" #include "a8293.h" #include "qt1010.h" +#include "mb86a20s.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -177,7 +178,8 @@ static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; - struct em28xx *dev = dvb->adapter.priv; + struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; + struct em28xx *dev = i2c_bus->dev; int dvb_max_packet_size, packet_multiplier, dvb_alt; if (dev->dvb_xfer_bulk) { @@ -216,12 +218,11 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { - struct em28xx *dev = dvb->adapter.priv; + struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; + struct em28xx *dev = i2c_bus->dev; em28xx_stop_urbs(dev); - em28xx_set_mode(dev, EM28XX_SUSPEND); - return 0; } @@ -269,7 +270,8 @@ static int em28xx_stop_feed(struct dvb_demux_feed *feed) /* ------------------------------------------------------------------ */ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) { - struct em28xx *dev = fe->dvb->priv; + struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; + struct em28xx *dev = i2c_bus->dev; if (acquire) return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); @@ -465,10 +467,10 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); msleep(10); - dev->i2c_client.addr = 0x82 >> 1; + dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); em28xx_gpio_set(dev, hauppauge_hvr930c_end); msleep(100); @@ -522,10 +524,10 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); msleep(10); - dev->i2c_client.addr = 0x82 >> 1; + dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_h5_end); }; @@ -575,10 +577,10 @@ static void terratec_htc_stick_init(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); msleep(10); - dev->i2c_client.addr = 0x82 >> 1; + dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_htc_stick_end); }; @@ -633,10 +635,10 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); msleep(10); - dev->i2c_client.addr = 0x82 >> 1; + dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_htc_usb_xs_end); }; @@ -662,10 +664,10 @@ static void pctv_520e_init(struct em28xx *dev) {{ 0x01, 0x00, 0x73, 0xaf }, 4}, }; - dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */ + dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; /* 0x41 */ for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); }; static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe) @@ -768,9 +770,25 @@ static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { }; static struct qt1010_config em28xx_qt1010_config = { .i2c_address = 0x62 +}; +static const struct mb86a20s_config c3tech_duo_mb86a20s_config = { + .demod_address = 0x10, + .is_serial = true, +}; + +static struct tda18271_std_map mb86a20s_tda18271_config = { + .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, }; +static struct tda18271_config c3tech_duo_tda18271_config = { + .std_map = &mb86a20s_tda18271_config, + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, +}; + + /* ------------------------------------------------------------------ */ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) @@ -779,7 +797,7 @@ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) struct xc2028_config cfg; memset(&cfg, 0, sizeof(cfg)); - cfg.i2c_adap = &dev->i2c_adap; + cfg.i2c_adap = &dev->i2c_adap[dev->def_i2c_bus]; cfg.i2c_addr = addr; if (!dev->dvb->fe[0]) { @@ -824,7 +842,7 @@ static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, if (dvb->fe[1]) dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; - dvb->adapter.priv = dev; + dvb->adapter.priv = &dev->i2c_bus[dev->def_i2c_bus]; /* register frontend */ result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]); @@ -962,7 +980,7 @@ static int em28xx_dvb_init(struct em28xx *dev) switch (dev->model) { case EM2874_BOARD_LEADERSHIP_ISDBT: dvb->fe[0] = dvb_attach(s921_attach, - &sharp_isdbt, &dev->i2c_adap); + &sharp_isdbt, &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; @@ -976,7 +994,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -985,7 +1003,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_KWORLD_DVB_310U: dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -996,7 +1014,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1009,13 +1027,13 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2882_BOARD_KWORLD_VS_DVBT: dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0] == NULL) { /* This board could have either a zl10353 or a mt352. If the chip id isn't for zl10353, try mt352 */ dvb->fe[0] = dvb_attach(mt352_attach, &terratec_xs_mt352_cfg, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); } if (em28xx_attach_xc3028(0x61, dev) < 0) { @@ -1026,16 +1044,16 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2870_BOARD_KWORLD_355U: dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_no_i2c_gate_dev, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0] != NULL) dvb_attach(qt1010_attach, dvb->fe[0], - &dev->i2c_adap, &em28xx_qt1010_config); + &dev->i2c_adap[dev->def_i2c_bus], &em28xx_qt1010_config); break; case EM2883_BOARD_KWORLD_HYBRID_330U: case EM2882_BOARD_EVGA_INDTUBE: dvb->fe[0] = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1044,10 +1062,10 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2882_BOARD_KWORLD_ATSC_315U: dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0] != NULL) { if (!dvb_attach(simple_tuner_attach, dvb->fe[0], - &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) { + &dev->i2c_adap[dev->def_i2c_bus], 0x61, TUNER_THOMSON_DTT761X)) { result = -EINVAL; goto out_free; } @@ -1056,7 +1074,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, - &dev->i2c_adap, &dev->udev->dev); + &dev->i2c_adap[dev->def_i2c_bus], &dev->udev->dev); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1066,10 +1084,10 @@ static int em28xx_dvb_init(struct em28xx *dev) /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ dvb->fe[0] = dvb_attach(tda10023_attach, &em28xx_tda10023_config, - &dev->i2c_adap, 0x48); + &dev->i2c_adap[dev->def_i2c_bus], 0x48); if (dvb->fe[0]) { if (!dvb_attach(simple_tuner_attach, dvb->fe[0], - &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { + &dev->i2c_adap[dev->def_i2c_bus], 0x60, TUNER_PHILIPS_CU1216L)) { result = -EINVAL; goto out_free; } @@ -1078,10 +1096,10 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2870_BOARD_KWORLD_A340: dvb->fe[0] = dvb_attach(lgdt3305_attach, &em2870_lgdt3304_dev, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0] != NULL) dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, &kworld_a340_config); + &dev->i2c_adap[dev->def_i2c_bus], &kworld_a340_config); break; case EM28174_BOARD_PCTV_290E: /* set default GPIO0 for LNA, used if GPIOLIB is undefined */ @@ -1089,14 +1107,14 @@ static int em28xx_dvb_init(struct em28xx *dev) CXD2820R_GPIO_L; dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, - &dev->i2c_adap, + &dev->i2c_adap[dev->def_i2c_bus], &dvb->lna_gpio); if (dvb->fe[0]) { /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, + &dev->i2c_adap[dev->def_i2c_bus], &em28xx_cxd2820r_tda18271_config)) { dvb_frontend_detach(dvb->fe[0]); @@ -1126,7 +1144,7 @@ static int em28xx_dvb_init(struct em28xx *dev) hauppauge_hvr930c_init(dev); dvb->fe[0] = dvb_attach(drxk_attach, - &hauppauge_930c_drxk, &dev->i2c_adap); + &hauppauge_930c_drxk, &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1144,7 +1162,7 @@ static int em28xx_dvb_init(struct em28xx *dev) if (dvb->fe[0]->ops.i2c_gate_ctrl) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap, + if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], &cfg)) { result = -EINVAL; goto out_free; @@ -1157,7 +1175,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2884_BOARD_TERRATEC_H5: terratec_h5_init(dev); - dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap); + dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1171,7 +1189,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* Attach tda18271 to DVB-C frontend */ if (dvb->fe[0]->ops.i2c_gate_ctrl) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) { + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x60)) { result = -EINVAL; goto out_free; } @@ -1179,20 +1197,29 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); break; + case EM2884_BOARD_C3TECH_DIGITAL_DUO: + dvb->fe[0] = dvb_attach(mb86a20s_attach, + &c3tech_duo_mb86a20s_config, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0] != NULL) + dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap[dev->def_i2c_bus], + &c3tech_duo_tda18271_config); + break; case EM28174_BOARD_PCTV_460E: /* attach demod */ dvb->fe[0] = dvb_attach(tda10071_attach, - &em28xx_tda10071_config, &dev->i2c_adap); + &em28xx_tda10071_config, &dev->i2c_adap[dev->def_i2c_bus]); /* attach SEC */ if (dvb->fe[0]) - dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, + dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], &em28xx_a8293_config); break; case EM2874_BOARD_MAXMEDIA_UB425_TC: /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0]) { /* disable I2C-gate */ @@ -1200,7 +1227,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach tuner */ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], - &dev->i2c_adap, 0x60)) { + &dev->i2c_adap[dev->def_i2c_bus], 0x60)) { dvb_frontend_detach(dvb->fe[0]); result = -EINVAL; goto out_free; @@ -1218,12 +1245,12 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0]) { /* attach tuner */ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, + &dev->i2c_adap[dev->def_i2c_bus], &em28xx_cxd2820r_tda18271_config)) { dvb_frontend_detach(dvb->fe[0]); result = -EINVAL; @@ -1236,7 +1263,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1244,7 +1271,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* Attach the demodulator. */ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, + &dev->i2c_adap[dev->def_i2c_bus], &em28xx_cxd2820r_tda18271_config)) { result = -EINVAL; goto out_free; @@ -1255,7 +1282,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, - &dev->i2c_adap); + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1263,7 +1290,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* Attach the demodulator. */ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, + &dev->i2c_adap[dev->def_i2c_bus], &em28xx_cxd2820r_tda18271_config)) { result = -EINVAL; goto out_free; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 8532c1d..4851cc2 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -5,6 +5,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,14 +42,6 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -#define dprintk2(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__ , ##args); \ - } \ -} while (0) - /* * em2800_i2c_send_bytes() * send up to 4 bytes to the em2800 i2c device @@ -76,8 +69,8 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { - em28xx_warn("failed to trigger write to i2c address 0x%x " - "(error=%i)\n", addr, ret); + em28xx_warn("failed to trigger write to i2c address 0x%x (error=%i)\n", + addr, ret); return (ret < 0) ? ret : -EIO; } /* wait for completion */ @@ -89,8 +82,8 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } else if (ret == 0x94 + len - 1) { return -ENODEV; } else if (ret < 0) { - em28xx_warn("failed to get i2c transfer status from " - "bridge register (error=%i)\n", ret); + em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", + ret); return ret; } msleep(5); @@ -118,8 +111,8 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) buf2[0] = addr; ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2); if (ret != 2) { - em28xx_warn("failed to trigger read from i2c address 0x%x " - "(error=%i)\n", addr, ret); + em28xx_warn("failed to trigger read from i2c address 0x%x (error=%i)\n", + addr, ret); return (ret < 0) ? ret : -EIO; } @@ -132,8 +125,8 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } else if (ret == 0x94 + len - 1) { return -ENODEV; } else if (ret < 0) { - em28xx_warn("failed to get i2c transfer status from " - "bridge register (error=%i)\n", ret); + em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", + ret); return ret; } msleep(5); @@ -144,9 +137,8 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) /* get the received message */ ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); if (ret != len) { - em28xx_warn("reading from i2c device at 0x%x failed: " - "couldn't get the received message from the bridge " - "(error=%i)\n", addr, ret); + em28xx_warn("reading from i2c device at 0x%x failed: couldn't get the received message from the bridge (error=%i)\n", + addr, ret); return (ret < 0) ? ret : -EIO; } for (i = 0; i < len; i++) @@ -180,19 +172,20 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, if (len < 1 || len > 64) return -EOPNOTSUPP; - /* NOTE: limited by the USB ctrl message constraints - * Zero length reads always succeed, even if no device is connected */ + /* + * NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected + */ /* Write to i2c device */ ret = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); if (ret != len) { if (ret < 0) { - em28xx_warn("writing to i2c device at 0x%x failed " - "(error=%i)\n", addr, ret); + em28xx_warn("writing to i2c device at 0x%x failed (error=%i)\n", + addr, ret); return ret; } else { - em28xx_warn("%i bytes write to i2c device at 0x%x " - "requested, but %i bytes written\n", + em28xx_warn("%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", len, addr, ret); return -EIO; } @@ -207,14 +200,16 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, } else if (ret == 0x10) { return -ENODEV; } else if (ret < 0) { - em28xx_warn("failed to read i2c transfer status from " - "bridge (error=%i)\n", ret); + em28xx_warn("failed to read i2c transfer status from bridge (error=%i)\n", + ret); return ret; } msleep(5); - /* NOTE: do we really have to wait for success ? - Never seen anything else than 0x00 or 0x10 - (even with high payload) ... */ + /* + * NOTE: do we really have to wait for success ? + * Never seen anything else than 0x00 or 0x10 + * (even with high payload) ... + */ } em28xx_warn("write to i2c device at 0x%x timed out\n", addr); return -EIO; @@ -230,29 +225,32 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) if (len < 1 || len > 64) return -EOPNOTSUPP; - /* NOTE: limited by the USB ctrl message constraints - * Zero length reads always succeed, even if no device is connected */ + /* + * NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected + */ /* Read data from i2c device */ ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); - if (ret != len) { - if (ret < 0) { - em28xx_warn("reading from i2c device at 0x%x failed " - "(error=%i)\n", addr, ret); - return ret; - } else { - em28xx_warn("%i bytes requested from i2c device at " - "0x%x, but %i bytes received\n", - len, addr, ret); - return -EIO; - } + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed (error=%i)\n", + addr, ret); + return ret; } + /* + * NOTE: some devices with two i2c busses have the bad habit to return 0 + * bytes if we are on bus B AND there was no write attempt to the + * specified slave address before AND no device is present at the + * requested slave address. + * Anyway, the next check will fail with -ENODEV in this case, so avoid + * spamming the system log on device probing and do nothing here. + */ /* Check success of the i2c operation */ ret = dev->em28xx_read_reg(dev, 0x05); if (ret < 0) { - em28xx_warn("failed to read i2c transfer status from " - "bridge (error=%i)\n", ret); + em28xx_warn("failed to read i2c transfer status from bridge (error=%i)\n", + ret); return ret; } if (ret > 0) { @@ -282,77 +280,254 @@ static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) } /* + * em25xx_bus_B_send_bytes + * write bytes to the i2c device + */ +static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len) +{ + int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* + * NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected + */ + + /* Set register and write value */ + ret = dev->em28xx_write_regs_req(dev, 0x06, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("writing to i2c device at 0x%x failed (error=%i)\n", + addr, ret); + return ret; + } else { + em28xx_warn("%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", + len, addr, ret); + return -EIO; + } + } + /* Check success */ + ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000); + /* + * NOTE: the only error we've seen so far is + * 0x01 when the slave device is not present + */ + if (!ret) + return len; + else if (ret > 0) + return -ENODEV; + + return ret; + /* + * NOTE: With chip types (other chip IDs) which actually don't support + * this operation, it seems to succeed ALWAYS ! (even if there is no + * slave device or even no second i2c bus provided) + */ +} + +/* + * em25xx_bus_B_recv_bytes + * read bytes from the i2c device + */ +static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len) +{ + int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + /* + * NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected + */ + + /* Read value */ + ret = dev->em28xx_read_reg_req_len(dev, 0x06, addr, buf, len); + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed (error=%i)\n", + addr, ret); + return ret; + } + /* + * NOTE: some devices with two i2c busses have the bad habit to return 0 + * bytes if we are on bus B AND there was no write attempt to the + * specified slave address before AND no device is present at the + * requested slave address. + * Anyway, the next check will fail with -ENODEV in this case, so avoid + * spamming the system log on device probing and do nothing here. + */ + + /* Check success */ + ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000); + /* + * NOTE: the only error we've seen so far is + * 0x01 when the slave device is not present + */ + if (!ret) + return len; + else if (ret > 0) + return -ENODEV; + + return ret; + /* + * NOTE: With chip types (other chip IDs) which actually don't support + * this operation, it seems to succeed ALWAYS ! (even if there is no + * slave device or even no second i2c bus provided) + */ +} + +/* + * em25xx_bus_B_check_for_device() + * check if there is a i2c device at the supplied address + */ +static int em25xx_bus_B_check_for_device(struct em28xx *dev, u16 addr) +{ + u8 buf; + int ret; + + ret = em25xx_bus_B_recv_bytes(dev, addr, &buf, 1); + if (ret < 0) + return ret; + + return 0; + /* + * NOTE: With chips which do not support this operation, + * it seems to succeed ALWAYS ! (even if no device connected) + */ +} + +static inline int i2c_check_for_device(struct em28xx_i2c_bus *i2c_bus, u16 addr) +{ + struct em28xx *dev = i2c_bus->dev; + int rc = -EOPNOTSUPP; + + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) + rc = em28xx_i2c_check_for_device(dev, addr); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) + rc = em2800_i2c_check_for_device(dev, addr); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) + rc = em25xx_bus_B_check_for_device(dev, addr); + if (rc == -ENODEV) { + if (i2c_debug) + printk(" no device\n"); + } + return rc; +} + +static inline int i2c_recv_bytes(struct em28xx_i2c_bus *i2c_bus, + struct i2c_msg msg) +{ + struct em28xx *dev = i2c_bus->dev; + u16 addr = msg.addr << 1; + int byte, rc = -EOPNOTSUPP; + + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) + rc = em28xx_i2c_recv_bytes(dev, addr, msg.buf, msg.len); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) + rc = em2800_i2c_recv_bytes(dev, addr, msg.buf, msg.len); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) + rc = em25xx_bus_B_recv_bytes(dev, addr, msg.buf, msg.len); + if (i2c_debug) { + for (byte = 0; byte < msg.len; byte++) + printk(" %02x", msg.buf[byte]); + } + return rc; +} + +static inline int i2c_send_bytes(struct em28xx_i2c_bus *i2c_bus, + struct i2c_msg msg, int stop) +{ + struct em28xx *dev = i2c_bus->dev; + u16 addr = msg.addr << 1; + int byte, rc = -EOPNOTSUPP; + + if (i2c_debug) { + for (byte = 0; byte < msg.len; byte++) + printk(" %02x", msg.buf[byte]); + } + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) + rc = em28xx_i2c_send_bytes(dev, addr, msg.buf, msg.len, stop); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) + rc = em2800_i2c_send_bytes(dev, addr, msg.buf, msg.len); + else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) + rc = em25xx_bus_B_send_bytes(dev, addr, msg.buf, msg.len); + return rc; +} + +/* * em28xx_i2c_xfer() * the main i2c transfer function */ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { - struct em28xx *dev = i2c_adap->algo_data; - int addr, rc, i, byte; + struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; + struct em28xx *dev = i2c_bus->dev; + unsigned bus = i2c_bus->bus; + int addr, rc, i; + u8 reg; + + rc = rt_mutex_trylock(&dev->i2c_bus_lock); + if (rc < 0) + return rc; + + /* Switch I2C bus if needed */ + if (bus != dev->cur_i2c_bus && + i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) { + if (bus == 1) + reg = EM2874_I2C_SECONDARY_BUS_SELECT; + else + reg = 0; + em28xx_write_reg_bits(dev, EM28XX_R06_I2C_CLK, reg, + EM2874_I2C_SECONDARY_BUS_SELECT); + dev->cur_i2c_bus = bus; + } - if (num <= 0) + if (num <= 0) { + rt_mutex_unlock(&dev->i2c_bus_lock); return 0; + } for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; - dprintk2(2, "%s %s addr=%x len=%d:", - (msgs[i].flags & I2C_M_RD) ? "read" : "write", - i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (i2c_debug) + printk(KERN_DEBUG "%s at %s: %s %s addr=%02x len=%d:", + dev->name, __func__ , + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", + addr, msgs[i].len); if (!msgs[i].len) { /* no len: check only for device presence */ - if (dev->board.is_em2800) - rc = em2800_i2c_check_for_device(dev, addr); - else - rc = em28xx_i2c_check_for_device(dev, addr); + rc = i2c_check_for_device(i2c_bus, addr); if (rc == -ENODEV) { - if (i2c_debug >= 2) - printk(" no device\n"); + rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - if (dev->board.is_em2800) - rc = em2800_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } + rc = i2c_recv_bytes(i2c_bus, msgs[i]); } else { /* write bytes */ - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - if (dev->board.is_em2800) - rc = em2800_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len, - i == num - 1); + rc = i2c_send_bytes(i2c_bus, msgs[i], i == num - 1); } if (rc < 0) { - if (i2c_debug >= 2) + if (i2c_debug) printk(" ERROR: %i\n", rc); + rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } - if (i2c_debug >= 2) + if (i2c_debug) printk("\n"); } + rt_mutex_unlock(&dev->i2c_bus_lock); return num; } -/* based on linux/sunrpc/svcauth.h and linux/hash.h +/* + * based on linux/sunrpc/svcauth.h and linux/hash.h * The original hash function returns a different value, if arch is x86_64 - * or i386. + * or i386. */ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) { @@ -375,127 +550,230 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) return (hash >> (32 - bits)) & 0xffffffffUL; } -static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) +/* + * Helper function to read data blocks from i2c clients with 8 or 16 bit + * address width, 8 bit register width and auto incrementation been activated + */ +static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr, + bool addr_w16, u16 len, u8 *data) { - unsigned char buf, *p = eedata; - struct em28xx_eeprom *em_eeprom = (void *)eedata; - int i, err, size = len, block, block_max; - - if (dev->chip_id == CHIP_ID_EM2874 || - dev->chip_id == CHIP_ID_EM28174 || - dev->chip_id == CHIP_ID_EM2884) { - /* Empia switched to a 16-bit addressable eeprom in newer - devices. While we could certainly write a routine to read - the eeprom, there is nothing of use in there that cannot be - accessed through registers, and there is the risk that we - could corrupt the eeprom (since a 16-bit read call is - interpreted as a write call by 8-bit eeproms). - */ - return 0; + int remain = len, rsize, rsize_max, ret; + u8 buf[2]; + + /* Sanity check */ + if (addr + remain > (addr_w16 * 0xff00 + 0xff + 1)) + return -EINVAL; + /* Select address */ + buf[0] = addr >> 8; + buf[1] = addr & 0xff; + ret = i2c_master_send(&dev->i2c_client[bus], buf + !addr_w16, 1 + addr_w16); + if (ret < 0) + return ret; + /* Read data */ + if (dev->board.is_em2800) + rsize_max = 4; + else + rsize_max = 64; + while (remain > 0) { + if (remain > rsize_max) + rsize = rsize_max; + else + rsize = remain; + + ret = i2c_master_recv(&dev->i2c_client[bus], data, rsize); + if (ret < 0) + return ret; + + remain -= rsize; + data += rsize; } - dev->i2c_client.addr = 0xa0 >> 1; + return len; +} + +static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, + u8 **eedata, u16 *eedata_len) +{ + const u16 len = 256; + /* + * FIXME common length/size for bytes to read, to display, hash + * calculation and returned device dataset. Simplifies the code a lot, + * but we might have to deal with multiple sizes in the future ! + */ + int i, err; + struct em28xx_eeprom *dev_config; + u8 buf, *data; + + *eedata = NULL; + *eedata_len = 0; + + /* EEPROM is always on i2c bus 0 on all known devices. */ + + dev->i2c_client[bus].addr = 0xa0 >> 1; /* Check if board has eeprom */ - err = i2c_master_recv(&dev->i2c_client, &buf, 0); + err = i2c_master_recv(&dev->i2c_client[bus], &buf, 0); if (err < 0) { - em28xx_errdev("board has no eeprom\n"); - memset(eedata, 0, len); + em28xx_info("board has no eeprom\n"); return -ENODEV; } - buf = 0; - - err = i2c_master_send(&dev->i2c_client, &buf, 1); - if (err != 1) { - printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", - dev->name, err); - return err; + data = kzalloc(len, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + /* Read EEPROM content */ + err = em28xx_i2c_read_block(dev, bus, 0x0000, + dev->eeprom_addrwidth_16bit, + len, data); + if (err != len) { + em28xx_errdev("failed to read eeprom (err=%d)\n", err); + goto error; } - if (dev->board.is_em2800) - block_max = 4; - else - block_max = 64; - - while (size > 0) { - if (size > block_max) - block = block_max; - else - block = size; - - if (block != - (err = i2c_master_recv(&dev->i2c_client, p, block))) { - printk(KERN_WARNING - "%s: i2c eeprom read error (err=%d)\n", - dev->name, err); - return err; - } - size -= block; - p += block; - } + /* Display eeprom content */ for (i = 0; i < len; i++) { - if (0 == (i % 16)) - printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); - printk(" %02x", eedata[i]); + if (0 == (i % 16)) { + if (dev->eeprom_addrwidth_16bit) + em28xx_info("i2c eeprom %04x:", i); + else + em28xx_info("i2c eeprom %02x:", i); + } + printk(" %02x", data[i]); if (15 == (i % 16)) printk("\n"); } + if (dev->eeprom_addrwidth_16bit) + em28xx_info("i2c eeprom %04x: ... (skipped)\n", i); + + if (dev->eeprom_addrwidth_16bit && + data[0] == 0x26 && data[3] == 0x00) { + /* new eeprom format; size 4-64kb */ + u16 mc_start; + u16 hwconf_offset; + + dev->hash = em28xx_hash_mem(data, len, 32); + mc_start = (data[1] << 8) + 4; /* usually 0x0004 */ + + em28xx_info("EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n", + data[0], data[1], data[2], data[3], dev->hash); + em28xx_info("EEPROM info:\n"); + em28xx_info("\tmicrocode start address = 0x%04x, boot configuration = 0x%02x\n", + mc_start, data[2]); + /* + * boot configuration (address 0x0002): + * [0] microcode download speed: 1 = 400 kHz; 0 = 100 kHz + * [1] always selects 12 kb RAM + * [2] USB device speed: 1 = force Full Speed; 0 = auto detect + * [4] 1 = force fast mode and no suspend for device testing + * [5:7] USB PHY tuning registers; determined by device + * characterization + */ + + /* + * Read hardware config dataset offset from address + * (microcode start + 46) + */ + err = em28xx_i2c_read_block(dev, bus, mc_start + 46, 1, 2, + data); + if (err != 2) { + em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n", + err); + goto error; + } - if (em_eeprom->id == 0x9567eb1a) - dev->hash = em28xx_hash_mem(eedata, len, 32); + /* Calculate hardware config dataset start address */ + hwconf_offset = mc_start + data[0] + (data[1] << 8); + + /* Read hardware config dataset */ + /* + * NOTE: the microcode copy can be multiple pages long, but + * we assume the hardware config dataset is the same as in + * the old eeprom and not longer than 256 bytes. + * tveeprom is currently also limited to 256 bytes. + */ + err = em28xx_i2c_read_block(dev, bus, hwconf_offset, 1, len, + data); + if (err != len) { + em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n", + err); + goto error; + } - printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n", - dev->name, em_eeprom->id, dev->hash); + /* Verify hardware config dataset */ + /* NOTE: not all devices provide this type of dataset */ + if (data[0] != 0x1a || data[1] != 0xeb || + data[2] != 0x67 || data[3] != 0x95) { + em28xx_info("\tno hardware configuration dataset found in eeprom\n"); + kfree(data); + return 0; + } - printk(KERN_INFO "%s: EEPROM info:\n", dev->name); + /* TODO: decrypt eeprom data for camera bridges (em25xx, em276x+) */ + + } else if (!dev->eeprom_addrwidth_16bit && + data[0] == 0x1a && data[1] == 0xeb && + data[2] == 0x67 && data[3] == 0x95) { + dev->hash = em28xx_hash_mem(data, len, 32); + em28xx_info("EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n", + data[0], data[1], data[2], data[3], dev->hash); + em28xx_info("EEPROM info:\n"); + } else { + em28xx_info("unknown eeprom format or eeprom corrupted !\n"); + err = -ENODEV; + goto error; + } - switch (em_eeprom->chip_conf >> 4 & 0x3) { + *eedata = data; + *eedata_len = len; + dev_config = (void *)eedata; + + switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) { case 0: - printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name); + em28xx_info("\tNo audio on board.\n"); break; case 1: - printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n", - dev->name); + em28xx_info("\tAC97 audio (5 sample rates)\n"); break; case 2: - printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", - dev->name); + em28xx_info("\tI2S audio, sample rate=32k\n"); break; case 3: - printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", - dev->name); + em28xx_info("\tI2S audio, 3 sample rates\n"); break; } - if (em_eeprom->chip_conf & 1 << 3) - printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name); + if (le16_to_cpu(dev_config->chip_conf) & 1 << 3) + em28xx_info("\tUSB Remote wakeup capable\n"); - if (em_eeprom->chip_conf & 1 << 2) - printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name); + if (le16_to_cpu(dev_config->chip_conf) & 1 << 2) + em28xx_info("\tUSB Self power capable\n"); - switch (em_eeprom->chip_conf & 0x3) { + switch (le16_to_cpu(dev_config->chip_conf) & 0x3) { case 0: - printk(KERN_INFO "%s:\t500mA max power\n", dev->name); + em28xx_info("\t500mA max power\n"); break; case 1: - printk(KERN_INFO "%s:\t400mA max power\n", dev->name); + em28xx_info("\t400mA max power\n"); break; case 2: - printk(KERN_INFO "%s:\t300mA max power\n", dev->name); + em28xx_info("\t300mA max power\n"); break; case 3: - printk(KERN_INFO "%s:\t200mA max power\n", dev->name); + em28xx_info("\t200mA max power\n"); break; } - printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", - dev->name, - em_eeprom->string_idx_table, - em_eeprom->string1, - em_eeprom->string2, - em_eeprom->string3); + em28xx_info("\tTable at offset 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + dev_config->string_idx_table, + le16_to_cpu(dev_config->string1), + le16_to_cpu(dev_config->string2), + le16_to_cpu(dev_config->string3)); return 0; + +error: + kfree(data); + return err; } /* ----------------------------------------------------------- */ @@ -503,13 +781,20 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) /* * functionality() */ -static u32 functionality(struct i2c_adapter *adap) +static u32 functionality(struct i2c_adapter *i2c_adap) { - struct em28xx *dev = adap->algo_data; - u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; - if (dev->board.is_em2800) - func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; - return func_flags; + struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; + + if ((i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) || + (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B)) { + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) & + ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; + } + + WARN(1, "Unknown i2c bus algorithm.\n"); + return 0; } static struct i2c_algorithm em28xx_algo = { @@ -556,7 +841,7 @@ static char *i2c_devs[128] = { * do_i2c_scan() * check i2c address range for devices */ -void em28xx_do_i2c_scan(struct em28xx *dev) +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus) { u8 i2c_devicelist[128]; unsigned char buf; @@ -565,55 +850,68 @@ void em28xx_do_i2c_scan(struct em28xx *dev) memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist)); for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { - dev->i2c_client.addr = i; - rc = i2c_master_recv(&dev->i2c_client, &buf, 0); + dev->i2c_client[bus].addr = i; + rc = i2c_master_recv(&dev->i2c_client[bus], &buf, 0); if (rc < 0) continue; i2c_devicelist[i] = i; - printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", - dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + em28xx_info("found i2c device @ 0x%x on bus %d [%s]\n", + i << 1, bus, i2c_devs[i] ? i2c_devs[i] : "???"); } - dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, - ARRAY_SIZE(i2c_devicelist), 32); + if (bus == dev->def_i2c_bus) + dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, + ARRAY_SIZE(i2c_devicelist), 32); } /* * em28xx_i2c_register() * register i2c bus */ -int em28xx_i2c_register(struct em28xx *dev) +int em28xx_i2c_register(struct em28xx *dev, unsigned bus, + enum em28xx_i2c_algo_type algo_type) { int retval; BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); - dev->i2c_adap = em28xx_adap_template; - dev->i2c_adap.dev.parent = &dev->udev->dev; - strcpy(dev->i2c_adap.name, dev->name); - dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - retval = i2c_add_adapter(&dev->i2c_adap); + if (bus >= NUM_I2C_BUSES) + return -ENODEV; + + dev->i2c_adap[bus] = em28xx_adap_template; + dev->i2c_adap[bus].dev.parent = &dev->udev->dev; + strcpy(dev->i2c_adap[bus].name, dev->name); + + dev->i2c_bus[bus].bus = bus; + dev->i2c_bus[bus].algo_type = algo_type; + dev->i2c_bus[bus].dev = dev; + dev->i2c_adap[bus].algo_data = &dev->i2c_bus[bus]; + i2c_set_adapdata(&dev->i2c_adap[bus], &dev->v4l2_dev); + + retval = i2c_add_adapter(&dev->i2c_adap[bus]); if (retval < 0) { em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", __func__, retval); return retval; } - dev->i2c_client = em28xx_client_template; - dev->i2c_client.adapter = &dev->i2c_adap; + dev->i2c_client[bus] = em28xx_client_template; + dev->i2c_client[bus].adapter = &dev->i2c_adap[bus]; - retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); - if ((retval < 0) && (retval != -ENODEV)) { - em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", - __func__, retval); + /* Up to now, all eeproms are at bus 0 */ + if (!bus) { + retval = em28xx_i2c_eeprom(dev, bus, &dev->eedata, &dev->eedata_len); + if ((retval < 0) && (retval != -ENODEV)) { + em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", + __func__, retval); - return retval; + return retval; + } } if (i2c_scan) - em28xx_do_i2c_scan(dev); + em28xx_do_i2c_scan(dev, bus); return 0; } @@ -622,8 +920,11 @@ int em28xx_i2c_register(struct em28xx *dev) * em28xx_i2c_unregister() * unregister i2c_bus */ -int em28xx_i2c_unregister(struct em28xx *dev) +int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus) { - i2c_del_adapter(&dev->i2c_adap); + if (bus >= NUM_I2C_BUSES) + return -ENODEV; + + i2c_del_adapter(&dev->i2c_adap[bus]); return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 1bef990..466b19d 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -280,11 +280,12 @@ 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 ir_key; int rc; struct i2c_client client; - client.adapter = &ir->dev->i2c_adap; + client.adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; client.addr = ir->i2c_dev_addr; rc = ir->get_key_i2c(&client, &ir_key); @@ -461,7 +462,7 @@ static int em28xx_probe_i2c_ir(struct em28xx *dev) }; while (addr_list[i] != I2C_CLIENT_END) { - if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) + if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus], addr_list[i]) == 1) return addr_list[i]; i++; } diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 885089e..622871d 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -48,7 +48,7 @@ #define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03 - /* GPIO/GPO registers */ +/* GPIO/GPO registers */ #define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ #define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ @@ -120,12 +120,23 @@ #define EM28XX_R1E_CWIDTH 0x1e #define EM28XX_R1F_CHEIGHT 0x1f -#define EM28XX_R20_YGAIN 0x20 -#define EM28XX_R21_YOFFSET 0x21 -#define EM28XX_R22_UVGAIN 0x22 -#define EM28XX_R23_UOFFSET 0x23 -#define EM28XX_R24_VOFFSET 0x24 -#define EM28XX_R25_SHARPNESS 0x25 +#define EM28XX_R20_YGAIN 0x20 /* contrast [0:4] */ +#define CONTRAST_DEFAULT 0x10 + +#define EM28XX_R21_YOFFSET 0x21 /* brightness */ /* signed */ +#define BRIGHTNESS_DEFAULT 0x00 + +#define EM28XX_R22_UVGAIN 0x22 /* saturation [0:4] */ +#define SATURATION_DEFAULT 0x10 + +#define EM28XX_R23_UOFFSET 0x23 /* blue balance */ /* signed */ +#define BLUE_BALANCE_DEFAULT 0x00 + +#define EM28XX_R24_VOFFSET 0x24 /* red balance */ /* signed */ +#define RED_BALANCE_DEFAULT 0x00 + +#define EM28XX_R25_SHARPNESS 0x25 /* sharpness [0:4] */ +#define SHARPNESS_DEFAULT 0x00 #define EM28XX_R26_COMPR 0x26 #define EM28XX_R27_OUTFMT 0x27 @@ -152,8 +163,17 @@ #define EM28XX_R31_HSCALEHIGH 0x31 #define EM28XX_R32_VSCALELOW 0x32 #define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_HVSCALE_MAX 0x3fff /* => 20% */ + #define EM28XX_R34_VBI_START_H 0x34 #define EM28XX_R35_VBI_START_V 0x35 +/* + * NOTE: the EM276x (and EM25xx, EM277x/8x ?) (camera bridges) use these + * registers for a different unknown purpose. + * => register 0x34 is set to capture width / 16 + * => register 0x35 is set to capture height / 16 + */ + #define EM28XX_R36_VBI_WIDTH 0x36 #define EM28XX_R37_VBI_HEIGHT 0x37 @@ -206,6 +226,7 @@ enum em28xx_chip_id { CHIP_ID_EM2860 = 34, CHIP_ID_EM2870 = 35, CHIP_ID_EM2883 = 36, + CHIP_ID_EM2765 = 54, CHIP_ID_EM2874 = 65, CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 32bd7de..32d60e5 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -52,7 +52,7 @@ #define DRIVER_DESC "Empia em28xx based USB video device driver" -#define EM28XX_VERSION "0.1.3" +#define EM28XX_VERSION "0.2.0" #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ @@ -76,6 +76,16 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); + +#define EM25XX_FRMDATAHDR_BYTE1 0x02 +#define EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE 0x20 +#define EM25XX_FRMDATAHDR_BYTE2_FRAME_END 0x02 +#define EM25XX_FRMDATAHDR_BYTE2_FRAME_ID 0x01 +#define EM25XX_FRMDATAHDR_BYTE2_MASK (EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE | \ + EM25XX_FRMDATAHDR_BYTE2_FRAME_END | \ + EM25XX_FRMDATAHDR_BYTE2_FRAME_ID) + + static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; @@ -408,6 +418,62 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, em28xx_copy_video(dev, buf, data_pkt, data_len); } +/* + * Process data packet according to the em25xx/em276x/7x/8x frame data format + */ +static inline void process_frame_data_em25xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) +{ + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_dmaqueue *dmaq = &dev->vidq; + bool frame_end = 0; + + /* Check for header */ + /* NOTE: at least with bulk transfers, only the first packet + * has a header and has always set the FRAME_END bit */ + if (data_len >= 2) { /* em25xx header is only 2 bytes long */ + if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) && + ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) { + dev->top_field = !(data_pkt[1] & + EM25XX_FRMDATAHDR_BYTE2_FRAME_ID); + frame_end = data_pkt[1] & + EM25XX_FRMDATAHDR_BYTE2_FRAME_END; + data_pkt += 2; + data_len -= 2; + } + + /* Finish field and prepare next (BULK only) */ + if (dev->analog_xfer_bulk && frame_end) { + buf = finish_field_prepare_next(dev, buf, dmaq); + dev->usb_ctl.vid_buf = buf; + } + /* NOTE: in ISOC mode when a new frame starts and buf==NULL, + * we COULD already prepare a buffer here to avoid skipping the + * first frame. + */ + } + + /* Copy data */ + if (buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); + + /* Finish frame (ISOC only) => avoids lag of 1 frame */ + if (!dev->analog_xfer_bulk && frame_end) { + buf = finish_field_prepare_next(dev, buf, dmaq); + dev->usb_ctl.vid_buf = buf; + } + + /* NOTE: Tested with USB bulk transfers only ! + * The wording in the datasheet suggests that isoc might work different. + * The current code assumes that with isoc transfers each packet has a + * header like with the other em28xx devices. + */ + /* NOTE: Support for interlaced mode is pure theory. It has not been + * tested and it is unknown if these devices actually support it. */ + /* NOTE: No VBI support yet (these chips likely do not support VBI). */ +} + /* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { @@ -460,7 +526,13 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) continue; } - process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); + if (dev->is_em25xx) + process_frame_data_em25xx(dev, + usb_data_pkt, usb_data_len); + else + process_frame_data_em28xx(dev, + usb_data_pkt, usb_data_len); + } return 1; } @@ -700,6 +772,7 @@ int em28xx_vb2_setup(struct em28xx *dev) q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->drv_priv = dev; q->buf_struct_size = sizeof(struct em28xx_buffer); q->ops = &em28xx_video_qops; @@ -713,6 +786,7 @@ int em28xx_vb2_setup(struct em28xx *dev) q = &dev->vb_vbiq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->drv_priv = dev; q->buf_struct_size = sizeof(struct em28xx_buffer); q->ops = &em28xx_vbi_qops; @@ -782,33 +856,45 @@ void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) { struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler); + int ret = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: dev->mute = ctrl->val; + ret = em28xx_audio_analog_set(dev); break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->val; + ret = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_CONTRAST: + ret = em28xx_write_reg(dev, EM28XX_R20_YGAIN, ctrl->val); + break; + case V4L2_CID_BRIGHTNESS: + ret = em28xx_write_reg(dev, EM28XX_R21_YOFFSET, ctrl->val); + break; + case V4L2_CID_SATURATION: + ret = em28xx_write_reg(dev, EM28XX_R22_UVGAIN, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + ret = em28xx_write_reg(dev, EM28XX_R23_UOFFSET, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + ret = em28xx_write_reg(dev, EM28XX_R24_VOFFSET, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + ret = em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, ctrl->val); break; } - return em28xx_audio_analog_set(dev); + return (ret < 0) ? ret : 0; } const struct v4l2_ctrl_ops em28xx_ctrl_ops = { .s_ctrl = em28xx_s_ctrl, }; -static int check_dev(struct em28xx *dev) -{ - if (dev->disconnected) { - em28xx_errdev("v4l2 ioctl: device not present\n"); - return -ENODEV; - } - return 0; -} - -static void get_scale(struct em28xx *dev, +static void size_to_scale(struct em28xx *dev, unsigned int width, unsigned int height, unsigned int *hscale, unsigned int *vscale) { @@ -816,12 +902,23 @@ static void get_scale(struct em28xx *dev, unsigned int maxh = norm_maxh(dev); *hscale = (((unsigned long)maxw) << 12) / width - 4096L; - if (*hscale >= 0x4000) - *hscale = 0x3fff; + if (*hscale > EM28XX_HVSCALE_MAX) + *hscale = EM28XX_HVSCALE_MAX; *vscale = (((unsigned long)maxh) << 12) / height - 4096L; - if (*vscale >= 0x4000) - *vscale = 0x3fff; + if (*vscale > EM28XX_HVSCALE_MAX) + *vscale = EM28XX_HVSCALE_MAX; +} + +static void scale_to_size(struct em28xx *dev, + unsigned int hscale, unsigned int vscale, + unsigned int *width, unsigned int *height) +{ + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + *width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + *height = (((unsigned long)maxh) << 12) / (vscale + 4096L); } /* ------------------------------------------------------------------ @@ -898,10 +995,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 1, 0); } - get_scale(dev, width, height, &hscale, &vscale); - - width = (((unsigned long)maxw) << 12) / (hscale + 4096L); - height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + size_to_scale(dev, width, height, &hscale, &vscale); + scale_to_size(dev, hscale, vscale, &width, &height); f->fmt.pix.width = width; f->fmt.pix.height = height; @@ -932,7 +1027,7 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, dev->height = height; /* set new image size */ - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_resolution_set(dev); @@ -957,13 +1052,6 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - if (dev->board.is_webcam) - return -ENOTTY; - rc = check_dev(dev); - if (rc < 0) - return rc; *norm = dev->norm; @@ -974,48 +1062,35 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - if (dev->board.is_webcam) - return -ENOTTY; - rc = check_dev(dev); - if (rc < 0) - return rc; v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; struct v4l2_format f; - int rc; - if (dev->board.is_webcam) - return -ENOTTY; - if (*norm == dev->norm) + if (norm == dev->norm) return 0; - rc = check_dev(dev); - if (rc < 0) - return rc; if (dev->streaming_users > 0) return -EBUSY; - dev->norm = *norm; + dev->norm = norm; /* Adjusts width/height, if needed */ f.fmt.pix.width = 720; - f.fmt.pix.height = (*norm & V4L2_STD_525_60) ? 480 : 576; + f.fmt.pix.height = (norm & V4L2_STD_525_60) ? 480 : 576; vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ dev->width = f.fmt.pix.width; dev->height = f.fmt.pix.height; - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_resolution_set(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); @@ -1030,9 +1105,6 @@ static int vidioc_g_parm(struct file *file, void *priv, struct em28xx *dev = fh->dev; int rc = 0; - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, @@ -1050,12 +1122,6 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - if (!dev->board.is_webcam) - return -ENOTTY; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - p->parm.capture.readbuffers = EM28XX_MIN_BUF; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); } @@ -1116,11 +1182,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (i >= MAX_EM28XX_INPUT) return -EINVAL; @@ -1136,9 +1197,6 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - if (!dev->audio_mode.has_audio) - return -EINVAL; - switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -1179,10 +1237,6 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - - if (!dev->audio_mode.has_audio) - return -EINVAL; - if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -1202,11 +1256,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (0 != t->index) return -EINVAL; @@ -1218,15 +1267,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (0 != t->index) return -EINVAL; @@ -1249,39 +1293,22 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { + struct v4l2_frequency new_freq = *f; struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (0 != f->tuner) return -EINVAL; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); - dev->ctl_freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq); + dev->ctl_freq = new_freq.frequency; return 0; } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int em28xx_reg_len(int reg) -{ - switch (reg) { - case EM28XX_R40_AC97LSB: - case EM28XX_R30_HSCALELOW: - case EM28XX_R32_VSCALELOW: - return 2; - default: - return 1; - } -} - static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { @@ -1290,9 +1317,9 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match.type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(&chip->match)) - chip->ident = V4L2_IDENT_NONE; + if (chip->match.type == V4L2_CHIP_MATCH_BRIDGE) { + if (chip->match.addr > 1) + return -EINVAL; return 0; } if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && @@ -1304,6 +1331,33 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_chip_info(struct file *file, void *priv, + struct v4l2_dbg_chip_info *chip) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (chip->match.addr > 1) + return -EINVAL; + if (chip->match.addr == 1) + strlcpy(chip->name, "ac97", sizeof(chip->name)); + else + strlcpy(chip->name, dev->v4l2_dev.name, sizeof(chip->name)); + return 0; +} + +static int em28xx_reg_len(int reg) +{ + switch (reg) { + case EM28XX_R40_AC97LSB: + case EM28XX_R30_HSCALELOW: + case EM28XX_R32_VSCALELOW: + return 2; + default: + return 1; + } +} static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) @@ -1313,6 +1367,12 @@ static int vidioc_g_register(struct file *file, void *priv, int ret; switch (reg->match.type) { + case V4L2_CHIP_MATCH_BRIDGE: + if (reg->match.addr > 1) + return -EINVAL; + if (!reg->match.addr) + break; + /* fall-through */ case V4L2_CHIP_MATCH_AC97: ret = em28xx_read_ac97(dev, reg->reg); if (ret < 0) @@ -1329,8 +1389,7 @@ static int vidioc_g_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + return -EINVAL; } /* Match host */ @@ -1356,13 +1415,19 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; __le16 buf; switch (reg->match.type) { + case V4L2_CHIP_MATCH_BRIDGE: + if (reg->match.addr > 1) + return -EINVAL; + if (!reg->match.addr) + break; + /* fall-through */ case V4L2_CHIP_MATCH_AC97: return em28xx_write_ac97(dev, reg->reg, reg->val); case V4L2_CHIP_MATCH_I2C_DRIVER: @@ -1373,8 +1438,7 @@ static int vidioc_s_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + return -EINVAL; } /* Match host */ @@ -1386,26 +1450,6 @@ static int vidioc_s_register(struct file *file, void *priv, #endif -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cc->bounds.left = 0; - cc->bounds.top = 0; - cc->bounds.width = dev->width; - cc->bounds.height = dev->height; - cc->defrect = cc->bounds; - cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ - cc->pixelaspect.denominator = 59; - - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1482,8 +1526,12 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, /* Report a continuous range */ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = 48; - fsize->stepwise.min_height = 32; + scale_to_size(dev, EM28XX_HVSCALE_MAX, EM28XX_HVSCALE_MAX, + &fsize->stepwise.min_width, &fsize->stepwise.min_height); + if (fsize->stepwise.min_width < 48) + fsize->stepwise.min_width = 48; + if (fsize->stepwise.min_height < 38) + fsize->stepwise.min_height = 38; fsize->stepwise.max_width = maxw; fsize->stepwise.max_height = maxh; fsize->stepwise.step_width = 1; @@ -1522,35 +1570,6 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - format->fmt.vbi.samples_per_line = dev->vbi_width; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 0; - format->fmt.vbi.flags = 0; - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; - memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); - - /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - format->fmt.vbi.start[0] = 6; - format->fmt.vbi.start[1] = 318; - } - - return 0; -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ @@ -1564,7 +1583,6 @@ static int radio_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); @@ -1572,7 +1590,7 @@ static int radio_g_tuner(struct file *file, void *priv, } static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; @@ -1749,11 +1767,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, - .vidioc_cropcap = vidioc_cropcap, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -1778,10 +1795,11 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_frequency = vidioc_s_frequency, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, - .vidioc_g_chip_ident = vidioc_g_chip_ident, #endif }; @@ -1808,7 +1826,9 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_frequency = vidioc_s_frequency, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif @@ -1887,9 +1907,42 @@ int em28xx_register_analog_devices(struct em28xx *dev) (EM28XX_XCLK_AUDIO_UNMUTE | val)); em28xx_set_outfmt(dev); - em28xx_colorlevels_set_default(dev); em28xx_compression_disable(dev); + /* Add image controls */ + /* NOTE: at this point, the subdevices are already registered, so bridge + * controls are only added/enabled when no subdevice provides them */ + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_CONTRAST)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_CONTRAST, + 0, 0x1f, 1, CONTRAST_DEFAULT); + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BRIGHTNESS)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, + -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT); + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SATURATION)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_SATURATION, + 0, 0x1f, 1, SATURATION_DEFAULT); + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BLUE_BALANCE)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_BLUE_BALANCE, + -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT); + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_RED_BALANCE)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_RED_BALANCE, + -0x30, 0x30, 1, RED_BALANCE_DEFAULT); + if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SHARPNESS)) + v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + V4L2_CID_SHARPNESS, + 0, 0x0f, 1, SHARPNESS_DEFAULT); + + /* Reset image controls */ + em28xx_colorlevels_set_default(dev); + v4l2_ctrl_handler_setup(&dev->ctrl_handler); + if (dev->ctrl_handler.error) + return dev->ctrl_handler.error; + /* allocate and fill video video_device struct */ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); if (!dev->vdev) { @@ -1899,6 +1952,25 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->vdev->queue = &dev->vb_vidq; dev->vdev->queue->lock = &dev->vb_queue_lock; + /* disable inapplicable ioctls */ + if (dev->board.is_webcam) { + v4l2_disable_ioctl(dev->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(dev->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_STD); + } else { + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM); + } + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(dev->vdev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(dev->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_FREQUENCY); + } + if (!dev->audio_mode.has_audio) { + v4l2_disable_ioctl(dev->vdev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_AUDIO); + } + /* register v4l2 video video_device */ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); @@ -1916,6 +1988,19 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->vbi_dev->queue = &dev->vb_vbiq; dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; + /* disable inapplicable ioctls */ + v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM); + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_FREQUENCY); + } + if (!dev->audio_mode.has_audio) { + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_AUDIO); + } + /* register v4l2 vbi video_device */ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 5f0b2c5..a9323b6 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -42,28 +42,28 @@ #include "em28xx-reg.h" /* Boards supported by driver */ -#define EM2800_BOARD_UNKNOWN 0 -#define EM2820_BOARD_UNKNOWN 1 -#define EM2820_BOARD_TERRATEC_CINERGY_250 2 -#define EM2820_BOARD_PINNACLE_USB_2 3 -#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 -#define EM2820_BOARD_MSI_VOX_USB_2 5 -#define EM2800_BOARD_TERRATEC_CINERGY_200 6 -#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 -#define EM2800_BOARD_KWORLD_USB2800 8 -#define EM2820_BOARD_PINNACLE_DVC_90 9 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 -#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 -#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 -#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 -#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 -#define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 -#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 -#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19 -#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 -#define EM2800_BOARD_GRABBEEX_USB2800 21 +#define EM2800_BOARD_UNKNOWN 0 +#define EM2820_BOARD_UNKNOWN 1 +#define EM2820_BOARD_TERRATEC_CINERGY_250 2 +#define EM2820_BOARD_PINNACLE_USB_2 3 +#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 +#define EM2820_BOARD_MSI_VOX_USB_2 5 +#define EM2800_BOARD_TERRATEC_CINERGY_200 6 +#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 +#define EM2800_BOARD_KWORLD_USB2800 8 +#define EM2820_BOARD_PINNACLE_DVC_90 9 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 +#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 +#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 +#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 +#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 +#define EM2800_BOARD_VGEAR_POCKETTV 15 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 +#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 +#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19 +#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 +#define EM2800_BOARD_GRABBEEX_USB2800 21 #define EM2750_BOARD_UNKNOWN 22 #define EM2750_BOARD_DLCW_130 23 #define EM2820_BOARD_DLINK_USB_TV 24 @@ -99,36 +99,37 @@ #define EM2882_BOARD_KWORLD_VS_DVBT 54 #define EM2882_BOARD_TERRATEC_HYBRID_XS 55 #define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56 -#define EM2883_BOARD_KWORLD_HYBRID_330U 57 +#define EM2883_BOARD_KWORLD_HYBRID_330U 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 #define EM2820_BOARD_GADMEI_TVR200 62 -#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 -#define EM2860_BOARD_EASYCAP 64 +#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 +#define EM2860_BOARD_EASYCAP 64 #define EM2820_BOARD_IODATA_GVMVP_SZ 65 #define EM2880_BOARD_EMPIRE_DUAL_TV 66 #define EM2860_BOARD_TERRATEC_GRABBY 67 #define EM2860_BOARD_TERRATEC_AV350 68 #define EM2882_BOARD_KWORLD_ATSC_315U 69 #define EM2882_BOARD_EVGA_INDTUBE 70 -#define EM2820_BOARD_SILVERCREST_WEBCAM 71 -#define EM2861_BOARD_GADMEI_UTV330PLUS 72 -#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 +#define EM2820_BOARD_SILVERCREST_WEBCAM 71 +#define EM2861_BOARD_GADMEI_UTV330PLUS 72 +#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 #define EM2800_BOARD_VC211A 74 #define EM2882_BOARD_DIKOM_DK300 75 #define EM2870_BOARD_KWORLD_A340 76 #define EM2874_BOARD_LEADERSHIP_ISDBT 77 -#define EM28174_BOARD_PCTV_290E 78 +#define EM28174_BOARD_PCTV_290E 78 #define EM2884_BOARD_TERRATEC_H5 79 -#define EM28174_BOARD_PCTV_460E 80 +#define EM28174_BOARD_PCTV_460E 80 #define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81 #define EM2884_BOARD_CINERGY_HTC_STICK 82 -#define EM2860_BOARD_HT_VIDBOX_NW03 83 -#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 -#define EM2884_BOARD_PCTV_510E 85 -#define EM2884_BOARD_PCTV_520E 86 +#define EM2860_BOARD_HT_VIDBOX_NW03 83 +#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 +#define EM2884_BOARD_PCTV_510E 85 +#define EM2884_BOARD_PCTV_520E 86 #define EM2884_BOARD_TERRATEC_HTC_USB_XS 87 +#define EM2884_BOARD_C3TECH_DIGITAL_DUO 88 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -157,6 +158,9 @@ #define EM28XX_NUM_BUFS 5 #define EM28XX_DVB_NUM_BUFS 5 +/* max number of I2C buses on em28xx devices */ +#define NUM_I2C_BUSES 2 + /* isoc transfers: number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! @@ -172,27 +176,6 @@ #define EM28XX_INTERLACED_DEFAULT 1 -/* -#define (use usbview if you want to get the other alternate number infos) -#define -#define alternate number 2 -#define Endpoint Address: 82 - Direction: in - Attribute: 1 - Type: Isoc - Max Packet Size: 1448 - Interval: 125us - - alternate number 7 - - Endpoint Address: 82 - Direction: in - Attribute: 1 - Type: Isoc - Max Packet Size: 3072 - Interval: 125us -*/ - /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_XFER_TIMEOUT 20 @@ -381,6 +364,7 @@ enum em28xx_sensor { EM28XX_MT9V011, EM28XX_MT9M001, EM28XX_MT9M111, + EM28XX_OV2640, }; enum em28xx_adecoder { @@ -393,6 +377,7 @@ struct em28xx_board { int vchannels; int tuner_type; int tuner_addr; + unsigned def_i2c_bus; /* Default I2C bus */ /* i2c flags */ unsigned int tda9887_conf; @@ -426,15 +411,15 @@ struct em28xx_board { }; struct em28xx_eeprom { - u32 id; /* 0x9567eb1a */ - u16 vendor_ID; - u16 product_ID; + u8 id[4]; /* 1a eb 67 95 */ + __le16 vendor_ID; + __le16 product_ID; - u16 chip_conf; + __le16 chip_conf; - u16 board_conf; + __le16 board_conf; - u16 string1, string2, string3; + __le16 string1, string2, string3; u8 string_idx_table; }; @@ -477,6 +462,20 @@ struct em28xx_fh { enum v4l2_buf_type type; }; +enum em28xx_i2c_algo_type { + EM28XX_I2C_ALGO_EM28XX = 0, + EM28XX_I2C_ALGO_EM2800, + EM28XX_I2C_ALGO_EM25XX_BUS_B, +}; + +struct em28xx_i2c_bus { + struct em28xx *dev; + + unsigned bus; + enum em28xx_i2c_algo_type algo_type; +}; + + /* main device struct */ struct em28xx { /* generic device properties */ @@ -484,6 +483,7 @@ struct em28xx { int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; + unsigned int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */ unsigned char disconnected:1; /* device has been diconnected */ @@ -491,8 +491,6 @@ struct em28xx { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; - /* provides ac97 mute and volume overrides */ - struct v4l2_ctrl_handler ac97_ctrl_handler; struct em28xx_board board; /* Webcam specific fields */ @@ -511,8 +509,8 @@ struct em28xx { unsigned int is_audio_only:1; /* Controls audio streaming */ - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ struct em28xx_fmt *format; @@ -530,9 +528,17 @@ struct em28xx { int tuner_type; /* type of the tuner */ int tuner_addr; /* tuner address */ int tda9887_conf; + /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; + struct i2c_adapter i2c_adap[NUM_I2C_BUSES]; + struct i2c_client i2c_client[NUM_I2C_BUSES]; + struct em28xx_i2c_bus i2c_bus[NUM_I2C_BUSES]; + + unsigned char eeprom_addrwidth_16bit:1; + unsigned def_i2c_bus; /* Default I2C bus */ + unsigned cur_i2c_bus; /* Current I2C bus */ + struct rt_mutex i2c_bus_lock; + /* video for linux */ int users; /* user count for exclusive use */ int streaming_users; /* Number of actively streaming users */ @@ -584,7 +590,9 @@ struct em28xx { /* resources in use */ unsigned int resources; - unsigned char eedata[256]; + /* eeprom content */ + u8 *eedata; + u16 eedata_len; /* Isoc control struct */ struct em28xx_dmaqueue vidq; @@ -600,7 +608,7 @@ struct em28xx { u8 analog_ep_isoc; /* address of isoc endpoint for analog */ u8 analog_ep_bulk; /* address of bulk endpoint for analog */ u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ - u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */ + u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */ int alt; /* alternate setting */ int max_pkt_size; /* max packet size of the selected ep at alt */ int packet_multiplier; /* multiplier for wMaxPacketSize, used for @@ -651,16 +659,12 @@ struct em28xx_ops { }; /* Provided by em28xx-i2c.c */ -void em28xx_do_i2c_scan(struct em28xx *dev); -int em28xx_i2c_register(struct em28xx *dev); -int em28xx_i2c_unregister(struct em28xx *dev); +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus); +int em28xx_i2c_register(struct em28xx *dev, unsigned bus, + enum em28xx_i2c_algo_type algo_type); +int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus); /* Provided by em28xx-core.c */ - -u32 em28xx_request_buffers(struct em28xx *dev, u32 count); -void em28xx_queue_unusedframes(struct em28xx *dev); -void em28xx_release_buffers(struct em28xx *dev); - int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg); @@ -693,7 +697,6 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); -int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); void em28xx_wake_i2c(struct em28xx *dev); @@ -712,16 +715,18 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq); extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ -extern int em2800_variant_detect(struct usb_device *udev, int model); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; -extern const unsigned int em28xx_bcount; int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-vbi.c */ extern struct vb2_ops em28xx_vbi_qops; +/* Provided by em28xx-camera.c */ +int em28xx_detect_sensor(struct em28xx *dev); +int em28xx_init_camera(struct em28xx *dev); + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ @@ -744,72 +749,6 @@ static inline int em28xx_compression_disable(struct em28xx *dev) return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); } -static inline int em28xx_contrast_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; -} - -static inline int em28xx_brightness_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); -} - -static inline int em28xx_saturation_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; -} - -static inline int em28xx_u_balance_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); -} - -static inline int em28xx_v_balance_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); -} - -static inline int em28xx_gamma_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; -} - -static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); -} - -static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); -} - -static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); -} - -static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); -} - -static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); -} - -static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); -} - /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { diff --git a/drivers/media/usb/gspca/autogain_functions.h b/drivers/media/usb/gspca/autogain_functions.h deleted file mode 100644 index d625eaf..0000000 --- a/drivers/media/usb/gspca/autogain_functions.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Functions for auto gain. - * - * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef WANT_REGULAR_AUTOGAIN -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -static inline int auto_gain_n_exposure( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone, - int gain_knee, - int exposure_knee) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, steps, gain, orig_gain, exposure, orig_exposure; - int retval = 0; - - orig_gain = gain = sd->ctrls[GAIN].val; - orig_exposure = exposure = sd->ctrls[EXPOSURE].val; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > sd->ctrls[GAIN].def) - gain--; - else if (exposure > sd->ctrls[EXPOSURE].min) - exposure--; - else if (gain > sd->ctrls[GAIN].min) - gain--; - else - break; - } else { - if (gain < sd->ctrls[GAIN].def) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < sd->ctrls[EXPOSURE].max) - exposure++; - else if (gain < sd->ctrls[GAIN].max) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - retval = 1; - } - if (exposure != orig_exposure) { - sd->ctrls[EXPOSURE].val = exposure; - setexposure(gspca_dev); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -#endif - -#ifdef WANT_COARSE_EXPO_AUTOGAIN -/* Autogain + exposure algorithm for cameras with a coarse exposure control - (usually this means we can only control the clockdiv to change exposure) - As changing the clockdiv so that the fps drops from 30 to 15 fps for - example, will lead to a huge exposure change (it effectively doubles), - this algorithm normally tries to only adjust the gain (between 40 and - 80 %) and if that does not help, only then changes exposure. This leads - to a much more stable image then using the knee algorithm which at - certain points of the knee graph will only try to adjust exposure, - which leads to oscilating as one exposure step is huge. - - Note this assumes that the sd struct for the cam in question has - exp_too_low_cnt and exp_too_high_cnt int members for use by this function. - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -static inline int coarse_grained_expo_autogain( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone) -{ - struct sd *sd = (struct sd *) gspca_dev; - int steps, gain, orig_gain, exposure, orig_exposure; - int gain_low, gain_high; - int retval = 0; - - orig_gain = gain = sd->ctrls[GAIN].val; - orig_exposure = exposure = sd->ctrls[EXPOSURE].val; - - gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2; - gain_low += sd->ctrls[GAIN].min; - gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4; - gain_high += sd->ctrls[GAIN].min; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = (desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - if ((gain + steps) > gain_high && - exposure < sd->ctrls[EXPOSURE].max) { - gain = gain_high; - sd->exp_too_low_cnt++; - sd->exp_too_high_cnt = 0; - } else if ((gain + steps) < gain_low && - exposure > sd->ctrls[EXPOSURE].min) { - gain = gain_low; - sd->exp_too_high_cnt++; - sd->exp_too_low_cnt = 0; - } else { - gain += steps; - if (gain > sd->ctrls[GAIN].max) - gain = sd->ctrls[GAIN].max; - else if (gain < sd->ctrls[GAIN].min) - gain = sd->ctrls[GAIN].min; - sd->exp_too_high_cnt = 0; - sd->exp_too_low_cnt = 0; - } - - if (sd->exp_too_high_cnt > 3) { - exposure--; - sd->exp_too_high_cnt = 0; - } else if (sd->exp_too_low_cnt > 3) { - exposure++; - sd->exp_too_low_cnt = 0; - } - - if (gain != orig_gain) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - retval = 1; - } - if (exposure != orig_exposure) { - sd->ctrls[EXPOSURE].val = exposure; - setexposure(gspca_dev); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -#endif diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c index 352f321..05f406d 100644 --- a/drivers/media/usb/gspca/benq.c +++ b/drivers/media/usb/gspca/benq.c @@ -186,7 +186,7 @@ static void sd_isoc_irq(struct urb *urb) /* check the packet status and length */ if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) { - PDEBUG(D_ERR, "ISOC bad lengths %d / %d", + PERR("ISOC bad lengths %d / %d", urb0->iso_frame_desc[i].actual_length, urb->iso_frame_desc[i].actual_length); gspca_dev->last_packet_type = DISCARD_PACKET; diff --git a/drivers/media/usb/gspca/conex.c b/drivers/media/usb/gspca/conex.c index c9052f2..38714df 100644 --- a/drivers/media/usb/gspca/conex.c +++ b/drivers/media/usb/gspca/conex.c @@ -73,12 +73,11 @@ static void reg_r(struct gspca_dev *gspca_dev, { struct usb_device *dev = gspca_dev->dev; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); + PERR("reg_r: buffer overflow\n"); return; } -#endif + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0, @@ -113,13 +112,12 @@ static void reg_w(struct gspca_dev *gspca_dev, { struct usb_device *dev = gspca_dev->dev; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - pr_err("reg_w: buffer overflow\n"); + PERR("reg_w: buffer overflow\n"); return; } PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); -#endif + memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -689,7 +687,7 @@ static void cx11646_jpeg(struct gspca_dev*gspca_dev) reg_w_val(gspca_dev, 0x0053, 0x00); } while (--retry); if (retry == 0) - PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); + PERR("Damned Errors sending jpeg Table"); /* send the qtable now */ reg_r(gspca_dev, 0x0001, 1); /* -> 0x18 */ length = 8; diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 1dcdd9f..064b530 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -421,8 +421,7 @@ static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command) pipe = usb_sndctrlpipe(gspca_dev->dev, 0); requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE; } else { - PDEBUG(D_ERR, "Unexpected first byte of command: %x", - command[0]); + PERR("Unexpected first byte of command: %x", command[0]); return -EINVAL; } @@ -701,7 +700,7 @@ static void reset_camera_params(struct gspca_dev *gspca_dev) params->qx3.cradled = 0; } -static void printstatus(struct cam_params *params) +static void printstatus(struct gspca_dev *gspca_dev, struct cam_params *params) { PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x", params->status.systemState, params->status.grabState, @@ -725,10 +724,9 @@ static int goto_low_power(struct gspca_dev *gspca_dev) if (sd->params.status.systemState != LO_POWER_STATE) { if (sd->params.status.systemState != WARM_BOOT_STATE) { - PDEBUG(D_ERR, - "unexpected state after lo power cmd: %02x", - sd->params.status.systemState); - printstatus(&sd->params); + PERR("unexpected state after lo power cmd: %02x", + sd->params.status.systemState); + printstatus(gspca_dev, &sd->params); } return -EIO; } @@ -756,9 +754,9 @@ static int goto_high_power(struct gspca_dev *gspca_dev) return ret; if (sd->params.status.systemState != HI_POWER_STATE) { - PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x", - sd->params.status.systemState); - printstatus(&sd->params); + PERR("unexpected state after hi power cmd: %02x", + sd->params.status.systemState); + printstatus(gspca_dev, &sd->params); return -EIO; } @@ -1449,8 +1447,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->params.version.firmwareVersion = 0; get_version_information(gspca_dev); if (sd->params.version.firmwareVersion != 1) { - PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)", - sd->params.version.firmwareVersion); + PERR("only firmware version 1 is supported (got: %d)", + sd->params.version.firmwareVersion); return -ENODEV; } @@ -1475,9 +1473,9 @@ static int sd_start(struct gspca_dev *gspca_dev) /* Start the camera in low power mode */ if (goto_low_power(gspca_dev)) { if (sd->params.status.systemState != WARM_BOOT_STATE) { - PDEBUG(D_ERR, "unexpected systemstate: %02x", - sd->params.status.systemState); - printstatus(&sd->params); + PERR("unexpected systemstate: %02x", + sd->params.status.systemState); + printstatus(gspca_dev, &sd->params); return -ENODEV; } @@ -1523,9 +1521,8 @@ static int sd_start(struct gspca_dev *gspca_dev) return ret; if (sd->params.status.fatalError) { - PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x", - sd->params.status.fatalError, - sd->params.status.vpStatus); + PERR("fatal_error: %04x, vp_status: %04x", + sd->params.status.fatalError, sd->params.status.vpStatus); return -EIO; } diff --git a/drivers/media/usb/gspca/etoms.c b/drivers/media/usb/gspca/etoms.c index 38f68e1..26c9ee1 100644 --- a/drivers/media/usb/gspca/etoms.c +++ b/drivers/media/usb/gspca/etoms.c @@ -163,12 +163,11 @@ static void reg_r(struct gspca_dev *gspca_dev, { struct usb_device *dev = gspca_dev->dev; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); + PERR("reg_r: buffer overflow\n"); return; } -#endif + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0, @@ -201,13 +200,12 @@ static void reg_w(struct gspca_dev *gspca_dev, { struct usb_device *dev = gspca_dev->dev; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { pr_err("reg_w: buffer overflow\n"); return; } PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); -#endif + memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -274,7 +272,7 @@ static int et_video(struct gspca_dev *gspca_dev, : 0); /* stopvideo */ ret = Et_WaitStatus(gspca_dev); if (ret != 0) - PDEBUG(D_ERR, "timeout video on/off"); + PERR("timeout video on/off"); return ret; } @@ -768,9 +766,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, -#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, -#endif {} }; diff --git a/drivers/media/usb/gspca/gl860/gl860.c b/drivers/media/usb/gspca/gl860/gl860.c index ced3b71..cb1e64c 100644 --- a/drivers/media/usb/gspca/gl860/gl860.c +++ b/drivers/media/usb/gspca/gl860/gl860.c @@ -58,115 +58,135 @@ MODULE_PARM_DESC(sensor, /*============================ webcam controls =============================*/ -/* Functions to get and set a control value */ -#define SD_SETGET(thename) \ -static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ -{\ - struct sd *sd = (struct sd *) gspca_dev;\ -\ - sd->vcur.thename = val;\ - if (gspca_dev->streaming)\ - sd->waitSet = 1;\ - return 0;\ -} \ -static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ -{\ - struct sd *sd = (struct sd *) gspca_dev;\ -\ - *val = sd->vcur.thename;\ - return 0;\ -} +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; -SD_SETGET(mirror) -SD_SETGET(flip) -SD_SETGET(AC50Hz) -SD_SETGET(backlight) -SD_SETGET(brightness) -SD_SETGET(gamma) -SD_SETGET(hue) -SD_SETGET(saturation) -SD_SETGET(sharpness) -SD_SETGET(whitebal) -SD_SETGET(contrast) - -#define GL860_NCTRLS 11 - -/* control table */ -static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; -static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; -static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; -static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; - -#define SET_MY_CTRL(theid, \ - thetype, thelabel, thename) \ - if (sd->vmax.thename != 0) {\ - sd_ctrls[nCtrls].qctrl.id = theid;\ - sd_ctrls[nCtrls].qctrl.type = thetype;\ - strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ - sd_ctrls[nCtrls].qctrl.minimum = 0;\ - sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ - sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ - sd_ctrls[nCtrls].qctrl.step = \ - (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ - sd_ctrls[nCtrls].set = sd_set_##thename;\ - sd_ctrls[nCtrls].get = sd_get_##thename;\ - nCtrls++;\ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + sd->vcur.brightness = ctrl->val; + break; + case V4L2_CID_CONTRAST: + sd->vcur.contrast = ctrl->val; + break; + case V4L2_CID_SATURATION: + sd->vcur.saturation = ctrl->val; + break; + case V4L2_CID_HUE: + sd->vcur.hue = ctrl->val; + break; + case V4L2_CID_GAMMA: + sd->vcur.gamma = ctrl->val; + break; + case V4L2_CID_HFLIP: + sd->vcur.mirror = ctrl->val; + break; + case V4L2_CID_VFLIP: + sd->vcur.flip = ctrl->val; + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + sd->vcur.AC50Hz = ctrl->val; + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + sd->vcur.whitebal = ctrl->val; + break; + case V4L2_CID_SHARPNESS: + sd->vcur.sharpness = ctrl->val; + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + sd->vcur.backlight = ctrl->val; + break; + default: + return -EINVAL; } -static int gl860_build_control_table(struct gspca_dev *gspca_dev) + if (gspca_dev->streaming) + sd->waitSet = 1; + + return 0; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct ctrl *sd_ctrls; - int nCtrls = 0; - - if (_MI1320_) - sd_ctrls = sd_ctrls_mi1320; - else if (_MI2020_) - sd_ctrls = sd_ctrls_mi2020; - else if (_OV2640_) - sd_ctrls = sd_ctrls_ov2640; - else if (_OV9655_) - sd_ctrls = sd_ctrls_ov9655; - else - return 0; - - memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); - - SET_MY_CTRL(V4L2_CID_BRIGHTNESS, - V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) - SET_MY_CTRL(V4L2_CID_SHARPNESS, - V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) - SET_MY_CTRL(V4L2_CID_CONTRAST, - V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) - SET_MY_CTRL(V4L2_CID_GAMMA, - V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) - SET_MY_CTRL(V4L2_CID_HUE, - V4L2_CTRL_TYPE_INTEGER, "Palette", hue) - SET_MY_CTRL(V4L2_CID_SATURATION, - V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) - SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, - V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) - SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, - V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) - - SET_MY_CTRL(V4L2_CID_HFLIP, - V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) - SET_MY_CTRL(V4L2_CID_VFLIP, - V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) - SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) - - return nCtrls; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 11); + + if (sd->vmax.brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS, + 0, sd->vmax.brightness, 1, + sd->vcur.brightness); + + if (sd->vmax.contrast) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_CONTRAST, + 0, sd->vmax.contrast, 1, + sd->vcur.contrast); + + if (sd->vmax.saturation) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SATURATION, + 0, sd->vmax.saturation, 1, + sd->vcur.saturation); + + if (sd->vmax.hue) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HUE, + 0, sd->vmax.hue, 1, sd->vcur.hue); + + if (sd->vmax.gamma) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAMMA, + 0, sd->vmax.gamma, 1, sd->vcur.gamma); + + if (sd->vmax.mirror) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP, + 0, sd->vmax.mirror, 1, sd->vcur.mirror); + + if (sd->vmax.flip) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP, + 0, sd->vmax.flip, 1, sd->vcur.flip); + + if (sd->vmax.AC50Hz) + v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + sd->vmax.AC50Hz, 0, sd->vcur.AC50Hz); + + if (sd->vmax.whitebal) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, sd->vmax.whitebal, 1, sd->vcur.whitebal); + + if (sd->vmax.sharpness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SHARPNESS, + 0, sd->vmax.sharpness, 1, + sd->vcur.sharpness); + + if (sd->vmax.backlight) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BACKLIGHT_COMPENSATION, + 0, sd->vmax.backlight, 1, + sd->vcur.backlight); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + return 0; } /*==================== sud-driver structure initialisation =================*/ static const struct sd_desc sd_desc_mi1320 = { .name = MODULE_NAME, - .ctrls = sd_ctrls_mi1320, - .nctrls = GL860_NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stop0 = sd_stop0, @@ -176,10 +196,9 @@ static const struct sd_desc sd_desc_mi1320 = { static const struct sd_desc sd_desc_mi2020 = { .name = MODULE_NAME, - .ctrls = sd_ctrls_mi2020, - .nctrls = GL860_NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stop0 = sd_stop0, @@ -189,10 +208,9 @@ static const struct sd_desc sd_desc_mi2020 = { static const struct sd_desc sd_desc_ov2640 = { .name = MODULE_NAME, - .ctrls = sd_ctrls_ov2640, - .nctrls = GL860_NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stop0 = sd_stop0, @@ -202,10 +220,9 @@ static const struct sd_desc sd_desc_ov2640 = { static const struct sd_desc sd_desc_ov9655 = { .name = MODULE_NAME, - .ctrls = sd_ctrls_ov9655, - .nctrls = GL860_NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stop0 = sd_stop0, @@ -371,7 +388,6 @@ static int sd_config(struct gspca_dev *gspca_dev, dev_init_settings(gspca_dev); if (AC50Hz != 0xff) ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; - gl860_build_control_table(gspca_dev); return 0; } @@ -566,7 +582,7 @@ int gl860_RTx(struct gspca_dev *gspca_dev, pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", r, pref, req, val, index, len); else if (len > 1 && r < len) - PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); + PERR("short ctrl transfer %d/%d", r, len); msleep(1); diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 3564bdb..5995ec4 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -60,14 +60,14 @@ MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(GSPCA_VERSION); -#ifdef GSPCA_DEBUG -int gspca_debug = D_ERR | D_PROBE; +int gspca_debug; EXPORT_SYMBOL(gspca_debug); -static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) +static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt, + __u32 pixfmt, int w, int h) { if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { - PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d", + PDEBUG(debug, "%s %c%c%c%c %dx%d", txt, pixfmt & 0xff, (pixfmt >> 8) & 0xff, @@ -75,15 +75,12 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) pixfmt >> 24, w, h); } else { - PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d", + PDEBUG(debug, "%s 0x%08x %dx%d", txt, pixfmt, w, h); } } -#else -#define PDEBUG_MODE(txt, pixfmt, w, h) -#endif /* specific memory types - !! should be different from V4L2_MEMORY_xxx */ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ @@ -129,7 +126,7 @@ static void int_irq(struct urb *urb) case 0: if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, urb->transfer_buffer, urb->actual_length) < 0) { - PDEBUG(D_ERR, "Unknown packet received"); + PERR("Unknown packet received"); } break; @@ -143,7 +140,7 @@ static void int_irq(struct urb *urb) break; default: - PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status); + PERR("URB error %i, resubmitting", urb->status); urb->status = 0; ret = 0; } @@ -229,7 +226,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { - PDEBUG(D_ERR, "submit int URB failed with error %i", ret); + PERR("submit int URB failed with error %i", ret); goto error_submit; } gspca_dev->int_urb = urb; @@ -315,7 +312,7 @@ static void fill_frame(struct gspca_dev *gspca_dev, if (gspca_dev->frozen) return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + PERR("urb status: %d", urb->status); urb->status = 0; goto resubmit; } @@ -388,7 +385,7 @@ static void bulk_irq(struct urb *urb) if (gspca_dev->frozen) return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + PERR("urb status: %d", urb->status); urb->status = 0; goto resubmit; } @@ -460,7 +457,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, /* append the packet to the frame buffer */ if (len > 0) { if (gspca_dev->image_len + len > gspca_dev->frsz) { - PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d", + PERR("frame overflow %d > %d", gspca_dev->image_len + len, gspca_dev->frsz); packet_type = DISCARD_PACKET; @@ -570,11 +567,10 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) gspca_dev->urb[i] = NULL; usb_kill_urb(urb); - if (urb->transfer_buffer != NULL) - usb_free_coherent(gspca_dev->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); + usb_free_coherent(gspca_dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); usb_free_urb(urb); } } @@ -960,9 +956,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* the bandwidth is not wide enough * negotiate or try a lower alternate setting */ retry: - PDEBUG(D_ERR|D_STREAM, - "alt %d - bandwidth not wide enough - trying again", - alt); + PERR("alt %d - bandwidth not wide enough, trying again", alt); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); @@ -984,7 +978,6 @@ out: static void gspca_set_default_mode(struct gspca_dev *gspca_dev) { - struct gspca_ctrl *ctrl; int i; i = gspca_dev->cam.nmodes - 1; /* take the highest mode */ @@ -993,17 +986,8 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) gspca_dev->height = gspca_dev->cam.cam_mode[i].height; gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat; - /* set the current control values to their default values - * which may have changed in sd_init() */ /* does nothing if ctrl_handler == NULL */ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); - ctrl = gspca_dev->cam.ctrls; - if (ctrl != NULL) { - for (i = 0; - i < gspca_dev->sd_desc->nctrls; - i++, ctrl++) - ctrl->val = ctrl->def; - } } static int wxh_to_mode(struct gspca_dev *gspca_dev, @@ -1055,7 +1039,7 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct gspca_dev *gspca_dev = video_drvdata(file); @@ -1137,10 +1121,9 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; -#ifdef GSPCA_DEBUG - if (gspca_debug & D_CONF) - PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); -#endif + PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap", + fmt->fmt.pix.pixelformat, w, h); + /* search the closest mode for width and height */ mode = wxh_to_mode(gspca_dev, w, h); @@ -1153,8 +1136,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, fmt->fmt.pix.pixelformat); if (mode2 >= 0) mode = mode2; -/* else - ; * no chance, return this mode */ } fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; /* some drivers use priv internally, zero it before giving it to @@ -1290,15 +1271,6 @@ static int dev_open(struct file *file) if (!try_module_get(gspca_dev->module)) return -ENODEV; -#ifdef GSPCA_DEBUG - /* activate the v4l2 debug */ - if (gspca_debug & D_V4L2) - gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL - | V4L2_DEBUG_IOCTL_ARG; - else - gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL - | V4L2_DEBUG_IOCTL_ARG); -#endif return v4l2_fh_open(file); } @@ -1357,134 +1329,6 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int get_ctrl(struct gspca_dev *gspca_dev, - int id) -{ - const struct ctrl *ctrls; - int i; - - for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrls++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (id == ctrls->qctrl.id) - return i; - } - return -1; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *q_ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - struct gspca_ctrl *gspca_ctrl; - int i, idx; - u32 id; - - id = q_ctrl->id; - if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { - id &= V4L2_CTRL_ID_MASK; - id++; - idx = -1; - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) - continue; - if (idx >= 0 - && gspca_dev->sd_desc->ctrls[i].qctrl.id - > gspca_dev->sd_desc->ctrls[idx].qctrl.id) - continue; - idx = i; - } - } else { - idx = get_ctrl(gspca_dev, id); - } - if (idx < 0) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl); - if (gspca_dev->cam.ctrls != NULL) { - gspca_ctrl = &gspca_dev->cam.ctrls[idx]; - q_ctrl->default_value = gspca_ctrl->def; - q_ctrl->minimum = gspca_ctrl->min; - q_ctrl->maximum = gspca_ctrl->max; - } - if (gspca_dev->ctrl_inac & (1 << idx)) - q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - struct gspca_ctrl *gspca_ctrl; - int idx; - - idx = get_ctrl(gspca_dev, ctrl->id); - if (idx < 0) - return -EINVAL; - if (gspca_dev->ctrl_inac & (1 << idx)) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - if (gspca_dev->cam.ctrls != NULL) { - gspca_ctrl = &gspca_dev->cam.ctrls[idx]; - if (ctrl->value < gspca_ctrl->min - || ctrl->value > gspca_ctrl->max) - return -ERANGE; - } else { - gspca_ctrl = NULL; - if (ctrl->value < ctrls->qctrl.minimum - || ctrl->value > ctrls->qctrl.maximum) - return -ERANGE; - } - PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - gspca_dev->usb_err = 0; - if (ctrls->set != NULL) - return ctrls->set(gspca_dev, ctrl->value); - if (gspca_ctrl != NULL) { - gspca_ctrl->val = ctrl->value; - if (ctrls->set_control != NULL - && gspca_dev->streaming) - ctrls->set_control(gspca_dev); - } - return gspca_dev->usb_err; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - int idx; - - idx = get_ctrl(gspca_dev, ctrl->id); - if (idx < 0) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - - gspca_dev->usb_err = 0; - if (ctrls->get != NULL) - return ctrls->get(gspca_dev, &ctrl->value); - if (gspca_dev->cam.ctrls != NULL) - ctrl->value = gspca_dev->cam.ctrls[idx].val; - return 0; -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - if (!gspca_dev->sd_desc->querymenu) - return -ENOTTY; - return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); -} - static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { @@ -1621,14 +1465,8 @@ static int vidioc_streamon(struct file *file, void *priv, if (ret < 0) goto out; } -#ifdef GSPCA_DEBUG - if (gspca_debug & D_STREAM) { - PDEBUG_MODE("stream on OK", - gspca_dev->pixfmt, - gspca_dev->width, - gspca_dev->height); - } -#endif + PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", gspca_dev->pixfmt, + gspca_dev->width, gspca_dev->height); ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1879,8 +1717,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, frame->v4l2_buf.bytesused)) { - PDEBUG(D_ERR|D_STREAM, - "dqbuf cp to user failed"); + PERR("dqbuf cp to user failed"); ret = -EFAULT; } } @@ -2092,8 +1929,7 @@ static ssize_t dev_read(struct file *file, char __user *data, count = frame->v4l2_buf.bytesused; ret = copy_to_user(data, frame->data, count); if (ret != 0) { - PDEBUG(D_ERR|D_STREAM, - "read cp to user lack %d / %zd", ret, count); + PERR("read cp to user lack %d / %zd", ret, count); ret = -EFAULT; goto out; } @@ -2125,10 +1961,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_streamon = vidioc_streamon, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, @@ -2157,22 +1989,6 @@ static const struct video_device gspca_template = { .release = video_device_release_empty, /* We use v4l2_dev.release */ }; -/* initialize the controls */ -static void ctrls_init(struct gspca_dev *gspca_dev) -{ - struct gspca_ctrl *ctrl; - int i; - - for (i = 0, ctrl = gspca_dev->cam.ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrl++) { - ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value; - ctrl->val = ctrl->def; - ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum; - ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum; - } -} - /* * probe and create a new gspca device * @@ -2249,8 +2065,6 @@ int gspca_dev_probe2(struct usb_interface *intf, ret = sd_desc->config(gspca_dev, id); if (ret < 0) goto out; - if (gspca_dev->cam.ctrls != NULL) - ctrls_init(gspca_dev); ret = sd_desc->init(gspca_dev); if (ret < 0) goto out; @@ -2450,10 +2264,6 @@ static void __exit gspca_exit(void) module_init(gspca_init); module_exit(gspca_exit); -#ifdef GSPCA_DEBUG module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, - "Debug (bit) 0x01:error 0x02:probe 0x04:config" - " 0x08:stream 0x10:frame 0x20:packet" - " 0x0100: v4l2"); -#endif + "1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo"); diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index 5559932..ef8efeb 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -10,30 +10,26 @@ #include <media/v4l2-device.h> #include <linux/mutex.h> -/* compilation option */ -/*#define GSPCA_DEBUG 1*/ -#ifdef GSPCA_DEBUG -/* GSPCA our debug messages */ + +/* GSPCA debug codes */ + +#define D_PROBE 1 +#define D_CONF 2 +#define D_STREAM 3 +#define D_FRAM 4 +#define D_PACK 5 +#define D_USBI 6 +#define D_USBO 7 + extern int gspca_debug; -#define PDEBUG(level, fmt, ...) \ -do { \ - if (gspca_debug & (level)) \ - pr_info(fmt, ##__VA_ARGS__); \ -} while (0) -#define D_ERR 0x01 -#define D_PROBE 0x02 -#define D_CONF 0x04 -#define D_STREAM 0x08 -#define D_FRAM 0x10 -#define D_PACK 0x20 -#define D_USBI 0x00 -#define D_USBO 0x00 -#define D_V4L2 0x0100 -#else -#define PDEBUG(level, fmt, ...) do {} while(0) -#endif + +#define PDEBUG(level, fmt, ...) \ + v4l2_dbg(level, gspca_debug, &gspca_dev->v4l2_dev, fmt, ##__VA_ARGS__) + +#define PERR(fmt, ...) \ + v4l2_err(&gspca_dev->v4l2_dev, fmt, ##__VA_ARGS__) #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* image transfers */ @@ -46,20 +42,11 @@ struct framerates { int nrates; }; -/* control definition */ -struct gspca_ctrl { - s16 val; /* current value */ - s16 def; /* default value */ - s16 min, max; /* minimum and maximum values */ -}; - /* device information - set at probe time */ struct cam { const struct v4l2_pix_format *cam_mode; /* size nmodes */ const struct framerates *mode_framerates; /* must have size nmodes, * just like cam_mode */ - struct gspca_ctrl *ctrls; /* control table - size nctrls */ - /* may be NULL */ u32 bulk_size; /* buffer size when image transfer by bulk */ u32 input_flags; /* value for ENUM_INPUT status flags */ u8 nmodes; /* size of cam_mode */ @@ -87,14 +74,14 @@ typedef int (*cam_get_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); typedef int (*cam_set_jpg_op) (struct gspca_dev *, const struct v4l2_jpegcompression *); -typedef int (*cam_reg_op) (struct gspca_dev *, +typedef int (*cam_get_reg_op) (struct gspca_dev *, struct v4l2_dbg_register *); +typedef int (*cam_set_reg_op) (struct gspca_dev *, + const struct v4l2_dbg_register *); typedef int (*cam_ident_op) (struct gspca_dev *, struct v4l2_dbg_chip_ident *); typedef void (*cam_streamparm_op) (struct gspca_dev *, struct v4l2_streamparm *); -typedef int (*cam_qmnu_op) (struct gspca_dev *, - struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, u8 *data, int len); @@ -102,20 +89,10 @@ typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev, u8 *data, int len); -struct ctrl { - struct v4l2_queryctrl qctrl; - int (*set)(struct gspca_dev *, __s32); - int (*get)(struct gspca_dev *, __s32 *); - cam_v_op set_control; -}; - /* subdriver description */ struct sd_desc { /* information */ const char *name; /* sub-driver name */ -/* controls */ - const struct ctrl *ctrls; /* static control definition */ - int nctrls; /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ @@ -130,12 +107,11 @@ struct sd_desc { cam_v_op dq_callback; /* called when a frame has been dequeued */ cam_get_jpg_op get_jcomp; cam_set_jpg_op set_jcomp; - cam_qmnu_op querymenu; cam_streamparm_op get_streamparm; cam_streamparm_op set_streamparm; #ifdef CONFIG_VIDEO_ADV_DEBUG - cam_reg_op set_register; - cam_reg_op get_register; + cam_set_reg_op set_register; + cam_get_reg_op get_register; #endif cam_ident_op get_chip_ident; #if IS_ENABLED(CONFIG_INPUT) @@ -174,8 +150,6 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ - unsigned ctrl_dis; /* disabled controls (bit map) */ - unsigned ctrl_inac; /* inactive controls (bit map) */ struct v4l2_ctrl_handler ctrl_handler; /* autogain and exposure or gain control cluster, these are global as diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c index 1ba29fe..8da3dde 100644 --- a/drivers/media/usb/gspca/jeilinj.c +++ b/drivers/media/usb/gspca/jeilinj.c @@ -266,7 +266,7 @@ static int jlj_start(struct gspca_dev *gspca_dev) msleep(2); setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); if (gspca_dev->usb_err < 0) - PDEBUG(D_ERR, "Start streaming command failed"); + PERR("Start streaming command failed"); return gspca_dev->usb_err; } diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c index 61e25db..39c96bb 100644 --- a/drivers/media/usb/gspca/konica.c +++ b/drivers/media/usb/gspca/konica.c @@ -277,7 +277,7 @@ static void sd_isoc_irq(struct urb *urb) if (gspca_dev->frozen) return; #endif - PDEBUG(D_ERR, "urb status: %d", urb->status); + PERR("urb status: %d", urb->status); st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) pr_err("resubmit urb error %d\n", st); @@ -295,33 +295,30 @@ static void sd_isoc_irq(struct urb *urb) sd->last_data_urb = NULL; if (!data_urb || data_urb->start_frame != status_urb->start_frame) { - PDEBUG(D_ERR|D_PACK, "lost sync on frames"); + PERR("lost sync on frames"); goto resubmit; } if (data_urb->number_of_packets != status_urb->number_of_packets) { - PDEBUG(D_ERR|D_PACK, - "no packets does not match, data: %d, status: %d", - data_urb->number_of_packets, - status_urb->number_of_packets); + PERR("no packets does not match, data: %d, status: %d", + data_urb->number_of_packets, + status_urb->number_of_packets); goto resubmit; } for (i = 0; i < status_urb->number_of_packets; i++) { if (data_urb->iso_frame_desc[i].status || status_urb->iso_frame_desc[i].status) { - PDEBUG(D_ERR|D_PACK, - "pkt %d data-status %d, status-status %d", i, - data_urb->iso_frame_desc[i].status, - status_urb->iso_frame_desc[i].status); + PERR("pkt %d data-status %d, status-status %d", i, + data_urb->iso_frame_desc[i].status, + status_urb->iso_frame_desc[i].status); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } if (status_urb->iso_frame_desc[i].actual_length != 1) { - PDEBUG(D_ERR|D_PACK, - "bad status packet length %d", - status_urb->iso_frame_desc[i].actual_length); + PERR("bad status packet length %d", + status_urb->iso_frame_desc[i].actual_length); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } @@ -366,12 +363,11 @@ resubmit: if (data_urb) { st = usb_submit_urb(data_urb, GFP_ATOMIC); if (st < 0) - PDEBUG(D_ERR|D_PACK, - "usb_submit_urb(data_urb) ret %d", st); + PERR("usb_submit_urb(data_urb) ret %d", st); } st = usb_submit_urb(status_urb, GFP_ATOMIC); if (st < 0) - pr_err("usb_submit_urb(status_urb) ret %d\n", st); + PERR("usb_submit_urb(status_urb) ret %d\n", st); } static int sd_s_ctrl(struct v4l2_ctrl *ctrl) diff --git a/drivers/media/usb/gspca/m5602/m5602_bridge.h b/drivers/media/usb/gspca/m5602/m5602_bridge.h index 51af3ee..19eb1a6 100644 --- a/drivers/media/usb/gspca/m5602/m5602_bridge.h +++ b/drivers/media/usb/gspca/m5602/m5602_bridge.h @@ -136,16 +136,33 @@ struct sd { /* A pointer to the currently connected sensor */ const struct m5602_sensor *sensor; - struct sd_desc *desc; - - /* Sensor private data */ - void *sensor_priv; - /* The current frame's id, used to detect frame boundaries */ u8 frame_id; /* The current frame count */ u32 frame_count; + + /* Camera rotation polling thread for "flipable" cams */ + struct task_struct *rotation_thread; + + struct { /* auto-white-bal + green/red/blue balance control cluster */ + struct v4l2_ctrl *auto_white_bal; + struct v4l2_ctrl *red_bal; + struct v4l2_ctrl *blue_bal; + struct v4l2_ctrl *green_bal; + }; + struct { /* autoexpo / expo cluster */ + struct v4l2_ctrl *autoexpo; + struct v4l2_ctrl *expo; + }; + struct { /* autogain / gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct { /* hflip/vflip cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; }; int m5602_read_bridge( diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c index ed22638..d926e62 100644 --- a/drivers/media/usb/gspca/m5602/m5602_core.c +++ b/drivers/media/usb/gspca/m5602/m5602_core.c @@ -41,6 +41,7 @@ MODULE_DEVICE_TABLE(usb, m5602_table); int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *) sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -62,6 +63,7 @@ int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *) sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -98,6 +100,7 @@ int m5602_read_sensor(struct sd *sd, const u8 address, u8 *i2c_data, const u8 len) { int err, i; + struct gspca_dev *gspca_dev = (struct gspca_dev *) sd; if (!len || len > sd->sensor->i2c_regW) return -EINVAL; @@ -147,6 +150,7 @@ int m5602_write_sensor(struct sd *sd, const u8 address, { int err, i; u8 *p; + struct gspca_dev *gspca_dev = (struct gspca_dev *) sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -252,6 +256,16 @@ static int m5602_init(struct gspca_dev *gspca_dev) return err; } +static int m5602_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->sensor->init_controls) + return 0; + + return sd->sensor->init_controls(sd); +} + static int m5602_start_transfer(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -336,11 +350,12 @@ static void m5602_stop_transfer(struct gspca_dev *gspca_dev) sd->sensor->stop(sd); } -/* sub-driver description, the ctrl and nctrl is filled at probe time */ -static struct sd_desc sd_desc = { +/* sub-driver description */ +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .config = m5602_configure, .init = m5602_init, + .init_controls = m5602_init_controls, .start = m5602_start_transfer, .stopN = m5602_stop_transfer, .pkt_scan = m5602_urb_complete @@ -355,7 +370,6 @@ static int m5602_configure(struct gspca_dev *gspca_dev, int err; cam = &gspca_dev->cam; - sd->desc = &sd_desc; if (dump_bridge) m5602_dump_bridge(sd); @@ -368,7 +382,7 @@ static int m5602_configure(struct gspca_dev *gspca_dev, return 0; fail: - PDEBUG(D_ERR, "ALi m5602 webcam failed"); + PERR("ALi m5602 webcam failed"); cam->cam_mode = NULL; cam->nmodes = 0; diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c index 6268aa2..cfa4663 100644 --- a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c @@ -20,22 +20,8 @@ #include "m5602_mt9m111.h" -static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl); +static void mt9m111_dump_registers(struct sd *sd); static struct v4l2_pix_format mt9m111_modes[] = { { @@ -50,118 +36,27 @@ static struct v4l2_pix_format mt9m111_modes[] = { } }; -static const struct ctrl mt9m111_ctrls[] = { -#define VFLIP_IDX 0 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_vflip, - .get = mt9m111_get_vflip - }, -#define HFLIP_IDX 1 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_hflip, - .get = mt9m111_get_hflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0, - .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, - .step = 1, - .default_value = MT9M111_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_gain, - .get = mt9m111_get_gain - }, -#define AUTO_WHITE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = mt9m111_set_auto_white_balance, - .get = mt9m111_get_auto_white_balance - }, -#define GREEN_BALANCE_IDX 4 - { - { - .id = M5602_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_GREEN_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_green_balance, - .get = mt9m111_get_green_balance - }, -#define BLUE_BALANCE_IDX 5 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_blue_balance, - .get = mt9m111_get_blue_balance - }, -#define RED_BALANCE_IDX 5 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_red_balance, - .get = mt9m111_get_red_balance - }, +static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = { + .s_ctrl = mt9m111_s_ctrl, }; -static void mt9m111_dump_registers(struct sd *sd); +static const struct v4l2_ctrl_config mt9m111_greenbal_cfg = { + .ops = &mt9m111_ctrl_ops, + .id = M5602_V4L2_CID_GREEN_BALANCE, + .name = "Green Balance", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x7ff, + .step = 1, + .def = MT9M111_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, +}; int mt9m111_probe(struct sd *sd) { u8 data[2] = {0x00, 0x00}; int i; - s32 *sensor_settings; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { if (force_sensor == MT9M111_SENSOR) { @@ -200,19 +95,8 @@ int mt9m111_probe(struct sd *sd) return -ENODEV; sensor_found: - sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = mt9m111_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes); - sd->desc->ctrls = mt9m111_ctrls; - sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls); - - for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++) - sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; return 0; } @@ -220,7 +104,6 @@ sensor_found: int mt9m111_init(struct sd *sd) { int i, err = 0; - s32 *sensor_settings = sd->sensor_priv; /* Init the sensor */ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) { @@ -241,30 +124,45 @@ int mt9m111_init(struct sd *sd) if (dump_sensor) mt9m111_dump_registers(sd); - err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_green_balance(&sd->gspca_dev, - sensor_settings[GREEN_BALANCE_IDX]); - if (err < 0) - return err; + return 0; +} - err = mt9m111_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; +int mt9m111_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + + sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 0); + sd->green_bal = v4l2_ctrl_new_custom(hdl, &mt9m111_greenbal_cfg, NULL); + sd->red_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 0x7ff, 1, + MT9M111_RED_GAIN_DEFAULT); + sd->blue_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 0x7ff, 1, + MT9M111_BLUE_GAIN_DEFAULT); + + v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_GAIN, 0, + (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, 1, + MT9M111_DEFAULT_GAIN); + + sd->hflip = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } - err = mt9m111_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; + v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); - return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + return 0; } int mt9m111_start(struct sd *sd) @@ -272,7 +170,7 @@ int mt9m111_start(struct sd *sd) int i, err = 0; u8 data[2]; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1; int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; @@ -333,26 +231,11 @@ int mt9m111_start(struct sd *sd) switch (width) { case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); - data[0] = MT9M111_RMB_OVER_SIZED; - data[1] = MT9M111_RMB_ROW_SKIP_2X | - MT9M111_RMB_COLUMN_SKIP_2X | - (sensor_settings[VFLIP_IDX] << 0) | - (sensor_settings[HFLIP_IDX] << 1); - - err = m5602_write_sensor(sd, - MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + PDEBUG(D_CONF, "Configuring camera for VGA mode"); break; case 320: - PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); - data[0] = MT9M111_RMB_OVER_SIZED; - data[1] = MT9M111_RMB_ROW_SKIP_4X | - MT9M111_RMB_COLUMN_SKIP_4X | - (sensor_settings[VFLIP_IDX] << 0) | - (sensor_settings[HFLIP_IDX] << 1); - err = m5602_write_sensor(sd, - MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + PDEBUG(D_CONF, "Configuring camera for QVGA mode"); break; } return err; @@ -361,105 +244,46 @@ int mt9m111_start(struct sd *sd) void mt9m111_disconnect(struct sd *sd) { sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2] = {0x00, 0x00}; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - - sensor_settings[VFLIP_IDX] = val; - - /* The mt9m111 is flipped by default */ - val = !val; - - /* Set the correct page map */ - err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - if (err < 0) - return err; - - data[1] = (data[1] & 0xfe) | val; - err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, - data, 2); - return err; -} - -static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; } -static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int mt9m111_set_hvflip(struct gspca_dev *gspca_dev) { int err; u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + int hflip; + int vflip; - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - - sensor_settings[HFLIP_IDX] = val; + PDEBUG(D_CONF, "Set hvflip to %d %d", sd->hflip->val, sd->vflip->val); /* The mt9m111 is flipped by default */ - val = !val; + hflip = !sd->hflip->val; + vflip = !sd->vflip->val; /* Set the correct page map */ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) return err; - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - if (err < 0) - return err; - - data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02); + data[0] = MT9M111_RMB_OVER_SIZED; + if (gspca_dev->width == 640) { + data[1] = MT9M111_RMB_ROW_SKIP_2X | + MT9M111_RMB_COLUMN_SKIP_2X | + (hflip << 1) | vflip; + } else { + data[1] = MT9M111_RMB_ROW_SKIP_4X | + MT9M111_RMB_COLUMN_SKIP_4X | + (hflip << 1) | vflip; + } err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); return err; } -static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - - return 0; -} - static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; int err; u8 data[2]; @@ -467,33 +291,19 @@ static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, if (err < 0) return err; - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01; data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1)); err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); - PDEBUG(D_V4L2, "Set auto white balance %d", val); + PDEBUG(D_CONF, "Set auto white balance %d", val); return err; } -static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) { - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read auto white balance %d", *val); - return 0; -} - static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err, tmp; u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[GAIN_IDX] = val; /* Set the correct page map */ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); @@ -518,7 +328,7 @@ static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) data[1] = (tmp & 0xff); data[0] = (tmp & 0xff00) >> 8; - PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp, + PDEBUG(D_CONF, "tmp=%d, data[1]=%d, data[0]=%d", tmp, data[1], data[0]); err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, @@ -532,13 +342,11 @@ static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) int err; u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[GREEN_BALANCE_IDX] = val; data[1] = (val & 0xff); data[0] = (val & 0xff00) >> 8; - PDEBUG(D_V4L2, "Set green balance %d", val); + PDEBUG(D_CONF, "Set green balance %d", val); err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN, data, 2); if (err < 0) @@ -548,66 +356,68 @@ static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) data, 2); } -static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GREEN_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read green balance %d", *val); - return 0; -} - static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[BLUE_BALANCE_IDX] = val; data[1] = (val & 0xff); data[0] = (val & 0xff00) >> 8; - PDEBUG(D_V4L2, "Set blue balance %d", val); + PDEBUG(D_CONF, "Set blue balance %d", val); return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN, data, 2); } -static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue balance %d", *val); - return 0; -} - static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[RED_BALANCE_IDX] = val; data[1] = (val & 0xff); data[0] = (val & 0xff00) >> 8; - PDEBUG(D_V4L2, "Set red balance %d", val); + PDEBUG(D_CONF, "Set red balance %d", val); return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN, data, 2); } -static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) { + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + int err; - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red balance %d", *val); - return 0; + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = mt9m111_set_auto_white_balance(gspca_dev, ctrl->val); + if (err || ctrl->val) + return err; + err = mt9m111_set_green_balance(gspca_dev, sd->green_bal->val); + if (err) + return err; + err = mt9m111_set_red_balance(gspca_dev, sd->red_bal->val); + if (err) + return err; + err = mt9m111_set_blue_balance(gspca_dev, sd->blue_bal->val); + break; + case V4L2_CID_GAIN: + err = mt9m111_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + err = mt9m111_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; + } + + return err; } static void mt9m111_dump_registers(struct sd *sd) diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.h b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h index 8c672b5..07448d3 100644 --- a/drivers/media/usb/gspca/m5602/m5602_mt9m111.h +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h @@ -110,6 +110,7 @@ extern bool dump_sensor; int mt9m111_probe(struct sd *sd); int mt9m111_init(struct sd *sd); +int mt9m111_init_controls(struct sd *sd); int mt9m111_start(struct sd *sd); void mt9m111_disconnect(struct sd *sd); @@ -121,6 +122,7 @@ static const struct m5602_sensor mt9m111 = { .probe = mt9m111_probe, .init = mt9m111_init, + .init_controls = mt9m111_init_controls, .disconnect = mt9m111_disconnect, .start = mt9m111_start, }; diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.c b/drivers/media/usb/gspca/m5602/m5602_ov7660.c index 9a14835..64b3b03 100644 --- a/drivers/media/usb/gspca/m5602/m5602_ov7660.c +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.c @@ -20,111 +20,8 @@ #include "m5602_ov7660.h" -static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl ov7660_ctrls[] = { -#define GAIN_IDX 1 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = OV7660_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov7660_set_gain, - .get = ov7660_get_gain - }, -#define BLUE_BALANCE_IDX 2 -#define RED_BALANCE_IDX 3 -#define AUTO_WHITE_BALANCE_IDX 4 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_white_balance, - .get = ov7660_get_auto_white_balance - }, -#define AUTO_GAIN_CTRL_IDX 5 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto gain control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_gain, - .get = ov7660_get_auto_gain - }, -#define AUTO_EXPOSURE_IDX 6 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_exposure, - .get = ov7660_get_auto_exposure - }, -#define HFLIP_IDX 7 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov7660_set_hflip, - .get = ov7660_get_hflip - }, -#define VFLIP_IDX 8 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov7660_set_vflip, - .get = ov7660_get_vflip - }, - -}; +static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl); +static void ov7660_dump_registers(struct sd *sd); static struct v4l2_pix_format ov7660_modes[] = { { @@ -140,15 +37,15 @@ static struct v4l2_pix_format ov7660_modes[] = { } }; -static void ov7660_dump_registers(struct sd *sd); +static const struct v4l2_ctrl_ops ov7660_ctrl_ops = { + .s_ctrl = ov7660_s_ctrl, +}; int ov7660_probe(struct sd *sd) { int err = 0, i; u8 prod_id = 0, ver_id = 0; - s32 *sensor_settings; - if (force_sensor) { if (force_sensor == OV7660_SENSOR) { pr_info("Forcing an %s sensor\n", ov7660.name); @@ -191,27 +88,15 @@ int ov7660_probe(struct sd *sd) return -ENODEV; sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = ov7660_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); - sd->desc->ctrls = ov7660_ctrls; - sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls); - - for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++) - sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; return 0; } int ov7660_init(struct sd *sd) { - int i, err = 0; - s32 *sensor_settings = sd->sensor_priv; + int i, err; /* Init the sensor */ for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { @@ -226,38 +111,47 @@ int ov7660_init(struct sd *sd) err = m5602_write_sensor(sd, init_ov7660[i][1], data, 1); } + if (err < 0) + return err; } if (dump_sensor) ov7660_dump_registers(sd); - err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; + return 0; +} - err = ov7660_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; +int ov7660_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - err = ov7660_set_auto_gain(&sd->gspca_dev, - sensor_settings[AUTO_GAIN_CTRL_IDX]); - if (err < 0) - return err; + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 6); - err = ov7660_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - if (err < 0) - return err; - err = ov7660_set_hflip(&sd->gspca_dev, - sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; + v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(hdl, &ov7660_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); - err = ov7660_set_vflip(&sd->gspca_dev, - sensor_settings[VFLIP_IDX]); + sd->autogain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->gain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_GAIN, 0, + 255, 1, OV7660_DEFAULT_GAIN); - return err; + sd->hflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); + + return 0; } int ov7660_start(struct sd *sd) @@ -275,56 +169,29 @@ void ov7660_disconnect(struct sd *sd) ov7660_stop(sd); sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - return 0; } static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; - u8 i2c_data; + u8 i2c_data = val; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Setting gain to %d", val); - - sensor_settings[GAIN_IDX] = val; + PDEBUG(D_CONF, "Setting gain to %d", val); err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); return err; } - -static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - return 0; -} - static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto white balance to %d", val); + PDEBUG(D_CONF, "Set auto white balance to %d", val); - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); if (err < 0) return err; @@ -335,26 +202,14 @@ static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, return err; } -static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; - PDEBUG(D_V4L2, "Read auto gain control %d", *val); - return 0; -} - static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto gain control to %d", val); + PDEBUG(D_CONF, "Set auto gain control to %d", val); - sensor_settings[AUTO_GAIN_CTRL_IDX] = val; err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); if (err < 0) return err; @@ -364,94 +219,69 @@ static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); } -static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read auto exposure control %d", *val); - return 0; -} - static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto exposure control to %d", val); + PDEBUG(D_CONF, "Set auto exposure control to %d", val); - sensor_settings[AUTO_EXPOSURE_IDX] = val; err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); if (err < 0) return err; + val = (val == V4L2_EXPOSURE_AUTO); i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); } -static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return 0; -} - -static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int ov7660_set_hvflip(struct gspca_dev *gspca_dev) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + PDEBUG(D_CONF, "Set hvflip to %d, %d", sd->hflip->val, sd->vflip->val); - sensor_settings[HFLIP_IDX] = val; - - i2c_data = ((val & 0x01) << 5) | - (sensor_settings[VFLIP_IDX] << 4); + i2c_data = (sd->hflip->val << 5) | (sd->vflip->val << 4); err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); return err; } -static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl) { + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - sensor_settings[VFLIP_IDX] = val; - i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); - err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); - if (err < 0) - return err; - - /* When vflip is toggled we need to readjust the bridge hsync/vsync */ - if (gspca_dev->streaming) - err = ov7660_start(sd); + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = ov7660_set_auto_white_balance(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + err = ov7660_set_auto_exposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + err = ov7660_set_auto_gain(gspca_dev, ctrl->val); + if (err || ctrl->val) + return err; + err = ov7660_set_gain(gspca_dev, sd->gain->val); + break; + case V4L2_CID_HFLIP: + err = ov7660_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; + } return err; } diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.h b/drivers/media/usb/gspca/m5602/m5602_ov7660.h index 2b6a13b..6fece1c 100644 --- a/drivers/media/usb/gspca/m5602/m5602_ov7660.h +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.h @@ -90,6 +90,8 @@ extern bool dump_sensor; int ov7660_probe(struct sd *sd); int ov7660_init(struct sd *sd); +int ov7660_init(struct sd *sd); +int ov7660_init_controls(struct sd *sd); int ov7660_start(struct sd *sd); int ov7660_stop(struct sd *sd); void ov7660_disconnect(struct sd *sd); @@ -100,6 +102,7 @@ static const struct m5602_sensor ov7660 = { .i2c_regW = 1, .probe = ov7660_probe, .init = ov7660_init, + .init_controls = ov7660_init_controls, .start = ov7660_start, .stop = ov7660_stop, .disconnect = ov7660_disconnect, diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.c b/drivers/media/usb/gspca/m5602/m5602_ov9650.c index 2114a8b..59bc62b 100644 --- a/drivers/media/usb/gspca/m5602/m5602_ov9650.c +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.c @@ -20,26 +20,8 @@ #include "m5602_ov9650.h" -static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl); +static void ov9650_dump_registers(struct sd *sd); /* Vertically and horizontally flips the image if matched, needed for machines where the sensor is mounted upside down */ @@ -113,140 +95,6 @@ static {} }; -static const struct ctrl ov9650_ctrls[] = { -#define EXPOSURE_IDX 0 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x1ff, - .step = 0x4, - .default_value = EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_exposure, - .get = ov9650_get_exposure - }, -#define GAIN_IDX 1 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x3ff, - .step = 0x1, - .default_value = GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_gain, - .get = ov9650_get_gain - }, -#define RED_BALANCE_IDX 2 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_red_balance, - .get = ov9650_get_red_balance - }, -#define BLUE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_blue_balance, - .get = ov9650_get_blue_balance - }, -#define HFLIP_IDX 4 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_hflip, - .get = ov9650_get_hflip - }, -#define VFLIP_IDX 5 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_vflip, - .get = ov9650_get_vflip - }, -#define AUTO_WHITE_BALANCE_IDX 6 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_white_balance, - .get = ov9650_get_auto_white_balance - }, -#define AUTO_GAIN_CTRL_IDX 7 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto gain control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_gain, - .get = ov9650_get_auto_gain - }, -#define AUTO_EXPOSURE_IDX 8 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_exposure, - .get = ov9650_get_auto_exposure - } - -}; - static struct v4l2_pix_format ov9650_modes[] = { { 176, @@ -291,13 +139,15 @@ static struct v4l2_pix_format ov9650_modes[] = { } }; -static void ov9650_dump_registers(struct sd *sd); +static const struct v4l2_ctrl_ops ov9650_ctrl_ops = { + .s_ctrl = ov9650_s_ctrl, +}; int ov9650_probe(struct sd *sd) { int err = 0; u8 prod_id = 0, ver_id = 0, i; - s32 *sensor_settings; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { if (force_sensor == OV9650_SENSOR) { @@ -338,19 +188,9 @@ int ov9650_probe(struct sd *sd) return -ENODEV; sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = ov9650_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); - sd->desc->ctrls = ov9650_ctrls; - sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls); - for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++) - sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; return 0; } @@ -358,7 +198,6 @@ int ov9650_init(struct sd *sd) { int i, err = 0; u8 data; - s32 *sensor_settings = sd->sensor_priv; if (dump_sensor) ov9650_dump_registers(sd); @@ -372,46 +211,52 @@ int ov9650_init(struct sd *sd) err = m5602_write_bridge(sd, init_ov9650[i][1], data); } - err = ov9650_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = ov9650_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; + return 0; +} - err = ov9650_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - if (err < 0) - return err; +int ov9650_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 9); + + sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 255, 1, + RED_GAIN_DEFAULT); + sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 255, 1, + BLUE_GAIN_DEFAULT); + + sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); + sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE, + 0, 0x1ff, 4, EXPOSURE_DEFAULT); + + sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0, + 0x3ff, 1, GAIN_DEFAULT); + + sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } - err = ov9650_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; + v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false); + v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); - err = ov9650_set_auto_gain(&sd->gspca_dev, - sensor_settings[AUTO_GAIN_CTRL_IDX]); - return err; + return 0; } int ov9650_start(struct sd *sd) @@ -419,17 +264,17 @@ int ov9650_start(struct sd *sd) u8 data; int i, err = 0; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; int hor_offs = OV9650_LEFT_OFFSET; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if ((!dmi_check_system(ov9650_flip_dmi_table) && - sensor_settings[VFLIP_IDX]) || + sd->vflip->val) || (dmi_check_system(ov9650_flip_dmi_table) && - !sensor_settings[VFLIP_IDX])) + !sd->vflip->val)) ver_offs--; if (width <= 320) @@ -508,7 +353,7 @@ int ov9650_start(struct sd *sd) switch (width) { case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + PDEBUG(D_CONF, "Configuring camera for VGA mode"); data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | OV9650_RAW_RGB_SELECT; @@ -516,7 +361,7 @@ int ov9650_start(struct sd *sd) break; case 352: - PDEBUG(D_V4L2, "Configuring camera for CIF mode"); + PDEBUG(D_CONF, "Configuring camera for CIF mode"); data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | OV9650_RAW_RGB_SELECT; @@ -524,7 +369,7 @@ int ov9650_start(struct sd *sd) break; case 320: - PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); + PDEBUG(D_CONF, "Configuring camera for QVGA mode"); data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | OV9650_RAW_RGB_SELECT; @@ -532,7 +377,7 @@ int ov9650_start(struct sd *sd) break; case 176: - PDEBUG(D_V4L2, "Configuring camera for QCIF mode"); + PDEBUG(D_CONF, "Configuring camera for QCIF mode"); data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | OV9650_RAW_RGB_SELECT; @@ -553,29 +398,16 @@ void ov9650_disconnect(struct sd *sd) ov9650_stop(sd); sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read exposure %d", *val); - return 0; } static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - PDEBUG(D_V4L2, "Set exposure to %d", val); + PDEBUG(D_CONF, "Set exposure to %d", val); - sensor_settings[EXPOSURE_IDX] = val; /* The 6 MSBs */ i2c_data = (val >> 10) & 0x3f; err = m5602_write_sensor(sd, OV9650_AECHM, @@ -596,26 +428,13 @@ static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - return 0; -} - static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Setting gain to %d", val); - - sensor_settings[GAIN_IDX] = val; + PDEBUG(D_CONF, "Setting gain to %d", val); /* The 2 MSB */ /* Read the OV9650_VREF register first to avoid @@ -637,117 +456,46 @@ static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red gain %d", *val); - return 0; -} - static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set red gain to %d", val); - - sensor_settings[RED_BALANCE_IDX] = val; + PDEBUG(D_CONF, "Set red gain to %d", val); i2c_data = val & 0xff; err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); return err; } -static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue gain %d", *val); - - return 0; -} - static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set blue gain to %d", val); - sensor_settings[BLUE_BALANCE_IDX] = val; + PDEBUG(D_CONF, "Set blue gain to %d", val); i2c_data = val & 0xff; err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); return err; } -static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return 0; -} - -static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_hvflip(struct gspca_dev *gspca_dev) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + int hflip = sd->hflip->val; + int vflip = sd->vflip->val; - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - - sensor_settings[HFLIP_IDX] = val; - - if (!dmi_check_system(ov9650_flip_dmi_table)) - i2c_data = ((val & 0x01) << 5) | - (sensor_settings[VFLIP_IDX] << 4); - else - i2c_data = ((val & 0x01) << 5) | - (!sensor_settings[VFLIP_IDX] << 4); - - err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); - - return err; -} - -static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - sensor_settings[VFLIP_IDX] = val; + PDEBUG(D_CONF, "Set hvflip to %d %d", hflip, vflip); if (dmi_check_system(ov9650_flip_dmi_table)) - val = !val; + vflip = !vflip; - i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); + i2c_data = (hflip << 5) | (vflip << 4); err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (err < 0) return err; @@ -759,57 +507,34 @@ static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read auto exposure control %d", *val); - return 0; -} - static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto exposure control to %d", val); + PDEBUG(D_CONF, "Set auto exposure control to %d", val); - sensor_settings[AUTO_EXPOSURE_IDX] = val; err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) return err; + val = (val == V4L2_EXPOSURE_AUTO); i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); } -static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - return 0; -} - static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto white balance to %d", val); + PDEBUG(D_CONF, "Set auto white balance to %d", val); - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) return err; @@ -820,26 +545,14 @@ static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, return err; } -static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; - PDEBUG(D_V4L2, "Read auto gain control %d", *val); - return 0; -} - static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set auto gain control to %d", val); + PDEBUG(D_CONF, "Set auto gain control to %d", val); - sensor_settings[AUTO_GAIN_CTRL_IDX] = val; err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) return err; @@ -849,6 +562,48 @@ static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); } +static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; + int err; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val); + if (err || ctrl->val) + return err; + err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val); + if (err) + return err; + err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + err = ov9650_set_auto_exposure(gspca_dev, ctrl->val); + if (err || ctrl->val == V4L2_EXPOSURE_AUTO) + return err; + err = ov9650_set_exposure(gspca_dev, sd->expo->val); + break; + case V4L2_CID_AUTOGAIN: + err = ov9650_set_auto_gain(gspca_dev, ctrl->val); + if (err || ctrl->val) + return err; + err = ov9650_set_gain(gspca_dev, sd->gain->val); + break; + case V4L2_CID_HFLIP: + err = ov9650_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; + } + + return err; +} + static void ov9650_dump_registers(struct sd *sd) { int address; diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.h b/drivers/media/usb/gspca/m5602/m5602_ov9650.h index f7aa5bf..f9f5870 100644 --- a/drivers/media/usb/gspca/m5602/m5602_ov9650.h +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.h @@ -139,6 +139,7 @@ extern bool dump_sensor; int ov9650_probe(struct sd *sd); int ov9650_init(struct sd *sd); +int ov9650_init_controls(struct sd *sd); int ov9650_start(struct sd *sd); int ov9650_stop(struct sd *sd); void ov9650_disconnect(struct sd *sd); @@ -149,6 +150,7 @@ static const struct m5602_sensor ov9650 = { .i2c_regW = 1, .probe = ov9650_probe, .init = ov9650_init, + .init_controls = ov9650_init_controls, .start = ov9650_start, .stop = ov9650_stop, .disconnect = ov9650_disconnect, diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c index b877169..4bf5c43 100644 --- a/drivers/media/usb/gspca/m5602/m5602_po1030.c +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c @@ -20,28 +20,8 @@ #include "m5602_po1030.h" -static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, - __s32 val); -static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, - __s32 *val); +static int po1030_s_ctrl(struct v4l2_ctrl *ctrl); +static void po1030_dump_registers(struct sd *sd); static struct v4l2_pix_format po1030_modes[] = { { @@ -56,146 +36,26 @@ static struct v4l2_pix_format po1030_modes[] = { } }; -static const struct ctrl po1030_ctrls[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x4f, - .step = 0x1, - .default_value = PO1030_GLOBAL_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_gain, - .get = po1030_get_gain - }, -#define EXPOSURE_IDX 1 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x02ff, - .step = 0x1, - .default_value = PO1030_EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_exposure, - .get = po1030_get_exposure - }, -#define RED_BALANCE_IDX 2 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_red_balance, - .get = po1030_get_red_balance - }, -#define BLUE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_blue_balance, - .get = po1030_get_blue_balance - }, -#define HFLIP_IDX 4 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_hflip, - .get = po1030_get_hflip - }, -#define VFLIP_IDX 5 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_vflip, - .get = po1030_get_vflip - }, -#define AUTO_WHITE_BALANCE_IDX 6 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_auto_white_balance, - .get = po1030_get_auto_white_balance - }, -#define AUTO_EXPOSURE_IDX 7 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_auto_exposure, - .get = po1030_get_auto_exposure - }, -#define GREEN_BALANCE_IDX 8 - { - { - .id = M5602_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_GREEN_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_green_balance, - .get = po1030_get_green_balance - }, +static const struct v4l2_ctrl_ops po1030_ctrl_ops = { + .s_ctrl = po1030_s_ctrl, }; -static void po1030_dump_registers(struct sd *sd); +static const struct v4l2_ctrl_config po1030_greenbal_cfg = { + .ops = &po1030_ctrl_ops, + .id = M5602_V4L2_CID_GREEN_BALANCE, + .name = "Green Balance", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 255, + .step = 1, + .def = PO1030_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, +}; int po1030_probe(struct sd *sd) { u8 dev_id_h = 0, i; - s32 *sensor_settings; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { if (force_sensor == PO1030_SENSOR) { @@ -229,26 +89,14 @@ int po1030_probe(struct sd *sd) return -ENODEV; sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = po1030_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes); - sd->desc->ctrls = po1030_ctrls; - sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls); - - for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++) - sensor_settings[i] = po1030_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; return 0; } int po1030_init(struct sd *sd) { - s32 *sensor_settings = sd->sensor_priv; int i, err = 0; /* Init the sensor */ @@ -279,46 +127,50 @@ int po1030_init(struct sd *sd) if (dump_sensor) po1030_dump_registers(sd); - err = po1030_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - err = po1030_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; - - err = po1030_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; + return 0; +} - err = po1030_set_green_balance(&sd->gspca_dev, - sensor_settings[GREEN_BALANCE_IDX]); - if (err < 0) - return err; +int po1030_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 9); + + sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 0); + sd->green_bal = v4l2_ctrl_new_custom(hdl, &po1030_greenbal_cfg, NULL); + sd->red_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 255, 1, + PO1030_RED_GAIN_DEFAULT); + sd->blue_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 255, 1, + PO1030_BLUE_GAIN_DEFAULT); + + sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &po1030_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_MANUAL); + sd->expo = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_EXPOSURE, + 0, 0x2ff, 1, PO1030_EXPOSURE_DEFAULT); + + sd->gain = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_GAIN, 0, + 0x4f, 1, PO1030_GLOBAL_GAIN_DEFAULT); + + sd->hflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } - err = po1030_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; + v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false); + v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); - err = po1030_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - return err; + return 0; } int po1030_start(struct sd *sd) @@ -448,28 +300,16 @@ int po1030_start(struct sd *sd) return err; } -static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Exposure read as %d", *val); - return 0; -} - static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[EXPOSURE_IDX] = val; - PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff); + PDEBUG(D_CONF, "Set exposure to %d", val & 0xffff); i2c_data = ((val & 0xff00) >> 8); - PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x", + PDEBUG(D_CONF, "Set exposure to high byte to 0x%x", i2c_data); err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, @@ -478,7 +318,7 @@ static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) return err; i2c_data = (val & 0xff); - PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x", + PDEBUG(D_CONF, "Set exposure to low byte to 0x%x", i2c_data); err = m5602_write_sensor(sd, PO1030_INTEGLINES_M, &i2c_data, 1); @@ -486,91 +326,32 @@ static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read global gain %d", *val); - return 0; -} - static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[GAIN_IDX] = val; - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); + PDEBUG(D_CONF, "Set global gain to %d", i2c_data); err = m5602_write_sensor(sd, PO1030_GLOBALGAIN, &i2c_data, 1); return err; } -static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read hflip %d", *val); - - return 0; -} - -static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[HFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set hflip %d", val); - err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7); - - err = m5602_write_sensor(sd, PO1030_CONTROL2, - &i2c_data, 1); - - return err; -} - -static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_set_hvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vflip %d", *val); - - return 0; -} - -static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[VFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set vflip %d", val); + PDEBUG(D_CONF, "Set hvflip %d %d", sd->hflip->val, sd->vflip->val); err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); if (err < 0) return err; - i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6); + i2c_data = (0x3f & i2c_data) | (sd->hflip->val << 7) | + (sd->vflip->val << 6); err = m5602_write_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); @@ -578,81 +359,41 @@ static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red gain %d", *val); - return 0; -} - static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[RED_BALANCE_IDX] = val; - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); + PDEBUG(D_CONF, "Set red gain to %d", i2c_data); err = m5602_write_sensor(sd, PO1030_RED_GAIN, &i2c_data, 1); return err; } -static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue gain %d", *val); - - return 0; -} - static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[BLUE_BALANCE_IDX] = val; - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); + PDEBUG(D_CONF, "Set blue gain to %d", i2c_data); err = m5602_write_sensor(sd, PO1030_BLUE_GAIN, &i2c_data, 1); return err; } -static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GREEN_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read green gain %d", *val); - - return 0; -} - static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[GREEN_BALANCE_IDX] = val; i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set green gain to %d", i2c_data); + PDEBUG(D_CONF, "Set green gain to %d", i2c_data); err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN, &i2c_data, 1); @@ -663,63 +404,36 @@ static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) &i2c_data, 1); } -static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Auto white balancing is %d", *val); - - return 0; -} - static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; - err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); if (err < 0) return err; - PDEBUG(D_V4L2, "Set auto white balance to %d", val); + PDEBUG(D_CONF, "Set auto white balance to %d", val); i2c_data = (i2c_data & 0xfe) | (val & 0x01); err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); return err; } -static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Auto exposure is %d", *val); - return 0; -} - static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - sensor_settings[AUTO_EXPOSURE_IDX] = val; err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); if (err < 0) return err; - PDEBUG(D_V4L2, "Set auto exposure to %d", val); + PDEBUG(D_CONF, "Set auto exposure to %d", val); + val = (val == V4L2_EXPOSURE_AUTO); i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1); return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); } @@ -727,7 +441,48 @@ static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, void po1030_disconnect(struct sd *sd) { sd->sensor = NULL; - kfree(sd->sensor_priv); +} + +static int po1030_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; + int err; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = po1030_set_auto_white_balance(gspca_dev, ctrl->val); + if (err || ctrl->val) + return err; + err = po1030_set_green_balance(gspca_dev, sd->green_bal->val); + if (err) + return err; + err = po1030_set_red_balance(gspca_dev, sd->red_bal->val); + if (err) + return err; + err = po1030_set_blue_balance(gspca_dev, sd->blue_bal->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + err = po1030_set_auto_exposure(gspca_dev, ctrl->val); + if (err || ctrl->val == V4L2_EXPOSURE_AUTO) + return err; + err = po1030_set_exposure(gspca_dev, sd->expo->val); + break; + case V4L2_CID_GAIN: + err = po1030_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + err = po1030_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; + } + + return err; } static void po1030_dump_registers(struct sd *sd) diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.h b/drivers/media/usb/gspca/m5602/m5602_po1030.h index 81a2bcb..a6ab761 100644 --- a/drivers/media/usb/gspca/m5602/m5602_po1030.h +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.h @@ -151,6 +151,7 @@ extern bool dump_sensor; int po1030_probe(struct sd *sd); int po1030_init(struct sd *sd); +int po1030_init_controls(struct sd *sd); int po1030_start(struct sd *sd); void po1030_disconnect(struct sd *sd); @@ -162,6 +163,7 @@ static const struct m5602_sensor po1030 = { .probe = po1030_probe, .init = po1030_init, + .init_controls = po1030_init_controls, .start = po1030_start, .disconnect = po1030_disconnect, }; diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c index c8e1572..7d12599 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c @@ -20,18 +20,12 @@ #include "m5602_s5k4aa.h" -static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl); +static void s5k4aa_dump_registers(struct sd *sd); + +static const struct v4l2_ctrl_ops s5k4aa_ctrl_ops = { + .s_ctrl = s5k4aa_s_ctrl, +}; static const @@ -147,104 +141,12 @@ static struct v4l2_pix_format s5k4aa_modes[] = { } }; -static const struct ctrl s5k4aa_ctrls[] = { -#define VFLIP_IDX 0 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_vflip, - .get = s5k4aa_get_vflip - }, -#define HFLIP_IDX 1 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_hflip, - .get = s5k4aa_get_hflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = S5K4AA_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_gain, - .get = s5k4aa_get_gain - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 13, - .maximum = 0xfff, - .step = 1, - .default_value = 0x100, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_exposure, - .get = s5k4aa_get_exposure - }, -#define NOISE_SUPP_IDX 4 - { - { - .id = V4L2_CID_PRIVATE_BASE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Noise suppression (smoothing)", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - .set = s5k4aa_set_noise, - .get = s5k4aa_get_noise - }, -#define BRIGHTNESS_IDX 5 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0x1f, - .step = 1, - .default_value = S5K4AA_DEFAULT_BRIGHTNESS, - }, - .set = s5k4aa_set_brightness, - .get = s5k4aa_get_brightness - }, - -}; - -static void s5k4aa_dump_registers(struct sd *sd); - int s5k4aa_probe(struct sd *sd) { u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int i, err = 0; - s32 *sensor_settings; if (force_sensor) { if (force_sensor == S5K4AA_SENSOR) { @@ -303,19 +205,8 @@ int s5k4aa_probe(struct sd *sd) pr_info("Detected a s5k4aa sensor\n"); sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = s5k4aa_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes); - sd->desc->ctrls = s5k4aa_ctrls; - sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls); - - for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++) - sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; return 0; } @@ -325,11 +216,11 @@ int s5k4aa_start(struct sd *sd) int i, err = 0; u8 data[2]; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) { case 1280: - PDEBUG(D_V4L2, "Configuring camera for SXGA mode"); + PDEBUG(D_CONF, "Configuring camera for SXGA mode"); for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) { switch (SXGA_s5k4aa[i][0]) { @@ -359,13 +250,10 @@ int s5k4aa_start(struct sd *sd) return -EINVAL; } } - err = s5k4aa_set_noise(&sd->gspca_dev, 0); - if (err < 0) - return err; break; case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + PDEBUG(D_CONF, "Configuring camera for VGA mode"); for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) { switch (VGA_s5k4aa[i][0]) { @@ -395,37 +283,12 @@ int s5k4aa_start(struct sd *sd) return -EINVAL; } } - err = s5k4aa_set_noise(&sd->gspca_dev, 1); - if (err < 0) - return err; break; } if (err < 0) return err; - err = s5k4aa_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_brightness(&sd->gspca_dev, - sensor_settings[BRIGHTNESS_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + return 0; } int s5k4aa_init(struct sd *sd) @@ -466,13 +329,36 @@ int s5k4aa_init(struct sd *sd) return err; } -static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +int s5k4aa_init_controls(struct sd *sd) { - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 6); - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read exposure %d", *val); + v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_BRIGHTNESS, + 0, 0x1f, 1, S5K4AA_DEFAULT_BRIGHTNESS); + + v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_EXPOSURE, + 13, 0xfff, 1, 0x100); + + v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_GAIN, + 0, 127, 1, S5K4AA_DEFAULT_GAIN); + + v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_SHARPNESS, + 0, 1, 1, 1); + + sd->hflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_cluster(2, &sd->hflip); return 0; } @@ -480,12 +366,10 @@ static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - sensor_settings[EXPOSURE_IDX] = val; - PDEBUG(D_V4L2, "Set exposure to %d", val); + PDEBUG(D_CONF, "Set exposure to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; @@ -499,27 +383,15 @@ static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k4aa_set_hvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; + int hflip = sd->hflip->val; + int vflip = sd->vflip->val; - sensor_settings[VFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); + PDEBUG(D_CONF, "Set hvflip %d %d", hflip, vflip); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; @@ -528,93 +400,48 @@ static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) return err; - if (dmi_check_system(s5k4aa_vflip_dmi_table)) - val = !val; + if (dmi_check_system(s5k4aa_vflip_dmi_table)) { + hflip = !hflip; + vflip = !vflip; + } - data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); + data = (data & 0x7f) | (vflip << 7) | (hflip << 6); err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) return err; - err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); if (err < 0) return err; - if (val) + if (hflip) data &= 0xfe; else data |= 0x01; - err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - return err; -} - -static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - -static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[HFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; - - if (dmi_check_system(s5k4aa_vflip_dmi_table)) - val = !val; - - data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); if (err < 0) return err; - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); if (err < 0) return err; - if (val) + if (vflip) data &= 0xfe; else data |= 0x01; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - return err; -} - -static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + if (err < 0) + return err; - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); return 0; } static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - sensor_settings[GAIN_IDX] = val; - - PDEBUG(D_V4L2, "Set gain to %d", val); + PDEBUG(D_CONF, "Set gain to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; @@ -625,26 +452,13 @@ static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BRIGHTNESS_IDX]; - PDEBUG(D_V4L2, "Read brightness %d", *val); - return 0; -} - static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - sensor_settings[BRIGHTNESS_IDX] = val; - - PDEBUG(D_V4L2, "Set brightness to %d", val); + PDEBUG(D_CONF, "Set brightness to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; @@ -653,26 +467,13 @@ static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val) return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1); } -static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[NOISE_SUPP_IDX]; - PDEBUG(D_V4L2, "Read noise %d", *val); - return 0; -} - static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - sensor_settings[NOISE_SUPP_IDX] = val; - - PDEBUG(D_V4L2, "Set noise to %d", val); + PDEBUG(D_CONF, "Set noise to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; @@ -681,10 +482,41 @@ static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val) return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1); } +static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = s5k4aa_set_brightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = s5k4aa_set_exposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = s5k4aa_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + err = s5k4aa_set_noise(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + err = s5k4aa_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; + } + + return err; +} + void s5k4aa_disconnect(struct sd *sd) { sd->sensor = NULL; - kfree(sd->sensor_priv); } static void s5k4aa_dump_registers(struct sd *sd) diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h index 8e0035e..9953e97 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h @@ -69,6 +69,7 @@ extern bool dump_sensor; int s5k4aa_probe(struct sd *sd); int s5k4aa_init(struct sd *sd); +int s5k4aa_init_controls(struct sd *sd); int s5k4aa_start(struct sd *sd); void s5k4aa_disconnect(struct sd *sd); @@ -79,6 +80,7 @@ static const struct m5602_sensor s5k4aa = { .probe = s5k4aa_probe, .init = s5k4aa_init, + .init_controls = s5k4aa_init_controls, .start = s5k4aa_start, .disconnect = s5k4aa_disconnect, }; diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c index 1de743a..7cbc3a0 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -21,16 +21,11 @@ #include <linux/kthread.h> #include "m5602_s5k83a.h" -static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl); + +static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = { + .s_ctrl = s5k83a_s_ctrl, +}; static struct v4l2_pix_format s5k83a_modes[] = { { @@ -46,83 +41,6 @@ static struct v4l2_pix_format s5k83a_modes[] = { } }; -static const struct ctrl s5k83a_ctrls[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_gain, - .get = s5k83a_get_gain - - }, -#define BRIGHTNESS_IDX 1 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_BRIGHTNESS, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_brightness, - .get = s5k83a_get_brightness, - }, -#define EXPOSURE_IDX 2 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = S5K83A_MAXIMUM_EXPOSURE, - .step = 0x01, - .default_value = S5K83A_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_exposure, - .get = s5k83a_get_exposure - }, -#define HFLIP_IDX 3 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_hflip, - .get = s5k83a_get_hflip - }, -#define VFLIP_IDX 4 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_vflip, - .get = s5k83a_get_vflip - } -}; - static void s5k83a_dump_registers(struct sd *sd); static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data); static int s5k83a_set_led_indication(struct sd *sd, u8 val); @@ -131,9 +49,9 @@ static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, int s5k83a_probe(struct sd *sd) { - struct s5k83a_priv *sens_priv; u8 prod_id = 0, ver_id = 0; int i, err = 0; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { if (force_sensor == S5K83A_SENSOR) { @@ -173,38 +91,18 @@ int s5k83a_probe(struct sd *sd) pr_info("Detected a s5k83a sensor\n"); sensor_found: - sens_priv = kmalloc( - sizeof(struct s5k83a_priv), GFP_KERNEL); - if (!sens_priv) - return -ENOMEM; - - sens_priv->settings = - kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); - if (!sens_priv->settings) { - kfree(sens_priv); - return -ENOMEM; - } - sd->gspca_dev.cam.cam_mode = s5k83a_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); - sd->desc->ctrls = s5k83a_ctrls; - sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); /* null the pointer! thread is't running now */ - sens_priv->rotation_thread = NULL; - - for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++) - sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value; + sd->rotation_thread = NULL; - sd->sensor_priv = sens_priv; return 0; } int s5k83a_init(struct sd *sd) { int i, err = 0; - s32 *sensor_settings = - ((struct s5k83a_priv *) sd->sensor_priv)->settings; for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { u8 data[2] = {0x00, 0x00}; @@ -237,33 +135,44 @@ int s5k83a_init(struct sd *sd) if (dump_sensor) s5k83a_dump_registers(sd); - err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; + return err; +} - err = s5k83a_set_brightness(&sd->gspca_dev, - sensor_settings[BRIGHTNESS_IDX]); - if (err < 0) - return err; +int s5k83a_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - err = s5k83a_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; + sd->gspca_dev.vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 6); - err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; + v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_BRIGHTNESS, + 0, 255, 1, S5K83A_DEFAULT_BRIGHTNESS); - err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_EXPOSURE, + 0, S5K83A_MAXIMUM_EXPOSURE, 1, + S5K83A_DEFAULT_EXPOSURE); - return err; + v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_GAIN, + 0, 255, 1, S5K83A_DEFAULT_GAIN); + + sd->hflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_cluster(2, &sd->hflip); + + return 0; } static int rotation_thread_function(void *data) { struct sd *sd = (struct sd *) data; - struct s5k83a_priv *sens_priv = sd->sensor_priv; u8 reg, previous_rotation = 0; __s32 vflip, hflip; @@ -277,8 +186,8 @@ static int rotation_thread_function(void *data) previous_rotation = reg; pr_info("Camera was flipped\n"); - s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); - s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + hflip = sd->hflip->val; + vflip = sd->vflip->val; if (reg) { vflip = !vflip; @@ -294,26 +203,25 @@ static int rotation_thread_function(void *data) /* return to "front" flip */ if (previous_rotation) { - s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); - s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + hflip = sd->hflip->val; + vflip = sd->vflip->val; s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip); } - sens_priv->rotation_thread = NULL; + sd->rotation_thread = NULL; return 0; } int s5k83a_start(struct sd *sd) { int i, err = 0; - struct s5k83a_priv *sens_priv = sd->sensor_priv; /* Create another thread, polling the GPIO ports of the camera to check if it got rotated. This is how the windows driver does it so we have to assume that there is no better way of accomplishing this */ - sens_priv->rotation_thread = kthread_create(rotation_thread_function, - sd, "rotation thread"); - wake_up_process(sens_priv->rotation_thread); + sd->rotation_thread = kthread_create(rotation_thread_function, + sd, "rotation thread"); + wake_up_process(sd->rotation_thread); /* Preinit the sensor */ for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) { @@ -333,32 +241,17 @@ int s5k83a_start(struct sd *sd) int s5k83a_stop(struct sd *sd) { - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - if (sens_priv->rotation_thread) - kthread_stop(sens_priv->rotation_thread); + if (sd->rotation_thread) + kthread_stop(sd->rotation_thread); return s5k83a_set_led_indication(sd, 0); } void s5k83a_disconnect(struct sd *sd) { - struct s5k83a_priv *sens_priv = sd->sensor_priv; - s5k83a_stop(sd); sd->sensor = NULL; - kfree(sens_priv->settings); - kfree(sens_priv); -} - -static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[GAIN_IDX]; - return 0; } static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -366,9 +259,6 @@ static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) int err; u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[GAIN_IDX] = val; data[0] = 0x00; data[1] = 0x20; @@ -391,60 +281,29 @@ static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[BRIGHTNESS_IDX]; - return 0; -} - static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[1]; struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - sens_priv->settings[BRIGHTNESS_IDX] = val; data[0] = val; err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); return err; } -static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[EXPOSURE_IDX]; - return 0; -} - static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - sens_priv->settings[EXPOSURE_IDX] = val; data[0] = 0; data[1] = val; err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); return err; } -static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[VFLIP_IDX]; - return 0; -} - static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, __s32 vflip, __s32 hflip) { @@ -476,60 +335,52 @@ static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, return err; } -static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev) { int err; u8 reg; - __s32 hflip; struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[VFLIP_IDX] = val; - - s5k83a_get_hflip(gspca_dev, &hflip); + int hflip = sd->hflip->val; + int vflip = sd->vflip->val; err = s5k83a_get_rotation(sd, ®); if (err < 0) return err; if (reg) { - val = !val; hflip = !hflip; + vflip = !vflip; } - err = s5k83a_set_flip_real(gspca_dev, val, hflip); + err = s5k83a_set_flip_real(gspca_dev, vflip, hflip); return err; } -static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[HFLIP_IDX]; - return 0; -} - -static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl) { + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); int err; - u8 reg; - __s32 vflip; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[HFLIP_IDX] = val; - s5k83a_get_vflip(gspca_dev, &vflip); - - err = s5k83a_get_rotation(sd, ®); - if (err < 0) - return err; - if (reg) { - val = !val; - vflip = !vflip; + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = s5k83a_set_brightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = s5k83a_set_exposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = s5k83a_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + err = s5k83a_set_hvflip(gspca_dev); + break; + default: + return -EINVAL; } - err = s5k83a_set_flip_real(gspca_dev, vflip, val); return err; } diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.h b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h index 7995224..d61b918 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k83a.h +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h @@ -45,6 +45,7 @@ extern bool dump_sensor; int s5k83a_probe(struct sd *sd); int s5k83a_init(struct sd *sd); +int s5k83a_init_controls(struct sd *sd); int s5k83a_start(struct sd *sd); int s5k83a_stop(struct sd *sd); void s5k83a_disconnect(struct sd *sd); @@ -53,6 +54,7 @@ static const struct m5602_sensor s5k83a = { .name = "S5K83A", .probe = s5k83a_probe, .init = s5k83a_init, + .init_controls = s5k83a_init_controls, .start = s5k83a_start, .stop = s5k83a_stop, .disconnect = s5k83a_disconnect, @@ -60,13 +62,6 @@ static const struct m5602_sensor s5k83a = { .i2c_regW = 2, }; -struct s5k83a_priv { - /* We use another thread periodically - probing the orientation of the camera */ - struct task_struct *rotation_thread; - s32 *settings; -}; - static const unsigned char preinit_s5k83a[][4] = { {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, diff --git a/drivers/media/usb/gspca/m5602/m5602_sensor.h b/drivers/media/usb/gspca/m5602/m5602_sensor.h index edff4f1..48341b4 100644 --- a/drivers/media/usb/gspca/m5602/m5602_sensor.h +++ b/drivers/media/usb/gspca/m5602/m5602_sensor.h @@ -57,6 +57,9 @@ struct m5602_sensor { /* Performs a initialization sequence */ int (*init)(struct sd *sd); + /* Controls initialization, maybe NULL */ + int (*init_controls)(struct sd *sd); + /* Executed when the camera starts to send data */ int (*start)(struct sd *sd); diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c index 8f4714d..68bb2f3 100644 --- a/drivers/media/usb/gspca/mr97310a.c +++ b/drivers/media/usb/gspca/mr97310a.c @@ -289,7 +289,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return err_code; } if (status != 0x0a) - PDEBUG(D_ERR, "status is %02x", status); + PERR("status is %02x", status); tries = 0; while (tries < 4) { @@ -330,7 +330,7 @@ static void stream_stop(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0] = 0x01; gspca_dev->usb_buf[1] = 0x00; if (mr_write(gspca_dev, 2) < 0) - PDEBUG(D_ERR, "Stream Stop failed"); + PERR("Stream Stop failed"); } static void lcd_stop(struct gspca_dev *gspca_dev) @@ -338,7 +338,7 @@ static void lcd_stop(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0] = 0x19; gspca_dev->usb_buf[1] = 0x54; if (mr_write(gspca_dev, 2) < 0) - PDEBUG(D_ERR, "LCD Stop failed"); + PERR("LCD Stop failed"); } static int isoc_enable(struct gspca_dev *gspca_dev) @@ -1026,7 +1026,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(&sd->sof_read, data, len); + sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); if (sof) { int n; diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index 9ad19a7..a3958ee 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -2034,6 +2034,7 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) /* Write a OV519 register */ static void reg_w(struct sd *sd, u16 index, u16 value) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret, req = 0; if (sd->gspca_dev.usb_err < 0) @@ -2071,7 +2072,7 @@ static void reg_w(struct sd *sd, u16 index, u16 value) sd->gspca_dev.usb_buf, 1, 500); leave: if (ret < 0) { - pr_err("reg_w %02x failed %d\n", index, ret); + PERR("reg_w %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; return; } @@ -2081,6 +2082,7 @@ leave: /* returns: negative is error, pos or zero is data */ static int reg_r(struct sd *sd, u16 index) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret; int req; @@ -2110,7 +2112,7 @@ static int reg_r(struct sd *sd, u16 index) PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, ret); } else { - pr_err("reg_r %02x failed %d\n", index, ret); + PERR("reg_r %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } @@ -2121,6 +2123,7 @@ static int reg_r(struct sd *sd, u16 index) static int reg_r8(struct sd *sd, u16 index) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret; if (sd->gspca_dev.usb_err < 0) @@ -2135,7 +2138,7 @@ static int reg_r8(struct sd *sd, if (ret >= 0) { ret = sd->gspca_dev.usb_buf[0]; } else { - pr_err("reg_r8 %02x failed %d\n", index, ret); + PERR("reg_r8 %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } @@ -2174,6 +2177,7 @@ static void reg_w_mask(struct sd *sd, */ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret; if (sd->gspca_dev.usb_err < 0) @@ -2188,13 +2192,14 @@ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) 0, index, sd->gspca_dev.usb_buf, n, 500); if (ret < 0) { - pr_err("reg_w32 %02x failed %d\n", index, ret); + PERR("reg_w32 %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } } static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int rc, retries; PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value); @@ -2228,6 +2233,7 @@ static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value) static int ov511_i2c_r(struct sd *sd, u8 reg) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int rc, value, retries; /* Two byte write cycle */ @@ -2300,6 +2306,8 @@ static void ov518_i2c_w(struct sd *sd, u8 reg, u8 value) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value); /* Select camera register */ @@ -2325,6 +2333,7 @@ static void ov518_i2c_w(struct sd *sd, */ static int ov518_i2c_r(struct sd *sd, u8 reg) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int value; /* Select camera register */ @@ -2345,6 +2354,7 @@ static int ov518_i2c_r(struct sd *sd, u8 reg) static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret; if (sd->gspca_dev.usb_err < 0) @@ -2357,7 +2367,7 @@ static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) (u16) value, (u16) reg, NULL, 0, 500); if (ret < 0) { - pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret); + PERR("ovfx2_i2c_w %02x failed %d\n", reg, ret); sd->gspca_dev.usb_err = ret; } @@ -2366,6 +2376,7 @@ static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) static int ovfx2_i2c_r(struct sd *sd, u8 reg) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret; if (sd->gspca_dev.usb_err < 0) @@ -2381,7 +2392,7 @@ static int ovfx2_i2c_r(struct sd *sd, u8 reg) ret = sd->gspca_dev.usb_buf[0]; PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret); } else { - pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret); + PERR("ovfx2_i2c_r %02x failed %d\n", reg, ret); sd->gspca_dev.usb_err = ret; } @@ -2478,6 +2489,8 @@ static void i2c_w_mask(struct sd *sd, * registers while the camera is streaming */ static inline void ov51x_stop(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_STREAM, "stopping"); sd->stopped = 1; switch (sd->bridge) { @@ -2507,6 +2520,8 @@ static inline void ov51x_stop(struct sd *sd) * actually stopped (for performance). */ static inline void ov51x_restart(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_STREAM, "restarting"); if (!sd->stopped) return; @@ -2545,6 +2560,7 @@ static void ov51x_set_slave_ids(struct sd *sd, u8 slave); static int init_ov_sensor(struct sd *sd, u8 slave) { int i; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; ov51x_set_slave_ids(sd, slave); @@ -2624,10 +2640,11 @@ static void write_i2c_regvals(struct sd *sd, /* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */ static void ov_hires_configure(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int high, low; if (sd->bridge != BRIDGE_OVFX2) { - pr_err("error hires sensors only supported with ovfx2\n"); + PERR("error hires sensors only supported with ovfx2\n"); return; } @@ -2662,7 +2679,7 @@ static void ov_hires_configure(struct sd *sd) } break; } - pr_err("Error unknown sensor type: %02x%02x\n", high, low); + PERR("Error unknown sensor type: %02x%02x\n", high, low); } /* This initializes the OV8110, OV8610 sensor. The OV8110 uses @@ -2670,6 +2687,7 @@ static void ov_hires_configure(struct sd *sd) */ static void ov8xx0_configure(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int rc; PDEBUG(D_PROBE, "starting ov8xx0 configuration"); @@ -2677,13 +2695,13 @@ static void ov8xx0_configure(struct sd *sd) /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + PERR("Error detecting sensor type"); return; } if ((rc & 3) == 1) sd->sensor = SEN_OV8610; else - pr_err("Unknown image sensor version: %d\n", rc & 3); + PERR("Unknown image sensor version: %d\n", rc & 3); } /* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses @@ -2691,6 +2709,7 @@ static void ov8xx0_configure(struct sd *sd) */ static void ov7xx0_configure(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int rc, high, low; PDEBUG(D_PROBE, "starting OV7xx0 configuration"); @@ -2701,7 +2720,7 @@ static void ov7xx0_configure(struct sd *sd) /* add OV7670 here * it appears to be wrongly detected as a 7610 by default */ if (rc < 0) { - pr_err("Error detecting sensor type\n"); + PERR("Error detecting sensor type\n"); return; } if ((rc & 3) == 3) { @@ -2729,19 +2748,19 @@ static void ov7xx0_configure(struct sd *sd) /* try to read product id registers */ high = i2c_r(sd, 0x0a); if (high < 0) { - pr_err("Error detecting camera chip PID\n"); + PERR("Error detecting camera chip PID\n"); return; } low = i2c_r(sd, 0x0b); if (low < 0) { - pr_err("Error detecting camera chip VER\n"); + PERR("Error detecting camera chip VER\n"); return; } if (high == 0x76) { switch (low) { case 0x30: - pr_err("Sensor is an OV7630/OV7635\n"); - pr_err("7630 is not supported by this driver\n"); + PERR("Sensor is an OV7630/OV7635\n"); + PERR("7630 is not supported by this driver\n"); return; case 0x40: PDEBUG(D_PROBE, "Sensor is an OV7645"); @@ -2760,7 +2779,7 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7660; break; default: - pr_err("Unknown sensor: 0x76%02x\n", low); + PERR("Unknown sensor: 0x76%02x\n", low); return; } } else { @@ -2768,20 +2787,22 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7620; } } else { - pr_err("Unknown image sensor version: %d\n", rc & 3); + PERR("Unknown image sensor version: %d\n", rc & 3); } } /* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ static void ov6xx0_configure(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int rc; + PDEBUG(D_PROBE, "starting OV6xx0 configuration"); /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { - pr_err("Error detecting sensor type\n"); + PERR("Error detecting sensor type\n"); return; } @@ -2810,7 +2831,7 @@ static void ov6xx0_configure(struct sd *sd) pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n"); break; default: - pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc); + PERR("FATAL: Unknown sensor version: 0x%02x\n", rc); return; } @@ -2907,6 +2928,7 @@ static void ov51x_upload_quan_tables(struct sd *sd) 7, 7, 7, 7, 7, 7, 8, 8 }; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; const unsigned char *pYTable, *pUVTable; unsigned char val0, val1; int i, size, reg = R51x_COMP_LUT_BEGIN; @@ -3300,7 +3322,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { ov_hires_configure(sd); } else { - pr_err("Can't determine sensor slave IDs\n"); + PERR("Can't determine sensor slave IDs\n"); goto error; } @@ -3433,7 +3455,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } return gspca_dev->usb_err; error: - PDEBUG(D_ERR, "OV519 Config failed"); + PERR("OV519 Config failed"); return -EINVAL; } @@ -3459,6 +3481,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) */ static void ov511_mode_init_regs(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int hsegs, vsegs, packet_size, fps, needed; int interlaced = 0; struct usb_host_interface *alt; @@ -3467,7 +3490,7 @@ static void ov511_mode_init_regs(struct sd *sd) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - pr_err("Couldn't get altsetting\n"); + PERR("Couldn't get altsetting\n"); sd->gspca_dev.usb_err = -EIO; return; } @@ -3583,6 +3606,7 @@ static void ov511_mode_init_regs(struct sd *sd) */ static void ov518_mode_init_regs(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int hsegs, vsegs, packet_size; struct usb_host_interface *alt; struct usb_interface *intf; @@ -3590,7 +3614,7 @@ static void ov518_mode_init_regs(struct sd *sd) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - pr_err("Couldn't get altsetting\n"); + PERR("Couldn't get altsetting\n"); sd->gspca_dev.usb_err = -EIO; return; } @@ -3750,6 +3774,8 @@ static void ov519_mode_init_regs(struct sd *sd) /* windows reads 0x55 at this point, why? */ }; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + /******** Set the mode ********/ switch (sd->sensor) { default: @@ -3865,11 +3891,10 @@ static void ov519_mode_init_regs(struct sd *sd) static void mode_init_ov_sensor_regs(struct sd *sd) { - struct gspca_dev *gspca_dev; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int qvga, xstart, xend, ystart, yend; u8 v; - gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ @@ -4304,7 +4329,7 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, /* Frame end */ if ((in[9] + 1) * 8 != gspca_dev->width || (in[10] + 1) * 8 != gspca_dev->height) { - PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," + PERR("Invalid frame size, got: %dx%d," " requested: %dx%d\n", (in[9] + 1) * 8, (in[10] + 1) * 8, gspca_dev->width, gspca_dev->height); @@ -4355,7 +4380,7 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, except that they may contain part of the footer), are numbered 0 */ else if (sd->packet_nr == 0 || data[len]) { - PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", + PERR("Invalid packet nr: %d (expect: %d)", (int)data[len], (int)sd->packet_nr); gspca_dev->last_packet_type = DISCARD_PACKET; return; @@ -4898,7 +4923,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); if (hdl->error) { - pr_err("Could not initialize controls\n"); + PERR("Could not initialize controls\n"); return hdl->error; } if (gspca_dev->autogain) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index bb09d78..2e28c81 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -690,7 +690,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev) case 0x03: break; default: - PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", + PERR("sccb status 0x%02x, attempt %d/5", data, i + 1); } } diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index 3b75097..83519be 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -373,7 +373,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(&sd->sof_read, data, len); + sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); if (sof) { int n; diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index add6f72..6008c8d 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -344,13 +344,10 @@ static void reg_w_var(struct gspca_dev *gspca_dev, reg_w_page(gspca_dev, page3, page3_len); break; default: -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - PDEBUG(D_ERR|D_STREAM, - "Incorrect variable sequence"); + PERR("Incorrect variable sequence"); return; } -#endif while (len > 0) { if (len < 8) { reg_w_buf(gspca_dev, @@ -795,7 +792,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *image; u8 *sof; - sof = pac_find_sof(&sd->sof_read, data, len); + sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; @@ -843,7 +840,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, #ifdef CONFIG_VIDEO_ADV_DEBUG static int sd_dbg_s_register(struct gspca_dev *gspca_dev, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { u8 index; u8 value; diff --git a/drivers/media/usb/gspca/pac7311.c b/drivers/media/usb/gspca/pac7311.c index a12dfbf..1a5bdc8 100644 --- a/drivers/media/usb/gspca/pac7311.c +++ b/drivers/media/usb/gspca/pac7311.c @@ -262,8 +262,7 @@ static void reg_w_var(struct gspca_dev *gspca_dev, break; default: if (len > USB_BUF_SZ) { - PDEBUG(D_ERR|D_STREAM, - "Incorrect variable sequence"); + PERR("Incorrect variable sequence"); return; } while (len > 0) { @@ -575,7 +574,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *image; unsigned char *sof; - sof = pac_find_sof(&sd->sof_read, data, len); + sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; diff --git a/drivers/media/usb/gspca/pac_common.h b/drivers/media/usb/gspca/pac_common.h index 8462a7c..fbc5e22 100644 --- a/drivers/media/usb/gspca/pac_common.h +++ b/drivers/media/usb/gspca/pac_common.h @@ -71,7 +71,7 @@ static const unsigned char pac_sof_marker[5] = +----------+ */ -static unsigned char *pac_find_sof(u8 *sof_read, +static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, u8 *sof_read, unsigned char *m, int len) { int i; diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c index 03fa3fd..39b6b2e 100644 --- a/drivers/media/usb/gspca/sn9c2028.c +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -650,13 +650,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) result = sn9c2028_read1(gspca_dev); if (result < 0) - PDEBUG(D_ERR, "Camera Stop read failed"); + PERR("Camera Stop read failed"); memset(data, 0, 6); data[0] = 0x14; result = sn9c2028_command(gspca_dev, data); if (result < 0) - PDEBUG(D_ERR, "Camera Stop command failed"); + PERR("Camera Stop command failed"); } /* Include sn9c2028 sof detection functions */ diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c index 4ec544f..ead9a1f 100644 --- a/drivers/media/usb/gspca/sn9c20x.c +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -1598,7 +1598,7 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev, } static int sd_dbg_s_register(struct gspca_dev *gspca_dev, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 104ae25..3fe207e 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -1379,27 +1379,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; - } - return -EINVAL; -} - #if IS_ENABLED(CONFIG_INPUT) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -1428,7 +1407,6 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, - .querymenu = sd_querymenu, .dq_callback = do_autogain, #if IS_ENABLED(CONFIG_INPUT) .int_pkt_scan = sd_int_pkt_scan, diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 671d0c6..3b5ccb1 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -31,32 +31,26 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - COLORS, - BLUE, - RED, - GAMMA, - EXPOSURE, - AUTOGAIN, - GAIN, - HFLIP, - VFLIP, - SHARPNESS, - ILLUM, - FREQ, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; - atomic_t avg_lum; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct { /* red/blue balance control cluster */ + struct v4l2_ctrl *red_bal; + struct v4l2_ctrl *blue_bal; + }; + struct { /* hflip/vflip control cluster */ + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + }; + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *illum; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *freq; u32 exposure; struct work_struct work; @@ -127,283 +121,6 @@ static void qual_upd(struct work_struct *work); #define SEN_CLK_EN 0x20 /* enable sensor clock */ #define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */ -/* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev); -static void setcontrast(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); -static void setredblue(struct gspca_dev *gspca_dev); -static void setgamma(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static void setgain(struct gspca_dev *gspca_dev); -static void sethvflip(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static void setillum(struct gspca_dev *gspca_dev); -static void setfreq(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x80, - }, - .set_control = setbrightness - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, -#define CONTRAST_MAX 127 - .maximum = CONTRAST_MAX, - .step = 1, - .default_value = 20, - }, - .set_control = setcontrast - }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 40, - .step = 1, -#define COLORS_DEF 25 - .default_value = COLORS_DEF, - }, - .set_control = setcolors - }, -[BLUE] = { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 24, - .maximum = 40, - .step = 1, - .default_value = 32, - }, - .set_control = setredblue - }, -[RED] = { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 24, - .maximum = 40, - .step = 1, - .default_value = 32, - }, - .set_control = setredblue - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 40, - .step = 1, -#define GAMMA_DEF 20 - .default_value = GAMMA_DEF, - }, - .set_control = setgamma - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 500, - .maximum = 1500, - .step = 1, - .default_value = 1024 - }, - .set_control = setexposure - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = sd_setautogain, - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 4, - .maximum = 49, - .step = 1, - .default_value = 15 - }, - .set_control = setgain - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 90, - }, - .set_control = setsharpness - }, -[ILLUM] = { - { - .id = V4L2_CID_ILLUMINATORS_1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Illuminator / infrared", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = setillum - }, -/* ov7630/ov7648/ov7660 only */ -[FREQ] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Light frequency filter", - .minimum = 0, - .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ - .step = 1, - .default_value = 1, - }, - .set_control = setfreq - }, -}; - -/* table of the disabled controls */ -static const __u32 ctrl_dis[] = { -[SENSOR_ADCM1700] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_GC0307] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_HV7131R] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << FREQ), - -[SENSOR_MI0360] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MI0360B] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MO4000] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MT9V111] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_OM6802] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_OV7630] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP), - -[SENSOR_OV7648] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP), - -[SENSOR_OV7660] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP), - -[SENSOR_PO1030] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_PO2030N] = (1 << FREQ), - -[SENSOR_SOI768] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_SP80708] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), -}; - static const struct v4l2_pix_format cif_mode[] = { {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 352, @@ -1442,12 +1159,11 @@ static void reg_r(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); + PERR("reg_r: buffer overflow\n"); return; } -#endif + ret = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0, @@ -1496,12 +1212,12 @@ static void reg_w(struct gspca_dev *gspca_dev, return; PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..", value, buffer[0], buffer[1]); -#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { - pr_err("reg_w: buffer overflow\n"); + PERR("reg_w: buffer overflow\n"); return; } -#endif + memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -1822,7 +1538,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(vga_mode); } cam->npkt = 24; /* 24 packets per ISOC message */ - cam->ctrls = sd->ctrls; sd->ag_cnt = -1; sd->quality = QUALITY_DEF; @@ -1888,9 +1603,6 @@ static int sd_init(struct gspca_dev *gspca_dev) break; } - if (sd->sensor == SENSOR_OM6802) - sd->ctrls[SHARPNESS].def = 0x10; - /* Note we do not disable the sensor clock here (power saving mode), as that also disables the button on the cam. */ reg_w1(gspca_dev, 0xf1, 0x00); @@ -1899,13 +1611,92 @@ static int sd_init(struct gspca_dev *gspca_dev) sn9c1xx = sn_tb[sd->sensor]; sd->i2c_addr = sn9c1xx[9]; - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; - if (!(sd->flags & F_ILLUM)) - gspca_dev->ctrl_dis |= (1 << ILLUM); - return gspca_dev->usb_err; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl); + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 14); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); +#define CONTRAST_MAX 127 + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, CONTRAST_MAX, 1, 20); +#define COLORS_DEF 25 + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 40, 1, COLORS_DEF); + sd->red_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 24, 40, 1, 32); + sd->blue_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 24, 40, 1, 32); +#define GAMMA_DEF 20 + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, 40, 1, GAMMA_DEF); + + if (sd->sensor == SENSOR_OM6802) + sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 255, 1, 16); + else + sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 255, 1, 90); + + if (sd->flags & F_ILLUM) + sd->illum = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + + if (sd->sensor == SENSOR_PO2030N) { + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 500, 1500, 1, 1024); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 4, 49, 1, 15); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + } + + if (sd->sensor != SENSOR_ADCM1700 && sd->sensor != SENSOR_OV7660 && + sd->sensor != SENSOR_PO1030 && sd->sensor != SENSOR_SOI768 && + sd->sensor != SENSOR_SP80708) + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + + if (sd->sensor == SENSOR_HV7131R || sd->sensor == SENSOR_OV7630 || + sd->sensor == SENSOR_OV7648 || sd->sensor == SENSOR_PO2030N) + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (sd->sensor == SENSOR_OV7630 || sd->sensor == SENSOR_OV7648 || + sd->sensor == SENSOR_OV7660) + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_cluster(2, &sd->red_bal); + if (sd->sensor == SENSOR_PO2030N) { + v4l2_ctrl_cluster(2, &sd->vflip); + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + } + + return 0; +} + static u32 expo_adjust(struct gspca_dev *gspca_dev, u32 expo) { @@ -2014,10 +1805,9 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int expo; - int brightness; + int brightness = sd->brightness->val; u8 k2; - brightness = sd->ctrls[BRIGHTNESS].val; k2 = (brightness - 0x80) >> 2; switch (sd->sensor) { case SENSOR_ADCM1700: @@ -2064,7 +1854,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) u8 k2; u8 contrast[6]; - k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1) + k2 = sd->contrast->val * 37 / (CONTRAST_MAX + 1) + 37; /* 37..73 */ contrast[0] = (k2 + 1) / 2; /* red */ contrast[1] = 0; @@ -2090,7 +1880,7 @@ static void setcolors(struct gspca_dev *gspca_dev) 60, -51, -9 /* VR VG VB */ }; - colors = sd->ctrls[COLORS].val; + colors = sd->saturation->val; if (sd->sensor == SENSOR_MI0360B) uv = uv_mi0360b; else @@ -2112,14 +1902,14 @@ static void setredblue(struct gspca_dev *gspca_dev) {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10}; /* 0x40 = normal value = gain x 1 */ - rg1b[3] = sd->ctrls[RED].val * 2; - rg1b[5] = sd->ctrls[BLUE].val * 2; + rg1b[3] = sd->red_bal->val * 2; + rg1b[5] = sd->blue_bal->val * 2; i2c_w8(gspca_dev, rg1b); return; } - reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val); + reg_w1(gspca_dev, 0x05, sd->red_bal->val); /* reg_w1(gspca_dev, 0x07, 32); */ - reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val); + reg_w1(gspca_dev, 0x06, sd->blue_bal->val); } static void setgamma(struct gspca_dev *gspca_dev) @@ -2153,7 +1943,7 @@ static void setgamma(struct gspca_dev *gspca_dev) break; } - val = sd->ctrls[GAMMA].val; + val = sd->gamma->val; for (i = 0; i < sizeof gamma; i++) gamma[i] = gamma_base[i] + delta[i] * (val - GAMMA_DEF) / 32; @@ -2168,11 +1958,11 @@ static void setexposure(struct gspca_dev *gspca_dev) u8 rexpo[] = /* 1a: expo H, 1b: expo M */ {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10}; - rexpo[3] = sd->ctrls[EXPOSURE].val >> 8; + rexpo[3] = gspca_dev->exposure->val >> 8; i2c_w8(gspca_dev, rexpo); msleep(6); rexpo[2] = 0x1b; - rexpo[3] = sd->ctrls[EXPOSURE].val; + rexpo[3] = gspca_dev->exposure->val; i2c_w8(gspca_dev, rexpo); } } @@ -2181,8 +1971,6 @@ static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) - return; switch (sd->sensor) { case SENSOR_OV7630: case SENSOR_OV7648: { @@ -2192,13 +1980,13 @@ static void setautogain(struct gspca_dev *gspca_dev) comb = 0xc0; else comb = 0xa0; - if (sd->ctrls[AUTOGAIN].val) + if (gspca_dev->autogain->val) comb |= 0x03; i2c_w1(&sd->gspca_dev, 0x13, comb); return; } } - if (sd->ctrls[AUTOGAIN].val) + if (gspca_dev->autogain->val) sd->ag_cnt = AG_CNT_START; else sd->ag_cnt = -1; @@ -2212,7 +2000,7 @@ static void setgain(struct gspca_dev *gspca_dev) u8 rgain[] = /* 15: gain */ {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15}; - rgain[3] = sd->ctrls[GAIN].val; + rgain[3] = gspca_dev->gain->val; i2c_w8(gspca_dev, rgain); } } @@ -2225,19 +2013,19 @@ static void sethvflip(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_HV7131R: comn = 0x18; /* clkdiv = 1, ablcen = 1 */ - if (sd->ctrls[VFLIP].val) + if (sd->vflip->val) comn |= 0x01; i2c_w1(gspca_dev, 0x01, comn); /* sctra */ break; case SENSOR_OV7630: comn = 0x02; - if (!sd->ctrls[VFLIP].val) + if (!sd->vflip->val) comn |= 0x80; i2c_w1(gspca_dev, 0x75, comn); break; case SENSOR_OV7648: comn = 0x06; - if (sd->ctrls[VFLIP].val) + if (sd->vflip->val) comn |= 0x80; i2c_w1(gspca_dev, 0x75, comn); break; @@ -2251,9 +2039,9 @@ static void sethvflip(struct gspca_dev *gspca_dev) * bit3-0: X */ comn = 0x0a; - if (sd->ctrls[HFLIP].val) + if (sd->hflip->val) comn |= 0x80; - if (sd->ctrls[VFLIP].val) + if (sd->vflip->val) comn |= 0x40; i2c_w1(&sd->gspca_dev, 0x1e, comn); break; @@ -2264,23 +2052,21 @@ static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val); + reg_w1(gspca_dev, 0x99, sd->sharpness->val); } static void setillum(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (gspca_dev->ctrl_dis & (1 << ILLUM)) - return; switch (sd->sensor) { case SENSOR_ADCM1700: reg_w1(gspca_dev, 0x02, /* gpio */ - sd->ctrls[ILLUM].val ? 0x64 : 0x60); + sd->illum->val ? 0x64 : 0x60); break; case SENSOR_MT9V111: reg_w1(gspca_dev, 0x02, - sd->ctrls[ILLUM].val ? 0x77 : 0x74); + sd->illum->val ? 0x77 : 0x74); /* should have been: */ /* 0x55 : 0x54); * 370i */ /* 0x66 : 0x64); * Clip */ @@ -2292,13 +2078,11 @@ static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (gspca_dev->ctrl_dis & (1 << FREQ)) - return; if (sd->sensor == SENSOR_OV7660) { u8 com8; com8 = 0xdf; /* auto gain/wb/expo */ - switch (sd->ctrls[FREQ].val) { + switch (sd->freq->val) { case 0: /* Banding filter disabled */ i2c_w1(gspca_dev, 0x13, com8 | 0x20); break; @@ -2326,7 +2110,7 @@ static void setfreq(struct gspca_dev *gspca_dev) break; } - switch (sd->ctrls[FREQ].val) { + switch (sd->freq->val) { case 0: /* Banding filter disabled */ break; case 1: /* 50 hz (filter on and framerate adj) */ @@ -2698,17 +2482,6 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->reg01 = reg01; sd->reg17 = reg17; - sethvflip(gspca_dev); - setbrightness(gspca_dev); - setcontrast(gspca_dev); - setcolors(gspca_dev); - setautogain(gspca_dev); - if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) { - setexposure(gspca_dev); - setgain(gspca_dev); - } - setfreq(gspca_dev); - sd->pktsz = sd->npkt = 0; sd->nchg = sd->short_mark = 0; sd->work_thread = create_singlethread_workqueue(MODULE_NAME); @@ -2803,9 +2576,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } -#define WANT_REGULAR_AUTOGAIN -#include "autogain_functions.h" - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2825,7 +2595,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) PDEBUG(D_FRAM, "mean lum %d", delta); if (sd->sensor == SENSOR_PO2030N) { - auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta, + gspca_expo_autogain(gspca_dev, delta, luma_mean, luma_delta, 15, 1024); return; } @@ -3042,39 +2812,53 @@ marker_found: } } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = (struct sd *) gspca_dev; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - sd->ctrls[AUTOGAIN].val = val; - if (val) - gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); - else - gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN); - if (gspca_dev->streaming) - setautogain(gspca_dev); - return gspca_dev->usb_err; -} + gspca_dev->usb_err = 0; -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev); + break; + case V4L2_CID_RED_BALANCE: + setredblue(gspca_dev); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev); + break; + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + break; + case V4L2_CID_VFLIP: + sethvflip(gspca_dev); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev); + break; + case V4L2_CID_ILLUMINATORS_1: + setillum(gspca_dev); + break; case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } + setfreq(gspca_dev); break; + default: + return -EINVAL; } - return -EINVAL; + return gspca_dev->usb_err; } #if IS_ENABLED(CONFIG_INPUT) @@ -3099,16 +2883,14 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, - .querymenu = sd_querymenu, #if IS_ENABLED(CONFIG_INPUT) .int_pkt_scan = sd_int_pkt_scan, #endif diff --git a/drivers/media/usb/gspca/spca1528.c b/drivers/media/usb/gspca/spca1528.c index 14d6352..688592b 100644 --- a/drivers/media/usb/gspca/spca1528.c +++ b/drivers/media/usb/gspca/spca1528.c @@ -146,7 +146,7 @@ static void wait_status_0(struct gspca_dev *gspca_dev) w += 15; msleep(w); } while (--i > 0); - PDEBUG(D_ERR, "wait_status_0 timeout"); + PERR("wait_status_0 timeout"); gspca_dev->usb_err = -ETIME; } @@ -164,7 +164,7 @@ static void wait_status_1(struct gspca_dev *gspca_dev) return; } } while (--i > 0); - PDEBUG(D_ERR, "wait_status_1 timeout"); + PERR("wait_status_1 timeout"); gspca_dev->usb_err = -ETIME; } diff --git a/drivers/media/usb/gspca/spca500.c b/drivers/media/usb/gspca/spca500.c index 25cb68d..9f8bf51 100644 --- a/drivers/media/usb/gspca/spca500.c +++ b/drivers/media/usb/gspca/spca500.c @@ -489,7 +489,7 @@ static int spca500_full_reset(struct gspca_dev *gspca_dev) return err; err = reg_r_wait(gspca_dev, 0x06, 0, 0); if (err < 0) { - PDEBUG(D_ERR, "reg_r_wait() failed"); + PERR("reg_r_wait() failed"); return err; } /* all ok */ @@ -505,7 +505,7 @@ static int spca500_full_reset(struct gspca_dev *gspca_dev) static int spca500_synch310(struct gspca_dev *gspca_dev) { if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { - PDEBUG(D_ERR, "Set packet size: set interface error"); + PERR("Set packet size: set interface error"); goto error; } spca500_ping310(gspca_dev); @@ -519,7 +519,7 @@ static int spca500_synch310(struct gspca_dev *gspca_dev) if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->alt) < 0) { - PDEBUG(D_ERR, "Set packet size: set interface error"); + PERR("Set packet size: set interface error"); goto error; } return 0; @@ -544,7 +544,7 @@ static void spca500_reinit(struct gspca_dev *gspca_dev) err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, qtable_pocketdv); if (err < 0) - PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); + PERR("spca50x_setup_qtable failed on init"); /* set qtable index */ reg_w(gspca_dev, 0x00, 0x8880, 2); @@ -639,7 +639,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); /* Init SDRAM - needed for SDRAM access */ reg_w(gspca_dev, 0x00, 0x870a, 0x04); @@ -647,7 +647,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8000, 0x0004); msleep(500); if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); + PERR("reg_r_wait() failed"); reg_r(gspca_dev, 0x816b, 1); Data = gspca_dev->usb_buf[0]; @@ -660,13 +660,13 @@ static int sd_start(struct gspca_dev *gspca_dev) /* enable drop packet */ err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); if (err < 0) - PDEBUG(D_ERR, "failed to enable drop packet"); + PERR("failed to enable drop packet"); reg_w(gspca_dev, 0x00, 0x8880, 3); err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); /* Init SDRAM - needed for SDRAM access */ reg_w(gspca_dev, 0x00, 0x870a, 0x04); @@ -675,7 +675,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8000, 0x0004); if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); + PERR("reg_r_wait() failed"); reg_r(gspca_dev, 0x816b, 1); Data = gspca_dev->usb_buf[0]; @@ -689,18 +689,18 @@ static int sd_start(struct gspca_dev *gspca_dev) /* do a full reset */ err = spca500_full_reset(gspca_dev); if (err < 0) - PDEBUG(D_ERR, "spca500_full_reset failed"); + PERR("spca500_full_reset failed"); /* enable drop packet */ err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); if (err < 0) - PDEBUG(D_ERR, "failed to enable drop packet"); + PERR("failed to enable drop packet"); reg_w(gspca_dev, 0x00, 0x8880, 3); err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); spca500_setmode(gspca_dev, xmult, ymult); reg_w(gspca_dev, 0x20, 0x0001, 0x0004); @@ -709,7 +709,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8000, 0x0004); if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); + PERR("reg_r_wait() failed"); reg_r(gspca_dev, 0x816b, 1); Data = gspca_dev->usb_buf[0]; @@ -722,7 +722,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* do a full reset */ err = spca500_full_reset(gspca_dev); if (err < 0) - PDEBUG(D_ERR, "spca500_full_reset failed"); + PERR("spca500_full_reset failed"); /* enable drop packet */ reg_w(gspca_dev, 0x00, 0x850a, 0x0001); reg_w(gspca_dev, 0x00, 0x8880, 0); @@ -730,7 +730,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x00, 0x8800, 0x8840, qtable_kodak_ez200); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); spca500_setmode(gspca_dev, xmult, ymult); reg_w(gspca_dev, 0x20, 0x0001, 0x0004); @@ -739,7 +739,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8000, 0x0004); if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); + PERR("reg_r_wait() failed"); reg_r(gspca_dev, 0x816b, 1); Data = gspca_dev->usb_buf[0]; @@ -765,7 +765,7 @@ static int sd_start(struct gspca_dev *gspca_dev) err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, qtable_pocketdv); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); reg_w(gspca_dev, 0x00, 0x8880, 2); /* familycam Quicksmart pocketDV stuff */ @@ -795,7 +795,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x00, 0x8800, 0x8840, qtable_creative_pccam); if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + PERR("spca50x_setup_qtable failed"); reg_w(gspca_dev, 0x00, 0x8880, 3); reg_w(gspca_dev, 0x00, 0x800a, 0x00); /* Init SDRAM - needed for SDRAM access */ diff --git a/drivers/media/usb/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c index 3b7f777..d92fd17 100644 --- a/drivers/media/usb/gspca/spca501.c +++ b/drivers/media/usb/gspca/spca501.c @@ -1756,10 +1756,11 @@ static const __u16 spca501c_mysterious_init_data[][3] = { {} }; -static int reg_write(struct usb_device *dev, - __u16 req, __u16 index, __u16 value) +static int reg_write(struct gspca_dev *gspca_dev, + __u16 req, __u16 index, __u16 value) { int ret; + struct usb_device *dev = gspca_dev->dev; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -1774,17 +1775,15 @@ static int reg_write(struct usb_device *dev, } -static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) +static int write_vector(struct gspca_dev *gspca_dev, const __u16 data[][3]) { - struct usb_device *dev = gspca_dev->dev; int ret, i = 0; while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { - ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + ret = reg_write(gspca_dev, data[i][0], data[i][2], + data[i][1]); if (ret < 0) { - PDEBUG(D_ERR, - "Reg write failed for 0x%02x,0x%02x,0x%02x", + PERR("Reg write failed for 0x%02x,0x%02x,0x%02x", data[i][0], data[i][1], data[i][2]); return ret; } @@ -1795,30 +1794,28 @@ static int write_vector(struct gspca_dev *gspca_dev, static void setbrightness(struct gspca_dev *gspca_dev, s32 val) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val); + reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x12, val); } static void setcontrast(struct gspca_dev *gspca_dev, s32 val) { - reg_write(gspca_dev->dev, 0x00, 0x00, - (val >> 8) & 0xff); - reg_write(gspca_dev->dev, 0x00, 0x01, - val & 0xff); + reg_write(gspca_dev, 0x00, 0x00, (val >> 8) & 0xff); + reg_write(gspca_dev, 0x00, 0x01, val & 0xff); } static void setcolors(struct gspca_dev *gspca_dev, s32 val) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val); + reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x0c, val); } static void setblue_balance(struct gspca_dev *gspca_dev, s32 val) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val); + reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x11, val); } static void setred_balance(struct gspca_dev *gspca_dev, s32 val) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val); + reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x13, val); } /* this function is called at probe time */ @@ -1868,7 +1865,6 @@ error: static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int mode; switch (sd->subtype) { @@ -1895,20 +1891,20 @@ static int sd_start(struct gspca_dev *gspca_dev) /* Enable ISO packet machine CTRL reg=2, * index=1 bitmask=0x2 (bit ordinal 1) */ - reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); + reg_write(gspca_dev, SPCA50X_REG_USB, 0x6, 0x94); switch (mode) { case 0: /* 640x480 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); + reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x004a); break; case 1: /* 320x240 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); + reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x104a); break; default: /* case 2: * 160x120 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); + reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x204a); break; } - reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); + reg_write(gspca_dev, SPCA501_REG_CTLRL, 0x01, 0x02); return 0; } @@ -1917,7 +1913,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { /* Disable ISO packet * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ - reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); + reg_write(gspca_dev, SPCA501_REG_CTLRL, 0x01, 0x00); } /* called on streamoff with alt 0 and on disconnect */ @@ -1925,7 +1921,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { if (!gspca_dev->present) return; - reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); + reg_write(gspca_dev, SPCA501_REG_CTLRL, 0x05, 0x00); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, diff --git a/drivers/media/usb/gspca/spca505.c b/drivers/media/usb/gspca/spca505.c index bc7d67c..232b330 100644 --- a/drivers/media/usb/gspca/spca505.c +++ b/drivers/media/usb/gspca/spca505.c @@ -544,10 +544,11 @@ static const u8 spca505b_open_data_ccd[][3] = { {} }; -static int reg_write(struct usb_device *dev, +static int reg_write(struct gspca_dev *gspca_dev, u16 req, u16 index, u16 value) { int ret; + struct usb_device *dev = gspca_dev->dev; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -584,11 +585,11 @@ static int reg_read(struct gspca_dev *gspca_dev, static int write_vector(struct gspca_dev *gspca_dev, const u8 data[][3]) { - struct usb_device *dev = gspca_dev->dev; int ret, i = 0; while (data[i][0] != 0) { - ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + ret = reg_write(gspca_dev, data[i][0], data[i][2], + data[i][1]); if (ret < 0) return ret; i++; @@ -629,14 +630,13 @@ static int sd_init(struct gspca_dev *gspca_dev) static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) { - reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6); - reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2); + reg_write(gspca_dev, 0x05, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev, 0x05, 0x01, (255 - brightness) << 2); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int ret, mode; static u8 mode_tb[][3] = { /* r00 r06 r07 */ @@ -654,9 +654,7 @@ static int sd_start(struct gspca_dev *gspca_dev) ret = reg_read(gspca_dev, 0x06, 0x16); if (ret < 0) { - PDEBUG(D_ERR|D_CONF, - "register read failed err: %d", - ret); + PERR("register read failed err: %d", ret); return ret; } if (ret != 0x0101) { @@ -664,22 +662,22 @@ static int sd_start(struct gspca_dev *gspca_dev) ret); } - ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a); + ret = reg_write(gspca_dev, 0x06, 0x16, 0x0a); if (ret < 0) return ret; - reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12); + reg_write(gspca_dev, 0x05, 0xc2, 0x12); /* necessary because without it we can see stream * only once after loading module */ /* stopping usb registers Tomasz change */ - reg_write(dev, 0x02, 0x00, 0x00); + reg_write(gspca_dev, 0x02, 0x00, 0x00); mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]); - reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]); - reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]); + reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]); + reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]); + reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]); - return reg_write(dev, SPCA50X_REG_USB, + return reg_write(gspca_dev, SPCA50X_REG_USB, SPCA50X_USB_CTRL, SPCA50X_CUSB_ENABLE); } @@ -687,7 +685,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { /* Disable ISO packet machine */ - reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); + reg_write(gspca_dev, 0x02, 0x00, 0x00); } /* called on streamoff with alt 0 and on disconnect */ @@ -697,11 +695,11 @@ static void sd_stop0(struct gspca_dev *gspca_dev) return; /* This maybe reset or power control */ - reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); - reg_write(gspca_dev->dev, 0x03, 0x01, 0x00); - reg_write(gspca_dev->dev, 0x03, 0x00, 0x01); - reg_write(gspca_dev->dev, 0x05, 0x10, 0x01); - reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f); + reg_write(gspca_dev, 0x03, 0x03, 0x20); + reg_write(gspca_dev, 0x03, 0x01, 0x00); + reg_write(gspca_dev, 0x03, 0x00, 0x01); + reg_write(gspca_dev, 0x05, 0x10, 0x01); + reg_write(gspca_dev, 0x05, 0x11, 0x0f); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, diff --git a/drivers/media/usb/gspca/spca508.c b/drivers/media/usb/gspca/spca508.c index 1286b41..75f2beb 100644 --- a/drivers/media/usb/gspca/spca508.c +++ b/drivers/media/usb/gspca/spca508.c @@ -1241,10 +1241,10 @@ static const u16 spca508_vista_init_data[][2] = { {} }; -static int reg_write(struct usb_device *dev, - u16 index, u16 value) +static int reg_write(struct gspca_dev *gspca_dev, u16 index, u16 value) { int ret; + struct usb_device *dev = gspca_dev->dev; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -1286,22 +1286,21 @@ static int reg_read(struct gspca_dev *gspca_dev, static int ssi_w(struct gspca_dev *gspca_dev, u16 reg, u16 val) { - struct usb_device *dev = gspca_dev->dev; int ret, retry; - ret = reg_write(dev, 0x8802, reg >> 8); + ret = reg_write(gspca_dev, 0x8802, reg >> 8); if (ret < 0) goto out; - ret = reg_write(dev, 0x8801, reg & 0x00ff); + ret = reg_write(gspca_dev, 0x8801, reg & 0x00ff); if (ret < 0) goto out; if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */ - ret = reg_write(dev, 0x8805, val & 0x00ff); + ret = reg_write(gspca_dev, 0x8805, val & 0x00ff); if (ret < 0) goto out; val >>= 8; } - ret = reg_write(dev, 0x8800, val); + ret = reg_write(gspca_dev, 0x8800, val); if (ret < 0) goto out; @@ -1314,8 +1313,7 @@ static int ssi_w(struct gspca_dev *gspca_dev, if (gspca_dev->usb_buf[0] == 0) break; if (--retry <= 0) { - PDEBUG(D_ERR, "ssi_w busy %02x", - gspca_dev->usb_buf[0]); + PERR("ssi_w busy %02x", gspca_dev->usb_buf[0]); ret = -1; break; } @@ -1329,7 +1327,6 @@ out: static int write_vector(struct gspca_dev *gspca_dev, const u16 (*data)[2]) { - struct usb_device *dev = gspca_dev->dev; int ret = 0; while ((*data)[1] != 0) { @@ -1337,7 +1334,8 @@ static int write_vector(struct gspca_dev *gspca_dev, if ((*data)[1] == 0xdd00) /* delay */ msleep((*data)[0]); else - ret = reg_write(dev, (*data)[1], (*data)[0]); + ret = reg_write(gspca_dev, (*data)[1], + (*data)[0]); } else { ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]); } @@ -1363,8 +1361,6 @@ static int sd_config(struct gspca_dev *gspca_dev, spca508cs110_init_data, /* MicroInnovationIC200 4 */ spca508_init_data, /* ViewQuestVQ110 5 */ }; - -#ifdef GSPCA_DEBUG int data1, data2; /* Read from global register the USB product and vendor IDs, just to @@ -1381,7 +1377,6 @@ static int sd_config(struct gspca_dev *gspca_dev, data1 = reg_read(gspca_dev, 0x8621); PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); -#endif cam = &gspca_dev->cam; cam->cam_mode = sif_mode; @@ -1404,26 +1399,26 @@ static int sd_start(struct gspca_dev *gspca_dev) int mode; mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - reg_write(gspca_dev->dev, 0x8500, mode); + reg_write(gspca_dev, 0x8500, mode); switch (mode) { case 0: case 1: - reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ + reg_write(gspca_dev, 0x8700, 0x28); /* clock */ break; default: /* case 2: */ /* case 3: */ - reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ + reg_write(gspca_dev, 0x8700, 0x23); /* clock */ break; } - reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); + reg_write(gspca_dev, 0x8112, 0x10 | 0x20); return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) { /* Video ISO disable, Video Drop Packet enable: */ - reg_write(gspca_dev->dev, 0x8112, 0x20); + reg_write(gspca_dev, 0x8112, 0x20); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -1450,10 +1445,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) { /* MX seem contrast */ - reg_write(gspca_dev->dev, 0x8651, brightness); - reg_write(gspca_dev->dev, 0x8652, brightness); - reg_write(gspca_dev->dev, 0x8653, brightness); - reg_write(gspca_dev->dev, 0x8654, brightness); + reg_write(gspca_dev, 0x8651, brightness); + reg_write(gspca_dev, 0x8652, brightness); + reg_write(gspca_dev, 0x8653, brightness); + reg_write(gspca_dev, 0x8654, brightness); } static int sd_s_ctrl(struct v4l2_ctrl *ctrl) diff --git a/drivers/media/usb/gspca/spca561.c b/drivers/media/usb/gspca/spca561.c index d1db3d8..403d71c 100644 --- a/drivers/media/usb/gspca/spca561.c +++ b/drivers/media/usb/gspca/spca561.c @@ -285,9 +285,10 @@ static const __u16 spca561_161rev12A_data2[][2] = { {} }; -static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) +static void reg_w_val(struct gspca_dev *gspca_dev, __u16 index, __u8 value) { int ret; + struct usb_device *dev = gspca_dev->dev; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0, /* request */ @@ -301,12 +302,11 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) static void write_vector(struct gspca_dev *gspca_dev, const __u16 data[][2]) { - struct usb_device *dev = gspca_dev->dev; int i; i = 0; while (data[i][1] != 0) { - reg_w_val(dev, data[i][1], data[i][0]); + reg_w_val(gspca_dev, data[i][1], data[i][0]); i++; } } @@ -339,9 +339,9 @@ static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) { int retry = 60; - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8805, value); - reg_w_val(gspca_dev->dev, 0x8800, value >> 8); + reg_w_val(gspca_dev, 0x8801, reg); + reg_w_val(gspca_dev, 0x8805, value); + reg_w_val(gspca_dev, 0x8800, value >> 8); do { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf[0]) @@ -355,9 +355,9 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) int retry = 60; __u8 value; - reg_w_val(gspca_dev->dev, 0x8804, 0x92); - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); + reg_w_val(gspca_dev, 0x8804, 0x92); + reg_w_val(gspca_dev, 0x8801, reg); + reg_w_val(gspca_dev, 0x8802, mode | 0x01); do { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf[0]) { @@ -459,14 +459,13 @@ static int sd_init_72a(struct gspca_dev *gspca_dev) write_sensor_72a(gspca_dev, rev72a_init_sensor1); write_vector(gspca_dev, rev72a_init_data2); write_sensor_72a(gspca_dev, rev72a_init_sensor2); - reg_w_val(gspca_dev->dev, 0x8112, 0x30); + reg_w_val(gspca_dev, 0x8112, 0x30); return 0; } static void setbrightness(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; __u16 reg; if (sd->chip_revision == Rev012A) @@ -474,16 +473,15 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) else reg = 0x8611; - reg_w_val(dev, reg + 0, val); /* R */ - reg_w_val(dev, reg + 1, val); /* Gr */ - reg_w_val(dev, reg + 2, val); /* B */ - reg_w_val(dev, reg + 3, val); /* Gb */ + reg_w_val(gspca_dev, reg + 0, val); /* R */ + reg_w_val(gspca_dev, reg + 1, val); /* Gr */ + reg_w_val(gspca_dev, reg + 2, val); /* B */ + reg_w_val(gspca_dev, reg + 3, val); /* Gb */ } static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; __u8 blue, red; __u16 reg; @@ -496,11 +494,11 @@ static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) reg = 0x8651; red += contrast - 0x20; blue += contrast - 0x20; - reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */ - reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */ + reg_w_val(gspca_dev, 0x8652, contrast + 0x20); /* Gr */ + reg_w_val(gspca_dev, 0x8654, contrast + 0x20); /* Gb */ } - reg_w_val(dev, reg, red); - reg_w_val(dev, reg + 2, blue); + reg_w_val(gspca_dev, reg, red); + reg_w_val(gspca_dev, reg + 2, blue); } /* rev 12a only */ @@ -570,7 +568,6 @@ static void setautogain(struct gspca_dev *gspca_dev, s32 val) static int sd_start_12a(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; int mode; static const __u8 Reg8391[8] = {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; @@ -578,34 +575,33 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode <= 1) { /* Use compression on 320x240 and above */ - reg_w_val(dev, 0x8500, 0x10 | mode); + reg_w_val(gspca_dev, 0x8500, 0x10 | mode); } else { /* I couldn't get the compression to work below 320x240 * Fortunately at these resolutions the bandwidth * is sufficient to push raw frames at ~20fps */ - reg_w_val(dev, 0x8500, mode); + reg_w_val(gspca_dev, 0x8500, mode); } /* -- qq@kuku.eu.org */ gspca_dev->usb_buf[0] = 0xaa; gspca_dev->usb_buf[1] = 0x00; reg_w_buf(gspca_dev, 0x8307, 2); /* clock - lower 0x8X values lead to fps > 30 */ - reg_w_val(gspca_dev->dev, 0x8700, 0x8a); + reg_w_val(gspca_dev, 0x8700, 0x8a); /* 0x8f 0x85 0x27 clock */ - reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); - reg_w_val(gspca_dev->dev, 0x850b, 0x03); + reg_w_val(gspca_dev, 0x8112, 0x1e | 0x20); + reg_w_val(gspca_dev, 0x850b, 0x03); memcpy(gspca_dev->usb_buf, Reg8391, 8); reg_w_buf(gspca_dev, 0x8391, 8); reg_w_buf(gspca_dev, 0x8390, 8); /* Led ON (bit 3 -> 0 */ - reg_w_val(gspca_dev->dev, 0x8114, 0x00); + reg_w_val(gspca_dev, 0x8114, 0x00); return 0; } static int sd_start_72a(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int Clck; int mode; @@ -630,15 +626,15 @@ static int sd_start_72a(struct gspca_dev *gspca_dev) Clck = 0x21; break; } - reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ - reg_w_val(dev, 0x8702, 0x81); - reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(gspca_dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(gspca_dev, 0x8702, 0x81); + reg_w_val(gspca_dev, 0x8500, mode); /* mode */ write_sensor_72a(gspca_dev, rev72a_init_sensor2); setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), v4l2_ctrl_g_ctrl(sd->contrast)); /* setbrightness(gspca_dev); * fixme: bad values */ setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - reg_w_val(dev, 0x8112, 0x10 | 0x20); + reg_w_val(gspca_dev, 0x8112, 0x10 | 0x20); return 0; } @@ -647,12 +643,12 @@ static void sd_stopN(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; if (sd->chip_revision == Rev012A) { - reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + reg_w_val(gspca_dev, 0x8112, 0x0e); /* Led Off (bit 3 -> 1 */ - reg_w_val(gspca_dev->dev, 0x8114, 0x08); + reg_w_val(gspca_dev, 0x8114, 0x08); } else { - reg_w_val(gspca_dev->dev, 0x8112, 0x20); -/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ + reg_w_val(gspca_dev, 0x8112, 0x20); +/* reg_w_val(gspca_dev, 0x8102, 0x00); ?? */ } } @@ -736,7 +732,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* This should never happen */ if (len < 2) { - PDEBUG(D_ERR, "Short SOF packet, ignoring"); + PERR("Short SOF packet, ignoring"); gspca_dev->last_packet_type = DISCARD_PACKET; return; } diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c index 1d99f10..a7ae0ec 100644 --- a/drivers/media/usb/gspca/sq905.c +++ b/drivers/media/usb/gspca/sq905.c @@ -387,7 +387,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } if (ret < 0) { - PDEBUG(D_ERR, "Start streaming command failed"); + PERR("Start streaming command failed"); return ret; } /* Start the workqueue function to do the streaming */ diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c index 410cdcb..acb19fb 100644 --- a/drivers/media/usb/gspca/sq905c.c +++ b/drivers/media/usb/gspca/sq905c.c @@ -215,13 +215,13 @@ static int sd_config(struct gspca_dev *gspca_dev, ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0); if (ret < 0) { - PDEBUG(D_ERR, "Get version command failed"); + PERR("Get version command failed"); return ret; } ret = sq905c_read(gspca_dev, 0xf5, 0, 20); if (ret < 0) { - PDEBUG(D_ERR, "Reading version command failed"); + PERR("Reading version command failed"); return ret; } /* Note we leave out the usb id and the manufacturing date */ @@ -286,7 +286,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } if (ret < 0) { - PDEBUG(D_ERR, "Start streaming command failed"); + PERR("Start streaming command failed"); return ret; } /* Start the workqueue function to do the streaming */ diff --git a/drivers/media/usb/gspca/sq930x.c b/drivers/media/usb/gspca/sq930x.c index 7e8748b..b10d082 100644 --- a/drivers/media/usb/gspca/sq930x.c +++ b/drivers/media/usb/gspca/sq930x.c @@ -541,13 +541,11 @@ static void ucbus_write(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return; -#ifdef GSPCA_DEBUG if ((batchsize - 1) * 3 > USB_BUF_SZ) { - pr_err("Bug: usb_buf overflow\n"); + PERR("Bug: usb_buf overflow\n"); gspca_dev->usb_err = -ENOMEM; return; } -#endif for (;;) { len = ncmds; diff --git a/drivers/media/usb/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c index 6760527..9c08276 100644 --- a/drivers/media/usb/gspca/stv0680.c +++ b/drivers/media/usb/gspca/stv0680.c @@ -86,7 +86,7 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) { stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ - PDEBUG(D_ERR, "last error: %i, command = 0x%x", + PERR("last error: %i, command = 0x%x", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); return ret; } @@ -98,7 +98,7 @@ static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0] = 0x0f; if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { - PDEBUG(D_ERR, "Get_Camera_Mode failed"); + PERR("Get_Camera_Mode failed"); return stv0680_handle_error(gspca_dev, -EIO); } @@ -116,13 +116,13 @@ static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) gspca_dev->usb_buf[0] = mode; if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { - PDEBUG(D_ERR, "Set_Camera_Mode failed"); + PERR("Set_Camera_Mode failed"); return stv0680_handle_error(gspca_dev, -EIO); } /* Verify we got what we've asked for */ if (stv0680_get_video_mode(gspca_dev) != mode) { - PDEBUG(D_ERR, "Error setting camera video mode!"); + PERR("Error setting camera video mode!"); return -EIO; } @@ -146,7 +146,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* ping camera to be sure STV0680 is present */ if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { - PDEBUG(D_ERR, "STV(e): camera ping failed!!"); + PERR("STV(e): camera ping failed!!"); return stv0680_handle_error(gspca_dev, -ENODEV); } @@ -156,7 +156,7 @@ static int sd_config(struct gspca_dev *gspca_dev, if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { - PDEBUG(D_ERR, "Could not get descriptor 0200."); + PERR("Could not get descriptor 0200."); return stv0680_handle_error(gspca_dev, -ENODEV); } if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) @@ -167,7 +167,7 @@ static int sd_config(struct gspca_dev *gspca_dev, return stv0680_handle_error(gspca_dev, -ENODEV); if (!(gspca_dev->usb_buf[7] & 0x09)) { - PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); + PERR("Camera supports neither CIF nor QVGA mode"); return -ENODEV; } if (gspca_dev->usb_buf[7] & 0x01) diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c index 657160b..55ee7a6 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c @@ -42,8 +42,10 @@ static bool dump_sensor; int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; + u8 len = (i2c_data > 0xff) ? 2 : 1; buf[0] = i2c_data & 0xff; @@ -62,6 +64,7 @@ int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -110,6 +113,7 @@ static int stv06xx_write_sensor_finish(struct sd *sd) int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) { int err, i, j; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -139,6 +143,7 @@ int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) { int err, i, j; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -170,6 +175,7 @@ int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; @@ -283,7 +289,7 @@ static int stv06xx_start(struct gspca_dev *gspca_dev) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - PDEBUG(D_ERR, "Couldn't get altsetting"); + PERR("Couldn't get altsetting"); return -EIO; } @@ -341,7 +347,7 @@ static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set alt 1 err %d", ret); + PERR("set alt 1 err %d", ret); return ret; } @@ -406,7 +412,7 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, len -= 4; if (len < chunk_len) { - PDEBUG(D_ERR, "URB packet length is smaller" + PERR("URB packet length is smaller" " than the specified chunk length"); gspca_dev->last_packet_type = DISCARD_PACKET; return; @@ -449,7 +455,7 @@ frame_data: sd->to_skip = gspca_dev->width * 4; if (chunk_len) - PDEBUG(D_ERR, "Chunk length is " + PERR("Chunk length is " "non-zero on a SOF"); break; @@ -463,7 +469,7 @@ frame_data: NULL, 0); if (chunk_len) - PDEBUG(D_ERR, "Chunk length is " + PERR("Chunk length is " "non-zero on a EOF"); break; @@ -596,7 +602,6 @@ MODULE_DEVICE_TABLE(usb, device_table); static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - PDEBUG(D_PROBE, "Probing for a stv06xx device"); return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), THIS_MODULE); } diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c index 06fa54c5e..2220b70 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c @@ -255,7 +255,7 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) return err; } - PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", + PDEBUG(D_CONF, "Writing exposure %d, rowexp %d, srowexp %d", val, rowexp, srowexp); return err; } @@ -280,7 +280,7 @@ static int hdcs_set_gains(struct sd *sd, u8 g) static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) { - PDEBUG(D_V4L2, "Writing gain %d", val); + PDEBUG(D_CONF, "Writing gain %d", val); return hdcs_set_gains((struct sd *) gspca_dev, val & 0xff); } @@ -467,6 +467,8 @@ static int hdcs_probe_1020(struct sd *sd) static int hdcs_start(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_STREAM, "Starting stream"); return hdcs_set_state(sd, HDCS_STATE_RUN); @@ -474,6 +476,8 @@ static int hdcs_start(struct sd *sd) static int hdcs_stop(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_STREAM, "Halting stream"); return hdcs_set_state(sd, HDCS_STATE_SLEEP); diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c index cdfc3d0..8206b77 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c @@ -190,6 +190,7 @@ static int pb0100_start(struct sd *sd) int err, packet_size, max_packet_size; struct usb_host_interface *alt; struct usb_interface *intf; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct cam *cam = &sd->gspca_dev.cam; u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; @@ -239,6 +240,7 @@ static int pb0100_start(struct sd *sd) static int pb0100_stop(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int err; err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); @@ -334,7 +336,7 @@ static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) err = stv06xx_write_sensor(sd, PB_G1GAIN, val); if (!err) err = stv06xx_write_sensor(sd, PB_G2GAIN, val); - PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); + PDEBUG(D_CONF, "Set green gain to %d, status: %d", val, err); if (!err) err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); @@ -357,7 +359,7 @@ static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) val = 255; err = stv06xx_write_sensor(sd, PB_RGAIN, val); - PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); + PDEBUG(D_CONF, "Set red gain to %d, status: %d", val, err); return err; } @@ -375,7 +377,7 @@ static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) val = 255; err = stv06xx_write_sensor(sd, PB_BGAIN, val); - PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); + PDEBUG(D_CONF, "Set blue gain to %d, status: %d", val, err); return err; } @@ -386,7 +388,7 @@ static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) int err; err = stv06xx_write_sensor(sd, PB_RINTTIME, val); - PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); + PDEBUG(D_CONF, "Set exposure to %d, status: %d", val, err); return err; } @@ -406,7 +408,7 @@ static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) val = 0; err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); - PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", + PDEBUG(D_CONF, "Set autogain to %d (natural: %d), status: %d", val, ctrls->natural->val, err); return err; @@ -428,7 +430,7 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) if (!err) err = stv06xx_write_sensor(sd, PB_R22, darkpixels); - PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); + PDEBUG(D_CONF, "Set autogain target to %d, status: %d", val, err); return err; } diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c index 8a57990..515a9e1 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c @@ -279,6 +279,8 @@ static int st6422_start(struct sd *sd) static int st6422_stop(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + PDEBUG(D_STREAM, "Halting stream"); return 0; diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index e95fa89..bf3e5c3 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -131,6 +131,7 @@ static int vv6410_init(struct sd *sd) static int vv6410_start(struct sd *sd) { int err; + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; struct cam *cam = &sd->gspca_dev.cam; u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; @@ -163,6 +164,7 @@ static int vv6410_start(struct sd *sd) static int vv6410_stop(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int err; /* Turn off LED */ @@ -208,7 +210,7 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) else i2c_data &= ~VV6410_HFLIP; - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + PDEBUG(D_CONF, "Set horizontal flip to %d", val); err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); return (err < 0) ? err : 0; @@ -229,7 +231,7 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) else i2c_data &= ~VV6410_VFLIP; - PDEBUG(D_V4L2, "Set vertical flip to %d", val); + PDEBUG(D_CONF, "Set vertical flip to %d", val); err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); return (err < 0) ? err : 0; @@ -240,7 +242,7 @@ static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) int err; struct sd *sd = (struct sd *) gspca_dev; - PDEBUG(D_V4L2, "Set analog gain to %d", val); + PDEBUG(D_CONF, "Set analog gain to %d", val); err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); return (err < 0) ? err : 0; @@ -257,7 +259,7 @@ static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) fine = val % VV6410_CIF_LINELENGTH; coarse = min(512, val / VV6410_CIF_LINELENGTH); - PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d", + PDEBUG(D_CONF, "Set coarse exposure to %d, fine expsure to %d", coarse, fine); err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); diff --git a/drivers/media/usb/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c index 9ccfcb1..af8767a 100644 --- a/drivers/media/usb/gspca/sunplus.c +++ b/drivers/media/usb/gspca/sunplus.c @@ -251,12 +251,10 @@ static void reg_r(struct gspca_dev *gspca_dev, { int ret; -#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); + PERR("reg_r: buffer overflow\n"); return; } -#endif if (gspca_dev->usb_err < 0) return; ret = usb_control_msg(gspca_dev->dev, @@ -357,12 +355,14 @@ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]); } -#ifdef GSPCA_DEBUG static void spca504_read_info(struct gspca_dev *gspca_dev) { int i; u8 info[6]; + if (gspca_debug < D_STREAM) + return; + for (i = 0; i < 6; i++) { reg_r(gspca_dev, 0, i, 1); info[i] = gspca_dev->usb_buf[0]; @@ -373,7 +373,6 @@ static void spca504_read_info(struct gspca_dev *gspca_dev) info[0], info[1], info[2], info[3], info[4], info[5]); } -#endif static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, u8 req, @@ -432,11 +431,13 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) } } -#ifdef GSPCA_DEBUG static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { u8 *data; + if (gspca_debug < D_STREAM) + return; + data = gspca_dev->usb_buf; reg_r(gspca_dev, 0x20, 0, 5); PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d", @@ -444,7 +445,6 @@ static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x23, 0, 64); reg_r(gspca_dev, 0x23, 1, 64); } -#endif static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) { @@ -457,9 +457,8 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) reg_w_riv(gspca_dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); -#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); -#endif + reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ reg_r(gspca_dev, 0x24, 8, 1); @@ -645,14 +644,10 @@ static int sd_init(struct gspca_dev *gspca_dev) /* fall thru */ case BRIDGE_SPCA533: spca504B_PollingDataReady(gspca_dev); -#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); -#endif break; case BRIDGE_SPCA536: -#ifdef GSPCA_DEBUG spca50x_GetFirmware(gspca_dev); -#endif reg_r(gspca_dev, 0x00, 0x5002, 1); reg_w_1(gspca_dev, 0x24, 0, 0, 0); reg_r(gspca_dev, 0x24, 0, 1); @@ -678,9 +673,7 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504: */ PDEBUG(D_STREAM, "Opening SPCA504"); if (sd->subtype == AiptekMiniPenCam13) { -#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); -#endif /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, @@ -752,9 +745,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case BRIDGE_SPCA504: if (sd->subtype == AiptekMiniPenCam13) { -#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); -#endif /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, @@ -766,9 +757,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0, 0, 0x9d, 1); } else { spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); -#ifdef GSPCA_DEBUG spca504_read_info(gspca_dev); -#endif spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); } diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c index e500795..c00ac57 100644 --- a/drivers/media/usb/gspca/vc032x.c +++ b/drivers/media/usb/gspca/vc032x.c @@ -2927,7 +2927,6 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 len) { reg_r_i(gspca_dev, req, index, len); -#ifdef GSPCA_DEBUG if (gspca_dev->usb_err < 0) return; if (len == 1) @@ -2936,7 +2935,6 @@ static void reg_r(struct gspca_dev *gspca_dev, else PDEBUG(D_USBI, "GET %02x 0001 %04x %*ph", req, index, 3, gspca_dev->usb_buf); -#endif } static void reg_w_i(struct gspca_dev *gspca_dev, @@ -2964,11 +2962,9 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) { -#ifdef GSPCA_DEBUG if (gspca_dev->usb_err < 0) return; PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); -#endif reg_w_i(gspca_dev, req, value, index); } @@ -3044,8 +3040,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) if (value == 0 && ptsensor_info->IdAdd == 0x82) value = read_sensor_register(gspca_dev, 0x83); if (value != 0) { - PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)", - value, i); + PDEBUG(D_PROBE, "Sensor ID %04x (%d)", value, i); if (value == ptsensor_info->VpId) return ptsensor_info->sensorId; @@ -3069,14 +3064,12 @@ static void i2c_write(struct gspca_dev *gspca_dev, { int retry; -#ifdef GSPCA_DEBUG if (gspca_dev->usb_err < 0) return; if (size == 1) PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val); else PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]); -#endif reg_r_i(gspca_dev, 0xa1, 0xb33f, 1); /*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ reg_w_i(gspca_dev, 0xa0, size, 0xb334); diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c index 9e3a909..2165da0 100644 --- a/drivers/media/usb/gspca/w996Xcf.c +++ b/drivers/media/usb/gspca/w996Xcf.c @@ -232,6 +232,7 @@ static void w9968cf_smbus_write_nack(struct sd *sd) static void w9968cf_smbus_read_ack(struct sd *sd) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int sda; /* Ensure SDA is high before raising clock to avoid a spurious stop */ @@ -248,6 +249,7 @@ static void w9968cf_smbus_read_ack(struct sd *sd) /* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; u16* data = (u16 *)sd->gspca_dev.usb_buf; data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); @@ -297,6 +299,7 @@ static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) /* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ static int w9968cf_i2c_r(struct sd *sd, u8 reg) { + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; int ret = 0; u8 value; @@ -326,7 +329,7 @@ static int w9968cf_i2c_r(struct sd *sd, u8 reg) ret = value; PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); } else - PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + PERR("i2c read [0x%02x] failed", reg); return ret; } diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index a8dc421..cbfc2f9 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -6259,12 +6259,11 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); if (retword == 0x2030) { -#ifdef GSPCA_DEBUG u8 retbyte; retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); -#endif + send_unknown(gspca_dev, SENSOR_PO2030); return retword; } diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index 5c61935..8247c19 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -174,6 +174,7 @@ static int device_authorization(struct hdpvr_device *dev) case HDPVR_FIRMWARE_VERSION_AC3: case HDPVR_FIRMWARE_VERSION_0X12: case HDPVR_FIRMWARE_VERSION_0X15: + case HDPVR_FIRMWARE_VERSION_0X1E: dev->flags |= HDPVR_FLAG_AC3_CAP; break; default: @@ -196,6 +197,7 @@ static int device_authorization(struct hdpvr_device *dev) 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); + kfree(print_buf); #endif msleep(100); @@ -385,12 +387,6 @@ static int hdpvr_probe(struct usb_interface *interface, } mutex_unlock(&dev->io_mutex); - if (hdpvr_register_videodev(dev, &interface->dev, - video_nr[atomic_inc_return(&dev_nr)])) { - v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); - goto error; - } - #if IS_ENABLED(CONFIG_I2C) retval = hdpvr_register_i2c_adapter(dev); if (retval < 0) { @@ -413,6 +409,13 @@ static int hdpvr_probe(struct usb_interface *interface, } #endif + retval = hdpvr_register_videodev(dev, &interface->dev, + video_nr[atomic_inc_return(&dev_nr)]); + if (retval < 0) { + v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); + goto error; + } + /* let the user know what node this device is now attached to */ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", video_device_node_name(dev->video_dev)); diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index da6b779..774ba0e 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -10,6 +10,7 @@ */ #include <linux/kernel.h> +#include <linux/kconfig.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> @@ -20,9 +21,11 @@ #include <linux/workqueue.h> #include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> #include <media/v4l2-dev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "hdpvr.h" #define BULK_URB_TIMEOUT 90 /* 0.09 seconds */ @@ -34,8 +37,23 @@ list_size(&dev->free_buff_list), \ list_size(&dev->rec_buff_list)); } +static const struct v4l2_dv_timings hdpvr_dv_timings[] = { + V4L2_DV_BT_CEA_720X480I59_94, + V4L2_DV_BT_CEA_720X576I50, + V4L2_DV_BT_CEA_720X480P59_94, + V4L2_DV_BT_CEA_720X576P50, + V4L2_DV_BT_CEA_1280X720P50, + V4L2_DV_BT_CEA_1280X720P60, + V4L2_DV_BT_CEA_1920X1080I50, + V4L2_DV_BT_CEA_1920X1080I60, +}; + +/* Use 480i59 as the default timings */ +#define HDPVR_DEF_DV_TIMINGS_IDX (0) + struct hdpvr_fh { - struct hdpvr_device *dev; + struct v4l2_fh fh; + bool legacy_mode; }; static uint list_size(struct list_head *list) @@ -359,53 +377,29 @@ static int hdpvr_stop_streaming(struct hdpvr_device *dev) static int hdpvr_open(struct file *file) { - struct hdpvr_device *dev; - struct hdpvr_fh *fh; - int retval = -ENOMEM; - - dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file)); - if (!dev) { - pr_err("open failing with with ENODEV\n"); - retval = -ENODEV; - goto err; - } - - fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL); - if (!fh) { - v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto err; - } - /* lock the device to allow correctly handling errors - * in resumption */ - mutex_lock(&dev->io_mutex); - dev->open_count++; - mutex_unlock(&dev->io_mutex); + struct hdpvr_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); - fh->dev = dev; - - /* save our object in the file's private structure */ + if (fh == NULL) + return -ENOMEM; + fh->legacy_mode = true; + v4l2_fh_init(&fh->fh, video_devdata(file)); + v4l2_fh_add(&fh->fh); file->private_data = fh; - - retval = 0; -err: - return retval; + return 0; } static int hdpvr_release(struct file *file) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - - if (!dev) - return -ENODEV; + struct hdpvr_device *dev = video_drvdata(file); mutex_lock(&dev->io_mutex); - if (!(--dev->open_count) && dev->status == STATUS_STREAMING) + if (file->private_data == dev->owner) { hdpvr_stop_streaming(dev); - + dev->owner = NULL; + } mutex_unlock(&dev->io_mutex); - return 0; + return v4l2_fh_release(file); } /* @@ -415,8 +409,7 @@ static int hdpvr_release(struct file *file) static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, loff_t *pos) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); struct hdpvr_buffer *buf = NULL; struct urb *urb; unsigned int ret = 0; @@ -425,9 +418,6 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, if (*pos) return -ESPIPE; - if (!dev) - return -ENODEV; - mutex_lock(&dev->io_mutex); if (dev->status == STATUS_IDLE) { if (hdpvr_start_streaming(dev)) { @@ -439,6 +429,7 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, mutex_unlock(&dev->io_mutex); goto err; } + dev->owner = file->private_data; print_buffer_status(); } mutex_unlock(&dev->io_mutex); @@ -516,23 +507,23 @@ err: static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct hdpvr_buffer *buf = NULL; - struct hdpvr_fh *fh = filp->private_data; - struct hdpvr_device *dev = fh->dev; - unsigned int mask = 0; + struct hdpvr_device *dev = video_drvdata(filp); + unsigned int mask = v4l2_ctrl_poll(filp, wait); - mutex_lock(&dev->io_mutex); + if (!(req_events & (POLLIN | POLLRDNORM))) + return mask; - if (!video_is_registered(dev->video_dev)) { - mutex_unlock(&dev->io_mutex); - return -EIO; - } + mutex_lock(&dev->io_mutex); if (dev->status == STATUS_IDLE) { if (hdpvr_start_streaming(dev)) { v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, "start_streaming failed\n"); dev->status = STATUS_IDLE; + } else { + dev->owner = filp->private_data; } print_buffer_status(); @@ -574,36 +565,188 @@ static int vidioc_querycap(struct file *file, void *priv, strcpy(cap->driver, "hdpvr"); strcpy(cap->card, "Hauppauge HD PVR"); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int vidioc_s_std(struct file *file, void *private_data, - v4l2_std_id *std) +static int vidioc_s_std(struct file *file, void *_fh, + v4l2_std_id std) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; u8 std_type = 1; - if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60)) + if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT) + return -ENODATA; + if (dev->status != STATUS_IDLE) + return -EBUSY; + if (std & V4L2_STD_525_60) std_type = 0; + dev->cur_std = std; + dev->width = 720; + dev->height = std_type ? 576 : 480; return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type); } +static int vidioc_g_std(struct file *file, void *_fh, + v4l2_std_id *std) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + + if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT) + return -ENODATA; + *std = dev->cur_std; + return 0; +} + +static int vidioc_querystd(struct file *file, void *_fh, v4l2_std_id *a) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_video_info *vid_info; + struct hdpvr_fh *fh = _fh; + + *a = V4L2_STD_ALL; + if (dev->options.video_input == HDPVR_COMPONENT) + return fh->legacy_mode ? 0 : -ENODATA; + vid_info = get_video_info(dev); + if (vid_info == NULL) + return 0; + if (vid_info->width == 720 && + (vid_info->height == 480 || vid_info->height == 576)) { + *a = (vid_info->height == 480) ? + V4L2_STD_525_60 : V4L2_STD_625_50; + } + kfree(vid_info); + return 0; +} + +static int vidioc_s_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + int i; + + fh->legacy_mode = false; + if (dev->options.video_input) + return -ENODATA; + if (dev->status != STATUS_IDLE) + return -EBUSY; + for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) + if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) + break; + if (i == ARRAY_SIZE(hdpvr_dv_timings)) + return -EINVAL; + dev->cur_dv_timings = hdpvr_dv_timings[i]; + dev->width = hdpvr_dv_timings[i].bt.width; + dev->height = hdpvr_dv_timings[i].bt.height; + return 0; +} + +static int vidioc_g_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + + fh->legacy_mode = false; + if (dev->options.video_input) + return -ENODATA; + *timings = dev->cur_dv_timings; + return 0; +} + +static int vidioc_query_dv_timings(struct file *file, void *_fh, + struct v4l2_dv_timings *timings) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + struct hdpvr_video_info *vid_info; + bool interlaced; + int ret = 0; + int i; + + fh->legacy_mode = false; + if (dev->options.video_input) + return -ENODATA; + vid_info = get_video_info(dev); + if (vid_info == NULL) + return -ENOLCK; + interlaced = vid_info->fps <= 30; + for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) { + const struct v4l2_bt_timings *bt = &hdpvr_dv_timings[i].bt; + unsigned hsize; + unsigned vsize; + unsigned fps; + + hsize = bt->hfrontporch + bt->hsync + bt->hbackporch + bt->width; + vsize = bt->vfrontporch + bt->vsync + bt->vbackporch + + bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch + + bt->height; + fps = (unsigned)bt->pixelclock / (hsize * vsize); + if (bt->width != vid_info->width || + bt->height != vid_info->height || + bt->interlaced != interlaced || + (fps != vid_info->fps && fps + 1 != vid_info->fps)) + continue; + *timings = hdpvr_dv_timings[i]; + break; + } + if (i == ARRAY_SIZE(hdpvr_dv_timings)) + ret = -ERANGE; + kfree(vid_info); + return ret; +} + +static int vidioc_enum_dv_timings(struct file *file, void *_fh, + struct v4l2_enum_dv_timings *timings) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + + fh->legacy_mode = false; + memset(timings->reserved, 0, sizeof(timings->reserved)); + if (dev->options.video_input) + return -ENODATA; + if (timings->index >= ARRAY_SIZE(hdpvr_dv_timings)) + return -EINVAL; + timings->timings = hdpvr_dv_timings[timings->index]; + return 0; +} + +static int vidioc_dv_timings_cap(struct file *file, void *_fh, + struct v4l2_dv_timings_cap *cap) +{ + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + + fh->legacy_mode = false; + if (dev->options.video_input) + return -ENODATA; + cap->type = V4L2_DV_BT_656_1120; + cap->bt.min_width = 720; + cap->bt.max_width = 1920; + cap->bt.min_height = 480; + cap->bt.max_height = 1080; + cap->bt.min_pixelclock = 27000000; + cap->bt.max_pixelclock = 74250000; + cap->bt.standards = V4L2_DV_BT_STD_CEA861; + cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE; + return 0; +} + static const char *iname[] = { [HDPVR_COMPONENT] = "Component", [HDPVR_SVIDEO] = "S-Video", [HDPVR_COMPOSITE] = "Composite", }; -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) +static int vidioc_enum_input(struct file *file, void *_fh, struct v4l2_input *i) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; unsigned int n; n = i->index; @@ -617,27 +760,42 @@ static int vidioc_enum_input(struct file *file, void *priv, i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF; - i->std = dev->video_dev->tvnorms; + i->capabilities = n ? V4L2_IN_CAP_STD : V4L2_IN_CAP_DV_TIMINGS; + i->std = n ? V4L2_STD_ALL : 0; return 0; } -static int vidioc_s_input(struct file *file, void *private_data, +static int vidioc_s_input(struct file *file, void *_fh, unsigned int index) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); int retval; if (index >= HDPVR_VIDEO_INPUTS) return -EINVAL; if (dev->status != STATUS_IDLE) - return -EAGAIN; + return -EBUSY; retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1); - if (!retval) + if (!retval) { dev->options.video_input = index; + /* + * Unfortunately gstreamer calls ENUMSTD and bails out if it + * won't find any formats, even though component input is + * selected. This means that we have to leave tvnorms at + * V4L2_STD_ALL. We cannot use the 'legacy' trick since + * tvnorms is set at the device node level and not at the + * filehandle level. + * + * Comment this out for now, but if the legacy mode can be + * removed in the future, then this code should be enabled + * again. + dev->video_dev->tvnorms = + (index != HDPVR_COMPONENT) ? V4L2_STD_ALL : 0; + */ + } return retval; } @@ -645,8 +803,7 @@ static int vidioc_s_input(struct file *file, void *private_data, static int vidioc_g_input(struct file *file, void *private_data, unsigned int *index) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); *index = dev->options.video_input; return 0; @@ -679,15 +836,14 @@ static int vidioc_enumaudio(struct file *file, void *priv, static int vidioc_s_audio(struct file *file, void *private_data, const struct v4l2_audio *audio) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); int retval; if (audio->index >= HDPVR_AUDIO_INPUTS) return -EINVAL; if (dev->status != STATUS_IDLE) - return -EAGAIN; + return -EBUSY; retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec); if (!retval) @@ -699,8 +855,7 @@ static int vidioc_s_audio(struct file *file, void *private_data, static int vidioc_g_audio(struct file *file, void *private_data, struct v4l2_audio *audio) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = video_drvdata(file); audio->index = dev->options.audio_input; audio->capability = V4L2_AUDCAP_STEREO; @@ -709,335 +864,69 @@ static int vidioc_g_audio(struct file *file, void *private_data, return 0; } -static const s32 supported_v4l2_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_SHARPNESS, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, -}; - -static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc, - int ac3, int fw_ver) -{ - int err; - - if (fw_ver > 0x15) { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - } - } else { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - } - } - - switch (qc->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_AUDIO_ENCODING_AAC, - ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 - : V4L2_MPEG_AUDIO_ENCODING_AAC, - 1, V4L2_MPEG_AUDIO_ENCODING_AAC); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); - -/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */ -/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000, - 6500000); - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000, - 9000000); - if (!err && opt->bitrate_mode == HDPVR_CONSTANT) - qc->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - default: - return -EINVAL; - } -} - -static int vidioc_queryctrl(struct file *file, void *private_data, - struct v4l2_queryctrl *qc) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, next; - u32 id = qc->id; - - memset(qc, 0, sizeof(*qc)); - - next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); - qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; - - for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) { - if (next) { - if (qc->id < supported_v4l2_ctrls[i]) - qc->id = supported_v4l2_ctrls[i]; - else - continue; - } - - if (qc->id == supported_v4l2_ctrls[i]) - return fill_queryctrl(&dev->options, qc, - dev->flags & HDPVR_FLAG_AC3_CAP, - dev->fw_ver); - - if (qc->id < supported_v4l2_ctrls[i]) - break; - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *private_data, - struct v4l2_control *ctrl) +static int hdpvr_try_ctrl(struct v4l2_ctrl *ctrl) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; + struct hdpvr_device *dev = + container_of(ctrl->handler, struct hdpvr_device, hdl); switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dev->options.brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dev->options.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dev->options.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dev->options.hue; - break; - case V4L2_CID_SHARPNESS: - ctrl->value = dev->options.sharpness; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + dev->video_bitrate->val >= dev->video_bitrate_peak->val) + dev->video_bitrate_peak->val = + dev->video_bitrate->val + 100000; break; - default: - return -EINVAL; } return 0; } -static int vidioc_s_ctrl(struct file *file, void *private_data, - struct v4l2_control *ctrl) +static int hdpvr_s_ctrl(struct v4l2_ctrl *ctrl) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int retval; + struct hdpvr_device *dev = + container_of(ctrl->handler, struct hdpvr_device, hdl); + struct hdpvr_options *opt = &dev->options; + int ret = -EINVAL; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value); - if (!retval) - dev->options.brightness = ctrl->value; - break; + ret = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->val); + if (ret) + break; + dev->options.brightness = ctrl->val; + return 0; case V4L2_CID_CONTRAST: - retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value); - if (!retval) - dev->options.contrast = ctrl->value; - break; + ret = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->val); + if (ret) + break; + dev->options.contrast = ctrl->val; + return 0; case V4L2_CID_SATURATION: - retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value); - if (!retval) - dev->options.saturation = ctrl->value; - break; + ret = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->val); + if (ret) + break; + dev->options.saturation = ctrl->val; + return 0; case V4L2_CID_HUE: - retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value); - if (!retval) - dev->options.hue = ctrl->value; - break; + ret = hdpvr_config_call(dev, CTRL_HUE, ctrl->val); + if (ret) + break; + dev->options.hue = ctrl->val; + return 0; case V4L2_CID_SHARPNESS: - retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value); - if (!retval) - dev->options.sharpness = ctrl->value; - break; - default: - return -EINVAL; - } - - return retval; -} - - -static int hdpvr_get_ctrl(struct hdpvr_options *opt, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - ctrl->value = opt->audio_codec; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; - break; -/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ -/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */ -/* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT - ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR - : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = opt->bitrate * 100000; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = opt->peak_bitrate * 100000; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_get_ctrl(&dev->options, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - - -static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) -{ - int ret = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC || - (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) - ret = 0; - break; -/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ -/* if (ctrl->value == 0 || ctrl->value == 128) */ -/* ret = 0; */ -/* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || - ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - { - uint bitrate = ctrl->value / 100000; - if (bitrate >= 10 && bitrate <= 135) - ret = 0; - break; - } - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - { - uint peak_bitrate = ctrl->value / 100000; - if (peak_bitrate >= 10 && peak_bitrate <= 202) - ret = 0; - break; - } - case V4L2_CID_MPEG_STREAM_TYPE: - if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) - ret = 0; - break; - default: - return -EINVAL; - } - return ret; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_try_ctrl(ctrl, - dev->flags & HDPVR_FLAG_AC3_CAP); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - - return -EINVAL; -} - - -static int hdpvr_set_ctrl(struct hdpvr_device *dev, - struct v4l2_ext_control *ctrl) -{ - struct hdpvr_options *opt = &dev->options; - int ret = 0; - - switch (ctrl->id) { + ret = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->val); + if (ret) + break; + dev->options.sharpness = ctrl->val; + return 0; case V4L2_CID_MPEG_AUDIO_ENCODING: if (dev->flags & HDPVR_FLAG_AC3_CAP) { - opt->audio_codec = ctrl->value; - ret = hdpvr_set_audio(dev, opt->audio_input, + opt->audio_codec = ctrl->val; + return hdpvr_set_audio(dev, opt->audio_input, opt->audio_codec); } - break; + return 0; case V4L2_CID_MPEG_VIDEO_ENCODING: - break; + return 0; /* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ /* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */ /* opt->gop_mode |= 0x2; */ @@ -1050,86 +939,41 @@ static int hdpvr_set_ctrl(struct hdpvr_device *dev, /* opt->gop_mode); */ /* } */ /* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && - opt->bitrate_mode != HDPVR_CONSTANT) { - opt->bitrate_mode = HDPVR_CONSTANT; - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, - opt->bitrate_mode); - } - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - opt->bitrate_mode == HDPVR_CONSTANT) { - opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: { + uint peak_bitrate = dev->video_bitrate_peak->val / 100000; + uint bitrate = dev->video_bitrate->val / 100000; + + if (ctrl->is_new) { + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + opt->bitrate_mode = HDPVR_CONSTANT; + else + opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, opt->bitrate_mode); + v4l2_ctrl_activate(dev->video_bitrate_peak, + ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); } - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: { - uint bitrate = ctrl->value / 100000; - - opt->bitrate = bitrate; - if (bitrate >= opt->peak_bitrate) - opt->peak_bitrate = bitrate+1; - - hdpvr_set_bitrate(dev); - break; - } - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { - uint peak_bitrate = ctrl->value / 100000; - - if (opt->bitrate_mode == HDPVR_CONSTANT) - break; - if (opt->bitrate < peak_bitrate) { + if (dev->video_bitrate_peak->is_new || + dev->video_bitrate->is_new) { + opt->bitrate = bitrate; opt->peak_bitrate = peak_bitrate; hdpvr_set_bitrate(dev); - } else - ret = -EINVAL; - break; + } + return 0; } case V4L2_CID_MPEG_STREAM_TYPE: - break; + return 0; default: - return -EINVAL; + break; } return ret; } -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_try_ctrl(ctrl, - dev->flags & HDPVR_FLAG_AC3_CAP); - if (err) { - ctrls->error_idx = i; - break; - } - err = hdpvr_set_ctrl(dev, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, struct v4l2_fmtdesc *f) { - - if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (f->index != 0) return -EINVAL; f->flags = V4L2_FMT_FLAG_COMPRESSED; @@ -1139,56 +983,92 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, return 0; } -static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, +static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh, struct v4l2_format *f) { - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - struct hdpvr_video_info *vid_info; - - if (!dev) - return -ENODEV; - - vid_info = get_video_info(dev); - if (!vid_info) - return -EFAULT; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + struct hdpvr_device *dev = video_drvdata(file); + struct hdpvr_fh *fh = _fh; + + /* + * The original driver would always returns the current detected + * resolution as the format (and EFAULT if it couldn't be detected). + * With the introduction of VIDIOC_QUERY_DV_TIMINGS there is now a + * better way of doing this, but to stay compatible with existing + * applications we assume legacy mode every time an application opens + * the device. Only if one of the new DV_TIMINGS ioctls is called + * will the filehandle go into 'normal' mode where g_fmt returns the + * last set format. + */ + if (fh->legacy_mode) { + struct hdpvr_video_info *vid_info; + + vid_info = get_video_info(dev); + if (!vid_info) + return -EFAULT; + f->fmt.pix.width = vid_info->width; + f->fmt.pix.height = vid_info->height; + kfree(vid_info); + } else { + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + } f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.width = vid_info->width; - f->fmt.pix.height = vid_info->height; f->fmt.pix.sizeimage = dev->bulk_in_size; - f->fmt.pix.colorspace = 0; f->fmt.pix.bytesperline = 0; - f->fmt.pix.field = V4L2_FIELD_ANY; - - kfree(vid_info); + f->fmt.pix.priv = 0; + if (f->fmt.pix.width == 720) { + /* SDTV formats */ + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + } else { + /* HDTV formats */ + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE240M; + f->fmt.pix.field = V4L2_FIELD_NONE; + } return 0; } static int vidioc_encoder_cmd(struct file *filp, void *priv, struct v4l2_encoder_cmd *a) { - struct hdpvr_fh *fh = filp->private_data; - struct hdpvr_device *dev = fh->dev; - int res; + struct hdpvr_device *dev = video_drvdata(filp); + int res = 0; mutex_lock(&dev->io_mutex); + a->flags = 0; - memset(&a->raw, 0, sizeof(a->raw)); switch (a->cmd) { case V4L2_ENC_CMD_START: - a->flags = 0; + if (dev->owner && filp->private_data != dev->owner) { + res = -EBUSY; + break; + } + if (dev->status == STATUS_STREAMING) + break; res = hdpvr_start_streaming(dev); + if (!res) + dev->owner = filp->private_data; + else + dev->status = STATUS_IDLE; break; case V4L2_ENC_CMD_STOP: + if (dev->owner && filp->private_data != dev->owner) { + res = -EBUSY; + break; + } + if (dev->status == STATUS_IDLE) + break; res = hdpvr_stop_streaming(dev); + if (!res) + dev->owner = NULL; break; default: v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "Unsupported encoder cmd %d\n", a->cmd); res = -EINVAL; + break; } + mutex_unlock(&dev->io_mutex); return res; } @@ -1196,6 +1076,7 @@ static int vidioc_encoder_cmd(struct file *filp, void *priv, static int vidioc_try_encoder_cmd(struct file *filp, void *priv, struct v4l2_encoder_cmd *a) { + a->flags = 0; switch (a->cmd) { case V4L2_ENC_CMD_START: case V4L2_ENC_CMD_STOP: @@ -1208,22 +1089,28 @@ static int vidioc_try_encoder_cmd(struct file *filp, void *priv, static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_querystd = vidioc_querystd, + .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_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_enumaudio = vidioc_enumaudio, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_encoder_cmd = vidioc_encoder_cmd, .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static void hdpvr_device_release(struct video_device *vdev) @@ -1236,9 +1123,10 @@ static void hdpvr_device_release(struct video_device *vdev) mutex_unlock(&dev->io_mutex); v4l2_device_unregister(&dev->v4l2_dev); + v4l2_ctrl_handler_free(&dev->hdl); /* deregister I2C adapter */ -#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) mutex_lock(&dev->i2c_mutex); i2c_del_adapter(&dev->i2c_adapter); mutex_unlock(&dev->i2c_mutex); @@ -1249,41 +1137,112 @@ static void hdpvr_device_release(struct video_device *vdev) } static const struct video_device hdpvr_video_template = { -/* .type = VFL_TYPE_GRABBER, */ -/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */ .fops = &hdpvr_fops, .release = hdpvr_device_release, .ioctl_ops = &hdpvr_ioctl_ops, - .tvnorms = - V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B | - V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | - V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | - V4L2_STD_PAL_60, - .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | - V4L2_STD_PAL_60, + .tvnorms = V4L2_STD_ALL, +}; + +static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = { + .try_ctrl = hdpvr_try_ctrl, + .s_ctrl = hdpvr_s_ctrl, }; int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, int devnum) { + struct v4l2_ctrl_handler *hdl = &dev->hdl; + bool ac3 = dev->flags & HDPVR_FLAG_AC3_CAP; + int res; + + dev->cur_std = V4L2_STD_525_60; + dev->width = 720; + dev->height = 480; + dev->cur_dv_timings = hdpvr_dv_timings[HDPVR_DEF_DV_TIMINGS_IDX]; + v4l2_ctrl_handler_init(hdl, 11); + if (dev->fw_ver > 0x15) { + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x80); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x40); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x40); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_HUE, 0x0, 0x1e, 1, 0xf); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80); + } else { + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x86); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x80); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x80); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_HUE, 0x0, 0xff, 1, 0x80); + v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80); + } + + v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + 0x1, V4L2_MPEG_STREAM_TYPE_MPEG2_TS); + v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_AUDIO_ENCODING, + ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : V4L2_MPEG_AUDIO_ENCODING_AAC, + 0x7, V4L2_MPEG_AUDIO_ENCODING_AAC); + v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 0x3, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + + dev->video_mode = v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + dev->video_bitrate = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 1000000, 13500000, 100000, 6500000); + dev->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 1100000, 20200000, 100000, 9000000); + dev->v4l2_dev.ctrl_handler = hdl; + if (hdl->error) { + res = hdl->error; + v4l2_err(&dev->v4l2_dev, "Could not register controls\n"); + goto error; + } + v4l2_ctrl_cluster(3, &dev->video_mode); + res = v4l2_ctrl_handler_setup(hdl); + if (res < 0) { + v4l2_err(&dev->v4l2_dev, "Could not setup controls\n"); + goto error; + } + /* setup and register video device */ dev->video_dev = video_device_alloc(); if (!dev->video_dev) { v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); + res = -ENOMEM; goto error; } - *(dev->video_dev) = hdpvr_video_template; + *dev->video_dev = hdpvr_video_template; strcpy(dev->video_dev->name, "Hauppauge HD PVR"); - dev->video_dev->parent = parent; + dev->video_dev->v4l2_dev = &dev->v4l2_dev; video_set_drvdata(dev->video_dev, dev); + set_bit(V4L2_FL_USE_FH_PRIO, &dev->video_dev->flags); - if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) { + res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum); + if (res < 0) { v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); goto error; } return 0; error: - return -ENOMEM; + v4l2_ctrl_handler_free(hdl); + return res; } diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h index fea3c69..1478f3d 100644 --- a/drivers/media/usb/hdpvr/hdpvr.h +++ b/drivers/media/usb/hdpvr/hdpvr.h @@ -16,6 +16,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/ir-kbd-i2c.h> #define HDPVR_MAX 8 @@ -37,6 +38,7 @@ #define HDPVR_FIRMWARE_VERSION_AC3 0x0d #define HDPVR_FIRMWARE_VERSION_0X12 0x12 #define HDPVR_FIRMWARE_VERSION_0X15 0x15 +#define HDPVR_FIRMWARE_VERSION_0X1E 0x1e /* #define HDPVR_DEBUG */ @@ -65,10 +67,19 @@ struct hdpvr_options { struct hdpvr_device { /* the v4l device for this device */ struct video_device *video_dev; + /* the control handler for this device */ + struct v4l2_ctrl_handler hdl; /* the usb device for this device */ struct usb_device *udev; /* v4l2-device unused */ struct v4l2_device v4l2_dev; + struct { /* video mode/bitrate control cluster */ + struct v4l2_ctrl *video_mode; + struct v4l2_ctrl *video_bitrate; + struct v4l2_ctrl *video_bitrate_peak; + }; + /* v4l2 format */ + uint width, height; /* the max packet size of the bulk endpoint */ size_t bulk_in_size; @@ -77,11 +88,11 @@ struct hdpvr_device { /* holds the current device status */ __u8 status; - /* count the number of openers */ - uint open_count; - /* holds the cureent set options */ + /* holds the current set options */ struct hdpvr_options options; + v4l2_std_id cur_std; + struct v4l2_dv_timings cur_dv_timings; uint flags; @@ -99,6 +110,8 @@ struct hdpvr_device { struct workqueue_struct *workqueue; /**/ struct work_struct worker; + /* current stream owner */ + struct v4l2_fh *owner; /* I2C adapter */ struct i2c_adapter i2c_adapter; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 299751a..e11267f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -5165,7 +5165,7 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - struct v4l2_dbg_match *match, u64 reg_id, + const struct v4l2_dbg_match *match, u64 reg_id, int setFl, u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h index 8060fc6..91bae93 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h @@ -240,7 +240,7 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ int pvr2_hdw_register_access(struct pvr2_hdw *, - struct v4l2_dbg_match *match, u64 reg_id, + const struct v4l2_dbg_match *match, u64 reg_id, int setFl, u64 *val_ptr); /* The following entry points are all lower level things you normally don't diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 34c3b6e..a8a65fa 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -196,13 +196,13 @@ static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) return ret; } -static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; return pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std); } static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) @@ -352,7 +352,7 @@ static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) return pvr2_hdw_get_tuner_status(hdw, vt); } -static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +static int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; @@ -365,7 +365,7 @@ static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) vt->audmode); } -static int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; @@ -815,7 +815,7 @@ static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_regist return ret; } -static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +static int pvr2_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *req) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5ec15cb..77bbf78 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1001,6 +1001,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf); pdev->vb_queue.ops = &pwc_vb_queue_ops; pdev->vb_queue.mem_ops = &vb2_vmalloc_memops; + pdev->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(&pdev->vb_queue); if (rc < 0) { PWC_ERROR("Oops, could not initialize vb2 queue.\n"); diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 498c57e..ab97e7d 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -43,12 +43,14 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/usb.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <linux/vmalloc.h> -#include <linux/usb.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #define S2255_VERSION "1.22.1" #define FIRMWARE_FILE_NAME "f2255usb.bin" @@ -217,12 +219,15 @@ struct s2255_dev; struct s2255_channel { struct video_device vdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *jpegqual_ctrl; int resources; struct s2255_dmaqueue vidq; struct s2255_bufferi buffer; struct s2255_mode mode; + v4l2_std_id std; /* jpeg compression */ - struct v4l2_jpegcompression jc; + unsigned jpegqual; /* capture parameters (for high quality mode full size) */ struct v4l2_captureparm cap_parm; int cur_frame; @@ -292,6 +297,8 @@ struct s2255_buffer { }; struct s2255_fh { + /* this must be the first field in this struct */ + struct v4l2_fh fh; struct s2255_dev *dev; struct videobuf_queue vb_vidq; enum v4l2_buf_type type; @@ -306,7 +313,7 @@ struct s2255_fh { /* Need DSP version 5+ for video status feature */ #define S2255_MIN_DSP_STATUS 5 #define S2255_MIN_DSP_COLORFILTER 8 -#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) +#define S2255_NORMS (V4L2_STD_ALL) /* private V4L2 controls */ @@ -336,7 +343,7 @@ struct s2255_fh { */ #define S2255_V4L2_YC_ON 1 #define S2255_V4L2_YC_OFF 0 -#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0) /* frame prefix size (sent once every frame) */ #define PREFIX_SIZE 512 @@ -409,11 +416,6 @@ MODULE_DEVICE_TABLE(usb, s2255_table); /* JPEG formats must be defined last to support jpeg_enable parameter */ static const struct s2255_fmt formats[] = { { - .name = "4:2:2, planar, YUV422P", - .fourcc = V4L2_PIX_FMT_YUV422P, - .depth = 16 - - }, { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16 @@ -423,6 +425,11 @@ static const struct s2255_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16 }, { + .name = "4:2:2, planar, YUV422P", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16 + + }, { .name = "8bpp GREY", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8 @@ -437,27 +444,27 @@ static const struct s2255_fmt formats[] = { } }; -static int norm_maxw(struct video_device *vdev) +static int norm_maxw(struct s2255_channel *channel) { - return (vdev->current_norm & V4L2_STD_NTSC) ? + return (channel->std & V4L2_STD_525_60) ? LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; } -static int norm_maxh(struct video_device *vdev) +static int norm_maxh(struct s2255_channel *channel) { - return (vdev->current_norm & V4L2_STD_NTSC) ? + return (channel->std & V4L2_STD_525_60) ? (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); } -static int norm_minw(struct video_device *vdev) +static int norm_minw(struct s2255_channel *channel) { - return (vdev->current_norm & V4L2_STD_NTSC) ? + return (channel->std & V4L2_STD_525_60) ? LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; } -static int norm_minh(struct video_device *vdev) +static int norm_minh(struct s2255_channel *channel) { - return (vdev->current_norm & V4L2_STD_NTSC) ? + return (channel->std & V4L2_STD_525_60) ? (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); } @@ -515,7 +522,7 @@ static void s2255_timer(unsigned long user_data) /* this loads the firmware asynchronously. - Originally this was done synchroously in probe. + Originally this was done synchronously in probe. But it is better to load it asynchronously here than block inside the probe function. Blocking inside probe affects boot time. FW loading is triggered by the timer in the probe function @@ -719,10 +726,10 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (channel->fmt == NULL) return -EINVAL; - if ((w < norm_minw(&channel->vdev)) || - (w > norm_maxw(&channel->vdev)) || - (h < norm_minh(&channel->vdev)) || - (h > norm_maxh(&channel->vdev))) { + if ((w < norm_minw(channel)) || + (w > norm_maxw(channel)) || + (h < norm_minh(channel)) || + (h > norm_maxh(channel))) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } @@ -810,37 +817,17 @@ static void res_free(struct s2255_fh *fh) dprintk(1, "res: put\n"); } -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - static const char *colorfilter[] = { - "Off", - "On", - NULL - }; - if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { - int i; - const char **menu_items = colorfilter; - for (i = 0; i < qmenu->index && menu_items[i]; i++) - ; /* do nothing (from v4l2-common.c) */ - if (menu_items[i] == NULL || menu_items[i][0] == '\0') - return -EINVAL; - strlcpy(qmenu->name, menu_items[qmenu->index], - sizeof(qmenu->name)); - return 0; - } - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct s2255_fh *fh = file->private_data; struct s2255_dev *dev = fh->dev; + strlcpy(cap->driver, "s2255", sizeof(cap->driver)); strlcpy(cap->card, "s2255", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -865,13 +852,20 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_channel *channel = fh->channel; + int is_ntsc = channel->std & V4L2_STD_525_60; f->fmt.pix.width = channel->width; f->fmt.pix.height = channel->height; - f->fmt.pix.field = fh->vb_vidq.field; + if (f->fmt.pix.height >= + (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + f->fmt.pix.field = V4L2_FIELD_TOP; f->fmt.pix.pixelformat = channel->fmt->fourcc; f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->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; } @@ -880,12 +874,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { const struct s2255_fmt *fmt; enum v4l2_field field; - int b_any_field = 0; struct s2255_fh *fh = priv; struct s2255_channel *channel = fh->channel; - int is_ntsc; - is_ntsc = - (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; + int is_ntsc = channel->std & V4L2_STD_525_60; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -893,8 +884,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - b_any_field = 1; dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); @@ -902,24 +891,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* NTSC */ if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } + field = V4L2_FIELD_INTERLACED; } else { f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - + field = V4L2_FIELD_TOP; } if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; @@ -933,41 +908,25 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* PAL */ if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } + field = V4L2_FIELD_INTERLACED; } else { f->fmt.pix.height = NUM_LINES_1CIFS_PAL; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } + field = V4L2_FIELD_TOP; } - if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { + if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) f->fmt.pix.width = LINE_SZ_4CIFS_PAL; - field = V4L2_FIELD_SEQ_TB; - } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { + else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) f->fmt.pix.width = LINE_SZ_2CIFS_PAL; - field = V4L2_FIELD_TOP; - } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { + else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } else { + else f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } } f->fmt.pix.field = field; 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; + f->fmt.pix.priv = 0; dprintk(50, "%s: set width %d height %d field %d\n", __func__, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); return 0; @@ -1012,8 +971,8 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - if (channel->width > norm_minw(&channel->vdev)) { - if (channel->height > norm_minh(&channel->vdev)) { + if (channel->width > norm_minw(channel)) { + if (channel->height > norm_minh(channel)) { if (channel->cap_parm.capturemode & V4L2_MODE_HIGHQUALITY) mode.scale = SCALE_4CIFSI; @@ -1035,7 +994,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, case V4L2_PIX_FMT_MJPEG: mode.color &= ~MASK_COLOR; mode.color |= COLOR_JPG; - mode.color |= (channel->jc.quality << 8); + mode.color |= (channel->jpegqual << 8); break; case V4L2_PIX_FMT_YUV422P: mode.color &= ~MASK_COLOR; @@ -1198,6 +1157,8 @@ static int s2255_set_mode(struct s2255_channel *channel, __le32 *buffer; unsigned long chn_rev; struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + int i; + chn_rev = G_chnmap[channel->idx]; dprintk(3, "%s channel: %d\n", __func__, channel->idx); /* if JPEG, set the quality */ @@ -1205,7 +1166,7 @@ static int s2255_set_mode(struct s2255_channel *channel, mode->color &= ~MASK_COLOR; mode->color |= COLOR_JPG; mode->color &= ~MASK_JPG_QUALITY; - mode->color |= (channel->jc.quality << 8); + mode->color |= (channel->jpegqual << 8); } /* save the mode */ channel->mode = *mode; @@ -1220,7 +1181,8 @@ static int s2255_set_mode(struct s2255_channel *channel, buffer[0] = IN_DATA_TOKEN; buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_SET_MODE; - memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); + for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++) + buffer[3 + i] = cpu_to_le32(((u32 *)&channel->mode)[i]); channel->setmode_ready = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) @@ -1332,12 +1294,14 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) { struct s2255_fh *fh = priv; struct s2255_mode mode; struct videobuf_queue *q = &fh->vb_vidq; + struct s2255_channel *channel = fh->channel; int ret = 0; + mutex_lock(&q->vb_lock); if (videobuf_queue_is_busy(q)) { dprintk(1, "queue busy\n"); @@ -1350,24 +1314,30 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) goto out_s_std; } mode = fh->channel->mode; - if (*i & V4L2_STD_NTSC) { - dprintk(4, "%s NTSC\n", __func__); + if (i & V4L2_STD_525_60) { + dprintk(4, "%s 60 Hz\n", __func__); /* if changing format, reset frame decimation/intervals */ if (mode.format != FORMAT_NTSC) { mode.restart = 1; mode.format = FORMAT_NTSC; mode.fdec = FDEC_1; + channel->width = LINE_SZ_4CIFS_NTSC; + channel->height = NUM_LINES_4CIFS_NTSC * 2; } - } else if (*i & V4L2_STD_PAL) { - dprintk(4, "%s PAL\n", __func__); + } else if (i & V4L2_STD_625_50) { + dprintk(4, "%s 50 Hz\n", __func__); if (mode.format != FORMAT_PAL) { mode.restart = 1; mode.format = FORMAT_PAL; mode.fdec = FDEC_1; + channel->width = LINE_SZ_4CIFS_PAL; + channel->height = NUM_LINES_4CIFS_PAL * 2; } } else { ret = -EINVAL; + goto out_s_std; } + fh->channel->std = i; if (mode.restart) s2255_set_mode(fh->channel, &mode); out_s_std: @@ -1375,6 +1345,14 @@ out_s_std: return ret; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct s2255_fh *fh = priv; + + *i = fh->channel->std; + return 0; +} + /* Sensoray 2255 is a multiple channel capture device. It does not have a "crossbar" of inputs. We use one V4L device per channel. The user must @@ -1427,110 +1405,36 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -/* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = fh->dev; - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - strlcpy(qc->name, "Color Filter", sizeof(qc->name)); - qc->type = V4L2_CTRL_TYPE_MENU; - qc->minimum = 0; - qc->maximum = 1; - qc->step = 1; - qc->default_value = 1; - qc->flags = 0; - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d\n", __func__, qc->id); - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = channel->mode.bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = channel->mode.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = channel->mode.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = channel->mode.hue; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) { - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_channel *channel = + container_of(ctrl->handler, struct s2255_channel, hdl); struct s2255_mode mode; + mode = channel->mode; dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - mode.bright = ctrl->value; + mode.bright = ctrl->val; break; case V4L2_CID_CONTRAST: - mode.contrast = ctrl->value; + mode.contrast = ctrl->val; break; case V4L2_CID_HUE: - mode.hue = ctrl->value; + mode.hue = ctrl->val; break; case V4L2_CID_SATURATION: - mode.saturation = ctrl->value; + mode.saturation = ctrl->val; break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; + case V4L2_CID_S2255_COLORFILTER: mode.color &= ~MASK_INPUT_TYPE; - mode.color |= ((ctrl->value ? 0 : 1) << 16); + mode.color |= !ctrl->val << 16; break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + channel->jpegqual = ctrl->val; + return 0; default: return -EINVAL; } @@ -1539,7 +1443,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, some V4L programs restart stream unnecessarily after a s_crtl. */ - s2255_set_mode(fh->channel, &mode); + s2255_set_mode(channel, &mode); return 0; } @@ -1548,7 +1452,9 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_channel *channel = fh->channel; - *jc = channel->jc; + + memset(jc, 0, sizeof(*jc)); + jc->quality = channel->jpegqual; dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1560,7 +1466,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, struct s2255_channel *channel = fh->channel; if (jc->quality < 0 || jc->quality > 100) return -EINVAL; - channel->jc.quality = jc->quality; + v4l2_ctrl_s_ctrl(channel->jpegqual_ctrl, jc->quality); dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1573,7 +1479,6 @@ static int vidioc_g_parm(struct file *file, void *priv, struct s2255_channel *channel = fh->channel; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - memset(sp, 0, sizeof(struct v4l2_streamparm)); sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; sp->parm.capture.capturemode = channel->cap_parm.capturemode; def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; @@ -1643,36 +1548,64 @@ static int vidioc_s_parm(struct file *file, void *priv, return 0; } +#define NUM_SIZE_ENUMS 3 +static const struct v4l2_frmsize_discrete ntsc_sizes[] = { + { 640, 480 }, + { 640, 240 }, + { 320, 240 }, +}; +static const struct v4l2_frmsize_discrete pal_sizes[] = { + { 704, 576 }, + { 704, 288 }, + { 352, 288 }, +}; + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fe) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + int is_ntsc = channel->std & V4L2_STD_525_60; + const struct s2255_fmt *fmt; + + if (fe->index >= NUM_SIZE_ENUMS) + return -EINVAL; + + fmt = format_by_fourcc(fe->pixel_format); + if (fmt == NULL) + return -EINVAL; + fe->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fe->discrete = is_ntsc ? ntsc_sizes[fe->index] : pal_sizes[fe->index]; + return 0; +} + static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fe) { - int is_ntsc = 0; + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + const struct s2255_fmt *fmt; + const struct v4l2_frmsize_discrete *sizes; + int is_ntsc = channel->std & V4L2_STD_525_60; #define NUM_FRAME_ENUMS 4 int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; + int i; + if (fe->index >= NUM_FRAME_ENUMS) return -EINVAL; - switch (fe->width) { - case 640: - if (fe->height != 240 && fe->height != 480) - return -EINVAL; - is_ntsc = 1; - break; - case 320: - if (fe->height != 240) - return -EINVAL; - is_ntsc = 1; - break; - case 704: - if (fe->height != 288 && fe->height != 576) - return -EINVAL; - break; - case 352: - if (fe->height != 288) - return -EINVAL; - break; - default: + + fmt = format_by_fourcc(fe->pixel_format); + if (fmt == NULL) return -EINVAL; - } + + sizes = is_ntsc ? ntsc_sizes : pal_sizes; + for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++) + if (fe->width == sizes->width && + fe->height == sizes->height) + break; + if (i == NUM_SIZE_ENUMS) + return -EINVAL; + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; fe->discrete.denominator = is_ntsc ? 30000 : 25000; fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; @@ -1757,7 +1690,9 @@ static int __s2255_open(struct file *file) fh = kzalloc(sizeof(*fh), GFP_KERNEL); if (NULL == fh) return -ENOMEM; - file->private_data = fh; + v4l2_fh_init(&fh->fh, vdev); + v4l2_fh_add(&fh->fh); + file->private_data = &fh->fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fh->channel = channel; @@ -1800,12 +1735,13 @@ static unsigned int s2255_poll(struct file *file, { struct s2255_fh *fh = file->private_data; struct s2255_dev *dev = fh->dev; - int rc; + int rc = v4l2_ctrl_poll(file, wait); + dprintk(100, "%s\n", __func__); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; mutex_lock(&dev->lock); - rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); + rc |= videobuf_poll_stream(file, &fh->vb_vidq, wait); mutex_unlock(&dev->lock); return rc; } @@ -1852,6 +1788,8 @@ static int s2255_release(struct file *file) videobuf_mmap_free(&fh->vb_vidq); mutex_unlock(&dev->lock); dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); return 0; } @@ -1886,7 +1824,6 @@ static const struct v4l2_file_operations s2255_fops_v4l = { }; static const struct v4l2_ioctl_ops s2255_ioctl_ops = { - .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1897,26 +1834,33 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .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_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_parm = vidioc_s_parm, .vidioc_g_parm = vidioc_g_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static void s2255_video_device_release(struct video_device *vdev) { struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - dprintk(4, "%s, chnls: %d \n", __func__, + struct s2255_channel *channel = + container_of(vdev, struct s2255_channel, vdev); + + v4l2_ctrl_handler_free(&channel->hdl); + dprintk(4, "%s, chnls: %d\n", __func__, atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); return; @@ -1928,7 +1872,20 @@ static struct video_device template = { .ioctl_ops = &s2255_ioctl_ops, .release = s2255_video_device_release, .tvnorms = S2255_NORMS, - .current_norm = V4L2_STD_NTSC_M, +}; + +static const struct v4l2_ctrl_ops s2255_ctrl_ops = { + .s_ctrl = s2255_s_ctrl, +}; + +static const struct v4l2_ctrl_config color_filter_ctrl = { + .ops = &s2255_ctrl_ops, + .name = "Color Filter", + .id = V4L2_CID_S2255_COLORFILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, + .def = 1, }; static int s2255_probe_v4l(struct s2255_dev *dev) @@ -1945,11 +1902,36 @@ static int s2255_probe_v4l(struct s2255_dev *dev) for (i = 0; i < MAX_CHANNELS; i++) { channel = &dev->channel[i]; INIT_LIST_HEAD(&channel->vidq.active); + + v4l2_ctrl_handler_init(&channel->hdl, 6); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, DEF_HUE); + channel->jpegqual_ctrl = v4l2_ctrl_new_std(&channel->hdl, + &s2255_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 100, 1, S2255_DEF_JPEG_QUAL); + if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER && + (dev->pid != 0x2257 || channel->idx <= 1)) + v4l2_ctrl_new_custom(&channel->hdl, &color_filter_ctrl, NULL); + if (channel->hdl.error) { + ret = channel->hdl.error; + v4l2_ctrl_handler_free(&channel->hdl); + dev_err(&dev->udev->dev, "couldn't register control\n"); + break; + } channel->vidq.dev = dev; /* register 4 video devices */ channel->vdev = template; + channel->vdev.ctrl_handler = &channel->hdl; channel->vdev.lock = &dev->lock; channel->vdev.v4l2_dev = &dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &channel->vdev.flags); video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) ret = video_register_device(&channel->vdev, @@ -2300,9 +2282,10 @@ static int s2255_board_init(struct s2255_dev *dev) channel->mode = mode_def; if (dev->pid == 0x2257 && j > 1) channel->mode.color |= (1 << 16); - channel->jc.quality = S2255_DEF_JPEG_QUAL; + channel->jpegqual = S2255_DEF_JPEG_QUAL; channel->width = LINE_SZ_4CIFS_NTSC; channel->height = NUM_LINES_4CIFS_NTSC * 2; + channel->std = V4L2_STD_NTSC_M; channel->fmt = &formats[0]; channel->mode.restart = 1; channel->req_image_size = get_transfer_size(&mode_def); @@ -2531,7 +2514,7 @@ static int s2255_probe(struct usb_interface *interface, return -ENOMEM; } atomic_set(&dev->num_channels, 0); - dev->pid = id->idProduct; + dev->pid = le16_to_cpu(id->idProduct); dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) goto errorFWDATA1; @@ -2601,7 +2584,7 @@ static int s2255_probe(struct usb_interface *interface, /* make sure firmware is the latest */ __le32 *pRel; pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; - printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + printk(KERN_INFO "s2255 dsp fw version %x\n", le32_to_cpu(*pRel)); dev->dsp_fw_ver = le32_to_cpu(*pRel); if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index de2c102..03761c6 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -35,16 +35,23 @@ module_param_named(debug, sms_dbg, int, 0644); MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); #define USB1_BUFFER_SIZE 0x1000 -#define USB2_BUFFER_SIZE 0x4000 +#define USB2_BUFFER_SIZE 0x2000 #define MAX_BUFFERS 50 #define MAX_URBS 10 struct smsusb_device_t; +enum smsusb_state { + SMSUSB_DISCONNECTED, + SMSUSB_SUSPENDED, + SMSUSB_ACTIVE +}; + struct smsusb_urb_t { + struct list_head entry; struct smscore_buffer_t *cb; - struct smsusb_device_t *dev; + struct smsusb_device_t *dev; struct urb urb; }; @@ -57,11 +64,23 @@ struct smsusb_device_t { int response_alignment; int buffer_size; + + unsigned char in_ep; + unsigned char out_ep; + enum smsusb_state state; }; static int smsusb_submit_urb(struct smsusb_device_t *dev, struct smsusb_urb_t *surb); +/** + * Completing URB's callback handler - top half (interrupt context) + * adds completing sms urb to the global surbs list and activtes the worker + * thread the surb + * IMPORTANT - blocking functions must not be called from here !!! + + * @param urb pointer to a completing urb object + */ static void smsusb_onresponse(struct urb *urb) { struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context; @@ -74,26 +93,26 @@ static void smsusb_onresponse(struct urb *urb) } if ((urb->actual_length > 0) && (urb->status == 0)) { - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p; + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)surb->cb->p; smsendian_handle_message_header(phdr); - if (urb->actual_length >= phdr->msgLength) { - surb->cb->size = phdr->msgLength; + if (urb->actual_length >= phdr->msg_length) { + surb->cb->size = phdr->msg_length; if (dev->response_alignment && - (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) { + (phdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG)) { surb->cb->offset = dev->response_alignment + - ((phdr->msgFlags >> 8) & 3); + ((phdr->msg_flags >> 8) & 3); /* sanity check */ - if (((int) phdr->msgLength + + if (((int) phdr->msg_length + surb->cb->offset) > urb->actual_length) { sms_err("invalid response " "msglen %d offset %d " "size %d", - phdr->msgLength, + phdr->msg_length, surb->cb->offset, urb->actual_length); goto exit_and_resubmit; @@ -102,16 +121,22 @@ static void smsusb_onresponse(struct urb *urb) /* move buffer pointer and * copy header to its new location */ memcpy((char *) phdr + surb->cb->offset, - phdr, sizeof(struct SmsMsgHdr_ST)); + phdr, sizeof(struct sms_msg_hdr)); } else surb->cb->offset = 0; + sms_debug("received %s(%d) size: %d", + smscore_translate_msg(phdr->msg_type), + phdr->msg_type, phdr->msg_length); + + smsendian_handle_rx_message((struct sms_msg_data *) phdr); + smscore_onresponse(dev->coredev, surb->cb); surb->cb = NULL; } else { sms_err("invalid response " "msglen %d actual %d", - phdr->msgLength, urb->actual_length); + phdr->msg_length, urb->actual_length); } } else sms_err("error, urb status %d, %d bytes", @@ -136,7 +161,7 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev, usb_fill_bulk_urb( &surb->urb, dev->udev, - usb_rcvbulkpipe(dev->udev, 0x81), + usb_rcvbulkpipe(dev->udev, dev->in_ep), surb->cb->p, dev->buffer_size, smsusb_onresponse, @@ -181,9 +206,18 @@ static int smsusb_start_streaming(struct smsusb_device_t *dev) static int smsusb_sendrequest(void *context, void *buffer, size_t size) { struct smsusb_device_t *dev = (struct smsusb_device_t *) context; + struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer; int dummy; - smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer); + if (dev->state != SMSUSB_ACTIVE) + return -ENOENT; + + sms_debug("sending %s(%d) size: %d", + smscore_translate_msg(phdr->msg_type), phdr->msg_type, + phdr->msg_length); + + smsendian_handle_tx_message((struct sms_msg_data *) phdr); + smsendian_handle_message_header((struct sms_msg_hdr *)buffer); return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), buffer, size, &dummy, 1000); } @@ -276,15 +310,15 @@ static void smsusb1_detectmode(void *context, int *mode) static int smsusb1_setmode(void *context, int mode) { - struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, - sizeof(struct SmsMsgHdr_ST), 0 }; + struct sms_msg_hdr msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, + sizeof(struct sms_msg_hdr), 0 }; if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { sms_err("invalid firmware id specified %d", mode); return -EINVAL; } - return smsusb_sendrequest(context, &Msg, sizeof(Msg)); + return smsusb_sendrequest(context, &msg, sizeof(msg)); } static void smsusb_term_device(struct usb_interface *intf) @@ -292,13 +326,15 @@ static void smsusb_term_device(struct usb_interface *intf) struct smsusb_device_t *dev = usb_get_intfdata(intf); if (dev) { + dev->state = SMSUSB_DISCONNECTED; + smsusb_stop_streaming(dev); /* unregister from smscore */ if (dev->coredev) smscore_unregister_device(dev->coredev); - sms_info("device %p destroyed", dev); + sms_info("device 0x%p destroyed", dev); kfree(dev); } @@ -321,6 +357,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) memset(¶ms, 0, sizeof(params)); usb_set_intfdata(intf, dev); dev->udev = interface_to_usbdev(intf); + dev->state = SMSUSB_DISCONNECTED; params.device_type = sms_get_board(board_id)->type; @@ -331,21 +368,29 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) params.setmode_handler = smsusb1_setmode; params.detectmode_handler = smsusb1_detectmode; break; - default: + case SMS_UNKNOWN_TYPE: sms_err("Unspecified sms device type!"); /* fall-thru */ - case SMS_NOVA_A0: - case SMS_NOVA_B0: - case SMS_VEGA: + default: dev->buffer_size = USB2_BUFFER_SIZE; dev->response_alignment = le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) - - sizeof(struct SmsMsgHdr_ST); + sizeof(struct sms_msg_hdr); params.flags |= SMS_DEVICE_FAMILY2; break; } + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + if (intf->cur_altsetting->endpoint[i].desc. bEndpointAddress & USB_DIR_IN) + dev->in_ep = intf->cur_altsetting->endpoint[i].desc.bEndpointAddress; + else + dev->out_ep = intf->cur_altsetting->endpoint[i].desc.bEndpointAddress; + } + + sms_info("in_ep = %02x, out_ep = %02x", + dev->in_ep, dev->out_ep); + params.device = &dev->udev->dev; params.buffer_size = dev->buffer_size; params.num_buffers = MAX_BUFFERS; @@ -363,6 +408,8 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) smscore_set_board_id(dev->coredev, board_id); + dev->coredev->is_usb_device = true; + /* initialize urbs */ for (i = 0; i < MAX_URBS; i++) { dev->surbs[i].dev = dev; @@ -377,6 +424,8 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) return rc; } + dev->state = SMSUSB_ACTIVE; + rc = smscore_start_device(dev->coredev); if (rc < 0) { sms_err("smscore_start_device(...) failed"); @@ -384,7 +433,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) return rc; } - sms_info("device %p created", dev); + sms_info("device 0x%p created", dev); return rc; } @@ -396,12 +445,21 @@ static int smsusb_probe(struct usb_interface *intf, char devpath[32]; int i, rc; - rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); - rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + sms_info("interface number %d", + intf->cur_altsetting->desc.bInterfaceNumber); - if (intf->num_altsetting > 0) { - rc = usb_set_interface( - udev, intf->cur_altsetting->desc.bInterfaceNumber, 0); + if (sms_get_board(id->driver_info)->intf_num != + intf->cur_altsetting->desc.bInterfaceNumber) { + sms_err("interface number is %d expecting %d", + sms_get_board(id->driver_info)->intf_num, + intf->cur_altsetting->desc.bInterfaceNumber); + return -ENODEV; + } + + if (intf->num_altsetting > 1) { + rc = usb_set_interface(udev, + intf->cur_altsetting->desc.bInterfaceNumber, + 0); if (rc < 0) { sms_err("usb_set_interface failed, rc %d", rc); return rc; @@ -410,19 +468,27 @@ static int smsusb_probe(struct usb_interface *intf, sms_info("smsusb_probe %d", intf->cur_altsetting->desc.bInterfaceNumber); - for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { sms_info("endpoint %d %02x %02x %d", i, intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, intf->cur_altsetting->endpoint[i].desc.bmAttributes, intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); - + if (intf->cur_altsetting->endpoint[i].desc.bEndpointAddress & + USB_DIR_IN) + rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress)); + else + rc = usb_clear_halt(udev, usb_sndbulkpipe(udev, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress)); + } if ((udev->actconfig->desc.bNumInterfaces == 2) && (intf->cur_altsetting->desc.bInterfaceNumber == 0)) { sms_err("rom interface 0 is not used"); return -ENODEV; } - if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + if (id->driver_info == SMS1XXX_BOARD_SIANO_STELLAR_ROM) { + sms_info("stellar device was found."); snprintf(devpath, sizeof(devpath), "usb\\%d-%s", udev->bus->busnum, udev->devpath); sms_info("stellar device was found."); @@ -445,7 +511,9 @@ static void smsusb_disconnect(struct usb_interface *intf) static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) { struct smsusb_device_t *dev = usb_get_intfdata(intf); - printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event); + printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event); + dev->state = SMSUSB_SUSPENDED; + /*smscore_set_power_mode(dev, SMS_POWER_MODE_SUSPENDED);*/ smsusb_stop_streaming(dev); return 0; } @@ -456,9 +524,9 @@ static int smsusb_resume(struct usb_interface *intf) struct smsusb_device_t *dev = usb_get_intfdata(intf); struct usb_device *udev = interface_to_usbdev(intf); - printk(KERN_INFO "%s: Entering.\n", __func__); - usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); - usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + printk(KERN_INFO "%s Entering.\n", __func__); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->in_ep)); + usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->out_ep)); for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, @@ -546,6 +614,26 @@ static const struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0xf5a0), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x187f, 0x0202), + .driver_info = SMS1XXX_BOARD_SIANO_NICE }, + { USB_DEVICE(0x187f, 0x0301), + .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, + { USB_DEVICE(0x187f, 0x0302), + .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, + { USB_DEVICE(0x187f, 0x0310), + .driver_info = SMS1XXX_BOARD_SIANO_MING }, + { USB_DEVICE(0x187f, 0x0500), + .driver_info = SMS1XXX_BOARD_SIANO_PELE }, + { USB_DEVICE(0x187f, 0x0600), + .driver_info = SMS1XXX_BOARD_SIANO_RIO }, + { USB_DEVICE(0x187f, 0x0700), + .driver_info = SMS1XXX_BOARD_SIANO_DENVER_2160 }, + { USB_DEVICE(0x187f, 0x0800), + .driver_info = SMS1XXX_BOARD_SIANO_DENVER_1530 }, + { USB_DEVICE(0x19D2, 0x0086), + .driver_info = SMS1XXX_BOARD_ZTE_DVB_DATA_CARD }, + { USB_DEVICE(0x19D2, 0x0078), + .driver_info = SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD }, { } /* Terminating entry */ }; diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 6694f9e..a59153d2 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -375,7 +375,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct stk1160 *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; @@ -388,7 +388,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) return -ENODEV; /* We need to set this now, before we call stk1160_set_std */ - dev->norm = *norm; + dev->norm = norm; /* This is taken from saa7115 video decoder */ if (dev->norm & V4L2_STD_525_60) { @@ -458,7 +458,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { switch (chip->match.type) { - case V4L2_CHIP_MATCH_HOST: + case V4L2_CHIP_MATCH_BRIDGE: chip->ident = V4L2_IDENT_NONE; chip->revision = 0; return 0; @@ -476,9 +476,6 @@ static int vidioc_g_register(struct file *file, void *priv, u8 val; switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - /* TODO: Support me please :-( */ - return -EINVAL; case V4L2_CHIP_MATCH_I2C_DRIVER: v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; @@ -500,13 +497,11 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct stk1160 *dev = video_drvdata(file); switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - return -EINVAL; case V4L2_CHIP_MATCH_I2C_DRIVER: v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; @@ -687,6 +682,7 @@ int stk1160_vb2_setup(struct stk1160 *dev) q->buf_struct_size = sizeof(struct stk1160_buffer); q->ops = &stk1160_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(q); if (rc < 0) diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 4cbab08..c43c8d3 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -35,6 +35,7 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "stk-webcam.h" @@ -63,7 +64,39 @@ static struct usb_device_id stkwebcam_table[] = { }; MODULE_DEVICE_TABLE(usb, stkwebcam_table); -/* The stk webcam laptop module is mounted upside down in some laptops :( */ +/* + * The stk webcam laptop module is mounted upside down in some laptops :( + * + * Some background information (thanks to Hans de Goede for providing this): + * + * 1) Once upon a time the stkwebcam driver was written + * + * 2) The webcam in question was used mostly in Asus laptop models, including + * the laptop of the original author of the driver, and in these models, in + * typical Asus fashion (see the long long list for uvc cams inside v4l-utils), + * they mounted the webcam-module the wrong way up. So the hflip and vflip + * module options were given a default value of 1 (the correct value for + * upside down mounted models) + * + * 3) Years later I got a bug report from a user with a laptop with stkwebcam, + * where the module was actually mounted the right way up, and thus showed + * upside down under Linux. So now I was facing the choice of 2 options: + * + * a) Add a not-upside-down list to stkwebcam, which overrules the default. + * + * b) Do it like all the other drivers do, and make the default right for + * cams mounted the proper way and add an upside-down model list, with + * models where we need to flip-by-default. + * + * Despite knowing that going b) would cause a period of pain where we were + * building the table I opted to go for option b), since a) is just too ugly, + * and worse different from how every other driver does it leading to + * confusion in the long run. This change was made in kernel 3.6. + * + * So for any user report about upside-down images since kernel 3.6 ask them + * to provide the output of 'sudo dmidecode' so the laptop can be added in + * the table below. + */ static const struct dmi_system_id stk_upside_down_dmi_table[] = { { .ident = "ASUS G1", @@ -71,6 +104,12 @@ static const struct dmi_system_id stk_upside_down_dmi_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "G1") } + }, { + .ident = "ASUS F3JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "F3JC") + } }, {} }; @@ -565,31 +604,31 @@ static void stk_free_buffers(struct stk_camera *dev) static int v4l_stk_open(struct file *fp) { - static int first_init = 1; /* webcam LED management */ - struct stk_camera *dev; - struct video_device *vdev; - - vdev = video_devdata(fp); - dev = vdev_to_camera(vdev); + struct stk_camera *dev = video_drvdata(fp); + int err; if (dev == NULL || !is_present(dev)) return -ENXIO; - if (!first_init) + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (!dev->first_init) stk_camera_write_reg(dev, 0x0, 0x24); else - first_init = 0; - - fp->private_data = dev; - usb_autopm_get_interface(dev->interface); + dev->first_init = 0; - return 0; + err = v4l2_fh_open(fp); + if (!err) + usb_autopm_get_interface(dev->interface); + mutex_unlock(&dev->lock); + return err; } static int v4l_stk_release(struct file *fp) { - struct stk_camera *dev = fp->private_data; + struct stk_camera *dev = video_drvdata(fp); + mutex_lock(&dev->lock); if (dev->owner == fp) { stk_stop_stream(dev); stk_free_buffers(dev); @@ -600,22 +639,22 @@ static int v4l_stk_release(struct file *fp) if (is_present(dev)) usb_autopm_put_interface(dev->interface); - - return 0; + mutex_unlock(&dev->lock); + return v4l2_fh_release(fp); } -static ssize_t v4l_stk_read(struct file *fp, char __user *buf, +static ssize_t stk_read(struct file *fp, char __user *buf, size_t count, loff_t *f_pos) { int i; int ret; unsigned long flags; struct stk_sio_buffer *sbuf; - struct stk_camera *dev = fp->private_data; + struct stk_camera *dev = video_drvdata(fp); if (!is_present(dev)) return -EIO; - if (dev->owner && dev->owner != fp) + if (dev->owner && (!dev->reading || dev->owner != fp)) return -EBUSY; dev->owner = fp; if (!is_streaming(dev)) { @@ -623,6 +662,7 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf, || stk_allocate_buffers(dev, 3) || stk_start_stream(dev)) return -ENOMEM; + dev->reading = 1; spin_lock_irqsave(&dev->spinlock, flags); for (i = 0; i < dev->n_sbufs; i++) { list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); @@ -665,9 +705,23 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf, return count; } +static ssize_t v4l_stk_read(struct file *fp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct stk_camera *dev = video_drvdata(fp); + int ret; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = stk_read(fp, buf, count, f_pos); + mutex_unlock(&dev->lock); + return ret; +} + static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) { - struct stk_camera *dev = fp->private_data; + struct stk_camera *dev = video_drvdata(fp); + unsigned res = v4l2_ctrl_poll(fp, wait); poll_wait(fp, &dev->wait_frame, wait); @@ -675,9 +729,9 @@ static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) return POLLERR; if (!list_empty(&dev->sio_full)) - return POLLIN | POLLRDNORM; + return res | POLLIN | POLLRDNORM; - return 0; + return res; } @@ -703,7 +757,7 @@ static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) unsigned int i; int ret; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct stk_camera *dev = fp->private_data; + struct stk_camera *dev = video_drvdata(fp); struct stk_sio_buffer *sbuf = NULL; if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) @@ -733,12 +787,15 @@ static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) static int stk_vidioc_querycap(struct file *filp, void *priv, struct v4l2_capability *cap) { + struct stk_camera *dev = video_drvdata(filp); + strcpy(cap->driver, "stk"); strcpy(cap->card, "stk"); - cap->version = DRIVER_VERSION_NUM; + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -762,111 +819,28 @@ static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - if (i != 0) - return -EINVAL; - else - return 0; -} - -/* from vivi.c */ -static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) -{ - return 0; -} - -/* List of all V4Lv2 controls supported by the driver */ -static struct v4l2_queryctrl stk_controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xffff, - .step = 0x0100, - .default_value = 0x6000, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, -}; - -static int stk_vidioc_queryctrl(struct file *filp, - void *priv, struct v4l2_queryctrl *c) -{ - int i; - int nbr; - nbr = ARRAY_SIZE(stk_controls); - - for (i = 0; i < nbr; i++) { - if (stk_controls[i].id == c->id) { - memcpy(c, &stk_controls[i], - sizeof(struct v4l2_queryctrl)); - return 0; - } - } - return -EINVAL; + return i ? -EINVAL : 0; } -static int stk_vidioc_g_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) +static int stk_s_ctrl(struct v4l2_ctrl *ctrl) { - struct stk_camera *dev = priv; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = dev->vsettings.brightness; - break; - case V4L2_CID_HFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - c->value = !dev->vsettings.hflip; - else - c->value = dev->vsettings.hflip; - break; - case V4L2_CID_VFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - c->value = !dev->vsettings.vflip; - else - c->value = dev->vsettings.vflip; - break; - default: - return -EINVAL; - } - return 0; -} + struct stk_camera *dev = + container_of(ctrl->handler, struct stk_camera, hdl); -static int stk_vidioc_s_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) -{ - struct stk_camera *dev = priv; - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - dev->vsettings.brightness = c->value; - return stk_sensor_set_brightness(dev, c->value >> 8); + return stk_sensor_set_brightness(dev, ctrl->val); case V4L2_CID_HFLIP: if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.hflip = !c->value; + dev->vsettings.hflip = !ctrl->val; else - dev->vsettings.hflip = c->value; + dev->vsettings.hflip = ctrl->val; return 0; case V4L2_CID_VFLIP: if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.vflip = !c->value; + dev->vsettings.vflip = !ctrl->val; else - dev->vsettings.vflip = c->value; + dev->vsettings.vflip = ctrl->val; return 0; default: return -EINVAL; @@ -921,7 +895,7 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *f) { struct v4l2_pix_format *pix_format = &f->fmt.pix; - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); int i; for (i = 0; i < ARRAY_SIZE(stk_sizes) && @@ -942,11 +916,12 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, pix_format->bytesperline = 2 * pix_format->width; pix_format->sizeimage = pix_format->bytesperline * pix_format->height; + pix_format->priv = 0; return 0; } -static int stk_vidioc_try_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) +static int stk_try_fmt_vid_cap(struct file *filp, + struct v4l2_format *fmtd, int *idx) { int i; switch (fmtd->fmt.pix.pixelformat) { @@ -968,11 +943,13 @@ static int stk_vidioc_try_fmt_vid_cap(struct file *filp, < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { fmtd->fmt.pix.height = stk_sizes[i-1].h; fmtd->fmt.pix.width = stk_sizes[i-1].w; - fmtd->fmt.pix.priv = i - 1; + if (idx) + *idx = i - 1; } else { fmtd->fmt.pix.height = stk_sizes[i].h; fmtd->fmt.pix.width = stk_sizes[i].w; - fmtd->fmt.pix.priv = i; + if (idx) + *idx = i; } fmtd->fmt.pix.field = V4L2_FIELD_NONE; @@ -983,9 +960,16 @@ static int stk_vidioc_try_fmt_vid_cap(struct file *filp, fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline * fmtd->fmt.pix.height; + fmtd->fmt.pix.priv = 0; return 0; } +static int stk_vidioc_try_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + return stk_try_fmt_vid_cap(filp, fmtd, NULL); +} + static int stk_setup_format(struct stk_camera *dev) { int i = 0; @@ -1026,7 +1010,8 @@ static int stk_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmtd) { int ret; - struct stk_camera *dev = priv; + int idx; + struct stk_camera *dev = video_drvdata(filp); if (dev == NULL) return -ENODEV; @@ -1034,17 +1019,16 @@ static int stk_vidioc_s_fmt_vid_cap(struct file *filp, return -ENODEV; if (is_streaming(dev)) return -EBUSY; - if (dev->owner && dev->owner != filp) + if (dev->owner) return -EBUSY; - ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); + ret = stk_try_fmt_vid_cap(filp, fmtd, &idx); if (ret) return ret; - dev->owner = filp; dev->vsettings.palette = fmtd->fmt.pix.pixelformat; stk_free_buffers(dev); dev->frame_size = fmtd->fmt.pix.sizeimage; - dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m; + dev->vsettings.mode = stk_sizes[idx].m; stk_initialise(dev); return stk_setup_format(dev); @@ -1053,7 +1037,7 @@ static int stk_vidioc_s_fmt_vid_cap(struct file *filp, static int stk_vidioc_reqbufs(struct file *filp, void *priv, struct v4l2_requestbuffers *rb) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); if (dev == NULL) return -ENODEV; @@ -1062,6 +1046,13 @@ static int stk_vidioc_reqbufs(struct file *filp, if (is_streaming(dev) || (dev->owner && dev->owner != filp)) return -EBUSY; + stk_free_buffers(dev); + if (rb->count == 0) { + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); + dev->owner = NULL; + return 0; + } dev->owner = filp; /*FIXME If they ask for zero, we must stop streaming and free */ @@ -1079,7 +1070,7 @@ static int stk_vidioc_reqbufs(struct file *filp, static int stk_vidioc_querybuf(struct file *filp, void *priv, struct v4l2_buffer *buf) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); struct stk_sio_buffer *sbuf; if (buf->index >= dev->n_sbufs) @@ -1092,7 +1083,7 @@ static int stk_vidioc_querybuf(struct file *filp, static int stk_vidioc_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); struct stk_sio_buffer *sbuf; unsigned long flags; @@ -1116,7 +1107,7 @@ static int stk_vidioc_qbuf(struct file *filp, static int stk_vidioc_dqbuf(struct file *filp, void *priv, struct v4l2_buffer *buf) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); struct stk_sio_buffer *sbuf; unsigned long flags; int ret; @@ -1149,7 +1140,7 @@ static int stk_vidioc_dqbuf(struct file *filp, static int stk_vidioc_streamon(struct file *filp, void *priv, enum v4l2_buf_type type) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); if (is_streaming(dev)) return 0; if (dev->sio_bufs == NULL) @@ -1161,7 +1152,7 @@ static int stk_vidioc_streamon(struct file *filp, static int stk_vidioc_streamoff(struct file *filp, void *priv, enum v4l2_buf_type type) { - struct stk_camera *dev = priv; + struct stk_camera *dev = video_drvdata(filp); unsigned long flags; int i; stk_stop_stream(dev); @@ -1206,6 +1197,10 @@ static int stk_vidioc_enum_framesizes(struct file *filp, } } +static const struct v4l2_ctrl_ops stk_ctrl_ops = { + .s_ctrl = stk_s_ctrl, +}; + static struct v4l2_file_operations v4l_stk_fops = { .owner = THIS_MODULE, .open = v4l_stk_open, @@ -1213,7 +1208,7 @@ static struct v4l2_file_operations v4l_stk_fops = { .read = v4l_stk_read, .poll = v4l_stk_poll, .mmap = v4l_stk_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { @@ -1225,18 +1220,17 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { .vidioc_enum_input = stk_vidioc_enum_input, .vidioc_s_input = stk_vidioc_s_input, .vidioc_g_input = stk_vidioc_g_input, - .vidioc_s_std = stk_vidioc_s_std, .vidioc_reqbufs = stk_vidioc_reqbufs, .vidioc_querybuf = stk_vidioc_querybuf, .vidioc_qbuf = stk_vidioc_qbuf, .vidioc_dqbuf = stk_vidioc_dqbuf, .vidioc_streamon = stk_vidioc_streamon, .vidioc_streamoff = stk_vidioc_streamoff, - .vidioc_queryctrl = stk_vidioc_queryctrl, - .vidioc_g_ctrl = stk_vidioc_g_ctrl, - .vidioc_s_ctrl = stk_vidioc_s_ctrl, .vidioc_g_parm = stk_vidioc_g_parm, .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static void stk_v4l_dev_release(struct video_device *vd) @@ -1251,8 +1245,6 @@ static void stk_v4l_dev_release(struct video_device *vd) static struct video_device stk_v4l_data = { .name = "stkwebcam", - .tvnorms = V4L2_STD_UNKNOWN, - .current_norm = V4L2_STD_UNKNOWN, .fops = &v4l_stk_fops, .ioctl_ops = &v4l_stk_ioctl_ops, .release = stk_v4l_dev_release, @@ -1264,8 +1256,11 @@ static int stk_register_video_device(struct stk_camera *dev) int err; dev->vdev = stk_v4l_data; + dev->vdev.lock = &dev->lock; dev->vdev.debug = debug; - dev->vdev.parent = &dev->interface->dev; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); + video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) STK_ERROR("v4l registration failed\n"); @@ -1281,8 +1276,9 @@ static int stk_register_video_device(struct stk_camera *dev) static int stk_camera_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int i; + struct v4l2_ctrl_handler *hdl; int err = 0; + int i; struct stk_camera *dev = NULL; struct usb_device *udev = interface_to_usbdev(interface); @@ -1294,9 +1290,31 @@ static int stk_camera_probe(struct usb_interface *interface, STK_ERROR("Out of memory !\n"); return -ENOMEM; } + err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + kfree(dev); + return err; + } + hdl = &dev->hdl; + v4l2_ctrl_handler_init(hdl, 3); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xff, 0x1, 0x60); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 1); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto error; + } + dev->v4l2_dev.ctrl_handler = hdl; spin_lock_init(&dev->spinlock); + mutex_init(&dev->lock); init_waitqueue_head(&dev->wait_frame); + dev->first_init = 1; /* webcam LED management */ dev->udev = udev; dev->interface = interface; @@ -1337,7 +1355,6 @@ static int stk_camera_probe(struct usb_interface *interface, err = -ENODEV; goto error; } - dev->vsettings.brightness = 0x7fff; dev->vsettings.palette = V4L2_PIX_FMT_RGB565; dev->vsettings.mode = MODE_VGA; dev->frame_size = 640 * 480 * 2; @@ -1354,6 +1371,8 @@ static int stk_camera_probe(struct usb_interface *interface, return 0; error: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); kfree(dev); return err; } @@ -1371,6 +1390,8 @@ static void stk_camera_disconnect(struct usb_interface *interface) video_device_node_name(&dev->vdev)); video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); } #ifdef CONFIG_PM diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h index 9f67366..9bbfa3d 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.h +++ b/drivers/media/usb/stkwebcam/stk-webcam.h @@ -23,6 +23,8 @@ #define STKWEBCAM_H #include <linux/usb.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-common.h> #define DRIVER_VERSION "v0.0.1" @@ -59,7 +61,6 @@ enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; struct stk_video { enum stk_mode mode; - int brightness; __u32 palette; int hflip; int vflip; @@ -91,11 +92,15 @@ struct regval { }; struct stk_camera { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct video_device vdev; struct usb_device *udev; struct usb_interface *interface; int webcam_model; struct file *owner; + struct mutex lock; + int first_init; u8 isoc_ep; @@ -113,6 +118,7 @@ struct stk_camera { int frame_size; /* Streaming buffers */ + int reading; unsigned int n_sbufs; struct stk_sio_buffer *sio_bufs; struct list_head sio_avail; diff --git a/drivers/media/usb/tlg2300/pd-common.h b/drivers/media/usb/tlg2300/pd-common.h index 5dd73b7..9e23ad32 100644 --- a/drivers/media/usb/tlg2300/pd-common.h +++ b/drivers/media/usb/tlg2300/pd-common.h @@ -10,6 +10,7 @@ #include <linux/poll.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include "dvb_frontend.h" #include "dvbdev.h" @@ -25,7 +26,6 @@ #define POSEIDON_STATE_ANALOG (0x0001) #define POSEIDON_STATE_FM (0x0002) #define POSEIDON_STATE_DVBT (0x0004) -#define POSEIDON_STATE_VBI (0x0008) #define POSEIDON_STATE_DISCONNECT (0x0080) #define PM_SUSPEND_DELAY 3 @@ -35,11 +35,11 @@ #define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2) #define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2) -#define TUNER_FREQ_MIN (45000000) -#define TUNER_FREQ_MAX (862000000) +#define TUNER_FREQ_MIN (45000000U) +#define TUNER_FREQ_MAX (862000000U) struct vbi_data { - struct video_device *v_dev; + struct video_device v_dev; struct video_data *video; struct front_face *front; @@ -62,7 +62,8 @@ struct running_context { struct video_data { /* v4l2 video device */ - struct video_device *v_dev; + struct video_device v_dev; + struct v4l2_ctrl_handler ctrl_handler; /* the working context */ struct running_context context; @@ -115,10 +116,10 @@ struct poseidon_audio { struct radio_data { __u32 fm_freq; - int users; unsigned int is_radio_streaming; int pre_emphasis; - struct video_device *fm_dev; + struct video_device fm_dev; + struct v4l2_ctrl_handler ctrl_handler; }; #define DVB_SBUF_NUM 4 @@ -233,7 +234,6 @@ void dvb_stop_streaming(struct pd_dvb_adapter *); /* FM */ int poseidon_fm_init(struct poseidon *); int poseidon_fm_exit(struct poseidon *); -struct video_device *vdev_init(struct poseidon *, struct video_device *); /* vendor command ops */ int send_set_req(struct poseidon*, u8, s32, s32*); @@ -249,7 +249,6 @@ void free_all_urb_generic(struct urb **urb_array, int num); /* misc */ void poseidon_delete(struct kref *kref); -void destroy_video_device(struct video_device **v_dev); extern int debug_mode; void set_debug_mode(struct video_device *vfd, int debug_mode); @@ -269,13 +268,4 @@ void set_debug_mode(struct video_device *vfd, int debug_mode); log();\ } while (0) -#define logs(f) do { \ - if ((debug_mode & 0x4) && \ - (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \ - log("type : VBI");\ - \ - if ((debug_mode & 0x8) && \ - (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \ - log("type : VIDEO");\ - } while (0) #endif diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c index 7b1f6eb..e07e4c6 100644 --- a/drivers/media/usb/tlg2300/pd-main.c +++ b/drivers/media/usb/tlg2300/pd-main.c @@ -55,7 +55,6 @@ MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); #define TLG2300_FIRMWARE "tlg2300_firmware.bin" static const char *firmware_name = TLG2300_FIRMWARE; -static struct usb_driver poseidon_driver; static LIST_HEAD(pd_device_list); /* @@ -268,7 +267,8 @@ static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) static inline int get_autopm_ref(struct poseidon *pd) { return pd->video_data.users + pd->vbi_data.users + pd->audio.users - + atomic_read(&pd->dvb_data.users) + pd->radio_data.users; + + atomic_read(&pd->dvb_data.users) + + !list_empty(&pd->radio_data.fm_dev.fh_list); } /* fixup something for poseidon */ @@ -316,7 +316,7 @@ static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) { pd->msg.event = PM_EVENT_AUTO_SUSPEND; pd->pm_resume = NULL; /* a good guard */ - printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n"); + printk(KERN_DEBUG "TLG2300 auto suspend\n"); } return 0; } @@ -331,7 +331,7 @@ static int poseidon_resume(struct usb_interface *intf) if (!pd) return 0; - printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n"); + printk(KERN_DEBUG "TLG2300 resume\n"); if (!is_working(pd)) { if (PM_EVENT_AUTO_SUSPEND == pd->msg.event) @@ -431,15 +431,11 @@ static int poseidon_probe(struct usb_interface *interface, usb_set_intfdata(interface, pd); if (new_one) { - struct device *dev = &interface->dev; - logpm(pd); mutex_init(&pd->lock); /* register v4l2 device */ - snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s", - dev->driver->name, dev_name(dev)); - ret = v4l2_device_register(NULL, &pd->v4l2_dev); + ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev); /* register devices in directory /dev */ ret = pd_video_init(pd); @@ -530,7 +526,7 @@ module_init(poseidon_init); module_exit(poseidon_exit); MODULE_AUTHOR("Telegent Systems"); -MODULE_DESCRIPTION("For tlg2300-based USB device "); +MODULE_DESCRIPTION("For tlg2300-based USB device"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.2"); MODULE_FIRMWARE(TLG2300_FIRMWARE); diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c index 25eeb16..ea6070b 100644 --- a/drivers/media/usb/tlg2300/pd-radio.c +++ b/drivers/media/usb/tlg2300/pd-radio.c @@ -9,6 +9,8 @@ #include <linux/mm.h> #include <linux/mutex.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> #include <linux/sched.h> #include "pd-common.h" @@ -18,8 +20,8 @@ static int set_frequency(struct poseidon *p, __u32 frequency); static int poseidon_fm_close(struct file *filp); static int poseidon_fm_open(struct file *filp); -#define TUNER_FREQ_MIN_FM 76000000 -#define TUNER_FREQ_MAX_FM 108000000 +#define TUNER_FREQ_MIN_FM 76000000U +#define TUNER_FREQ_MAX_FM 108000000U #define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1) static int preemphasis[MAX_PREEMPHASIS] = { @@ -77,13 +79,9 @@ static int pm_fm_resume(struct poseidon *p) static int poseidon_fm_open(struct file *filp) { - struct video_device *vfd = video_devdata(filp); - struct poseidon *p = video_get_drvdata(vfd); + struct poseidon *p = video_drvdata(filp); int ret = 0; - if (!p) - return -1; - mutex_lock(&p->lock); if (p->state & POSEIDON_STATE_DISCONNECT) { ret = -ENODEV; @@ -94,9 +92,14 @@ static int poseidon_fm_open(struct file *filp) ret = -EBUSY; goto out; } + ret = v4l2_fh_open(filp); + if (ret) + goto out; usb_autopm_get_interface(p->interface); if (0 == p->state) { + struct video_device *vfd = &p->radio_data.fm_dev; + /* default pre-emphasis */ if (p->radio_data.pre_emphasis == 0) p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; @@ -109,9 +112,7 @@ static int poseidon_fm_open(struct file *filp) } p->state |= POSEIDON_STATE_FM; } - p->radio_data.users++; kref_get(&p->kref); - filp->private_data = p; out: mutex_unlock(&p->lock); return ret; @@ -119,13 +120,12 @@ out: static int poseidon_fm_close(struct file *filp) { - struct poseidon *p = filp->private_data; + struct poseidon *p = video_drvdata(filp); struct radio_data *fm = &p->radio_data; uint32_t status; mutex_lock(&p->lock); - fm->users--; - if (0 == fm->users) + if (v4l2_fh_is_singular_file(filp)) p->state &= ~POSEIDON_STATE_FM; if (fm->is_radio_streaming && filp == p->file_for_stream) { @@ -136,19 +136,23 @@ static int poseidon_fm_close(struct file *filp) mutex_unlock(&p->lock); kref_put(&p->kref, poseidon_delete); - filp->private_data = NULL; - return 0; + return v4l2_fh_release(filp); } static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { - struct poseidon *p = file->private_data; + struct poseidon *p = video_drvdata(file); strlcpy(v->driver, "tele-radio", sizeof(v->driver)); strlcpy(v->card, "Telegent Poseidon", sizeof(v->card)); usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + /* Report all capabilities of the USB device */ + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; return 0; } @@ -156,27 +160,29 @@ static const struct v4l2_file_operations poseidon_fm_fops = { .owner = THIS_MODULE, .open = poseidon_fm_open, .release = poseidon_fm_close, - .ioctl = video_ioctl2, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, }; static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) { + struct poseidon *p = video_drvdata(file); struct tuner_fm_sig_stat_s fm_stat = {}; int ret, status, count = 5; - struct poseidon *p = file->private_data; if (vt->index != 0) return -EINVAL; vt->type = V4L2_TUNER_RADIO; - vt->capability = V4L2_TUNER_CAP_STEREO; - vt->rangelow = TUNER_FREQ_MIN_FM / 62500; - vt->rangehigh = TUNER_FREQ_MAX_FM / 62500; + vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; + vt->rangelow = TUNER_FREQ_MIN_FM * 2 / 125; + vt->rangehigh = TUNER_FREQ_MAX_FM * 2 / 125; vt->rxsubchans = V4L2_TUNER_SUB_STEREO; vt->audmode = V4L2_TUNER_MODE_STEREO; vt->signal = 0; vt->afc = 0; + strlcpy(vt->name, "Radio", sizeof(vt->name)); mutex_lock(&p->lock); ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, @@ -205,8 +211,10 @@ static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, static int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp) { - struct poseidon *p = file->private_data; + struct poseidon *p = video_drvdata(file); + if (argp->tuner) + return -EINVAL; argp->frequency = p->radio_data.fm_freq; return 0; } @@ -221,11 +229,8 @@ static int set_frequency(struct poseidon *p, __u32 frequency) ret = send_set_req(p, TUNER_AUD_ANA_STD, p->radio_data.pre_emphasis, &status); - freq = (frequency * 125) * 500 / 1000;/* kHZ */ - if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) { - ret = -EINVAL; - goto error; - } + freq = (frequency * 125) / 2; /* Hz */ + freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM); ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status); if (ret < 0) @@ -240,18 +245,20 @@ static int set_frequency(struct poseidon *p, __u32 frequency) TLG_TUNE_PLAY_SVC_START, &status); p->radio_data.is_radio_streaming = 1; } - p->radio_data.fm_freq = frequency; + p->radio_data.fm_freq = freq * 2 / 125; error: mutex_unlock(&p->lock); return ret; } static int fm_set_freq(struct file *file, void *priv, - struct v4l2_frequency *argp) + const struct v4l2_frequency *argp) { - struct poseidon *p = file->private_data; + struct poseidon *p = video_drvdata(file); - p->file_for_stream = file; + if (argp->tuner) + return -EINVAL; + p->file_for_stream = file; #ifdef CONFIG_PM p->pm_suspend = pm_fm_suspend; p->pm_resume = pm_fm_resume; @@ -259,163 +266,75 @@ static int fm_set_freq(struct file *file, void *priv, return set_frequency(p, argp->frequency); } -static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *arg) -{ - return 0; -} - -static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, - struct v4l2_ext_controls *ctrls) -{ - struct poseidon *p = file->private_data; - int i; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) - return -EINVAL; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) - continue; - - if (i < MAX_PREEMPHASIS) - ctrl->value = p->radio_data.pre_emphasis; - } - return 0; -} - -static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, - struct v4l2_ext_controls *ctrls) -{ - int i; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) - return -EINVAL; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) - continue; - - if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) { - struct poseidon *p = file->private_data; - int pre_emphasis = preemphasis[ctrl->value]; - u32 status; - - send_set_req(p, TUNER_AUD_ANA_STD, - pre_emphasis, &status); - p->radio_data.pre_emphasis = pre_emphasis; - } - } - return 0; -} - -static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl) { - return 0; -} - -static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) - return -EINVAL; + struct poseidon *p = container_of(ctrl->handler, struct poseidon, + radio_data.ctrl_handler); + int pre_emphasis; + u32 status; - ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) { - /* return the next supported control */ - ctrl->id = V4L2_CID_TUNE_PREEMPHASIS; - v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED, - V4L2_PREEMPHASIS_75_uS, 1, - V4L2_PREEMPHASIS_50_uS); - ctrl->flags = V4L2_CTRL_FLAG_UPDATE; + switch (ctrl->id) { + case V4L2_CID_TUNE_PREEMPHASIS: + pre_emphasis = preemphasis[ctrl->val]; + send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status); + p->radio_data.pre_emphasis = pre_emphasis; return 0; } return -EINVAL; } -static int tlg_fm_vidioc_querymenu(struct file *file, void *fh, - struct v4l2_querymenu *qmenu) -{ - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - -static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt) { return vt->index > 0 ? -EINVAL : 0; } -static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *va) -{ - return (va->index != 0) ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - a->index = 0; - a->mode = 0; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "Radio"); - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, u32 i) -{ - return (i != 0) ? -EINVAL : 0; -} -static int vidioc_g_input(struct file *filp, void *priv, u32 *i) -{ - return (*i != 0) ? -EINVAL : 0; -} +static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = { + .s_ctrl = tlg_fm_s_ctrl, +}; static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = tlg_fm_vidioc_queryctrl, - .vidioc_querymenu = tlg_fm_vidioc_querymenu, - .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl, - .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl, - .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl, - .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_tuner = tlg_fm_vidioc_g_tuner, .vidioc_g_frequency = fm_get_freq, .vidioc_s_frequency = fm_set_freq, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device poseidon_fm_template = { .name = "Telegent-Radio", .fops = &poseidon_fm_fops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &poseidon_fm_ioctl_ops, }; int poseidon_fm_init(struct poseidon *p) { - struct video_device *fm_dev; - - fm_dev = vdev_init(p, &poseidon_fm_template); - if (fm_dev == NULL) - return -1; - - if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) { - video_device_release(fm_dev); - return -1; + struct video_device *vfd = &p->radio_data.fm_dev; + struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler; + + *vfd = poseidon_fm_template; + + set_frequency(p, TUNER_FREQ_MIN_FM); + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS, + V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; } - p->radio_data.fm_dev = fm_dev; - return 0; + vfd->v4l2_dev = &p->v4l2_dev; + vfd->ctrl_handler = hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + video_set_drvdata(vfd, p); + return video_register_device(vfd, VFL_TYPE_RADIO, -1); } int poseidon_fm_exit(struct poseidon *p) { - destroy_video_device(&p->radio_data.fm_dev); + video_unregister_device(&p->radio_data.fm_dev); + v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler); return 0; } diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 2172337..8df668d 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -8,6 +8,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-dev.h> +#include <media/v4l2-ctrls.h> #include "pd-common.h" #include "vendorcmds.h" @@ -82,31 +83,6 @@ static const struct pd_input pd_inputs[] = { }; static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs); -struct poseidon_control { - struct v4l2_queryctrl v4l2_ctrl; - enum cmd_custom_param_id vc_id; -}; - -static struct poseidon_control controls[] = { - { - { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, - "brightness", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_BRIGHTNESS_CTRL - }, { - { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, - "contrast", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_CONTRAST_CTRL, - }, { - { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, - "hue", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_HUE_CTRL, - }, { - { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, - "saturation", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_SATURATION_CTRL, - }, -}; - struct video_std_to_audio_std { v4l2_std_id video_std; int audio_std; @@ -142,17 +118,20 @@ static int get_audio_std(v4l2_std_id v4l2_std) static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct front_face *front = fh; - struct poseidon *p = front->pd; - - logs(front); + struct video_device *vdev = video_devdata(file); + struct poseidon *p = video_get_drvdata(vdev); strcpy(cap->driver, "tele-video"); strcpy(cap->card, "Telegent Poseidon"); usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | - V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; + cap->device_caps = V4L2_CAP_TUNER | V4L2_CAP_AUDIO | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + 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_DEVICE_CAPS | + V4L2_CAP_RADIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE; return 0; } @@ -223,7 +202,6 @@ static void submit_frame(struct front_face *front) */ static void end_field(struct video_data *video) { - /* logs(video->front); */ if (1 == video->field_count) submit_frame(video->front); else @@ -718,17 +696,10 @@ static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) struct front_face *front = fh; struct poseidon *pd = front->pd; - logs(front); f->fmt.pix = pd->video_data.context.pix; return 0; } -static int vidioc_try_fmt(struct file *file, void *fh, - struct v4l2_format *f) -{ - return 0; -} - /* * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while * Mplayer calls them in the reverse order. @@ -787,7 +758,6 @@ static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) struct front_face *front = fh; struct poseidon *pd = front->pd; - logs(front); /* stop VBI here */ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) return -EINVAL; @@ -828,11 +798,10 @@ static int vidioc_g_fmt_vbi(struct file *file, void *fh, vbi_fmt->count[1] = V4L_PAL_VBI_LINES; } vbi_fmt->flags = V4L2_VBI_UNSYNC; - logs(front); return 0; } -static int set_std(struct poseidon *pd, v4l2_std_id *norm) +static int set_std(struct poseidon *pd, v4l2_std_id norm) { struct video_data *video = &pd->video_data; struct vbi_data *vbi = &pd->vbi_data; @@ -842,7 +811,7 @@ static int set_std(struct poseidon *pd, v4l2_std_id *norm) int height; for (i = 0; i < POSEIDON_TVNORMS; i++) { - if (*norm & poseidon_tvnorms[i].v4l2_id) { + if (norm & poseidon_tvnorms[i].v4l2_id) { param = poseidon_tvnorms[i].tlg_tvnorm; log("name : %s", poseidon_tvnorms[i].name); goto found; @@ -877,17 +846,23 @@ out: return ret; } -static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id norm) { struct front_face *front = fh; - logs(front); + return set_std(front->pd, norm); } -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) +static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) { struct front_face *front = fh; + *norm = front->pd->video_data.context.tvnormid; + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) +{ if (in->index >= POSEIDON_INPUTS) return -EINVAL; strcpy(in->name, pd_inputs[in->index].name); @@ -897,11 +872,10 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) * the audio input index mixed with this video input, * Poseidon only have one audio/video, set to "0" */ - in->audioset = 0; + in->audioset = 1; in->tuner = 0; in->std = V4L2_STD_ALL; in->status = 0; - logs(front); return 0; } @@ -911,7 +885,6 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) struct poseidon *pd = front->pd; struct running_context *context = &pd->video_data.context; - logs(front); *i = context->sig_index; return 0; } @@ -934,68 +907,28 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int i) return 0; } -static struct poseidon_control *check_control_id(__u32 id) -{ - struct poseidon_control *control = &controls[0]; - int array_size = ARRAY_SIZE(controls); - - for (; control < &controls[array_size]; control++) - if (control->v4l2_ctrl.id == id) - return control; - return NULL; -} - -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *a) -{ - struct poseidon_control *control = NULL; - - control = check_control_id(a->id); - if (!control) - return -EINVAL; - - *a = control->v4l2_ctrl; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct poseidon_control *control = NULL; - struct tuner_custom_parameter_s tuner_param; - s32 ret = 0, cmd_status; - - control = check_control_id(ctrl->id); - if (!control) - return -EINVAL; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id, - &tuner_param, &cmd_status, sizeof(tuner_param)); - mutex_unlock(&pd->lock); - - if (ret || cmd_status) - return -1; - - ctrl->value = tuner_param.param_value; - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +static int tlg_s_ctrl(struct v4l2_ctrl *c) { + struct poseidon *pd = container_of(c->handler, struct poseidon, + video_data.ctrl_handler); struct tuner_custom_parameter_s param = {0}; - struct poseidon_control *control = NULL; - struct front_face *front = fh; - struct poseidon *pd = front->pd; s32 ret = 0, cmd_status, params; - control = check_control_id(a->id); - if (!control) - return -EINVAL; - - param.param_value = a->value; - param.param_id = control->vc_id; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + param.param_id = CUST_PARM_ID_BRIGHTNESS_CTRL; + break; + case V4L2_CID_CONTRAST: + param.param_id = CUST_PARM_ID_CONTRAST_CTRL; + break; + case V4L2_CID_HUE: + param.param_id = CUST_PARM_ID_HUE_CTRL; + break; + case V4L2_CID_SATURATION: + param.param_id = CUST_PARM_ID_SATURATION_CTRL; + break; + } + param.param_value = c->val; params = *(s32 *)¶m; /* temp code */ mutex_lock(&pd->lock); @@ -1079,7 +1012,6 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner) tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub; tuner->audmode = pd_audio_modes[index].v4l2_audio_mode; tuner->afc = 0; - logs(front); return 0; } @@ -1099,7 +1031,7 @@ static int pd_vidioc_s_tuner(struct poseidon *pd, int index) return ret; } -static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a) +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *a) { struct front_face *front = fh; struct poseidon *pd = front->pd; @@ -1107,7 +1039,6 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a) if (0 != a->index) return -EINVAL; - logs(front); for (index = 0; index < POSEIDON_AUDIOMODS; index++) if (a->audmode == pd_audio_modes[index].v4l2_audio_mode) return pd_vidioc_s_tuner(pd, index); @@ -1128,51 +1059,51 @@ static int vidioc_g_frequency(struct file *file, void *fh, return 0; } -static int set_frequency(struct poseidon *pd, __u32 frequency) +static int set_frequency(struct poseidon *pd, u32 *frequency) { s32 ret = 0, param, cmd_status; struct running_context *context = &pd->video_data.context; - param = frequency * 62500 / 1000; - if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000) - return -EINVAL; + *frequency = clamp(*frequency, + TUNER_FREQ_MIN / 62500, TUNER_FREQ_MAX / 62500); + param = (*frequency) * 62500 / 1000; mutex_lock(&pd->lock); ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status); ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); msleep(250); /* wait for a while until the hardware is ready. */ - context->freq = frequency; + context->freq = *frequency; mutex_unlock(&pd->lock); return ret; } static int vidioc_s_frequency(struct file *file, void *fh, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct front_face *front = fh; struct poseidon *pd = front->pd; + u32 frequency = freq->frequency; - logs(front); + if (freq->tuner) + return -EINVAL; #ifdef CONFIG_PM pd->pm_suspend = pm_video_suspend; pd->pm_resume = pm_video_resume; #endif - return set_frequency(pd, freq->frequency); + return set_frequency(pd, &frequency); } static int vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { struct front_face *front = file->private_data; - logs(front); return videobuf_reqbufs(&front->q, b); } static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct front_face *front = file->private_data; - logs(front); return videobuf_querybuf(&front->q, b); } @@ -1261,7 +1192,6 @@ static int vidioc_streamon(struct file *file, void *fh, { struct front_face *front = fh; - logs(front); if (unlikely(type != front->type)) return -EINVAL; return videobuf_streamon(&front->q); @@ -1272,7 +1202,6 @@ static int vidioc_streamoff(struct file *file, void *fh, { struct front_face *front = file->private_data; - logs(front); if (unlikely(type != front->type)) return -EINVAL; return videobuf_streamoff(&front->q); @@ -1341,11 +1270,11 @@ static int restore_v4l2_context(struct poseidon *pd, pd_video_checkmode(pd); - set_std(pd, &context->tvnormid); + set_std(pd, context->tvnormid); vidioc_s_input(NULL, front, context->sig_index); pd_vidioc_s_tuner(pd, context->audio_idx); pd_vidioc_s_fmt(pd, &context->pix); - set_frequency(pd, context->freq); + set_frequency(pd, &context->freq); return 0; } @@ -1406,12 +1335,14 @@ static int pd_video_open(struct file *file) mutex_lock(&pd->lock); usb_autopm_get_interface(pd->interface); - if (vfd->vfl_type == VFL_TYPE_GRABBER - && !(pd->state & POSEIDON_STATE_ANALOG)) { - front = kzalloc(sizeof(struct front_face), GFP_KERNEL); - if (!front) - goto out; - + if (pd->state && !(pd->state & POSEIDON_STATE_ANALOG)) { + ret = -EBUSY; + goto out; + } + front = kzalloc(sizeof(struct front_face), GFP_KERNEL); + if (!front) + goto out; + if (vfd->vfl_type == VFL_TYPE_GRABBER) { pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ init_video_context(&pd->video_data.context); @@ -1422,7 +1353,6 @@ static int pd_video_open(struct file *file) goto out; } - pd->state |= POSEIDON_STATE_ANALOG; front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pd->video_data.users++; set_debug_mode(vfd, debug_mode); @@ -1433,13 +1363,7 @@ static int pd_video_open(struct file *file) V4L2_FIELD_INTERLACED,/* video is interlacd */ sizeof(struct videobuf_buffer),/*it's enough*/ front, NULL); - } else if (vfd->vfl_type == VFL_TYPE_VBI - && !(pd->state & POSEIDON_STATE_VBI)) { - front = kzalloc(sizeof(struct front_face), GFP_KERNEL); - if (!front) - goto out; - - pd->state |= POSEIDON_STATE_VBI; + } else { front->type = V4L2_BUF_TYPE_VBI_CAPTURE; pd->vbi_data.front = front; pd->vbi_data.users++; @@ -1450,19 +1374,15 @@ static int pd_video_open(struct file *file) V4L2_FIELD_NONE, /* vbi is NONE mode */ sizeof(struct videobuf_buffer), front, NULL); - } else { - /* maybe add FM support here */ - log("other "); - ret = -EINVAL; - goto out; } - front->pd = pd; - front->curr_frame = NULL; + pd->state |= POSEIDON_STATE_ANALOG; + front->pd = pd; + front->curr_frame = NULL; INIT_LIST_HEAD(&front->active); spin_lock_init(&front->queue_lock); - file->private_data = front; + file->private_data = front; kref_get(&pd->kref); mutex_unlock(&pd->lock); @@ -1479,12 +1399,9 @@ static int pd_video_release(struct file *file) struct poseidon *pd = front->pd; s32 cmd_status = 0; - logs(front); mutex_lock(&pd->lock); if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pd->state &= ~POSEIDON_STATE_ANALOG; - /* stop the device, and free the URBs */ usb_transfer_stop(&pd->video_data); free_all_urb(&pd->video_data); @@ -1496,10 +1413,11 @@ static int pd_video_release(struct file *file) pd->file_for_stream = NULL; pd->video_data.users--; } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - pd->state &= ~POSEIDON_STATE_VBI; pd->vbi_data.front = NULL; pd->vbi_data.users--; } + if (!pd->vbi_data.users && !pd->video_data.users) + pd->state &= ~POSEIDON_STATE_ANALOG; videobuf_stop(&front->q); videobuf_mmap_free(&front->q); @@ -1551,7 +1469,6 @@ static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, .vidioc_s_fmt_vid_cap = vidioc_s_fmt, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */ - .vidioc_try_fmt_vid_cap = vidioc_try_fmt, /* Input */ .vidioc_g_input = vidioc_g_input, @@ -1566,6 +1483,7 @@ static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { /* Tuner ioctls */ .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, @@ -1579,59 +1497,29 @@ static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { /* Stream on/off */ .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - - /* Control handling */ - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, }; static struct video_device pd_video_template = { .name = "Telegent-Video", .fops = &pd_video_fops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, .tvnorms = V4L2_STD_ALL, .ioctl_ops = &pd_video_ioctl_ops, }; -struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (vfd == NULL) - return NULL; - *vfd = *tmp; - vfd->minor = -1; - vfd->v4l2_dev = &pd->v4l2_dev; - /*vfd->parent = &(pd->udev->dev); */ - vfd->release = video_device_release; - video_set_drvdata(vfd, pd); - return vfd; -} - -void destroy_video_device(struct video_device **v_dev) -{ - struct video_device *dev = *v_dev; - - if (dev == NULL) - return; - - if (video_is_registered(dev)) - video_unregister_device(dev); - else - video_device_release(dev); - *v_dev = NULL; -} +static const struct v4l2_ctrl_ops tlg_ctrl_ops = { + .s_ctrl = tlg_s_ctrl, +}; void pd_video_exit(struct poseidon *pd) { struct video_data *video = &pd->video_data; struct vbi_data *vbi = &pd->vbi_data; - destroy_video_device(&video->v_dev); - destroy_video_device(&vbi->v_dev); + video_unregister_device(&video->v_dev); + video_unregister_device(&vbi->v_dev); + v4l2_ctrl_handler_free(&video->ctrl_handler); log(); } @@ -1639,23 +1527,39 @@ int pd_video_init(struct poseidon *pd) { struct video_data *video = &pd->video_data; struct vbi_data *vbi = &pd->vbi_data; + struct v4l2_ctrl_handler *hdl = &video->ctrl_handler; + u32 freq = TUNER_FREQ_MIN / 62500; int ret = -ENOMEM; - video->v_dev = vdev_init(pd, &pd_video_template); - if (video->v_dev == NULL) - goto out; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_BRIGHTNESS, + 0, 10000, 1, 100); + v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_CONTRAST, + 0, 10000, 1, 100); + v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_HUE, + 0, 10000, 1, 100); + v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_SATURATION, + 0, 10000, 1, 100); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; + } + set_frequency(pd, &freq); + video->v_dev = pd_video_template; + video->v_dev.v4l2_dev = &pd->v4l2_dev; + video->v_dev.ctrl_handler = hdl; + video_set_drvdata(&video->v_dev, pd); - ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->v_dev, VFL_TYPE_GRABBER, -1); if (ret != 0) goto out; /* VBI uses the same template as video */ - vbi->v_dev = vdev_init(pd, &pd_video_template); - if (vbi->v_dev == NULL) { - ret = -ENOMEM; - goto out; - } - ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1); + vbi->v_dev = pd_video_template; + vbi->v_dev.v4l2_dev = &pd->v4l2_dev; + vbi->v_dev.ctrl_handler = hdl; + video_set_drvdata(&vbi->v_dev, pd); + ret = video_register_device(&vbi->v_dev, VFL_TYPE_VBI, -1); if (ret != 0) goto out; log("register VIDEO/VBI devices"); @@ -1665,4 +1569,3 @@ out: pd_video_exit(pd); return ret; } - diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 1a68579..a78de1d 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1056,13 +1056,13 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { int rc = 0; struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - dev->norm = *norm; + dev->norm = norm; rc = tm6000_init_analog_mode(dev); fh->width = dev->width; @@ -1134,7 +1134,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) dev->input = i; - rc = vidioc_s_std(file, priv, &dev->vfd->current_norm); + rc = vidioc_s_std(file, priv, dev->vfd->current_norm); return rc; } @@ -1215,7 +1215,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; @@ -1255,7 +1255,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; @@ -1293,18 +1293,14 @@ static int radio_g_tuner(struct file *file, void *priv, } static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct tm6000_fh *fh = file->private_data; struct tm6000_core *dev = fh->dev; if (0 != t->index) return -EINVAL; - if (t->audmode > V4L2_TUNER_MODE_STEREO) - t->audmode = V4L2_TUNER_MODE_STEREO; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - return 0; } diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index e407185..21b9049 100644 --- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -930,11 +930,11 @@ static int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) if (dvbdmxfeed->type == DMX_TYPE_TS) { switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - case DMX_TS_PES_AUDIO: - case DMX_TS_PES_TELETEXT: - case DMX_TS_PES_PCR: - case DMX_TS_PES_OTHER: + case DMX_PES_VIDEO: + case DMX_PES_AUDIO: + case DMX_PES_TELETEXT: + case DMX_PES_PCR: + case DMX_PES_OTHER: break; default: return -EINVAL; diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 504c812..e52c3b9 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -951,34 +951,34 @@ static int ttusb_dec_start_ts_feed(struct dvb_demux_feed *dvbdmxfeed) switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - dprintk(" pes_type: DMX_TS_PES_VIDEO\n"); + case DMX_PES_VIDEO: + dprintk(" pes_type: DMX_PES_VIDEO\n"); dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; dec->pid[DMX_PES_VIDEO] = dvbdmxfeed->pid; dec->video_filter = dvbdmxfeed->filter; ttusb_dec_set_pids(dec); break; - case DMX_TS_PES_AUDIO: - dprintk(" pes_type: DMX_TS_PES_AUDIO\n"); + case DMX_PES_AUDIO: + dprintk(" pes_type: DMX_PES_AUDIO\n"); dec->pid[DMX_PES_AUDIO] = dvbdmxfeed->pid; dec->audio_filter = dvbdmxfeed->filter; ttusb_dec_set_pids(dec); break; - case DMX_TS_PES_TELETEXT: + case DMX_PES_TELETEXT: dec->pid[DMX_PES_TELETEXT] = dvbdmxfeed->pid; - dprintk(" pes_type: DMX_TS_PES_TELETEXT(not supported)\n"); + dprintk(" pes_type: DMX_PES_TELETEXT(not supported)\n"); return -ENOSYS; - case DMX_TS_PES_PCR: - dprintk(" pes_type: DMX_TS_PES_PCR\n"); + case DMX_PES_PCR: + dprintk(" pes_type: DMX_PES_PCR\n"); dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; ttusb_dec_set_pids(dec); break; - case DMX_TS_PES_OTHER: - dprintk(" pes_type: DMX_TS_PES_OTHER(not supported)\n"); + case DMX_PES_OTHER: + dprintk(" pes_type: DMX_PES_OTHER(not supported)\n"); return -ENOSYS; default: diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index cd1fe78..d34c2af 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -483,7 +483,7 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct usb_usbvision *usbvision = video_drvdata(file); int err_code; @@ -595,11 +595,11 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int input) return 0; } -static int vidioc_s_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 usb_usbvision *usbvision = video_drvdata(file); - usbvision->tvnorm_id = *id; + usbvision->tvnorm_id = id; call_all(usbvision, core, s_std, usbvision->tvnorm_id); /* propagate the change to the decoder */ @@ -628,7 +628,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *vt) + const struct v4l2_tuner *vt) { struct usb_usbvision *usbvision = video_drvdata(file); @@ -657,7 +657,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) + const struct v4l2_frequency *freq) { struct usb_usbvision *usbvision = video_drvdata(file); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 61e28de..a2f4501 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1487,7 +1487,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); if (!(step & value)) - return -ERANGE; + return -EINVAL; } break; diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 6c233a5..cd962be 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -149,6 +149,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; queue->queue.mem_ops = &vb2_vmalloc_memops; + queue->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(&queue->queue); if (ret) return ret; diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 976d029..8c05565 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -67,6 +67,7 @@ config VIDEOBUF2_MEMOPS config VIDEOBUF2_DMA_CONTIG tristate + depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS select DMA_SHARED_BUFFER diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 768aaf6..aa50c46 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -9,6 +9,9 @@ videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ ifeq ($(CONFIG_COMPAT),y) videodev-objs += v4l2-compat-ioctl32.o endif +ifeq ($(CONFIG_OF),y) + videodev-objs += v4l2-of.o +endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index b5a8aac..ddc9379 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -132,7 +132,7 @@ struct tuner { bool standby; /* Standby mode */ unsigned int type; /* chip type id */ - unsigned int config; + void *config; const char *name; }; @@ -218,26 +218,6 @@ static void fe_standby(struct dvb_frontend *fe) fe_tuner_ops->sleep(fe); } -static int fe_has_signal(struct dvb_frontend *fe) -{ - u16 strength = 0; - - if (fe->ops.tuner_ops.get_rf_strength) - fe->ops.tuner_ops.get_rf_strength(fe, &strength); - - return strength; -} - -static int fe_get_afc(struct dvb_frontend *fe) -{ - s32 afc = 0; - - if (fe->ops.tuner_ops.get_afc) - fe->ops.tuner_ops.get_afc(fe, &afc); - - return 0; -} - static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) { struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; @@ -253,11 +233,9 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) static void tuner_status(struct dvb_frontend *fe); -static struct analog_demod_ops tuner_analog_ops = { +static const struct analog_demod_ops tuner_analog_ops = { .set_params = fe_set_params, .standby = fe_standby, - .has_signal = fe_has_signal, - .get_afc = fe_get_afc, .set_config = fe_set_config, .tuner_status = tuner_status }; @@ -272,9 +250,8 @@ static struct analog_demod_ops tuner_analog_ops = { * @c: i2c_client descriptoy * @type: type of the tuner (e. g. tuner number) * @new_mode_mask: Indicates if tuner supports TV and/or Radio - * @new_config: an optional parameter ranging from 0-255 used by - a few tuners to adjust an internal parameter, - like LNA mode + * @new_config: an optional parameter used by a few tuners to adjust + internal parameters, like LNA mode * @tuner_callback: an optional function to be called when switching * to analog mode * @@ -282,7 +259,7 @@ static struct analog_demod_ops tuner_analog_ops = { * by tun_setup structure. It contains several per-tuner initialization "magic" */ static void set_type(struct i2c_client *c, unsigned int type, - unsigned int new_mode_mask, unsigned int new_config, + unsigned int new_mode_mask, void *new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); @@ -297,8 +274,7 @@ static void set_type(struct i2c_client *c, unsigned int type, } t->type = type; - /* prevent invalid config values */ - t->config = new_config < 256 ? new_config : 0; + t->config = new_config; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -316,11 +292,8 @@ static void set_type(struct i2c_client *c, unsigned int type, break; case TUNER_PHILIPS_TDA8290: { - struct tda829x_config cfg = { - .lna_cfg = t->config, - }; if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter, - t->i2c->addr, &cfg)) + t->i2c->addr, t->config)) goto attach_failed; break; } @@ -409,7 +382,6 @@ static void set_type(struct i2c_client *c, unsigned int type, case TUNER_NXP_TDA18271: { struct tda18271_config cfg = { - .config = t->config, .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; @@ -453,6 +425,11 @@ static void set_type(struct i2c_client *c, unsigned int type, memcpy(analog_ops, &tuner_analog_ops, sizeof(struct analog_demod_ops)); + if (fe_tuner_ops->get_rf_strength) + analog_ops->has_signal = fe_tuner_ops->get_rf_strength; + if (fe_tuner_ops->get_afc) + analog_ops->get_afc = fe_tuner_ops->get_afc; + } else { t->name = analog_ops->info.name; } @@ -506,7 +483,7 @@ static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner *t = to_tuner(sd); struct i2c_client *c = v4l2_get_subdevdata(sd); - tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=%p\n", tun_setup->type, tun_setup->addr, tun_setup->mode_mask, @@ -1073,9 +1050,12 @@ static void tuner_status(struct dvb_frontend *fe) if (tuner_status & TUNER_STATUS_STEREO) tuner_info("Stereo: yes\n"); } - if (analog_ops->has_signal) - tuner_info("Signal strength: %d\n", - analog_ops->has_signal(fe)); + if (analog_ops->has_signal) { + u16 signal; + + if (!analog_ops->has_signal(fe, &signal)) + tuner_info("Signal strength: %hu\n", signal); + } } /* @@ -1134,7 +1114,7 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +static int tuner_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) { struct tuner *t = to_tuner(sd); @@ -1193,9 +1173,13 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (check_mode(t, vt->type) == -EINVAL) return 0; if (vt->type == t->mode && analog_ops->get_afc) - vt->afc = analog_ops->get_afc(&t->fe); - if (analog_ops->has_signal) - vt->signal = analog_ops->has_signal(&t->fe); + analog_ops->get_afc(&t->fe, &vt->afc); + if (vt->type == t->mode && analog_ops->has_signal) { + u16 signal = (u16)vt->signal; + + if (!analog_ops->has_signal(&t->fe, &signal)) + vt->signal = signal; + } if (vt->type != V4L2_TUNER_RADIO) { vt->capability |= V4L2_TUNER_CAP_NORM; vt->rangelow = tv_range[0] * 16; @@ -1233,7 +1217,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) * Note: vt->type should be initialized before calling it. * This is done by either video_ioctl2 or by the bridge driver. */ -static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int tuner_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) { struct tuner *t = to_tuner(sd); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index aa044f4..3fed63f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -230,7 +230,7 @@ EXPORT_SYMBOL(v4l2_ctrl_next); int v4l2_chip_match_host(const struct v4l2_dbg_match *match) { switch (match->type) { - case V4L2_CHIP_MATCH_HOST: + case V4L2_CHIP_MATCH_BRIDGE: return match->addr == 0; default: return 0; @@ -251,12 +251,11 @@ int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match if (c->driver == NULL || c->driver->driver.name == NULL) return 0; len = strlen(c->driver->driver.name); - /* legacy drivers have a ' suffix, don't try to match that */ - if (len && c->driver->driver.name[len - 1] == '\'') - len--; return len && !strncmp(c->driver->driver.name, match->name, len); case V4L2_CHIP_MATCH_I2C_ADDR: return c->addr == match->addr; + case V4L2_CHIP_MATCH_SUBDEV: + return 1; default: return 0; } @@ -551,53 +550,6 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, EXPORT_SYMBOL_GPL(v4l_bound_align_image); /** - * v4l_fill_dv_preset_info - fill description of a digital video preset - * @preset - preset value - * @info - pointer to struct v4l2_dv_enum_preset - * - * drivers can use this helper function to fill description of dv preset - * in info. - */ -int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info) -{ - static const struct v4l2_dv_preset_info { - u16 width; - u16 height; - const char *name; - } dv_presets[] = { - { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */ - { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */ - { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */ - { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */ - { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */ - { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */ - { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */ - { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */ - { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */ - { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */ - { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */ - { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */ - { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */ - { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */ - { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */ - { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */ - { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */ - { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */ - { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */ - }; - - if (info == NULL || preset >= ARRAY_SIZE(dv_presets)) - return -EINVAL; - - info->preset = preset; - info->width = dv_presets[preset].width; - info->height = dv_presets[preset].height; - strlcpy(info->name, dv_presets[preset].name, sizeof(info->name)); - return 0; -} -EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info); - -/** * v4l_match_dv_timings - check if two timings match * @t1 - compare this v4l2_dv_timings struct... * @t2 - with this struct. diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 7157af3..f129551 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -1076,10 +1076,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: case VIDIOC_S_HW_FREQ_SEEK: - case VIDIOC_ENUM_DV_PRESETS: - case VIDIOC_S_DV_PRESET: - case VIDIOC_G_DV_PRESET: - case VIDIOC_QUERY_DV_PRESET: case VIDIOC_S_DV_TIMINGS: case VIDIOC_G_DV_TIMINGS: case VIDIOC_DQEVENT: diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 6b28b58..ebb8e48 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -234,6 +234,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Average", "Center Weighted", "Spot", + "Matrix", NULL }; static const char * const camera_auto_focus_range[] = { @@ -297,8 +298,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Text", NULL }; - static const char * const tune_preemphasis[] = { - "No Preemphasis", + static const char * const tune_emphasis[] = { + "None", "50 Microseconds", "75 Microseconds", NULL, @@ -508,7 +509,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) case V4L2_CID_SCENE_MODE: return scene_mode; case V4L2_CID_TUNE_PREEMPHASIS: - return tune_preemphasis; + return tune_emphasis; + case V4L2_CID_TUNE_DEEMPHASIS: + return tune_emphasis; case V4L2_CID_FLASH_LED_MODE: return flash_led_mode; case V4L2_CID_FLASH_STROBE_SOURCE: @@ -695,6 +698,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; + case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -799,6 +803,9 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_DV_RX_POWER_PRESENT: return "Power Present"; case V4L2_CID_DV_RX_RGB_RANGE: return "Rx RGB Quantization Range"; + case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls"; + case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis"; + case V4L2_CID_RDS_RECEPTION: return "RDS Reception"; default: return NULL; } @@ -844,8 +851,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: case V4L2_CID_WIDE_DYNAMIC_RANGE: case V4L2_CID_IMAGE_STABILIZATION: + case V4L2_CID_RDS_RECEPTION: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -904,6 +913,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_TX_RGB_RANGE: case V4L2_CID_DV_RX_RGB_RANGE: case V4L2_CID_TEST_PATTERN: + case V4L2_CID_TUNE_DEEMPHASIS: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -926,6 +936,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_IMAGE_SOURCE_CLASS: case V4L2_CID_IMAGE_PROC_CLASS: case V4L2_CID_DV_CLASS: + case V4L2_CID_FM_RX_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1362,11 +1373,13 @@ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) } /* Initialize the handler */ -int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, - unsigned nr_of_controls_hint) +int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, + unsigned nr_of_controls_hint, + struct lock_class_key *key, const char *name) { hdl->lock = &hdl->_lock; mutex_init(hdl->lock); + lockdep_set_class_and_name(hdl->lock, key, name); INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrl_refs); hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; @@ -1375,7 +1388,7 @@ int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, hdl->error = hdl->buckets ? 0 : -ENOMEM; return hdl->error; } -EXPORT_SYMBOL(v4l2_ctrl_handler_init); +EXPORT_SYMBOL(v4l2_ctrl_handler_init_class); /* Free all controls and control refs */ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index de1e9ab..5923c5d 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -592,8 +592,9 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); #ifdef CONFIG_VIDEO_ADV_DEBUG - SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register); - SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register); + set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls); + set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls); + set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls); #endif SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident); /* yes, really vidioc_subscribe_event */ @@ -685,7 +686,6 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); - SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset); SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings); } if (is_tx) { @@ -708,9 +708,6 @@ static void determine_valid_ioctls(struct video_device *vdev) (ops->vidioc_g_std || vdev->current_norm))) set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); - SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets); - SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset); - SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset); SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aa6e7c7..f81bda1 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -35,6 +35,8 @@ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) +#define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls) + struct std_descr { v4l2_std_id std; const char *descr; @@ -167,9 +169,11 @@ static void v4l_print_querycap(const void *arg, bool write_only) { const struct v4l2_capability *p = arg; - pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, " + pr_cont("driver=%.*s, card=%.*s, bus=%.*s, version=0x%08x, " "capabilities=0x%08x, device_caps=0x%08x\n", - p->driver, p->card, p->bus_info, + (int)sizeof(p->driver), p->driver, + (int)sizeof(p->card), p->card, + (int)sizeof(p->bus_info), p->bus_info, p->version, p->capabilities, p->device_caps); } @@ -177,20 +181,21 @@ static void v4l_print_enuminput(const void *arg, bool write_only) { const struct v4l2_input *p = arg; - pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, " + pr_cont("index=%u, name=%.*s, type=%u, audioset=0x%x, tuner=%u, " "std=0x%08Lx, status=0x%x, capabilities=0x%x\n", - p->index, p->name, p->type, p->audioset, p->tuner, - (unsigned long long)p->std, p->status, p->capabilities); + p->index, (int)sizeof(p->name), p->name, p->type, p->audioset, + p->tuner, (unsigned long long)p->std, p->status, + p->capabilities); } static void v4l_print_enumoutput(const void *arg, bool write_only) { const struct v4l2_output *p = arg; - pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, " + pr_cont("index=%u, name=%.*s, type=%u, audioset=0x%x, " "modulator=%u, std=0x%08Lx, capabilities=0x%x\n", - p->index, p->name, p->type, p->audioset, p->modulator, - (unsigned long long)p->std, p->capabilities); + p->index, (int)sizeof(p->name), p->name, p->type, p->audioset, + p->modulator, (unsigned long long)p->std, p->capabilities); } static void v4l_print_audio(const void *arg, bool write_only) @@ -200,8 +205,9 @@ static void v4l_print_audio(const void *arg, bool write_only) if (write_only) pr_cont("index=%u, mode=0x%x\n", p->index, p->mode); else - pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", - p->index, p->name, p->capability, p->mode); + pr_cont("index=%u, name=%.*s, capability=0x%x, mode=0x%x\n", + p->index, (int)sizeof(p->name), p->name, + p->capability, p->mode); } static void v4l_print_audioout(const void *arg, bool write_only) @@ -211,21 +217,22 @@ static void v4l_print_audioout(const void *arg, bool write_only) if (write_only) pr_cont("index=%u\n", p->index); else - pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", - p->index, p->name, p->capability, p->mode); + pr_cont("index=%u, name=%.*s, capability=0x%x, mode=0x%x\n", + p->index, (int)sizeof(p->name), p->name, + p->capability, p->mode); } static void v4l_print_fmtdesc(const void *arg, bool write_only) { const struct v4l2_fmtdesc *p = arg; - pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n", + pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%.*s'\n", p->index, prt_names(p->type, v4l2_type_names), p->flags, (p->pixelformat & 0xff), (p->pixelformat >> 8) & 0xff, (p->pixelformat >> 16) & 0xff, (p->pixelformat >> 24) & 0xff, - p->description); + (int)sizeof(p->description), p->description); } static void v4l_print_format(const void *arg, bool write_only) @@ -348,9 +355,9 @@ static void v4l_print_modulator(const void *arg, bool write_only) if (write_only) pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans); else - pr_cont("index=%u, name=%s, capability=0x%x, " + pr_cont("index=%u, name=%.*s, capability=0x%x, " "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n", - p->index, p->name, p->capability, + p->index, (int)sizeof(p->name), p->name, p->capability, p->rangelow, p->rangehigh, p->txsubchans); } @@ -361,10 +368,10 @@ static void v4l_print_tuner(const void *arg, bool write_only) if (write_only) pr_cont("index=%u, audmode=%u\n", p->index, p->audmode); else - pr_cont("index=%u, name=%s, type=%u, capability=0x%x, " + pr_cont("index=%u, name=%.*s, type=%u, capability=0x%x, " "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, " "rxsubchans=0x%x, audmode=%u\n", - p->index, p->name, p->type, + p->index, (int)sizeof(p->name), p->name, p->type, p->capability, p->rangelow, p->rangehigh, p->signal, p->afc, p->rxsubchans, p->audmode); @@ -382,9 +389,9 @@ static void v4l_print_standard(const void *arg, bool write_only) { const struct v4l2_standard *p = arg; - pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, " + pr_cont("index=%u, id=0x%Lx, name=%.*s, fps=%u/%u, " "framelines=%u\n", p->index, - (unsigned long long)p->id, p->name, + (unsigned long long)p->id, (int)sizeof(p->name), p->name, p->frameperiod.numerator, p->frameperiod.denominator, p->framelines); @@ -504,9 +511,9 @@ static void v4l_print_queryctrl(const void *arg, bool write_only) { const struct v4l2_queryctrl *p = arg; - pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, " + pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%d/%d, " "step=%d, default=%d, flags=0x%08x\n", - p->id, p->type, p->name, + p->id, p->type, (int)sizeof(p->name), p->name, p->minimum, p->maximum, p->step, p->default_value, p->flags); } @@ -623,39 +630,39 @@ static void v4l_print_dbg_chip_ident(const void *arg, bool write_only) pr_cont("type=%u, ", p->match.type); if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) - pr_cont("name=%s, ", p->match.name); + pr_cont("name=%.*s, ", + (int)sizeof(p->match.name), p->match.name); else pr_cont("addr=%u, ", p->match.addr); pr_cont("chip_ident=%u, revision=0x%x\n", p->ident, p->revision); } -static void v4l_print_dbg_register(const void *arg, bool write_only) +static void v4l_print_dbg_chip_info(const void *arg, bool write_only) { - const struct v4l2_dbg_register *p = arg; + const struct v4l2_dbg_chip_info *p = arg; pr_cont("type=%u, ", p->match.type); if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) - pr_cont("name=%s, ", p->match.name); + pr_cont("name=%.*s, ", + (int)sizeof(p->match.name), p->match.name); else pr_cont("addr=%u, ", p->match.addr); - pr_cont("reg=0x%llx, val=0x%llx\n", - p->reg, p->val); + pr_cont("name=%.*s\n", (int)sizeof(p->name), p->name); } -static void v4l_print_dv_enum_presets(const void *arg, bool write_only) -{ - const struct v4l2_dv_enum_preset *p = arg; - - pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n", - p->index, p->preset, p->name, p->width, p->height); -} - -static void v4l_print_dv_preset(const void *arg, bool write_only) +static void v4l_print_dbg_register(const void *arg, bool write_only) { - const struct v4l2_dv_preset *p = arg; + const struct v4l2_dbg_register *p = arg; - pr_cont("preset=%u\n", p->preset); + pr_cont("type=%u, ", p->match.type); + if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) + pr_cont("name=%.*s, ", + (int)sizeof(p->match.name), p->match.name); + else + pr_cont("addr=%u, ", p->match.addr); + pr_cont("reg=0x%llx, val=0x%llx\n", + p->reg, p->val); } static void v4l_print_dv_timings(const void *arg, bool write_only) @@ -997,20 +1004,17 @@ static int v4l_s_priority(const struct v4l2_ioctl_ops *ops, static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vfd = video_devdata(file); struct v4l2_input *p = arg; /* - * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS & + * We set the flags for CAP_DV_TIMINGS & * CAP_STD here based on ioctl handler provided by the * driver. If the driver doesn't support these * for a specific input, it must override these flags. */ - if (ops->vidioc_s_std) + if (is_valid_ioctl(vfd, VIDIOC_S_STD)) p->capabilities |= V4L2_IN_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_IN_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_IN_CAP_DV_TIMINGS; return ops->vidioc_enum_input(file, fh, p); } @@ -1018,20 +1022,17 @@ static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vfd = video_devdata(file); struct v4l2_output *p = arg; /* - * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS & + * We set the flags for CAP_DV_TIMINGS & * CAP_STD here based on ioctl handler provided by the * driver. If the driver doesn't support these * for a specific output, it must override these flags. */ - if (ops->vidioc_s_std) + if (is_valid_ioctl(vfd, VIDIOC_S_STD)) p->capabilities |= V4L2_OUT_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_OUT_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_OUT_CAP_DV_TIMINGS; return ops->vidioc_enum_output(file, fh, p); } @@ -1316,7 +1317,7 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct video_device *vfd = video_devdata(file); - struct v4l2_frequency *p = arg; + const struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; type = (vfd->vfl_type == VFL_TYPE_RADIO) ? @@ -1383,15 +1384,15 @@ static int v4l_s_std(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct video_device *vfd = video_devdata(file); - v4l2_std_id *id = arg, norm; + v4l2_std_id id = *(v4l2_std_id *)arg, norm; int ret; - norm = (*id) & vfd->tvnorms; + norm = id & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ return -EINVAL; /* Calls the specific handler */ - ret = ops->vidioc_s_std(file, fh, &norm); + ret = ops->vidioc_s_std(file, fh, norm); /* Updates standard information */ if (ret >= 0) @@ -1513,7 +1514,7 @@ static int v4l_g_parm(const struct v4l2_ioctl_ops *ops, p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; p->parm.capture.readbuffers = 2; - if (ops->vidioc_g_std) + if (is_valid_ioctl(vfd, VIDIOC_G_STD) && ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, &std); if (ret == 0) v4l2_video_std_frame_period(std, @@ -1792,10 +1793,23 @@ static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops, { #ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; + struct video_device *vfd = video_devdata(file); + struct v4l2_subdev *sd; + int idx = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return ops->vidioc_g_register(file, fh, p); + if (p->match.type == V4L2_CHIP_MATCH_SUBDEV) { + if (vfd->v4l2_dev == NULL) + return -EINVAL; + v4l2_device_for_each_subdev(sd, vfd->v4l2_dev) + if (p->match.addr == idx++) + return v4l2_subdev_call(sd, core, g_register, p); + return -EINVAL; + } + if (ops->vidioc_g_register) + return ops->vidioc_g_register(file, fh, p); + return -EINVAL; #else return -ENOTTY; #endif @@ -1805,11 +1819,24 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { #ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register *p = arg; + const struct v4l2_dbg_register *p = arg; + struct video_device *vfd = video_devdata(file); + struct v4l2_subdev *sd; + int idx = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return ops->vidioc_s_register(file, fh, p); + if (p->match.type == V4L2_CHIP_MATCH_SUBDEV) { + if (vfd->v4l2_dev == NULL) + return -EINVAL; + v4l2_device_for_each_subdev(sd, vfd->v4l2_dev) + if (p->match.addr == idx++) + return v4l2_subdev_call(sd, core, s_register, p); + return -EINVAL; + } + if (ops->vidioc_s_register) + return ops->vidioc_s_register(file, fh, p); + return -EINVAL; #else return -ENOTTY; #endif @@ -1822,9 +1849,59 @@ static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops, p->ident = V4L2_IDENT_NONE; p->revision = 0; + if (p->match.type == V4L2_CHIP_MATCH_SUBDEV) + return -EINVAL; return ops->vidioc_g_chip_ident(file, fh, p); } +static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct video_device *vfd = video_devdata(file); + struct v4l2_dbg_chip_info *p = arg; + struct v4l2_subdev *sd; + int idx = 0; + + switch (p->match.type) { + case V4L2_CHIP_MATCH_BRIDGE: + if (ops->vidioc_s_register) + p->flags |= V4L2_CHIP_FL_WRITABLE; + if (ops->vidioc_g_register) + p->flags |= V4L2_CHIP_FL_READABLE; + if (vfd->v4l2_dev) + strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name)); + else if (vfd->parent) + strlcpy(p->name, vfd->parent->driver->name, sizeof(p->name)); + else + strlcpy(p->name, "bridge", sizeof(p->name)); + if (ops->vidioc_g_chip_info) + return ops->vidioc_g_chip_info(file, fh, arg); + if (p->match.addr) + return -EINVAL; + return 0; + + case V4L2_CHIP_MATCH_SUBDEV: + if (vfd->v4l2_dev == NULL) + break; + v4l2_device_for_each_subdev(sd, vfd->v4l2_dev) { + if (p->match.addr != idx++) + continue; + if (sd->ops->core && sd->ops->core->s_register) + p->flags |= V4L2_CHIP_FL_WRITABLE; + if (sd->ops->core && sd->ops->core->g_register) + p->flags |= V4L2_CHIP_FL_READABLE; + strlcpy(p->name, sd->name, sizeof(p->name)); + return 0; + } + break; + } + return -EINVAL; +#else + return -ENOTTY; +#endif +} + static int v4l_dqevent(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1873,7 +1950,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, return -EINVAL; if (ops->vidioc_enum_freq_bands) return ops->vidioc_enum_freq_bands(file, fh, p); - if (ops->vidioc_g_tuner) { + if (is_valid_ioctl(vfd, VIDIOC_G_TUNER)) { struct v4l2_tuner t = { .index = p->tuner, .type = type, @@ -1891,7 +1968,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; return 0; } - if (ops->vidioc_g_modulator) { + if (is_valid_ioctl(vfd, VIDIOC_G_MODULATOR)) { struct v4l2_modulator m = { .index = p->tuner, }; @@ -2028,10 +2105,6 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0), IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0), - IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0), - IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0), IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), @@ -2043,6 +2116,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), + IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -2147,11 +2221,6 @@ static long __video_do_ioctl(struct file *file, } write_only = _IOC_DIR(cmd) == _IOC_WRITE; - if (write_only && debug > V4L2_DEBUG_IOCTL) { - v4l_printk_ioctl(video_device_node_name(vfd), cmd); - pr_cont(": "); - info->debug(arg, write_only); - } if (info->flags & INFO_FL_STD) { typedef int (*vidioc_op)(struct file *file, void *fh, void *p); const void *p = vfd->ioctl_ops; @@ -2170,16 +2239,10 @@ static long __video_do_ioctl(struct file *file, done: if (debug) { - if (write_only && debug > V4L2_DEBUG_IOCTL) { - if (ret < 0) - printk(KERN_DEBUG "%s: error %ld\n", - video_device_node_name(vfd), ret); - return ret; - } v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) - pr_cont(": error %ld\n", ret); - else if (debug == V4L2_DEBUG_IOCTL) + pr_cont(": error %ld", ret); + if (debug == V4L2_DEBUG_IOCTL) pr_cont("\n"); else if (_IOC_DIR(cmd) == _IOC_NONE) info->debug(arg, write_only); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index da99cf7..66f599f 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -230,12 +230,15 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) dprintk("No input buffers available\n"); return; } + spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags); if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags); spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); dprintk("No output buffers available\n"); return; } + spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags); spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); if (m2m_dev->m2m_ops->job_ready @@ -405,10 +408,35 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { - struct vb2_queue *vq; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_m2m_queue_ctx *q_ctx; + unsigned long flags_job, flags; + int ret; - vq = v4l2_m2m_get_vq(m2m_ctx, type); - return vb2_streamoff(vq, type); + q_ctx = get_queue_ctx(m2m_ctx, type); + ret = vb2_streamoff(&q_ctx->q, type); + if (ret) + return ret; + + m2m_dev = m2m_ctx->m2m_dev; + spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job); + /* We should not be scheduled anymore, since we're dropping a queue. */ + INIT_LIST_HEAD(&m2m_ctx->queue); + m2m_ctx->job_flags = 0; + + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + /* Drop queue, since streamoff returns device to the same state as after + * calling reqbufs. */ + INIT_LIST_HEAD(&q_ctx->rdy_queue); + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + + if (m2m_dev->curr_ctx == m2m_ctx) { + m2m_dev->curr_ctx = NULL; + wake_up(&m2m_ctx->finished); + } + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c new file mode 100644 index 0000000..aa59639 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -0,0 +1,266 @@ +/* + * V4L2 OF binding parsing library + * + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Copyright (C) 2012 Renesas Electronics Corp. + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <media/v4l2-of.h> + +static void v4l2_of_parse_csi_bus(const struct device_node *node, + struct v4l2_of_endpoint *endpoint) +{ + struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2; + u32 data_lanes[ARRAY_SIZE(bus->data_lanes)]; + struct property *prop; + bool have_clk_lane = false; + unsigned int flags = 0; + u32 v; + + prop = of_find_property(node, "data-lanes", NULL); + if (prop) { + const __be32 *lane = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(data_lanes); i++) { + lane = of_prop_next_u32(prop, lane, &data_lanes[i]); + if (!lane) + break; + } + bus->num_data_lanes = i; + while (i--) + bus->data_lanes[i] = data_lanes[i]; + } + + if (!of_property_read_u32(node, "clock-lanes", &v)) { + bus->clock_lane = v; + have_clk_lane = true; + } + + if (of_get_property(node, "clock-noncontinuous", &v)) + flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + else if (have_clk_lane || bus->num_data_lanes > 0) + flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + bus->flags = flags; + endpoint->bus_type = V4L2_MBUS_CSI2; +} + +static void v4l2_of_parse_parallel_bus(const struct device_node *node, + struct v4l2_of_endpoint *endpoint) +{ + struct v4l2_of_bus_parallel *bus = &endpoint->bus.parallel; + unsigned int flags = 0; + u32 v; + + if (!of_property_read_u32(node, "hsync-active", &v)) + flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : + V4L2_MBUS_HSYNC_ACTIVE_LOW; + + if (!of_property_read_u32(node, "vsync-active", &v)) + flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : + V4L2_MBUS_VSYNC_ACTIVE_LOW; + + if (!of_property_read_u32(node, "pclk-sample", &v)) + flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : + V4L2_MBUS_PCLK_SAMPLE_FALLING; + + if (!of_property_read_u32(node, "field-even-active", &v)) + flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : + V4L2_MBUS_FIELD_EVEN_LOW; + if (flags) + endpoint->bus_type = V4L2_MBUS_PARALLEL; + else + endpoint->bus_type = V4L2_MBUS_BT656; + + if (!of_property_read_u32(node, "data-active", &v)) + flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : + V4L2_MBUS_DATA_ACTIVE_LOW; + + if (of_get_property(node, "slave-mode", &v)) + flags |= V4L2_MBUS_SLAVE; + else + flags |= V4L2_MBUS_MASTER; + + if (!of_property_read_u32(node, "bus-width", &v)) + bus->bus_width = v; + + if (!of_property_read_u32(node, "data-shift", &v)) + bus->data_shift = v; + + bus->flags = flags; + +} + +/** + * v4l2_of_parse_endpoint() - parse all endpoint node properties + * @node: pointer to endpoint device_node + * @endpoint: pointer to the V4L2 OF endpoint data structure + * + * All properties are optional. If none are found, we don't set any flags. + * This means the port has a static configuration and no properties have + * to be specified explicitly. + * If any properties that identify the bus as parallel are found and + * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise + * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the + * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. + * The caller should hold a reference to @node. + */ +void v4l2_of_parse_endpoint(const struct device_node *node, + struct v4l2_of_endpoint *endpoint) +{ + struct device_node *port_node = of_get_parent(node); + + memset(endpoint, 0, offsetof(struct v4l2_of_endpoint, head)); + + endpoint->local_node = node; + /* + * It doesn't matter whether the two calls below succeed. + * If they don't then the default value 0 is used. + */ + of_property_read_u32(port_node, "reg", &endpoint->port); + of_property_read_u32(node, "reg", &endpoint->id); + + v4l2_of_parse_csi_bus(node, endpoint); + /* + * Parse the parallel video bus properties only if none + * of the MIPI CSI-2 specific properties were found. + */ + if (endpoint->bus.mipi_csi2.flags == 0) + v4l2_of_parse_parallel_bus(node, endpoint); + + of_node_put(port_node); +} +EXPORT_SYMBOL(v4l2_of_parse_endpoint); + +/** + * v4l2_of_get_next_endpoint() - get next endpoint node + * @parent: pointer to the parent device node + * @prev: previous endpoint node, or NULL to get first + * + * Return: An 'endpoint' node pointer with refcount incremented. Refcount + * of the passed @prev node is not decremented, the caller have to use + * of_node_put() on it when done. + */ +struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, + struct device_node *prev) +{ + struct device_node *endpoint; + struct device_node *port = NULL; + + if (!parent) + return NULL; + + if (!prev) { + struct device_node *node; + /* + * It's the first call, we have to find a port subnode + * within this node or within an optional 'ports' node. + */ + node = of_get_child_by_name(parent, "ports"); + if (node) + parent = node; + + for_each_child_of_node(parent, node) { + if (!of_node_cmp(node->name, "port")) { + port = node; + break; + } + } + if (port) { + /* Found a port, get an endpoint. */ + endpoint = of_get_next_child(port, NULL); + of_node_put(port); + } else { + endpoint = NULL; + } + + if (!endpoint) + pr_err("%s(): no endpoint nodes specified for %s\n", + __func__, parent->full_name); + } else { + port = of_get_parent(prev); + if (!port) + /* Hm, has someone given us the root node ?... */ + return NULL; + + /* Avoid dropping prev node refcount to 0. */ + of_node_get(prev); + endpoint = of_get_next_child(port, prev); + if (endpoint) { + of_node_put(port); + return endpoint; + } + + /* No more endpoints under this port, try the next one. */ + do { + port = of_get_next_child(parent, port); + if (!port) + return NULL; + } while (of_node_cmp(port->name, "port")); + + /* Pick up the first endpoint in this port. */ + endpoint = of_get_next_child(port, NULL); + of_node_put(port); + } + + return endpoint; +} +EXPORT_SYMBOL(v4l2_of_get_next_endpoint); + +/** + * v4l2_of_get_remote_port_parent() - get remote port's parent node + * @node: pointer to a local endpoint device_node + * + * Return: Remote device node associated with remote endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *v4l2_of_get_remote_port_parent( + const struct device_node *node) +{ + struct device_node *np; + unsigned int depth; + + /* Get remote endpoint node. */ + np = of_parse_phandle(node, "remote-endpoint", 0); + + /* Walk 3 levels up only if there is 'ports' node. */ + for (depth = 3; depth && np; depth--) { + np = of_get_next_parent(np); + if (depth == 2 && of_node_cmp(np->name, "ports")) + break; + } + return np; +} +EXPORT_SYMBOL(v4l2_of_get_remote_port_parent); + +/** + * v4l2_of_get_remote_port() - get remote port node + * @node: pointer to a local endpoint device_node + * + * Return: Remote port node associated with remote endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *v4l2_of_get_remote_port(const struct device_node *node) +{ + struct device_node *np; + + /* Get remote endpoint node. */ + np = of_parse_phandle(node, "remote-endpoint", 0); + if (!np) + return NULL; + return of_get_parent(np); +} +EXPORT_SYMBOL(v4l2_of_get_remote_port); diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c index 3a43ba0..67f572c 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -27,7 +27,6 @@ struct videobuf_dma_contig_memory { u32 magic; void *vaddr; dma_addr_t dma_handle; - bool cached; unsigned long size; }; @@ -43,26 +42,8 @@ static int __videobuf_dc_alloc(struct device *dev, unsigned long size, gfp_t flags) { mem->size = size; - if (mem->cached) { - mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); - if (mem->vaddr) { - int err; - - mem->dma_handle = dma_map_single(dev, mem->vaddr, - mem->size, - DMA_FROM_DEVICE); - err = dma_mapping_error(dev, mem->dma_handle); - if (err) { - dev_err(dev, "dma_map_single failed\n"); - - free_pages_exact(mem->vaddr, mem->size); - mem->vaddr = NULL; - return err; - } - } - } else - mem->vaddr = dma_alloc_coherent(dev, mem->size, - &mem->dma_handle, flags); + mem->vaddr = dma_alloc_coherent(dev, mem->size, + &mem->dma_handle, flags); if (!mem->vaddr) { dev_err(dev, "memory alloc size %ld failed\n", mem->size); @@ -77,14 +58,7 @@ static int __videobuf_dc_alloc(struct device *dev, static void __videobuf_dc_free(struct device *dev, struct videobuf_dma_contig_memory *mem) { - if (mem->cached) { - if (!mem->vaddr) - return; - dma_unmap_single(dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - free_pages_exact(mem->vaddr, mem->size); - } else - dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } @@ -234,7 +208,7 @@ out_up: return ret; } -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -244,22 +218,11 @@ static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) vb->priv = ((char *)vb) + size; mem = vb->priv; mem->magic = MAGIC_DC_MEM; - mem->cached = cached; } return vb; } -static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) -{ - return __videobuf_alloc_vb(size, false); -} - -static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) -{ - return __videobuf_alloc_vb(size, true); -} - static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -310,19 +273,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_buffer *buf, struct vm_area_struct *vma) @@ -331,8 +281,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_mapping *map; int retval; unsigned long size; - unsigned long pos, start = vma->vm_start; - struct page *page; dev_dbg(q->dev, "%s\n", __func__); @@ -359,43 +307,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - if (!mem->cached) { - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + retval = remap_pfn_range(vma, vma->vm_start, + mem->dma_handle >> PAGE_SHIFT, size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", - retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; - } - } else { - pos = (unsigned long)mem->vaddr; - - while (size > 0) { - page = virt_to_page((void *)pos); - if (NULL == page) { - dev_err(q->dev, "mmap: virt_to_page failed\n"); - __videobuf_dc_free(q->dev, mem); - goto error; - } - retval = vm_insert_page(vma, start, page); - if (retval) { - dev_err(q->dev, "mmap: insert failed with error %d\n", - retval); - __videobuf_dc_free(q->dev, mem); - goto error; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } + if (retval) { + dev_err(q->dev, "mmap: remap failed with error %d. ", + retval); + dma_free_coherent(q->dev, mem->size, + mem->vaddr, mem->dma_handle); + goto error; } vma->vm_ops = &videobuf_vm_ops; @@ -417,17 +338,8 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_uncached, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, -}; - -static struct videobuf_qtype_ops qops_cached = { - .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_cached, + .alloc_vb = __videobuf_alloc, .iolock = __videobuf_iolock, - .sync = __videobuf_sync, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = __videobuf_to_vaddr, }; @@ -447,20 +359,6 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, } EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); -void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, struct mutex *ext_lock) -{ - videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &qops_cached, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); - dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index db1235d..7d833ee 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -54,10 +54,15 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) void *mem_priv; int plane; - /* Allocate memory for all planes in this buffer */ + /* + * Allocate memory for all planes in this buffer + * NOTE: mmapped areas should be page aligned + */ for (plane = 0; plane < vb->num_planes; ++plane) { + unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]); + mem_priv = call_memop(q, alloc, q->alloc_ctx[plane], - q->plane_sizes[plane]); + size, q->gfp_flags); if (IS_ERR_OR_NULL(mem_priv)) goto free; @@ -403,7 +408,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) * Clear any buffer state related flags. */ b->flags &= ~V4L2_BUFFER_MASK_FLAGS; - b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->flags |= q->timestamp_type; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -855,7 +860,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) return; dprintk(4, "Done processing on buffer %d, state: %d\n", - vb->v4l2_buf.index, vb->state); + vb->v4l2_buf.index, state); /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) @@ -1852,6 +1857,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) struct vb2_buffer *vb; unsigned int buffer, plane; int ret; + unsigned long length; if (q->memory != V4L2_MEMORY_MMAP) { dprintk(1, "Queue is not currently set up for mmap\n"); @@ -1886,6 +1892,18 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) vb = q->bufs[buffer]; + /* + * MMAP requires page_aligned buffers. + * The buffer length was page_aligned at __vb2_buf_mem_alloc(), + * so, we need to do the same here. + */ + length = PAGE_ALIGN(vb->v4l2_planes[plane].length); + if (length < (vma->vm_end - vma->vm_start)) { + dprintk(1, + "MMAP invalid, as it would overflow buffer length\n"); + return -EINVAL; + } + ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma); if (ret) return ret; @@ -2039,9 +2057,13 @@ int vb2_queue_init(struct vb2_queue *q) WARN_ON(!q->type) || WARN_ON(!q->io_modes) || WARN_ON(!q->ops->queue_setup) || - WARN_ON(!q->ops->buf_queue)) + WARN_ON(!q->ops->buf_queue) || + WARN_ON(q->timestamp_type & ~V4L2_BUF_FLAG_TIMESTAMP_MASK)) return -EINVAL; + /* Warn that the driver should choose an appropriate timestamp type */ + WARN_ON(q->timestamp_type == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); + INIT_LIST_HEAD(&q->queued_list); INIT_LIST_HEAD(&q->done_list); spin_lock_init(&q->done_lock); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 10beaee..fd56f25 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -152,7 +152,7 @@ static void vb2_dc_put(void *buf_priv) kfree(buf); } -static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) +static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags) { struct vb2_dc_conf *conf = alloc_ctx; struct device *dev = conf->dev; @@ -162,10 +162,8 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) if (!buf) return ERR_PTR(-ENOMEM); - /* align image size to PAGE_SIZE */ - size = PAGE_ALIGN(size); - - buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL); + buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, + GFP_KERNEL | gfp_flags); if (!buf->vaddr) { dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size); kfree(buf); diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 25c3b36..16ae3dc 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -21,6 +21,15 @@ #include <media/videobuf2-memops.h> #include <media/videobuf2-dma-sg.h> +static int debug; +module_param(debug, int, 0644); + +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg); \ + } while (0) + struct vb2_dma_sg_buf { void *vaddr; struct page **pages; @@ -33,7 +42,7 @@ struct vb2_dma_sg_buf { static void vb2_dma_sg_put(void *buf_priv); -static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) +static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags) { struct vb2_dma_sg_buf *buf; int i; @@ -46,7 +55,8 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) buf->write = 0; buf->offset = 0; buf->sg_desc.size = size; - buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + /* size is already page aligned */ + buf->sg_desc.num_pages = size >> PAGE_SHIFT; buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist)); @@ -60,7 +70,8 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) goto fail_pages_array_alloc; for (i = 0; i < buf->sg_desc.num_pages; ++i) { - buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN); + buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_NOWARN | gfp_flags); if (NULL == buf->pages[i]) goto fail_pages_alloc; sg_set_page(&buf->sg_desc.sglist[i], @@ -73,7 +84,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) atomic_inc(&buf->refcount); - printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n", + dprintk(1, "%s: Allocated buffer of %d pages\n", __func__, buf->sg_desc.num_pages); return buf; @@ -96,7 +107,7 @@ static void vb2_dma_sg_put(void *buf_priv) int i = buf->sg_desc.num_pages; if (atomic_dec_and_test(&buf->refcount)) { - printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__, + dprintk(1, "%s: Freeing buffer of %d pages\n", __func__, buf->sg_desc.num_pages); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); @@ -162,7 +173,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, return buf; userptr_fail_get_user_pages: - printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n", + dprintk(1, "get_user_pages requested/got: %d/%d]\n", num_pages_from_user, buf->sg_desc.num_pages); while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); @@ -185,7 +196,7 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; int i = buf->sg_desc.num_pages; - printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n", + dprintk(1, "%s: Releasing userspace buffer of %d pages\n", __func__, buf->sg_desc.num_pages); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index a47fd4f..313d977 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -35,11 +35,11 @@ struct vb2_vmalloc_buf { static void vb2_vmalloc_put(void *buf_priv); -static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size) +static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags) { struct vb2_vmalloc_buf *buf; - buf = kzalloc(sizeof(*buf), GFP_KERNEL); + buf = kzalloc(sizeof(*buf), GFP_KERNEL | gfp_flags); if (!buf) return NULL; |