diff options
author | Johannes Berg <johannes.berg@intel.com> | 2014-02-20 11:55:12 +0100 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-02-20 11:55:12 +0100 |
commit | bf5f48339a019c9b4b42284c3f45d58942cbda27 (patch) | |
tree | 79976135166ffbdaf4ccb91bcadb17ddb551f060 /drivers/media | |
parent | mac80211: remove erroneous comment about RX radiotap header (diff) | |
parent | Staging: rtl8812ae: remove modules field of rate_control_ops (diff) | |
download | linux-bf5f48339a019c9b4b42284c3f45d58942cbda27.tar.xz linux-bf5f48339a019c9b4b42284c3f45d58942cbda27.zip |
Merge remote-tracking branch 'wireless-next/master' into mac80211-next
Diffstat (limited to 'drivers/media')
238 files changed, 16930 insertions, 17447 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 8270388e2a0d..1d0758aeb8e4 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -172,6 +172,9 @@ comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" config MEDIA_SUBDRV_AUTOSELECT bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT + depends on HAS_IOMEM + select I2C + select I2C_MUX default y help By default, a media driver auto-selects all possible ancillary diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 419a2d6b4349..f19a2ccd1e4b 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -239,6 +239,7 @@ #define USB_PID_AVERMEDIA_A835B_4835 0x4835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 +#define USB_PID_AVERMEDIA_H335 0x0335 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 @@ -317,6 +318,7 @@ #define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01 #define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029 +#define USB_PID_WINFAST_DTV_DONGLE_MINID 0x6f0f #define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200 #define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201 #define USB_PID_GENPIX_8PSK_REV_2 0x0202 @@ -365,6 +367,7 @@ #define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac #define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 #define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 +#define USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI 0x0003 #define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004 #define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 #define USB_PID_CPYTO_REDI_PC50A 0xa803 diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index bddbab43a2df..dd12a1ebda82 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -35,6 +35,13 @@ config DVB_STV6110x help A Silicon tuner that supports DVB-S and DVB-S2 modes +config DVB_M88DS3103 + tristate "Montage M88DS3103" + depends on DVB_CORE && I2C && I2C_MUX + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Multistandard (cable + terrestrial) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index f9cb43d9aed9..0c75a6aafb9d 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_DVB_STV6110) += stv6110.o obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o +obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o obj-$(CONFIG_DVB_HD29L2) += hd29l2.o diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 74fbb5d58bed..780da58132f1 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -96,6 +96,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe, if (ret) goto err; + usleep_range(1500, 50000); + return ret; err: dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index 476b422ccf19..a6c3c9e2e897 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -135,15 +135,33 @@ enum cmds { - CMD_SET_VCO = 0x10, - CMD_TUNEREQUEST = 0x11, - CMD_MPEGCONFIG = 0x13, - CMD_TUNERINIT = 0x14, - CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ - CMD_LNBDCLEVEL = 0x22, - CMD_SET_TONE = 0x23, - CMD_UPDFWVERS = 0x35, - CMD_TUNERSLEEP = 0x36, + CMD_SET_VCOFREQ = 0x10, + CMD_TUNEREQUEST = 0x11, + CMD_GLOBAL_MPEGCFG = 0x13, + CMD_MPEGCFG = 0x14, + CMD_TUNERINIT = 0x15, + CMD_GET_SRATE = 0x18, + CMD_SET_GOLDCODE = 0x19, + CMD_GET_AGCACC = 0x1a, + CMD_DEMODINIT = 0x1b, + CMD_GETCTLACC = 0x1c, + + CMD_LNBCONFIG = 0x20, + CMD_LNBSEND = 0x21, + CMD_LNBDCLEVEL = 0x22, + CMD_LNBPCBCONFIG = 0x23, + CMD_LNBSENDTONEBST = 0x24, + CMD_LNBUPDREPLY = 0x25, + + CMD_SET_GPIOMODE = 0x30, + CMD_SET_GPIOEN = 0x31, + CMD_SET_GPIODIR = 0x32, + CMD_SET_GPIOOUT = 0x33, + CMD_ENABLERSCORR = 0x34, + CMD_FWVERSION = 0x35, + CMD_SET_SLEEPMODE = 0x36, + CMD_BERCTRL = 0x3c, + CMD_EVENTCTRL = 0x3d, }; static LIST_HEAD(hybrid_tuner_instance_list); @@ -619,8 +637,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, cx24117_writereg(state, 0xf7, 0x0c); cx24117_writereg(state, 0xe0, 0x00); - /* CMD 1B */ - cmd.args[0] = 0x1b; + /* Init demodulator */ + cmd.args[0] = CMD_DEMODINIT; cmd.args[1] = 0x00; cmd.args[2] = 0x01; cmd.args[3] = 0x00; @@ -629,8 +647,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, if (ret != 0) goto error; - /* CMD 10 */ - cmd.args[0] = CMD_SET_VCO; + /* Set VCO frequency */ + cmd.args[0] = CMD_SET_VCOFREQ; cmd.args[1] = 0x06; cmd.args[2] = 0x2b; cmd.args[3] = 0xd8; @@ -648,8 +666,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, if (ret != 0) goto error; - /* CMD 15 */ - cmd.args[0] = 0x15; + /* Tuner init */ + cmd.args[0] = CMD_TUNERINIT; cmd.args[1] = 0x00; cmd.args[2] = 0x01; cmd.args[3] = 0x00; @@ -667,8 +685,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, if (ret != 0) goto error; - /* CMD 13 */ - cmd.args[0] = CMD_MPEGCONFIG; + /* Global MPEG config */ + cmd.args[0] = CMD_GLOBAL_MPEGCFG; cmd.args[1] = 0x00; cmd.args[2] = 0x00; cmd.args[3] = 0x00; @@ -679,9 +697,9 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, if (ret != 0) goto error; - /* CMD 14 */ + /* MPEG config for each demod */ for (i = 0; i < 2; i++) { - cmd.args[0] = CMD_TUNERINIT; + cmd.args[0] = CMD_MPEGCFG; cmd.args[1] = (u8) i; cmd.args[2] = 0x00; cmd.args[3] = 0x05; @@ -699,8 +717,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe, cx24117_writereg(state, 0xcf, 0x00); cx24117_writereg(state, 0xe5, 0x04); - /* Firmware CMD 35: Get firmware version */ - cmd.args[0] = CMD_UPDFWVERS; + /* Get firmware version */ + cmd.args[0] = CMD_FWVERSION; cmd.len = 2; for (i = 0; i < 4; i++) { cmd.args[1] = i; @@ -779,8 +797,8 @@ static int cx24117_read_signal_strength(struct dvb_frontend *fe, u8 reg = (state->demod == 0) ? CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1; - /* Firmware CMD 1A */ - cmd.args[0] = 0x1a; + /* Read AGC accumulator register */ + cmd.args[0] = CMD_GET_AGCACC; cmd.args[1] = (u8) state->demod; cmd.len = 2; ret = cx24117_cmd_execute(fe, &cmd); @@ -899,22 +917,15 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "SEC_VOLTAGE_OFF"); - /* CMD 32 */ - cmd.args[0] = 0x32; - cmd.args[1] = reg; - cmd.args[2] = reg; + /* Prepare a set GPIO logic level CMD */ + cmd.args[0] = CMD_SET_GPIOOUT; + cmd.args[2] = reg; /* mask */ cmd.len = 3; - ret = cx24117_cmd_execute(fe, &cmd); - if (ret) - return ret; if ((voltage == SEC_VOLTAGE_13) || (voltage == SEC_VOLTAGE_18)) { - /* CMD 33 */ - cmd.args[0] = 0x33; + /* power on LNB */ cmd.args[1] = reg; - cmd.args[2] = reg; - cmd.len = 3; ret = cx24117_cmd_execute(fe, &cmd); if (ret != 0) return ret; @@ -926,22 +937,22 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, /* Wait for voltage/min repeat delay */ msleep(100); - /* CMD 22 - CMD_LNBDCLEVEL */ + /* Set 13V/18V select pin */ cmd.args[0] = CMD_LNBDCLEVEL; cmd.args[1] = state->demod ? 0 : 1; cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); cmd.len = 3; + ret = cx24117_cmd_execute(fe, &cmd); /* Min delay time before DiSEqC send */ msleep(20); } else { - cmd.args[0] = 0x33; + /* power off LNB */ cmd.args[1] = 0x00; - cmd.args[2] = reg; - cmd.len = 3; + ret = cx24117_cmd_execute(fe, &cmd); } - return cx24117_cmd_execute(fe, &cmd); + return ret; } static int cx24117_set_tone(struct dvb_frontend *fe, @@ -968,8 +979,7 @@ static int cx24117_set_tone(struct dvb_frontend *fe, msleep(20); /* Set the tone */ - /* CMD 23 - CMD_SET_TONE */ - cmd.args[0] = CMD_SET_TONE; + cmd.args[0] = CMD_LNBPCBCONFIG; cmd.args[1] = (state->demod ? 0 : 1); cmd.args[2] = 0x00; cmd.args[3] = 0x00; @@ -1166,7 +1176,7 @@ struct dvb_frontend *cx24117_attach(const struct cx24117_config *config, switch (demod) { case 0: - dev_err(&state->priv->i2c->dev, + dev_err(&i2c->dev, "%s: Error attaching frontend %d\n", KBUILD_MODNAME, demod); goto error1; @@ -1190,12 +1200,6 @@ struct dvb_frontend *cx24117_attach(const struct cx24117_config *config, state->demod = demod - 1; state->priv = priv; - /* test i2c bus for ack */ - if (demod == 0) { - if (cx24117_readreg(state, 0x00) < 0) - goto error3; - } - dev_info(&state->priv->i2c->dev, "%s: Attaching frontend %d\n", KBUILD_MODNAME, state->demod); @@ -1206,8 +1210,6 @@ struct dvb_frontend *cx24117_attach(const struct cx24117_config *config, state->frontend.demodulator_priv = state; return &state->frontend; -error3: - kfree(state); error2: cx24117_release_priv(priv); error1: @@ -1231,8 +1233,8 @@ static int cx24117_initfe(struct dvb_frontend *fe) mutex_lock(&state->priv->fe_lock); - /* Firmware CMD 36: Power config */ - cmd.args[0] = CMD_TUNERSLEEP; + /* Set sleep mode off */ + cmd.args[0] = CMD_SET_SLEEPMODE; cmd.args[1] = (state->demod ? 1 : 0); cmd.args[2] = 0; cmd.len = 3; @@ -1244,8 +1246,8 @@ static int cx24117_initfe(struct dvb_frontend *fe) if (ret != 0) goto exit; - /* CMD 3C */ - cmd.args[0] = 0x3c; + /* Set BER control */ + cmd.args[0] = CMD_BERCTRL; cmd.args[1] = (state->demod ? 1 : 0); cmd.args[2] = 0x10; cmd.args[3] = 0x10; @@ -1254,12 +1256,22 @@ static int cx24117_initfe(struct dvb_frontend *fe) if (ret != 0) goto exit; - /* CMD 34 */ - cmd.args[0] = 0x34; + /* Set RS correction (enable/disable) */ + cmd.args[0] = CMD_ENABLERSCORR; cmd.args[1] = (state->demod ? 1 : 0); cmd.args[2] = CX24117_OCC; cmd.len = 3; ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto exit; + + /* Set GPIO direction */ + /* Set as output - controls LNB power on/off */ + cmd.args[0] = CMD_SET_GPIODIR; + cmd.args[1] = 0x30; + cmd.args[2] = 0x30; + cmd.len = 3; + ret = cx24117_cmd_execute_nolock(fe, &cmd); exit: mutex_unlock(&state->priv->fe_lock); @@ -1278,8 +1290,8 @@ static int cx24117_sleep(struct dvb_frontend *fe) dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", __func__, state->demod); - /* Firmware CMD 36: Power config */ - cmd.args[0] = CMD_TUNERSLEEP; + /* Set sleep mode on */ + cmd.args[0] = CMD_SET_SLEEPMODE; cmd.args[1] = (state->demod ? 1 : 0); cmd.args[2] = 1; cmd.len = 3; @@ -1558,7 +1570,8 @@ static int cx24117_get_frontend(struct dvb_frontend *fe) u8 buf[0x1f-4]; - cmd.args[0] = 0x1c; + /* Read current tune parameters */ + cmd.args[0] = CMD_GETCTLACC; cmd.args[1] = (u8) state->demod; cmd.len = 2; ret = cx24117_cmd_execute(fe, &cmd); diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 6dbbee453ee1..1632d78a5479 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> +#include <asm/div64.h> #include "dvb_math.h" @@ -118,6 +119,12 @@ struct dib8000_state { u8 longest_intlv_layer; u16 output_mode; + /* for DVBv5 stats */ + s64 init_ucb; + unsigned long per_jiffies_stats; + unsigned long ber_jiffies_stats; + unsigned long ber_jiffies_stats_layer[3]; + #ifdef DIB8000_AGC_FREEZE u16 agc1_max; u16 agc1_min; @@ -157,15 +164,10 @@ static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) return ret; } -static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +static u16 __dib8000_read_word(struct dib8000_state *state, u16 reg) { u16 ret; - if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { - dprintk("could not acquire lock"); - return 0; - } - state->i2c_write_buffer[0] = reg >> 8; state->i2c_write_buffer[1] = reg & 0xff; @@ -183,6 +185,21 @@ static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) dprintk("i2c read error on %d", reg); ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + + return ret; +} + +static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + + ret = __dib8000_read_word(state, reg); + mutex_unlock(&state->i2c_buffer_lock); return ret; @@ -192,8 +209,15 @@ static u32 dib8000_read32(struct dib8000_state *state, u16 reg) { u16 rw[2]; - rw[0] = dib8000_read_word(state, reg + 0); - rw[1] = dib8000_read_word(state, reg + 1); + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + + rw[0] = __dib8000_read_word(state, reg + 0); + rw[1] = __dib8000_read_word(state, reg + 1); + + mutex_unlock(&state->i2c_buffer_lock); return ((rw[0] << 16) | (rw[1])); } @@ -787,7 +811,7 @@ int dib8000_update_pll(struct dvb_frontend *fe, 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; } @@ -966,6 +990,45 @@ static u16 dib8000_identify(struct i2c_device *client) return value; } +static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 *unc); + +static void dib8000_reset_stats(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u32 ucb; + + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + + c->strength.len = 1; + c->cnr.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_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; + + dib8000_read_unc_blocks(fe, &ucb); + + state->init_ucb = -ucb; + state->ber_jiffies_stats = 0; + state->per_jiffies_stats = 0; + memset(&state->ber_jiffies_stats_layer, 0, + sizeof(state->ber_jiffies_stats_layer)); +} + static int dib8000_reset(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; @@ -1071,6 +1134,8 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); + dib8000_reset_stats(fe); + return 0; } @@ -2445,7 +2510,8 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) if (state->revision == 0x8090) internal = dib8000_read32(state, 23) / 1000; - if (state->autosearch_state == AS_SEARCHING_FFT) { + if ((state->revision >= 0x8002) && + (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 */ @@ -2481,7 +2547,8 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) 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) { + } else if ((state->revision >= 0x8002) && + (state->autosearch_state == AS_SEARCHING_GUARD)) { c->transmission_mode = TRANSMISSION_MODE_8K; c->guard_interval = GUARD_INTERVAL_1_8; c->inversion = 0; @@ -2583,7 +2650,8 @@ 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 (state->autosearch_state == AS_SEARCHING_FFT) { + if ((state->revision >= 0x8002) && + (state->autosearch_state == AS_SEARCHING_FFT)) { if (irq_pending & 0x1) { dprintk("dib8000_autosearch_irq: max correlation result available"); return 3; @@ -2853,6 +2921,91 @@ static int dib8090p_init_sdram(struct dib8000_state *state) return 0; } +/** + * is_manual_mode - Check if TMCC should be used for parameters settings + * @c: struct dvb_frontend_properties + * + * By default, TMCC table should be used for parameter settings on most + * usercases. However, sometimes it is desirable to lock the demod to + * use the manual parameters. + * + * On manual mode, the current dib8000_tune state machine is very restrict: + * It requires that both per-layer and per-transponder parameters to be + * properly specified, otherwise the device won't lock. + * + * Check if all those conditions are properly satisfied before allowing + * the device to use the manual frequency lock mode. + */ +static int is_manual_mode(struct dtv_frontend_properties *c) +{ + int i, n_segs = 0; + + /* Use auto mode on DVB-T compat mode */ + if (c->delivery_system != SYS_ISDBT) + return 0; + + /* + * Transmission mode is only detected on auto mode, currently + */ + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + dprintk("transmission mode auto"); + return 0; + } + + /* + * Guard interval is only detected on auto mode, currently + */ + if (c->guard_interval == GUARD_INTERVAL_AUTO) { + dprintk("guard interval auto"); + return 0; + } + + /* + * If no layer is enabled, assume auto mode, as at least one + * layer should be enabled + */ + if (!c->isdbt_layer_enabled) { + dprintk("no layer modulation specified"); + return 0; + } + + /* + * Check if the per-layer parameters aren't auto and + * disable a layer if segment count is 0 or invalid. + */ + for (i = 0; i < 3; i++) { + if (!(c->isdbt_layer_enabled & 1 << i)) + continue; + + if ((c->layer[i].segment_count > 13) || + (c->layer[i].segment_count == 0)) { + c->isdbt_layer_enabled &= ~(1 << i); + continue; + } + + n_segs += c->layer[i].segment_count; + + if ((c->layer[i].modulation == QAM_AUTO) || + (c->layer[i].fec == FEC_AUTO)) { + dprintk("layer %c has either modulation or FEC auto", + 'A' + i); + return 0; + } + } + + /* + * Userspace specified a wrong number of segments. + * fallback to auto mode. + */ + if (n_segs == 0 || n_segs > 13) { + dprintk("number of segments is invalid"); + return 0; + } + + /* Everything looks ok for manual mode */ + return 1; +} + static int dib8000_tune(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; @@ -2878,40 +3031,19 @@ static int dib8000_tune(struct dvb_frontend *fe) switch (*tune_state) { case CT_DEMOD_START: /* 30 */ + dib8000_reset_stats(fe); + 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 */ + state->channel_parameters_set = is_manual_mode(c); + + dprintk("Tuning channel on %s search mode", + state->channel_parameters_set ? "manual" : "auto"); dib8000_viterbi_state(state, 0); /* force chan dec in restart */ - /* Layer monit */ + /* Layer monitor */ dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); dib8000_set_frequency_offset(state); @@ -3256,15 +3388,27 @@ 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); } +static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat); + static int dib8000_get_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; u16 i, val = 0; - fe_status_t stat; + fe_status_t stat = 0; u8 index_frontend, sub_index_frontend; fe->dtv_property_cache.bandwidth_hz = 6000000; + /* + * If called to early, get_frontend makes dib8000_tune to either + * not lock or not sync. This causes dvbv5-scan/dvbv5-zap to fail. + * So, let's just return if frontend 0 has not locked. + */ + dib8000_read_status(fe, &stat); + if (!(stat & FE_HAS_SYNC)) + return 0; + + dprintk("TMCC lock"); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); if (stat&FE_HAS_SYNC) { @@ -3335,9 +3479,13 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); - val = dib8000_read_word(state, 499 + i); - fe->dtv_property_cache.layer[i].interleaving = val & 0x3; - dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving); + val = dib8000_read_word(state, 499 + i) & 0x3; + /* Interleaving can be 0, 1, 2 or 4 */ + if (val == 3) + val = 4; + fe->dtv_property_cache.layer[i].interleaving = val; + dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", + i, fe->dtv_property_cache.layer[i].interleaving); val = dib8000_read_word(state, 481 + i); switch (val & 0x7) { @@ -3556,6 +3704,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) return 0; } +static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat); + static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib8000_state *state = fe->demodulator_priv; @@ -3593,6 +3743,7 @@ static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) if (lock & 0x01) *stat |= FE_HAS_VITERBI; } + dib8000_get_stats(fe, *stat); return 0; } @@ -3699,6 +3850,357 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) return 0; } +struct per_layer_regs { + u16 lock, ber, per; +}; + +static const struct per_layer_regs per_layer_regs[] = { + { 554, 560, 562 }, + { 555, 576, 578 }, + { 556, 581, 583 }, +}; + +struct linear_segments { + unsigned x; + signed y; +}; + +/* + * Table to estimate signal strength in dBm. + * This table was empirically determinated by measuring the signal + * strength generated by a DTA-2111 RF generator directly connected into + * a dib8076 device (a PixelView PV-D231U stick), using a good quality + * 3 meters RC6 cable and good RC6 connectors. + * The real value can actually be different on other devices, depending + * on several factors, like if LNA is enabled or not, if diversity is + * enabled, type of connectors, etc. + * Yet, it is better to use this measure in dB than a random non-linear + * percentage value, especially for antenna adjustments. + * On my tests, the precision of the measure using this table is about + * 0.5 dB, with sounds reasonable enough. + */ +static struct linear_segments strength_to_db_table[] = { + { 55953, 108500 }, /* -22.5 dBm */ + { 55394, 108000 }, + { 53834, 107000 }, + { 52863, 106000 }, + { 52239, 105000 }, + { 52012, 104000 }, + { 51803, 103000 }, + { 51566, 102000 }, + { 51356, 101000 }, + { 51112, 100000 }, + { 50869, 99000 }, + { 50600, 98000 }, + { 50363, 97000 }, + { 50117, 96000 }, /* -35 dBm */ + { 49889, 95000 }, + { 49680, 94000 }, + { 49493, 93000 }, + { 49302, 92000 }, + { 48929, 91000 }, + { 48416, 90000 }, + { 48035, 89000 }, + { 47593, 88000 }, + { 47282, 87000 }, + { 46953, 86000 }, + { 46698, 85000 }, + { 45617, 84000 }, + { 44773, 83000 }, + { 43845, 82000 }, + { 43020, 81000 }, + { 42010, 80000 }, /* -51 dBm */ + { 0, 0 }, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx; + s32 dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i - 1].y - segments[i].y; + dx = segments[i - 1].x - segments[i].x; + + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y + tmp64; + + return ret; +} + +static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int ini_layer, end_layer, i; + u64 time_us, tmp64; + u32 tmp, denom; + int guard, rate_num, rate_denum = 1, bits_per_symbol, nsegs; + int interleaving = 0, fft_div; + + if (layer >= 0) { + ini_layer = layer; + end_layer = layer + 1; + } else { + ini_layer = 0; + end_layer = 3; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_4: + guard = 4; + break; + case GUARD_INTERVAL_1_8: + guard = 8; + break; + case GUARD_INTERVAL_1_16: + guard = 16; + break; + default: + case GUARD_INTERVAL_1_32: + guard = 32; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + fft_div = 4; + break; + case TRANSMISSION_MODE_4K: + fft_div = 2; + break; + default: + case TRANSMISSION_MODE_8K: + fft_div = 1; + break; + } + + denom = 0; + for (i = ini_layer; i < end_layer; i++) { + nsegs = c->layer[i].segment_count; + if (nsegs == 0 || nsegs > 13) + continue; + + switch (c->layer[i].modulation) { + case DQPSK: + case QPSK: + bits_per_symbol = 2; + break; + case QAM_16: + bits_per_symbol = 4; + break; + default: + case QAM_64: + bits_per_symbol = 6; + break; + } + + switch (c->layer[i].fec) { + case FEC_1_2: + rate_num = 1; + rate_denum = 2; + break; + case FEC_2_3: + rate_num = 2; + rate_denum = 3; + break; + case FEC_3_4: + rate_num = 3; + rate_denum = 4; + break; + case FEC_5_6: + rate_num = 5; + rate_denum = 6; + break; + default: + case FEC_7_8: + rate_num = 7; + rate_denum = 8; + break; + } + + interleaving = c->layer[i].interleaving; + + denom += bits_per_symbol * rate_num * fft_div * nsegs * 384; + } + + /* If all goes wrong, wait for 1s for the next stats */ + if (!denom) + return 0; + + /* Estimate the period for the total bit rate */ + time_us = rate_denum * (1008 * 1562500L); + tmp64 = time_us; + do_div(tmp64, guard); + time_us = time_us + tmp64; + time_us += denom / 2; + do_div(time_us, denom); + + tmp = 1008 * 96 * interleaving; + time_us += tmp + tmp / guard; + + return time_us; +} + +static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int i; + int show_per_stats = 0; + u32 time_us = 0, snr, val; + u64 blocks; + s32 db; + u16 strength; + + /* Get Signal strength */ + dib8000_read_signal_strength(fe, &strength); + val = strength; + db = interpolate_value(val, + strength_to_db_table, + ARRAY_SIZE(strength_to_db_table)) - 131000; + c->strength.stat[0].svalue = db; + + /* UCB/BER/CNR measures require lock */ + if (!(stat & FE_HAS_LOCK)) { + c->cnr.len = 1; + c->block_count.len = 1; + c->block_error.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->cnr.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; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Check if time for stats was elapsed */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + /* Get SNR */ + snr = dib8000_get_snr(fe); + for (i = 1; i < MAX_NUMBER_OF_FRONTENDS; i++) { + if (state->fe[i]) + snr += dib8000_get_snr(state->fe[i]); + } + snr = snr >> 16; + + if (snr) { + snr = 10 * intlog10(snr); + snr = (1000L * snr) >> 24; + } else { + snr = 0; + } + c->cnr.stat[0].svalue = snr; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + /* Get UCB measures */ + dib8000_read_unc_blocks(fe, &val); + if (val < state->init_ucb) + state->init_ucb += 0x100000000LL; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = val + state->init_ucb; + + /* Estimate the number of packets based on bitrate */ + if (!time_us) + time_us = dib8000_get_time_us(fe, -1); + + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + + show_per_stats = 1; + } + + /* Get post-BER measures */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + time_us = dib8000_get_time_us(fe, -1); + state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + + dprintk("Next all layers stats available in %u us.", time_us); + + dib8000_read_ber(fe, &val); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += val; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += 100000000; + } + + if (state->revision < 0x8002) + return 0; + + c->block_error.len = 4; + c->post_bit_error.len = 4; + c->post_bit_count.len = 4; + + for (i = 0; i < 3; i++) { + unsigned nsegs = c->layer[i].segment_count; + + if (nsegs == 0 || nsegs > 13) + continue; + + time_us = 0; + + if (time_after(jiffies, state->ber_jiffies_stats_layer[i])) { + time_us = dib8000_get_time_us(fe, i); + + state->ber_jiffies_stats_layer[i] = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + dprintk("Next layer %c stats will be available in %u us\n", + 'A' + i, time_us); + + val = dib8000_read_word(state, per_layer_regs[i].ber); + c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[1 + i].uvalue += val; + + c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[1 + i].uvalue += 100000000; + } + + if (show_per_stats) { + val = dib8000_read_word(state, per_layer_regs[i].per); + + c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->block_error.stat[1 + i].uvalue += val; + + if (!time_us) + time_us = dib8000_get_time_us(fe, i); + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + } + } + return 0; +} + int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) { struct dib8000_state *state = fe->demodulator_priv; diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h index f22eb9f13ad5..f6cb34660327 100644 --- a/drivers/media/dvb-frontends/drxk.h +++ b/drivers/media/dvb-frontends/drxk.h @@ -29,7 +29,6 @@ * A value of 0 (default) or lower indicates that * the correct number of parameters will be * automatically detected. - * @load_firmware_sync: Force the firmware load to be synchronous. * * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is * UIO-3. @@ -41,7 +40,6 @@ struct drxk_config { bool parallel_ts; bool dynamic_clk; bool enable_merr_cfg; - bool load_firmware_sync; bool antenna_dvbt; u16 antenna_gpio; diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index bf29a3f0e6f0..cce94a75b2e1 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -6830,25 +6830,13 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, /* Load firmware and initialize DRX-K */ if (state->microcode_name) { - if (config->load_firmware_sync) { - const struct firmware *fw = NULL; + const struct firmware *fw = NULL; - status = request_firmware(&fw, state->microcode_name, - state->i2c->dev.parent); - if (status < 0) - fw = NULL; - load_firmware_cb(fw, state); - } else { - status = request_firmware_nowait(THIS_MODULE, 1, - state->microcode_name, - state->i2c->dev.parent, - GFP_KERNEL, - state, load_firmware_cb); - if (status < 0) { - pr_err("failed to request a firmware\n"); - return NULL; - } - } + status = request_firmware(&fw, state->microcode_name, + state->i2c->dev.parent); + if (status < 0) + fw = NULL; + load_firmware_cb(fw, state); } else if (init_drxk(state) < 0) goto error; diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c new file mode 100644 index 000000000000..b8a7897e7bd8 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -0,0 +1,1311 @@ +/* + * Montage M88DS3103 demodulator driver + * + * 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. + */ + +#include "m88ds3103_priv.h" + +static struct dvb_frontend_ops m88ds3103_ops; + +/* write multiple registers */ +static int m88ds3103_wr_regs(struct m88ds3103_priv *priv, + u8 reg, const u8 *val, int len) +{ +#define MAX_WR_LEN 32 +#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) + int ret; + u8 buf[MAX_WR_XFER_LEN]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = 1 + len, + .buf = buf, + } + }; + + if (WARN_ON(len > MAX_WR_LEN)) + return -EINVAL; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + mutex_lock(&priv->i2c_mutex); + ret = i2c_transfer(priv->i2c, msg, 1); + mutex_unlock(&priv->i2c_mutex); + if (ret == 1) { + ret = 0; + } else { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int m88ds3103_rd_regs(struct m88ds3103_priv *priv, + u8 reg, u8 *val, int len) +{ +#define MAX_RD_LEN 3 +#define MAX_RD_XFER_LEN (MAX_RD_LEN) + int ret; + u8 buf[MAX_RD_XFER_LEN]; + struct i2c_msg msg[2] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->cfg->i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (WARN_ON(len > MAX_RD_LEN)) + return -EINVAL; + + mutex_lock(&priv->i2c_mutex); + ret = i2c_transfer(priv->i2c, msg, 2); + mutex_unlock(&priv->i2c_mutex); + if (ret == 2) { + memcpy(val, buf, len); + ret = 0; + } else { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* write single register */ +static int m88ds3103_wr_reg(struct m88ds3103_priv *priv, u8 reg, u8 val) +{ + return m88ds3103_wr_regs(priv, reg, &val, 1); +} + +/* read single register */ +static int m88ds3103_rd_reg(struct m88ds3103_priv *priv, u8 reg, u8 *val) +{ + return m88ds3103_rd_regs(priv, reg, val, 1); +} + +/* write single register with mask */ +static int m88ds3103_wr_reg_mask(struct m88ds3103_priv *priv, + u8 reg, u8 val, u8 mask) +{ + int ret; + u8 u8tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1); + if (ret) + return ret; + + val &= mask; + u8tmp &= ~mask; + val |= u8tmp; + } + + return m88ds3103_wr_regs(priv, reg, &val, 1); +} + +/* read single register with mask */ +static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv, + u8 reg, u8 *val, u8 mask) +{ + int ret, i; + u8 u8tmp; + + ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1); + if (ret) + return ret; + + u8tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = u8tmp >> i; + + return 0; +} + +/* write reg val table using reg addr auto increment */ +static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv, + const struct m88ds3103_reg_val *tab, int tab_len) +{ + int ret, i, j; + u8 buf[83]; + dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); + + if (tab_len > 83) { + ret = -EINVAL; + goto err; + } + + for (i = 0, j = 0; i < tab_len; i++, j++) { + buf[j] = tab[i].val; + + if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 || + !((j + 1) % (priv->cfg->i2c_wr_max - 1))) { + ret = m88ds3103_wr_regs(priv, tab[i].reg - j, buf, j + 1); + if (ret) + goto err; + + j = -1; + } + } + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 u8tmp; + + *status = 0; + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBS: + ret = m88ds3103_rd_reg_mask(priv, 0xd1, &u8tmp, 0x07); + if (ret) + goto err; + + if (u8tmp == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + break; + case SYS_DVBS2: + ret = m88ds3103_rd_reg_mask(priv, 0x0d, &u8tmp, 0x8f); + if (ret) + goto err; + + if (u8tmp == 0x8f) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); + ret = -EINVAL; + goto err; + } + + priv->fe_status = *status; + + dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n", + __func__, u8tmp, *status); + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_set_frontend(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len; + const struct m88ds3103_reg_val *init; + u8 u8tmp, u8tmp1, u8tmp2; + u8 buf[2]; + u16 u16tmp, divide_ratio; + u32 tuner_frequency, target_mclk, ts_clk; + s32 s32tmp; + dev_dbg(&priv->i2c->dev, + "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + __func__, c->delivery_system, + c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_frequency) { + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency); + if (ret) + goto err; + } + + /* reset */ + ret = m88ds3103_wr_reg(priv, 0x07, 0x80); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0x07, 0x00); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0x00, 0x01); + if (ret) + goto err; + + switch (c->delivery_system) { + case SYS_DVBS: + len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); + init = m88ds3103_dvbs_init_reg_vals; + target_mclk = 96000; + break; + case SYS_DVBS2: + len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); + init = m88ds3103_dvbs2_init_reg_vals; + + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + if (c->symbol_rate < 18000000) + target_mclk = 96000; + else + target_mclk = 144000; + break; + case M88DS3103_TS_PARALLEL: + case M88DS3103_TS_PARALLEL_12: + case M88DS3103_TS_PARALLEL_16: + case M88DS3103_TS_PARALLEL_19_2: + case M88DS3103_TS_CI: + if (c->symbol_rate < 18000000) + target_mclk = 96000; + else if (c->symbol_rate < 28000000) + target_mclk = 144000; + else + target_mclk = 192000; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", + __func__); + ret = -EINVAL; + goto err; + } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); + ret = -EINVAL; + goto err; + } + + /* program init table */ + if (c->delivery_system != priv->delivery_system) { + ret = m88ds3103_wr_reg_val_tab(priv, init, len); + if (ret) + goto err; + } + + u8tmp1 = 0; /* silence compiler warning */ + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + u8tmp1 = 0x00; + ts_clk = 0; + u8tmp = 0x46; + break; + case M88DS3103_TS_SERIAL_D7: + u8tmp1 = 0x20; + ts_clk = 0; + u8tmp = 0x46; + break; + case M88DS3103_TS_PARALLEL: + ts_clk = 24000; + u8tmp = 0x42; + break; + case M88DS3103_TS_PARALLEL_12: + ts_clk = 12000; + u8tmp = 0x42; + break; + case M88DS3103_TS_PARALLEL_16: + ts_clk = 16000; + u8tmp = 0x42; + break; + case M88DS3103_TS_PARALLEL_19_2: + ts_clk = 19200; + u8tmp = 0x42; + break; + case M88DS3103_TS_CI: + ts_clk = 6000; + u8tmp = 0x43; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); + ret = -EINVAL; + goto err; + } + + /* TS mode */ + ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp); + if (ret) + goto err; + + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20); + if (ret) + goto err; + } + + if (ts_clk) { + divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk); + u8tmp1 = divide_ratio / 2; + u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); + } else { + divide_ratio = 0; + u8tmp1 = 0; + u8tmp2 = 0; + } + + dev_dbg(&priv->i2c->dev, + "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n", + __func__, target_mclk, ts_clk, divide_ratio); + + u8tmp1--; + u8tmp2--; + /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */ + u8tmp1 &= 0x3f; + /* u8tmp2[5:0] => ea[5:0] */ + u8tmp2 &= 0x3f; + + ret = m88ds3103_rd_reg(priv, 0xfe, &u8tmp); + if (ret) + goto err; + + u8tmp = ((u8tmp & 0xf0) << 0) | u8tmp1 >> 2; + ret = m88ds3103_wr_reg(priv, 0xfe, u8tmp); + if (ret) + goto err; + + u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; + ret = m88ds3103_wr_reg(priv, 0xea, u8tmp); + if (ret) + goto err; + + switch (target_mclk) { + case 72000: + u8tmp1 = 0x00; /* 0b00 */ + u8tmp2 = 0x03; /* 0b11 */ + break; + case 96000: + u8tmp1 = 0x02; /* 0b10 */ + u8tmp2 = 0x01; /* 0b01 */ + break; + case 115200: + u8tmp1 = 0x01; /* 0b01 */ + u8tmp2 = 0x01; /* 0b01 */ + break; + case 144000: + u8tmp1 = 0x00; /* 0b00 */ + u8tmp2 = 0x01; /* 0b01 */ + break; + case 192000: + u8tmp1 = 0x03; /* 0b11 */ + u8tmp2 = 0x00; /* 0b00 */ + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__); + ret = -EINVAL; + goto err; + } + + ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0); + if (ret) + goto err; + + if (c->symbol_rate <= 3000000) + u8tmp = 0x20; + else if (c->symbol_rate <= 10000000) + u8tmp = 0x10; + else + u8tmp = 0x06; + + ret = m88ds3103_wr_reg(priv, 0xc3, 0x08); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xc8, u8tmp); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xc4, 0x08); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xc7, 0x00); + if (ret) + goto err; + + u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2); + buf[0] = (u16tmp >> 0) & 0xff; + buf[1] = (u16tmp >> 8) & 0xff; + ret = m88ds3103_wr_regs(priv, 0x61, buf, 2); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x4d, priv->cfg->spec_inv << 1, 0x02); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x30, priv->cfg->agc_inv << 4, 0x10); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0x33, priv->cfg->agc); + if (ret) + goto err; + + dev_dbg(&priv->i2c->dev, "%s: carrier offset=%d\n", __func__, + (tuner_frequency - c->frequency)); + + s32tmp = 0x10000 * (tuner_frequency - c->frequency); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ); + if (s32tmp < 0) + s32tmp += 0x10000; + + buf[0] = (s32tmp >> 0) & 0xff; + buf[1] = (s32tmp >> 8) & 0xff; + ret = m88ds3103_wr_regs(priv, 0x5e, buf, 2); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0x00, 0x00); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x00); + if (ret) + goto err; + + priv->delivery_system = c->delivery_system; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_init(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, len, remaining; + const struct firmware *fw = NULL; + u8 *fw_file = M88DS3103_FIRMWARE; + u8 u8tmp; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + /* set cold state by default */ + priv->warm = false; + + /* wake up device from sleep */ + ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x01, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10); + if (ret) + goto err; + + /* reset */ + ret = m88ds3103_wr_reg(priv, 0x07, 0x60); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0x07, 0x00); + if (ret) + goto err; + + /* firmware status */ + ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp); + if (ret) + goto err; + + dev_dbg(&priv->i2c->dev, "%s: firmware=%02x\n", __func__, u8tmp); + + if (u8tmp) + goto skip_fw_download; + + /* cold state - try to download firmware */ + dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, m88ds3103_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + if (ret) { + dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto err; + } + + dev_info(&priv->i2c->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); + if (ret) + goto err; + + for (remaining = fw->size; remaining > 0; + remaining -= (priv->cfg->i2c_wr_max - 1)) { + len = remaining; + if (len > (priv->cfg->i2c_wr_max - 1)) + len = (priv->cfg->i2c_wr_max - 1); + + ret = m88ds3103_wr_regs(priv, 0xb0, + &fw->data[fw->size - remaining], len); + if (ret) { + dev_err(&priv->i2c->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } + } + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x00); + if (ret) + goto err; + + release_firmware(fw); + fw = NULL; + + ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp); + if (ret) + goto err; + + if (!u8tmp) { + dev_info(&priv->i2c->dev, "%s: firmware did not run\n", + KBUILD_MODNAME); + ret = -EFAULT; + goto err; + } + + dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", + KBUILD_MODNAME, m88ds3103_ops.info.name); + dev_info(&priv->i2c->dev, "%s: firmware version %X.%X\n", + KBUILD_MODNAME, (u8tmp >> 4) & 0xf, (u8tmp >> 0 & 0xf)); + +skip_fw_download: + /* warm state */ + priv->warm = true; + + return 0; +err: + if (fw) + release_firmware(fw); + + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_sleep(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + priv->delivery_system = SYS_UNDEFINED; + + /* TS Hi-Z */ + ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01); + if (ret) + goto err; + + /* sleep */ + ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_get_frontend(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[3]; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBS: + ret = m88ds3103_rd_reg(priv, 0xe0, &buf[0]); + if (ret) + goto err; + + ret = m88ds3103_rd_reg(priv, 0xe6, &buf[1]); + if (ret) + goto err; + + switch ((buf[0] >> 2) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", + __func__); + } + + switch ((buf[1] >> 5) & 0x07) { + case 0: + c->fec_inner = FEC_7_8; + break; + case 1: + c->fec_inner = FEC_5_6; + break; + case 2: + c->fec_inner = FEC_3_4; + break; + case 3: + c->fec_inner = FEC_2_3; + break; + case 4: + c->fec_inner = FEC_1_2; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n", + __func__); + } + + c->modulation = QPSK; + + break; + case SYS_DVBS2: + ret = m88ds3103_rd_reg(priv, 0x7e, &buf[0]); + if (ret) + goto err; + + ret = m88ds3103_rd_reg(priv, 0x89, &buf[1]); + if (ret) + goto err; + + ret = m88ds3103_rd_reg(priv, 0xf2, &buf[2]); + if (ret) + goto err; + + switch ((buf[0] >> 0) & 0x0f) { + case 2: + c->fec_inner = FEC_2_5; + break; + case 3: + c->fec_inner = FEC_1_2; + break; + case 4: + c->fec_inner = FEC_3_5; + break; + case 5: + c->fec_inner = FEC_2_3; + break; + case 6: + c->fec_inner = FEC_3_4; + break; + case 7: + c->fec_inner = FEC_4_5; + break; + case 8: + c->fec_inner = FEC_5_6; + break; + case 9: + c->fec_inner = FEC_8_9; + break; + case 10: + c->fec_inner = FEC_9_10; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n", + __func__); + } + + switch ((buf[0] >> 5) & 0x01) { + case 0: + c->pilot = PILOT_OFF; + break; + case 1: + c->pilot = PILOT_ON; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", + __func__); + } + + switch ((buf[0] >> 6) & 0x07) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = PSK_8; + break; + case 2: + c->modulation = APSK_16; + break; + case 3: + c->modulation = APSK_32; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid modulation\n", + __func__); + } + + switch ((buf[1] >> 7) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", + __func__); + } + + switch ((buf[2] >> 0) & 0x03) { + case 0: + c->rolloff = ROLLOFF_35; + break; + case 1: + c->rolloff = ROLLOFF_25; + break; + case 2: + c->rolloff = ROLLOFF_20; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", + __func__); + } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); + ret = -EINVAL; + goto err; + } + + ret = m88ds3103_rd_regs(priv, 0x6d, buf, 2); + if (ret) + goto err; + + c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) * + M88DS3103_MCLK_KHZ * 1000 / 0x10000; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, tmp; + u8 buf[3]; + u16 noise, signal; + u32 noise_tot, signal_tot; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + /* reports SNR in resolution of 0.1 dB */ + + /* more iterations for more accurate estimation */ + #define M88DS3103_SNR_ITERATIONS 3 + + switch (c->delivery_system) { + case SYS_DVBS: + tmp = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = m88ds3103_rd_reg(priv, 0xff, &buf[0]); + if (ret) + goto err; + + tmp += buf[0]; + } + + /* use of one register limits max value to 15 dB */ + /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ + tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS); + if (tmp) + *snr = 100ul * intlog2(tmp) / intlog2(10); + else + *snr = 0; + break; + case SYS_DVBS2: + noise_tot = 0; + signal_tot = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = m88ds3103_rd_regs(priv, 0x8c, buf, 3); + if (ret) + goto err; + + noise = buf[1] << 6; /* [13:6] */ + noise |= buf[0] & 0x3f; /* [5:0] */ + noise >>= 2; + signal = buf[2] * buf[2]; + signal >>= 1; + + noise_tot += noise; + signal_tot += signal; + } + + noise = noise_tot / M88DS3103_SNR_ITERATIONS; + signal = signal_tot / M88DS3103_SNR_ITERATIONS; + + /* SNR(X) dB = 10 * log10(X) dB */ + if (signal > noise) { + tmp = signal / noise; + *snr = 100ul * intlog10(tmp) / (1 << 24); + } else { + *snr = 0; + } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + + +static int m88ds3103_set_tone(struct dvb_frontend *fe, + fe_sec_tone_mode_t fe_sec_tone_mode) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret; + u8 u8tmp, tone, reg_a1_mask; + dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__, + fe_sec_tone_mode); + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + switch (fe_sec_tone_mode) { + case SEC_TONE_ON: + tone = 0; + reg_a1_mask = 0x87; + break; + case SEC_TONE_OFF: + tone = 1; + reg_a1_mask = 0x00; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", + __func__); + ret = -EINVAL; + goto err; + } + + u8tmp = tone << 7 | priv->cfg->envelope_mode << 5; + ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + if (ret) + goto err; + + u8tmp = 1 << 2; + ret = m88ds3103_wr_reg_mask(priv, 0xa1, u8tmp, reg_a1_mask); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *diseqc_cmd) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, i; + u8 u8tmp; + dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__, + diseqc_cmd->msg_len, diseqc_cmd->msg); + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { + ret = -EINVAL; + goto err; + } + + u8tmp = priv->cfg->envelope_mode << 5; + ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + if (ret) + goto err; + + ret = m88ds3103_wr_regs(priv, 0xa3, diseqc_cmd->msg, + diseqc_cmd->msg_len); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xa1, + (diseqc_cmd->msg_len - 1) << 3 | 0x07); + if (ret) + goto err; + + /* DiSEqC message typical period is 54 ms */ + usleep_range(40000, 60000); + + /* wait DiSEqC TX ready */ + for (i = 20, u8tmp = 1; i && u8tmp; i--) { + usleep_range(5000, 10000); + + ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40); + if (ret) + goto err; + } + + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + + if (i == 0) { + dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__); + + ret = m88ds3103_wr_reg_mask(priv, 0xa1, 0x40, 0xc0); + if (ret) + goto err; + } + + ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0); + if (ret) + goto err; + + if (i == 0) { + ret = -ETIMEDOUT; + goto err; + } + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t fe_sec_mini_cmd) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, i; + u8 u8tmp, burst; + dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, + fe_sec_mini_cmd); + + if (!priv->warm) { + ret = -EAGAIN; + goto err; + } + + u8tmp = priv->cfg->envelope_mode << 5; + ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + if (ret) + goto err; + + switch (fe_sec_mini_cmd) { + case SEC_MINI_A: + burst = 0x02; + break; + case SEC_MINI_B: + burst = 0x01; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", + __func__); + ret = -EINVAL; + goto err; + } + + ret = m88ds3103_wr_reg(priv, 0xa1, burst); + if (ret) + goto err; + + /* DiSEqC ToneBurst period is 12.5 ms */ + usleep_range(11000, 20000); + + /* wait DiSEqC TX ready */ + for (i = 5, u8tmp = 1; i && u8tmp; i--) { + usleep_range(800, 2000); + + ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40); + if (ret) + goto err; + } + + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + + ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0); + if (ret) + goto err; + + if (i == 0) { + dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__); + ret = -ETIMEDOUT; + goto err; + } + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 3000; + + return 0; +} + +static void m88ds3103_release(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + i2c_del_mux_adapter(priv->i2c_adapter); + kfree(priv); +} + +static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +{ + struct m88ds3103_priv *priv = mux_priv; + int ret; + struct i2c_msg gate_open_msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = 2, + .buf = "\x03\x11", + } + }; + + mutex_lock(&priv->i2c_mutex); + + /* open tuner I2C repeater for 1 xfer, closes automatically */ + ret = __i2c_transfer(priv->i2c, gate_open_msg, 1); + if (ret != 1) { + dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n", + KBUILD_MODNAME, ret); + if (ret >= 0) + ret = -EREMOTEIO; + + return ret; + } + + return 0; +} + +static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv, + u32 chan) +{ + struct m88ds3103_priv *priv = mux_priv; + + mutex_unlock(&priv->i2c_mutex); + + return 0; +} + +struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, + struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter) +{ + int ret; + struct m88ds3103_priv *priv; + u8 chip_id, u8tmp; + + /* allocate memory for the internal priv */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } + + priv->cfg = cfg; + priv->i2c = i2c; + mutex_init(&priv->i2c_mutex); + + ret = m88ds3103_rd_reg(priv, 0x01, &chip_id); + if (ret) + goto err; + + dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + + switch (chip_id) { + case 0xd0: + break; + default: + goto err; + } + + switch (priv->cfg->clock_out) { + case M88DS3103_CLOCK_OUT_DISABLED: + u8tmp = 0x80; + break; + case M88DS3103_CLOCK_OUT_ENABLED: + u8tmp = 0x00; + break; + case M88DS3103_CLOCK_OUT_ENABLED_DIV2: + u8tmp = 0x10; + break; + default: + goto err; + } + + ret = m88ds3103_wr_reg(priv, 0x29, u8tmp); + if (ret) + goto err; + + /* sleep */ + ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10); + if (ret) + goto err; + + /* create mux i2c adapter for tuner */ + priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0, + m88ds3103_select, m88ds3103_deselect); + if (priv->i2c_adapter == NULL) + goto err; + + *tuner_i2c_adapter = priv->i2c_adapter; + + /* create dvb_frontend */ + memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; + + return &priv->fe; +err: + dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(m88ds3103_attach); + +static struct dvb_frontend_ops m88ds3103_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Montage M88DS3103", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_RECOVER | + FE_CAN_2G_MODULATION + }, + + .release = m88ds3103_release, + + .get_tune_settings = m88ds3103_get_tune_settings, + + .init = m88ds3103_init, + .sleep = m88ds3103_sleep, + + .set_frontend = m88ds3103_set_frontend, + .get_frontend = m88ds3103_get_frontend, + + .read_status = m88ds3103_read_status, + .read_snr = m88ds3103_read_snr, + + .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, + .diseqc_send_burst = m88ds3103_diseqc_send_burst, + + .set_tone = m88ds3103_set_tone, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(M88DS3103_FIRMWARE); diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h new file mode 100644 index 000000000000..bbb7e3aa5675 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -0,0 +1,114 @@ +/* + * Montage M88DS3103 demodulator driver + * + * 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. + */ + +#ifndef M88DS3103_H +#define M88DS3103_H + +#include <linux/dvb/frontend.h> + +struct m88ds3103_config { + /* + * I2C address + * Default: none, must set + * 0x68, ... + */ + u8 i2c_addr; + + /* + * clock + * Default: none, must set + * 27000000 + */ + u32 clock; + + /* + * max bytes I2C provider is asked to write at once + * Default: none, must set + * 33, 65, ... + */ + u16 i2c_wr_max; + + /* + * TS output mode + * Default: M88DS3103_TS_SERIAL + */ +#define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */ +#define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */ +#define M88DS3103_TS_PARALLEL 2 /* 24 MHz, normal */ +#define M88DS3103_TS_PARALLEL_12 3 /* 12 MHz */ +#define M88DS3103_TS_PARALLEL_16 4 /* 16 MHz */ +#define M88DS3103_TS_PARALLEL_19_2 5 /* 19.2 MHz */ +#define M88DS3103_TS_CI 6 /* 6 MHz */ + u8 ts_mode; + + /* + * spectrum inversion + * Default: 0 + */ + u8 spec_inv:1; + + /* + * AGC polarity + * Default: 0 + */ + u8 agc_inv:1; + + /* + * clock output + * Default: M88DS3103_CLOCK_OUT_DISABLED + */ +#define M88DS3103_CLOCK_OUT_DISABLED 0 +#define M88DS3103_CLOCK_OUT_ENABLED 1 +#define M88DS3103_CLOCK_OUT_ENABLED_DIV2 2 + u8 clock_out; + + /* + * DiSEqC envelope mode + * Default: 0 + */ + u8 envelope_mode:1; + + /* + * AGC configuration + * Default: none, must set + */ + u8 agc; +}; + +/* + * Driver implements own I2C-adapter for tuner I2C access. That's since chip + * has I2C-gate control which closes gate automatically after I2C transfer. + * Using own I2C adapter we can workaround that. + */ + +#if defined(CONFIG_DVB_M88DS3103) || \ + (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE)) +extern struct dvb_frontend *m88ds3103_attach( + const struct m88ds3103_config *config, + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c); +#else +static inline struct dvb_frontend *m88ds3103_attach( + const struct m88ds3103_config *config, + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h new file mode 100644 index 000000000000..84c3c06df622 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -0,0 +1,215 @@ +/* + * Montage M88DS3103 demodulator driver + * + * 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. + */ + +#ifndef M88DS3103_PRIV_H +#define M88DS3103_PRIV_H + +#include "dvb_frontend.h" +#include "m88ds3103.h" +#include "dvb_math.h" +#include <linux/firmware.h> +#include <linux/i2c-mux.h> + +#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" +#define M88DS3103_MCLK_KHZ 96000 + +struct m88ds3103_priv { + struct i2c_adapter *i2c; + /* mutex needed due to own tuner I2C adapter */ + struct mutex i2c_mutex; + const struct m88ds3103_config *cfg; + struct dvb_frontend fe; + fe_delivery_system_t delivery_system; + fe_status_t fe_status; + bool warm; /* FW running */ + struct i2c_adapter *i2c_adapter; +}; + +struct m88ds3103_reg_val { + u8 reg; + u8 val; +}; + +static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x03}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x31, 0x40}, + {0x32, 0x32}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x56, 0x01}, + {0x63, 0x0f}, + {0x64, 0x30}, + {0x65, 0x40}, + {0x68, 0x26}, + {0x69, 0x4c}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x7c, 0x00}, + {0xae, 0x82}, + {0x80, 0x64}, + {0x81, 0x66}, + {0x82, 0x44}, + {0x85, 0x04}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xe0, 0xf8}, + {0xe6, 0x8b}, + {0xd0, 0x40}, + {0xf8, 0x20}, + {0xfa, 0x0f}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + +static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x07}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x32, 0x32}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x56, 0x01}, + {0x63, 0x0f}, + {0x64, 0x10}, + {0x65, 0x20}, + {0x68, 0x46}, + {0x69, 0xcd}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x85, 0x08}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0x86, 0x00}, + {0x87, 0x0f}, + {0x89, 0x00}, + {0x8b, 0x44}, + {0x8c, 0x66}, + {0x9d, 0xc1}, + {0x8a, 0x10}, + {0xad, 0x40}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc1, 0x10}, + {0xc2, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xca, 0x23}, + {0xcb, 0x24}, + {0xcc, 0xf4}, + {0xce, 0x74}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + +#endif diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 4da5272075cb..b2351466b0da 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -110,28 +110,94 @@ static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) return b1[0]; } +static u32 m88rs2000_get_mclk(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u32 mclk; + u8 reg; + /* Must not be 0x00 or 0xff */ + reg = m88rs2000_readreg(state, 0x86); + if (!reg || reg == 0xff) + return 0; + + reg /= 2; + reg += 1; + + mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28; + + return mclk; +} + +static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u32 mclk; + s32 tmp; + u8 reg; + int ret; + + mclk = m88rs2000_get_mclk(fe); + if (!mclk) + return -EINVAL; + + tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk; + if (tmp < 0) + tmp += 4096; + + /* Carrier Offset */ + ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4)); + + reg = m88rs2000_readreg(state, 0x9d); + reg &= 0xf; + reg |= (u8)(tmp & 0xf) << 4; + + ret |= m88rs2000_writereg(state, 0x9d, reg); + + return ret; +} + static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) { struct m88rs2000_state *state = fe->demodulator_priv; int ret; - u32 temp; + u64 temp; + u32 mclk; u8 b[3]; if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + mclk = m88rs2000_get_mclk(fe); + if (!mclk) + return -EINVAL; + temp = srate / 1000; - temp *= 11831; - temp /= 68; - temp -= 3; + temp *= 1 << 24; + + do_div(temp, mclk); b[0] = (u8) (temp >> 16) & 0xff; b[1] = (u8) (temp >> 8) & 0xff; b[2] = (u8) temp & 0xff; + ret = m88rs2000_writereg(state, 0x93, b[2]); ret |= m88rs2000_writereg(state, 0x94, b[1]); ret |= m88rs2000_writereg(state, 0x95, b[0]); + if (srate > 10000000) + ret |= m88rs2000_writereg(state, 0xa0, 0x20); + else + ret |= m88rs2000_writereg(state, 0xa0, 0x60); + + ret |= m88rs2000_writereg(state, 0xa1, 0xe0); + + if (srate > 12000000) + ret |= m88rs2000_writereg(state, 0xa3, 0x20); + else if (srate > 2800000) + ret |= m88rs2000_writereg(state, 0xa3, 0x98); + else + ret |= m88rs2000_writereg(state, 0xa3, 0x90); + deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); return ret; } @@ -261,8 +327,6 @@ struct inittab m88rs2000_shutdown[] = { struct inittab fe_reset[] = { {DEMOD_WRITE, 0x00, 0x01}, - {DEMOD_WRITE, 0xf1, 0xbf}, - {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0x20, 0x81}, {DEMOD_WRITE, 0x21, 0x80}, {DEMOD_WRITE, 0x10, 0x33}, @@ -305,9 +369,6 @@ struct inittab fe_trigger[] = { {DEMOD_WRITE, 0x9b, 0x64}, {DEMOD_WRITE, 0x9e, 0x00}, {DEMOD_WRITE, 0x9f, 0xf8}, - {DEMOD_WRITE, 0xa0, 0x20}, - {DEMOD_WRITE, 0xa1, 0xe0}, - {DEMOD_WRITE, 0xa3, 0x38}, {DEMOD_WRITE, 0x98, 0xff}, {DEMOD_WRITE, 0xc0, 0x0f}, {DEMOD_WRITE, 0x89, 0x01}, @@ -408,7 +469,7 @@ static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) *status = 0; - if ((reg & 0x7) == 0x7) { + if ((reg & 0xee) == 0xee) { *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; if (state->config->set_ts_params) @@ -480,33 +541,38 @@ static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int m88rs2000_set_fec(struct m88rs2000_state *state, fe_code_rate_t fec) { - u16 fec_set; + u8 fec_set, reg; + int ret; + switch (fec) { - /* This is not confirmed kept for reference */ -/* case FEC_1_2: - fec_set = 0x88; + case FEC_1_2: + fec_set = 0x8; break; case FEC_2_3: - fec_set = 0x68; + fec_set = 0x10; break; case FEC_3_4: - fec_set = 0x48; + fec_set = 0x20; break; case FEC_5_6: - fec_set = 0x28; + fec_set = 0x40; break; case FEC_7_8: - fec_set = 0x18; - break; */ + fec_set = 0x80; + break; case FEC_AUTO: default: - fec_set = 0x08; + fec_set = 0x0; } - m88rs2000_writereg(state, 0x76, fec_set); - return 0; -} + reg = m88rs2000_readreg(state, 0x70); + reg &= 0x7; + ret = m88rs2000_writereg(state, 0x70, reg | fec_set); + ret |= m88rs2000_writereg(state, 0x76, 0x8); + + return ret; +} static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) { @@ -515,18 +581,20 @@ static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) reg = m88rs2000_readreg(state, 0x76); m88rs2000_writereg(state, 0x9a, 0xb0); + reg &= 0xf0; + reg >>= 5; + switch (reg) { - case 0x88: + case 0x4: return FEC_1_2; - case 0x68: + case 0x3: return FEC_2_3; - case 0x48: + case 0x2: return FEC_3_4; - case 0x28: + case 0x1: return FEC_5_6; - case 0x18: + case 0x0: return FEC_7_8; - case 0x08: default: break; } @@ -540,9 +608,8 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; fe_status_t status; int i, ret = 0; - s32 tmp; u32 tuner_freq; - u16 offset = 0; + s16 offset = 0; u8 reg; state->no_lock_count = 0; @@ -567,38 +634,31 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) if (ret < 0) return -ENODEV; - offset = tuner_freq - c->frequency; - - /* calculate offset assuming 96000kHz*/ - tmp = offset; - tmp *= 65536; - - tmp = (2 * tmp + 96000) / (2 * 96000); - if (tmp < 0) - tmp += 65536; + offset = (s16)((s32)tuner_freq - c->frequency); - offset = tmp & 0xffff; + /* default mclk value 96.4285 * 2 * 1000 = 192857 */ + if (((c->frequency % 192857) >= (192857 - 3000)) || + (c->frequency % 192857) <= 3000) + ret = m88rs2000_writereg(state, 0x86, 0xc2); + else + ret = m88rs2000_writereg(state, 0x86, 0xc6); - ret = m88rs2000_writereg(state, 0x9a, 0x30); - /* Unknown usually 0xc6 sometimes 0xc1 */ - reg = m88rs2000_readreg(state, 0x86); - ret |= m88rs2000_writereg(state, 0x86, reg); - /* Offset lower nibble always 0 */ - ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8)); - ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0); + ret |= m88rs2000_set_carrieroffset(fe, offset); + if (ret < 0) + return -ENODEV; + /* Reset demod by symbol rate */ + if (c->symbol_rate > 27500000) + ret = m88rs2000_writereg(state, 0xf1, 0xa4); + else + ret = m88rs2000_writereg(state, 0xf1, 0xbf); - /* Reset Demod */ - ret = m88rs2000_tab_set(state, fe_reset); + ret |= m88rs2000_tab_set(state, fe_reset); if (ret < 0) return -ENODEV; - /* Unknown */ - reg = m88rs2000_readreg(state, 0x70); - ret = m88rs2000_writereg(state, 0x70, reg); - /* Set FEC */ - ret |= m88rs2000_set_fec(state, c->fec_inner); + ret = m88rs2000_set_fec(state, c->fec_inner); ret |= m88rs2000_writereg(state, 0x85, 0x1); ret |= m88rs2000_writereg(state, 0x8a, 0xbf); ret |= m88rs2000_writereg(state, 0x8d, 0x1e); @@ -620,7 +680,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) for (i = 0; i < 25; i++) { reg = m88rs2000_readreg(state, 0x8c); - if ((reg & 0x7) == 0x7) { + if ((reg & 0xee) == 0xee) { status = FE_HAS_LOCK; break; } diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 14ce31e76ae6..0a50ea90736b 100644 --- a/drivers/media/dvb-frontends/m88rs2000.h +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -53,6 +53,8 @@ static inline struct dvb_frontend *m88rs2000_attach( } #endif /* CONFIG_DVB_M88RS2000 */ +#define RS2000_FE_CRYSTAL_KHZ 27000 + enum { DEMOD_WRITE = 0x1, WRITE_DELAY = 0x10, diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index fbca9856313a..8a8e1ecb762d 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -2,7 +2,7 @@ * Support for NXT2002 and NXT2004 - VSB/QAM * * Copyright (C) 2005 Kirk Lapray <kirk.lapray@gmail.com> - * Copyright (C) 2006 Michael Krufky <mkrufky@m1k.net> + * Copyright (C) 2006-2014 Michael Krufky <mkrufky@linuxtv.org> * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net> * and nxt2004 by Jean-Francois Thibert <jeanfrancois@sagetv.com> * @@ -40,7 +40,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 +#define MAX_XFER_SIZE 256 #define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw" #define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw" diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 842654d33317..4aa9c5311cc5 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -555,14 +555,6 @@ config VIDEO_MT9V032 This is a Video4Linux2 sensor-level driver for the Micron MT9V032 752x480 CMOS sensor. -config VIDEO_TCM825X - tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the Toshiba TCM825x VGA camera sensor. - It is used for example in Nokia N800. - config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 @@ -594,6 +586,13 @@ config VIDEO_S5K4ECGX This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. +config VIDEO_S5K5BAF + tristate "Samsung S5K5BAF sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M + camera sensor with an embedded SoC image signal processor. + source "drivers/media/i2c/smiapp/Kconfig" config VIDEO_S5C73M3 @@ -655,6 +654,18 @@ config VIDEO_UPD64083 To compile this driver as a module, choose M here: the module will be called upd64083. +comment "Audio/Video compression chips" + +config VIDEO_SAA6752HS + tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 + audio encoder with multiplexer. + + To compile this driver as a module, choose M here: the + module will be called saa6752hs. + comment "Miscellaneous helper chips" config VIDEO_THS7303 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e03f1776f4f4..48888ae876fb 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o +obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o @@ -57,7 +58,6 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.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 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o @@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o +obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index b06a7e54ee0d..83225d6a0dd9 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -66,11 +66,6 @@ MODULE_LICENSE("GPL"); ********************************************************************** */ -struct i2c_reg_value { - u8 reg; - u8 value; -}; - struct ad9389b_state_edid { /* total number of blocks */ u32 blocks; @@ -143,14 +138,14 @@ static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val) if (ret == 0) return 0; } - v4l2_err(sd, "I2C Write Problem\n"); + v4l2_err(sd, "%s: failed reg 0x%x, val 0x%x\n", __func__, reg, val); return ret; } /* To set specific bits in the register, a clear-mask is given (to be AND-ed), and then the value-mask (to be OR-ed). */ static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg, - u8 clr_mask, u8 val_mask) + u8 clr_mask, u8 val_mask) { ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask); } @@ -321,12 +316,12 @@ static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl) struct ad9389b_state *state = get_ad9389b_state(sd); v4l2_dbg(1, debug, sd, - "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); + "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); if (state->hdmi_mode_ctrl == ctrl) { /* Set HDMI or DVI-D */ ad9389b_wr_and_or(sd, 0xaf, 0xfd, - ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); + ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); return 0; } if (state->rgb_quantization_range_ctrl == ctrl) @@ -387,61 +382,57 @@ static int ad9389b_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "chip revision %d\n", state->chip_revision); v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", - (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? - "detected" : "no", - (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? - "detected" : "no", - edid->segments ? "found" : "no", edid->blocks); - if (state->have_monitor) { - v4l2_info(sd, "%s output %s\n", - (ad9389b_rd(sd, 0xaf) & 0x02) ? - "HDMI" : "DVI-D", - (ad9389b_rd(sd, 0xa1) & 0x3c) ? - "disabled" : "enabled"); - } + (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? + "detected" : "no", + (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? + "detected" : "no", + edid->segments ? "found" : "no", edid->blocks); + v4l2_info(sd, "%s output %s\n", + (ad9389b_rd(sd, 0xaf) & 0x02) ? + "HDMI" : "DVI-D", + (ad9389b_rd(sd, 0xa1) & 0x3c) ? + "disabled" : "enabled"); v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ? - "encrypted" : "no encryption"); + "encrypted" : "no encryption"); v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", - states[ad9389b_rd(sd, 0xc8) & 0xf], - errors[ad9389b_rd(sd, 0xc8) >> 4], - state->edid_detect_counter, - ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); + states[ad9389b_rd(sd, 0xc8) & 0xf], + errors[ad9389b_rd(sd, 0xc8) >> 4], + state->edid_detect_counter, + ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); manual_gear = ad9389b_rd(sd, 0x98) & 0x80; v4l2_info(sd, "ad9389b: RGB quantization: %s range\n", - ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); + ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); v4l2_info(sd, "ad9389b: %s gear %d\n", manual_gear ? "manual" : "automatic", manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) : - ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); - if (state->have_monitor) { - if (ad9389b_rd(sd, 0xaf) & 0x02) { - /* HDMI only */ - u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; - u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | - ad9389b_rd(sd, 0x02) << 8 | - ad9389b_rd(sd, 0x03); - u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; - u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; - u32 CTS; - - if (manual_cts) - CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | - ad9389b_rd(sd, 0x08) << 8 | - ad9389b_rd(sd, 0x09); - else - CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | - ad9389b_rd(sd, 0x05) << 8 | - ad9389b_rd(sd, 0x06); - N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | - ad9389b_rd(sd, 0x02) << 8 | - ad9389b_rd(sd, 0x03); - - v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", - manual_cts ? "manual" : "automatic", N, CTS); - - v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", - vic_detect, vic_sent); - } + ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); + if (ad9389b_rd(sd, 0xaf) & 0x02) { + /* HDMI only */ + u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; + u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | + ad9389b_rd(sd, 0x02) << 8 | + ad9389b_rd(sd, 0x03); + u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; + u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; + u32 CTS; + + if (manual_cts) + CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | + ad9389b_rd(sd, 0x08) << 8 | + ad9389b_rd(sd, 0x09); + else + CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | + ad9389b_rd(sd, 0x05) << 8 | + ad9389b_rd(sd, 0x06); + N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | + ad9389b_rd(sd, 0x02) << 8 | + ad9389b_rd(sd, 0x03); + + v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", + manual_cts ? "manual" : "automatic", N, CTS); + + v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", + vic_detect, vic_sent); } if (state->dv_timings.type == V4L2_DV_BT_656_1120) v4l2_print_dv_timings(sd->name, "timings: ", @@ -486,7 +477,7 @@ static int ad9389b_s_power(struct v4l2_subdev *sd, int on) } if (i > 1) v4l2_dbg(1, debug, sd, - "needed %d retries to powerup the ad9389b\n", i); + "needed %d retries to powerup the ad9389b\n", i); /* Select chip: AD9389B */ ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10); @@ -556,14 +547,16 @@ static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled) irq_status = ad9389b_rd(sd, 0x96); /* clear detected interrupts */ ad9389b_wr(sd, 0x96, irq_status); + /* enable interrupts */ + ad9389b_set_isr(sd, true); + + v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status); - if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT)) + if (irq_status & (MASK_AD9389B_HPD_INT)) ad9389b_check_monitor_present_status(sd); if (irq_status & MASK_AD9389B_EDID_RDY_INT) ad9389b_check_edid_status(sd); - /* enable interrupts */ - ad9389b_set_isr(sd, true); *handled = true; return 0; } @@ -599,7 +592,7 @@ static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi if (edid->blocks + edid->start_block >= state->edid.segments * 2) edid->blocks = state->edid.segments * 2 - edid->start_block; memcpy(edid->edid, &state->edid.data[edid->start_block * 128], - 128 * edid->blocks); + 128 * edid->blocks); return 0; } @@ -612,8 +605,6 @@ static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { /* Enable/disable ad9389b output */ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) { - struct ad9389b_state *state = get_ad9389b_state(sd); - v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); @@ -621,7 +612,6 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) ad9389b_check_monitor_present_status(sd); } else { ad9389b_s_power(sd, 0); - state->have_monitor = false; } return 0; } @@ -686,14 +676,14 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, } static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) + struct v4l2_enum_dv_timings *timings) { return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, NULL, NULL); } static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, - struct v4l2_dv_timings_cap *cap) + struct v4l2_dv_timings_cap *cap) { *cap = ad9389b_timings_cap; return 0; @@ -724,15 +714,15 @@ static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) u32 N; switch (freq) { - case 32000: N = 4096; break; - case 44100: N = 6272; break; - case 48000: N = 6144; break; - case 88200: N = 12544; break; - case 96000: N = 12288; break; + case 32000: N = 4096; break; + case 44100: N = 6272; break; + case 48000: N = 6144; break; + case 88200: N = 12544; break; + case 96000: N = 12288; break; case 176400: N = 25088; break; case 192000: N = 24576; break; default: - return -EINVAL; + return -EINVAL; } /* Set N (used with CTS to regenerate the audio clock) */ @@ -748,15 +738,15 @@ static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) u32 i2s_sf; switch (freq) { - case 32000: i2s_sf = 0x30; break; - case 44100: i2s_sf = 0x00; break; - case 48000: i2s_sf = 0x20; break; - case 88200: i2s_sf = 0x80; break; - case 96000: i2s_sf = 0xa0; break; + case 32000: i2s_sf = 0x30; break; + case 44100: i2s_sf = 0x00; break; + case 48000: i2s_sf = 0x20; break; + case 88200: i2s_sf = 0x80; break; + case 96000: i2s_sf = 0xa0; break; case 176400: i2s_sf = 0xc0; break; case 192000: i2s_sf = 0xe0; break; default: - return -EINVAL; + return -EINVAL; } /* Set sampling frequency for I2S audio to 48 kHz */ @@ -800,7 +790,7 @@ static const struct v4l2_subdev_ops ad9389b_ops = { /* ----------------------------------------------------------------------- */ static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, - int segment, u8 *buf) + int segment, u8 *buf) { int i, j; @@ -826,8 +816,8 @@ static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, static void ad9389b_edid_handler(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); - struct ad9389b_state *state = container_of(dwork, - struct ad9389b_state, edid_handler); + struct ad9389b_state *state = + container_of(dwork, struct ad9389b_state, edid_handler); struct v4l2_subdev *sd = &state->sd; struct ad9389b_edid_detect ed; @@ -845,11 +835,10 @@ static void ad9389b_edid_handler(struct work_struct *work) if (state->edid.read_retries) { state->edid.read_retries--; v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); - state->have_monitor = false; ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + &state->edid_handler, EDID_DELAY); return; } } @@ -915,49 +904,35 @@ static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd) v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt); } -static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) +static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd) { struct ad9389b_state *state = get_ad9389b_state(sd); /* read hotplug and rx-sense state */ u8 status = ad9389b_rd(sd, 0x42); v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", - __func__, - status, - status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", - status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); + __func__, + status, + status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", + status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); - if ((status & MASK_AD9389B_HPD_DETECT) && - ((status & MASK_AD9389B_MSEN_DETECT) || state->edid.segments)) { - v4l2_dbg(1, debug, sd, - "%s: hotplug and (rx-sense or edid)\n", __func__); - if (!state->have_monitor) { - v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); - state->have_monitor = true; - ad9389b_set_isr(sd, true); - if (!ad9389b_s_power(sd, true)) { - v4l2_dbg(1, debug, sd, - "%s: monitor detected, powerup failed\n", __func__); - return; - } - ad9389b_setup(sd); - ad9389b_notify_monitor_detect(sd); - state->edid.read_retries = EDID_MAX_RETRIES; - queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); - } - } else if (status & MASK_AD9389B_HPD_DETECT) { + if (status & MASK_AD9389B_HPD_DETECT) { v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); + state->have_monitor = true; + if (!ad9389b_s_power(sd, true)) { + v4l2_dbg(1, debug, sd, + "%s: monitor detected, powerup failed\n", __func__); + return; + } + ad9389b_setup(sd); + ad9389b_notify_monitor_detect(sd); state->edid.read_retries = EDID_MAX_RETRIES; queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + &state->edid_handler, EDID_DELAY); } else if (!(status & MASK_AD9389B_HPD_DETECT)) { v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); - if (state->have_monitor) { - v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); - state->have_monitor = false; - ad9389b_notify_monitor_detect(sd); - } + state->have_monitor = false; + ad9389b_notify_monitor_detect(sd); ad9389b_s_power(sd, false); memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); } @@ -966,6 +941,35 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0); v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0); v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + + /* update with setting from ctrls */ + ad9389b_s_ctrl(state->rgb_quantization_range_ctrl); + ad9389b_s_ctrl(state->hdmi_mode_ctrl); +} + +static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) +{ + struct ad9389b_state *state = get_ad9389b_state(sd); + int retry = 0; + + ad9389b_update_monitor_present_status(sd); + + /* + * Rapid toggling of the hotplug may leave the chip powered off, + * even if we think it is on. In that case reset and power up again. + */ + while (state->power_on && (ad9389b_rd(sd, 0x41) & 0x40)) { + if (++retry > 5) { + v4l2_err(sd, "retried %d times, give up\n", retry); + return; + } + v4l2_dbg(1, debug, sd, "%s: reset and re-check status (%d)\n", __func__, retry); + ad9389b_notify_monitor_detect(sd); + cancel_delayed_work_sync(&state->edid_handler); + memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); + ad9389b_s_power(sd, false); + ad9389b_update_monitor_present_status(sd); + } } static bool edid_block_verify_crc(u8 *edid_block) @@ -978,7 +982,7 @@ static bool edid_block_verify_crc(u8 *edid_block) return sum == 0; } -static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) +static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) { struct ad9389b_state *state = get_ad9389b_state(sd); u32 blocks = state->edid.blocks; @@ -992,6 +996,25 @@ static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) return false; } +static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) +{ + static const u8 hdmi_header[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 + }; + struct ad9389b_state *state = get_ad9389b_state(sd); + u8 *data = state->edid.data; + int i; + + if (segment) + return true; + + for (i = 0; i < ARRAY_SIZE(hdmi_header); i++) + if (data[i] != hdmi_header[i]) + return false; + + return true; +} + static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) { struct ad9389b_state *state = get_ad9389b_state(sd); @@ -1000,7 +1023,7 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) u8 edidRdy = ad9389b_rd(sd, 0xc5); v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", - __func__, EDID_MAX_RETRIES - state->edid.read_retries); + __func__, EDID_MAX_RETRIES - state->edid.read_retries); if (!(edidRdy & MASK_AD9389B_EDID_RDY)) return false; @@ -1013,16 +1036,16 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]); ad9389b_dbg_dump_edid(2, debug, sd, segment, - &state->edid.data[segment * 256]); + &state->edid.data[segment * 256]); if (segment == 0) { state->edid.blocks = state->edid.data[0x7e] + 1; v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", - __func__, state->edid.blocks); + __func__, state->edid.blocks); } - if (!edid_segment_verify_crc(sd, segment)) { + if (!edid_verify_crc(sd, segment) || + !edid_verify_header(sd, segment)) { /* edid crc error, force reread of edid segment */ - v4l2_err(sd, "%s: edid crc error\n", __func__); - state->have_monitor = false; + v4l2_err(sd, "%s: edid crc or header error\n", __func__); ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); return false; @@ -1032,12 +1055,12 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { /* Request next EDID segment */ v4l2_dbg(1, debug, sd, "%s: request segment %d\n", - __func__, state->edid.segments); + __func__, state->edid.segments); ad9389b_wr(sd, 0xc9, 0xf); ad9389b_wr(sd, 0xc4, state->edid.segments); state->edid.read_retries = EDID_MAX_RETRIES; queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + &state->edid_handler, EDID_DELAY); return false; } @@ -1081,7 +1104,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * return -EIO; v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n", - client->addr << 1); + client->addr << 1); state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -1140,7 +1163,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * goto err_entity; } v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", - ad9389b_rd(sd, 0x41), state->chip_revision); + ad9389b_rd(sd, 0x41), state->chip_revision); state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1)); if (state->edid_i2c_client == NULL) { @@ -1163,7 +1186,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * ad9389b_set_isr(sd, true); v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, - client->addr << 1, client->adapter->name); + client->addr << 1, client->adapter->name); return 0; err_unreg: diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 7c8d971f1f61..ee618942cb8e 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -452,6 +452,29 @@ static int adv7511_log_status(struct v4l2_subdev *sd) errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter, adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96)); v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full"); + if (adv7511_rd(sd, 0xaf) & 0x02) { + /* HDMI only */ + u8 manual_cts = adv7511_rd(sd, 0x0a) & 0x80; + u32 N = (adv7511_rd(sd, 0x01) & 0xf) << 16 | + adv7511_rd(sd, 0x02) << 8 | + adv7511_rd(sd, 0x03); + u8 vic_detect = adv7511_rd(sd, 0x3e) >> 2; + u8 vic_sent = adv7511_rd(sd, 0x3d) & 0x3f; + u32 CTS; + + if (manual_cts) + CTS = (adv7511_rd(sd, 0x07) & 0xf) << 16 | + adv7511_rd(sd, 0x08) << 8 | + adv7511_rd(sd, 0x09); + else + CTS = (adv7511_rd(sd, 0x04) & 0xf) << 16 | + adv7511_rd(sd, 0x05) << 8 | + adv7511_rd(sd, 0x06); + v4l2_info(sd, "CTS %s mode: N %d, CTS %d\n", + manual_cts ? "manual" : "automatic", N, CTS); + v4l2_info(sd, "VIC: detected %d, sent %d\n", + vic_detect, vic_sent); + } if (state->dv_timings.type == V4L2_DV_BT_656_1120) v4l2_print_dv_timings(sd->name, "timings: ", &state->dv_timings, false); @@ -942,26 +965,38 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) static bool edid_block_verify_crc(uint8_t *edid_block) { - int i; uint8_t sum = 0; + int i; for (i = 0; i < 128; i++) - sum += *(edid_block + i); - return (sum == 0); + sum += edid_block[i]; + return sum == 0; } -static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) +static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) { struct adv7511_state *state = get_adv7511_state(sd); u32 blocks = state->edid.blocks; uint8_t *data = state->edid.data; - if (edid_block_verify_crc(&data[segment * 256])) { - if ((segment + 1) * 2 <= blocks) - return edid_block_verify_crc(&data[segment * 256 + 128]); + if (!edid_block_verify_crc(&data[segment * 256])) + return false; + if ((segment + 1) * 2 <= blocks) + return edid_block_verify_crc(&data[segment * 256 + 128]); + return true; +} + +static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) +{ + static const u8 hdmi_header[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 + }; + struct adv7511_state *state = get_adv7511_state(sd); + u8 *data = state->edid.data; + + if (segment != 0) return true; - } - return false; + return !memcmp(data, hdmi_header, sizeof(hdmi_header)); } static bool adv7511_check_edid_status(struct v4l2_subdev *sd) @@ -990,9 +1025,10 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) state->edid.blocks = state->edid.data[0x7e] + 1; v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); } - if (!edid_segment_verify_crc(sd, segment)) { + if (!edid_verify_crc(sd, segment) || + !edid_verify_header(sd, segment)) { /* edid crc error, force reread of edid segment */ - v4l2_dbg(1, debug, sd, "%s: edid crc error\n", __func__); + v4l2_err(sd, "%s: edid crc or header error\n", __func__); state->have_monitor = false; adv7511_s_power(sd, false); adv7511_s_power(sd, true); @@ -1038,6 +1074,12 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) /* clear all interrupts */ adv7511_wr(sd, 0x96, 0xff); + /* + * Stop HPD from resetting a lot of registers. + * It might leave the chip in a partly un-initialized state, + * in particular with regards to hotplug bounces. + */ + adv7511_wr_and_or(sd, 0xd6, 0x3f, 0xc0); memset(edid, 0, sizeof(struct adv7511_state_edid)); state->have_monitor = false; adv7511_set_isr(sd, false); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index a324106b9f11..71c8570bd9ea 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -53,8 +53,6 @@ MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ #define ADV7604_fsc (28636360) -#define DIGITAL_INPUT (state->mode == ADV7604_MODE_HDMI) - /* ********************************************************************** * @@ -67,17 +65,19 @@ struct adv7604_state { struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; - enum adv7604_mode mode; + enum adv7604_input_port selected_input; struct v4l2_dv_timings timings; - u8 edid[256]; - unsigned edid_blocks; + struct { + u8 edid[256]; + u32 present; + unsigned blocks; + } edid; + u16 spa_port_a[2]; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; - bool connector_hdmi; bool restart_stdi_once; - u32 prev_input_status; /* i2c clients */ struct i2c_client *i2c_avlink; @@ -160,6 +160,7 @@ static const struct v4l2_dv_timings adv7604_timings[] = { V4L2_DV_BT_DMT_1792X1344P60, V4L2_DV_BT_DMT_1856X1392P60, V4L2_DV_BT_DMT_1920X1200P60_RB, + V4L2_DV_BT_DMT_1366X768P60_RB, V4L2_DV_BT_DMT_1366X768P60, V4L2_DV_BT_DMT_1920X1080P60, { }, @@ -507,57 +508,31 @@ static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) return 0; } -static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct adv7604_state *state = container_of(dwork, struct adv7604_state, - delayed_work_enable_hotplug); - struct v4l2_subdev *sd = &state->sd; - - v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1); -} - static inline int edid_write_block(struct v4l2_subdev *sd, unsigned len, const u8 *val) { - struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7604_state *state = to_state(sd); int err = 0; int i; v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); - - /* Disables I2C access to internal EDID ram from DDC port */ - rep_write_and_or(sd, 0x77, 0xf0, 0x0); - for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, I2C_SMBUS_BLOCK_MAX, val + i); - if (err) - return err; + return err; +} - /* adv7604 calculates the checksums and enables I2C access to internal - EDID ram from DDC port. */ - rep_write_and_or(sd, 0x77, 0xf0, 0x1); +static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct adv7604_state *state = container_of(dwork, struct adv7604_state, + delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; - for (i = 0; i < 1000; i++) { - if (rep_read(sd, 0x7d) & 1) - break; - mdelay(1); - } - if (i == 1000) { - v4l_err(client, "error enabling edid\n"); - return -EIO; - } + v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - /* enable hotplug after 100 ms */ - queue_delayed_work(state->work_queues, - &state->delayed_work_enable_hotplug, HZ / 10); - return 0; + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); } static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) @@ -574,6 +549,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); } +static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val); +} + static inline int test_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); @@ -623,6 +603,26 @@ static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) /* ----------------------------------------------------------------------- */ +static inline bool is_analog_input(struct v4l2_subdev *sd) +{ + struct adv7604_state *state = to_state(sd); + + return state->selected_input == ADV7604_INPUT_VGA_RGB || + state->selected_input == ADV7604_INPUT_VGA_COMP; +} + +static inline bool is_digital_input(struct v4l2_subdev *sd) +{ + struct adv7604_state *state = to_state(sd); + + return state->selected_input == ADV7604_INPUT_HDMI_PORT_A || + state->selected_input == ADV7604_INPUT_HDMI_PORT_B || + state->selected_input == ADV7604_INPUT_HDMI_PORT_C || + state->selected_input == ADV7604_INPUT_HDMI_PORT_D; +} + +/* ----------------------------------------------------------------------- */ + #ifdef CONFIG_VIDEO_ADV_DEBUG static void adv7604_inv_register(struct v4l2_subdev *sd) { @@ -696,45 +696,47 @@ static int adv7604_g_register(struct v4l2_subdev *sd, static int adv7604_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { + u8 val = reg->val & 0xff; + switch (reg->reg >> 8) { case 0: - io_write(sd, reg->reg & 0xff, reg->val & 0xff); + io_write(sd, reg->reg & 0xff, val); break; case 1: - avlink_write(sd, reg->reg & 0xff, reg->val & 0xff); + avlink_write(sd, reg->reg & 0xff, val); break; case 2: - cec_write(sd, reg->reg & 0xff, reg->val & 0xff); + cec_write(sd, reg->reg & 0xff, val); break; case 3: - infoframe_write(sd, reg->reg & 0xff, reg->val & 0xff); + infoframe_write(sd, reg->reg & 0xff, val); break; case 4: - esdp_write(sd, reg->reg & 0xff, reg->val & 0xff); + esdp_write(sd, reg->reg & 0xff, val); break; case 5: - dpp_write(sd, reg->reg & 0xff, reg->val & 0xff); + dpp_write(sd, reg->reg & 0xff, val); break; case 6: - afe_write(sd, reg->reg & 0xff, reg->val & 0xff); + afe_write(sd, reg->reg & 0xff, val); break; case 7: - rep_write(sd, reg->reg & 0xff, reg->val & 0xff); + rep_write(sd, reg->reg & 0xff, val); break; case 8: - edid_write(sd, reg->reg & 0xff, reg->val & 0xff); + edid_write(sd, reg->reg & 0xff, val); break; case 9: - hdmi_write(sd, reg->reg & 0xff, reg->val & 0xff); + hdmi_write(sd, reg->reg & 0xff, val); break; case 0xa: - test_write(sd, reg->reg & 0xff, reg->val & 0xff); + test_write(sd, reg->reg & 0xff, val); break; case 0xb: - cp_write(sd, reg->reg & 0xff, reg->val & 0xff); + cp_write(sd, reg->reg & 0xff, val); break; case 0xc: - vdp_write(sd, reg->reg & 0xff, reg->val & 0xff); + vdp_write(sd, reg->reg & 0xff, val); break; default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); @@ -748,10 +750,13 @@ static int adv7604_s_register(struct v4l2_subdev *sd, static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + u8 reg_io_6f = io_read(sd, 0x6f); - /* port A only */ return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, - ((io_read(sd, 0x6f) & 0x10) >> 4)); + ((reg_io_6f & 0x10) >> 4) | + ((reg_io_6f & 0x08) >> 2) | + (reg_io_6f & 0x04) | + ((reg_io_6f & 0x02) << 2)); } static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -759,12 +764,11 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, const struct adv7604_video_standards *predef_vid_timings, const struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); int i; for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, - DIGITAL_INPUT ? 250000 : 1000000)) + is_digital_input(sd) ? 250000 : 1000000)) continue; io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + @@ -799,27 +803,22 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, cp_write(sd, 0xab, 0x00); cp_write(sd, 0xac, 0x00); - switch (state->mode) { - case ADV7604_MODE_COMP: - case ADV7604_MODE_GR: + if (is_analog_input(sd)) { err = find_and_set_predefined_video_timings(sd, 0x01, adv7604_prim_mode_comp, timings); if (err) err = find_and_set_predefined_video_timings(sd, 0x02, adv7604_prim_mode_gr, timings); - break; - case ADV7604_MODE_HDMI: + } else if (is_digital_input(sd)) { err = find_and_set_predefined_video_timings(sd, 0x05, adv7604_prim_mode_hdmi_comp, timings); if (err) err = find_and_set_predefined_video_timings(sd, 0x06, adv7604_prim_mode_hdmi_gr, timings); - break; - default: - v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", - __func__, state->mode); + } else { + v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", + __func__, state->selected_input); err = -1; - break; } @@ -846,9 +845,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s\n", __func__); - switch (state->mode) { - case ADV7604_MODE_COMP: - case ADV7604_MODE_GR: + if (is_analog_input(sd)) { /* auto graphics */ io_write(sd, 0x00, 0x07); /* video std */ io_write(sd, 0x01, 0x02); /* prim mode */ @@ -858,33 +855,28 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { + if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); - break; - } /* active video - horizontal timing */ cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff); cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | - ((cp_start_eav >> 8) & 0x0f)); + ((cp_start_eav >> 8) & 0x0f)); cp_write(sd, 0xa4, cp_start_eav & 0xff); /* active video - vertical timing */ cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | - ((cp_end_vbi >> 8) & 0xf)); + ((cp_end_vbi >> 8) & 0xf)); cp_write(sd, 0xa7, cp_end_vbi & 0xff); - break; - case ADV7604_MODE_HDMI: + } else if (is_digital_input(sd)) { /* set default prim_mode/vid_std for HDMI according to [REF_03, c. 4.2] */ io_write(sd, 0x00, 0x02); /* video std */ io_write(sd, 0x01, 0x06); /* prim mode */ - break; - default: - v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", - __func__, state->mode); - break; + } else { + v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", + __func__, state->selected_input); } cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); @@ -893,43 +885,149 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, cp_write(sd, 0xac, (height & 0x0f) << 4); } +static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) +{ + struct adv7604_state *state = to_state(sd); + u8 offset_buf[4]; + + if (auto_offset) { + offset_a = 0x3ff; + offset_b = 0x3ff; + offset_c = 0x3ff; + } + + v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n", + __func__, auto_offset ? "Auto" : "Manual", + offset_a, offset_b, offset_c); + + offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4); + offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6); + offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8); + offset_buf[3] = offset_c & 0x0ff; + + /* Registers must be written in this order with no i2c access in between */ + if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf)) + v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); +} + +static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) +{ + struct adv7604_state *state = to_state(sd); + u8 gain_buf[4]; + u8 gain_man = 1; + u8 agc_mode_man = 1; + + if (auto_gain) { + gain_man = 0; + agc_mode_man = 0; + gain_a = 0x100; + gain_b = 0x100; + gain_c = 0x100; + } + + v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n", + __func__, auto_gain ? "Auto" : "Manual", + gain_a, gain_b, gain_c); + + gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4)); + gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6)); + gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8)); + gain_buf[3] = ((gain_c & 0x0ff)); + + /* Registers must be written in this order with no i2c access in between */ + if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf)) + v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); +} + static void set_rgb_quantization_range(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + bool rgb_output = io_read(sd, 0x02) & 0x02; + bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; + + v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", + __func__, state->rgb_quantization_range, + rgb_output, hdmi_signal); + + adv7604_set_gain(sd, true, 0x0, 0x0, 0x0); + adv7604_set_offset(sd, true, 0x0, 0x0, 0x0); switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: - /* automatic */ - if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) { - /* receiving DVI-D signal */ + if (state->selected_input == ADV7604_INPUT_VGA_RGB) { + /* Receiving analog RGB signal + * Set RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + break; + } + + if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + /* Receiving analog YPbPr signal + * Set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + if (hdmi_signal) { + /* Receiving HDMI signal + * Set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } - /* ADV7604 selects RGB limited range regardless of - input format (CE/IT) in automatic mode */ - if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + /* Receiving DVI-D signal + * ADV7604 selects RGB limited range regardless of + * input format (CE/IT) in automatic mode */ + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + if (is_digital_input(sd) && rgb_output) { + adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); } else { - /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); + adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); } - } else { - /* receiving HDMI or analog signal, set automode */ - io_write_and_or(sd, 0x02, 0x0f, 0xf0); } break; case V4L2_DV_RGB_RANGE_LIMITED: + if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + /* YCrCb limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x20); + break; + } + /* RGB limited range (16-235) */ io_write_and_or(sd, 0x02, 0x0f, 0x00); + break; case V4L2_DV_RGB_RANGE_FULL: + if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + /* YCrCb full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x60); + break; + } + /* RGB full range (0-255) */ io_write_and_or(sd, 0x02, 0x0f, 0x10); + + if (is_analog_input(sd) || hdmi_signal) + break; + + /* Adjust gain/offset for DVI-D signals only */ + if (rgb_output) { + adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); + } else { + adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); + adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); + } break; } } - static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -983,8 +1081,9 @@ static inline bool no_power(struct v4l2_subdev *sd) static inline bool no_signal_tmds(struct v4l2_subdev *sd) { - /* TODO port B, C and D */ - return !(io_read(sd, 0x6a) & 0x10); + struct adv7604_state *state = to_state(sd); + + return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input)); } static inline bool no_lock_tmds(struct v4l2_subdev *sd) @@ -1011,7 +1110,6 @@ static inline bool no_lock_stdi(struct v4l2_subdev *sd) static inline bool no_signal(struct v4l2_subdev *sd) { - struct adv7604_state *state = to_state(sd); bool ret; ret = no_power(sd); @@ -1019,7 +1117,7 @@ static inline bool no_signal(struct v4l2_subdev *sd) ret |= no_lock_stdi(sd); ret |= no_lock_sspd(sd); - if (DIGITAL_INPUT) { + if (is_digital_input(sd)) { ret |= no_lock_tmds(sd); ret |= no_signal_tmds(sd); } @@ -1036,13 +1134,11 @@ static inline bool no_lock_cp(struct v4l2_subdev *sd) static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status) { - struct adv7604_state *state = to_state(sd); - *status = 0; *status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0; *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; if (no_lock_cp(sd)) - *status |= DIGITAL_INPUT ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK; + *status |= is_digital_input(sd) ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK; v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); @@ -1157,13 +1253,11 @@ static int adv7604_enum_dv_timings(struct v4l2_subdev *sd, static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { - struct adv7604_state *state = to_state(sd); - cap->type = V4L2_DV_BT_656_1120; cap->bt.max_width = 1920; cap->bt.max_height = 1200; cap->bt.min_pixelclock = 25000000; - if (DIGITAL_INPUT) + if (is_digital_input(sd)) cap->bt.max_pixelclock = 225000000; else cap->bt.max_pixelclock = 170000000; @@ -1179,12 +1273,11 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { - struct adv7604_state *state = to_state(sd); int i; for (i = 0; adv7604_timings[i].bt.width; i++) { if (v4l2_match_dv_timings(timings, &adv7604_timings[i], - DIGITAL_INPUT ? 250000 : 1000000)) { + is_digital_input(sd) ? 250000 : 1000000)) { *timings = adv7604_timings[i]; break; } @@ -1204,6 +1297,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, memset(timings, 0, sizeof(struct v4l2_dv_timings)); if (no_signal(sd)) { + state->restart_stdi_once = true; v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); return -ENOLINK; } @@ -1216,7 +1310,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, bt->interlaced = stdi.interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - if (DIGITAL_INPUT) { + if (is_digital_input(sd)) { uint32_t freq; timings->type = V4L2_DV_BT_656_1120; @@ -1305,8 +1399,8 @@ found: return -ENOLINK; } - if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || - (DIGITAL_INPUT && bt->pixelclock > 225000000)) { + if ((is_analog_input(sd) && bt->pixelclock > 170000000) || + (is_digital_input(sd) && bt->pixelclock > 225000000)) { v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n", __func__, (u32)bt->pixelclock); return -ERANGE; @@ -1329,10 +1423,15 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, if (!timings) return -EINVAL; + if (v4l2_match_dv_timings(&state->timings, timings, 0)) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + bt = &timings->bt; - if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || - (DIGITAL_INPUT && bt->pixelclock > 225000000)) { + if ((is_analog_input(sd) && bt->pixelclock > 170000000) || + (is_digital_input(sd) && bt->pixelclock > 225000000)) { v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n", __func__, (u32)bt->pixelclock); return -ERANGE; @@ -1354,7 +1453,6 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, set_rgb_quantization_range(sd); - if (debug > 1) v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ", timings, true); @@ -1374,30 +1472,24 @@ static void enable_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - switch (state->mode) { - case ADV7604_MODE_COMP: - case ADV7604_MODE_GR: - /* enable */ + if (is_analog_input(sd)) { io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ - break; - case ADV7604_MODE_HDMI: - /* enable */ - hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ + } else if (is_digital_input(sd)) { + hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input); hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ - break; - default: - v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", - __func__, state->mode); - break; + hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */ + } else { + v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", + __func__, state->selected_input); } } static void disable_input(struct v4l2_subdev *sd) { - /* disable */ + hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio */ + msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ - hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ } @@ -1405,9 +1497,7 @@ static void select_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - switch (state->mode) { - case ADV7604_MODE_COMP: - case ADV7604_MODE_GR: + if (is_analog_input(sd)) { /* reset ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ @@ -1433,9 +1523,9 @@ static void select_input(struct v4l2_subdev *sd) cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ - break; + } else if (is_digital_input(sd)) { + hdmi_write(sd, 0x00, state->selected_input & 0x03); - case ADV7604_MODE_HDMI: /* set ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ @@ -1461,12 +1551,9 @@ static void select_input(struct v4l2_subdev *sd) cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ - - break; - default: - v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", - __func__, state->mode); - break; + } else { + v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", + __func__, state->selected_input); } } @@ -1475,9 +1562,13 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, { struct adv7604_state *state = to_state(sd); - v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input); + v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d", + __func__, input, state->selected_input); + + if (input == state->selected_input) + return 0; - state->mode = input; + state->selected_input = input; disable_input(sd); @@ -1516,36 +1607,47 @@ static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { - struct adv7604_state *state = to_state(sd); - u8 fmt_change, fmt_change_digital, tx_5v; - u32 input_status; + const u8 irq_reg_0x43 = io_read(sd, 0x43); + const u8 irq_reg_0x6b = io_read(sd, 0x6b); + const u8 irq_reg_0x70 = io_read(sd, 0x70); + u8 fmt_change_digital; + u8 fmt_change; + u8 tx_5v; + + if (irq_reg_0x43) + io_write(sd, 0x44, irq_reg_0x43); + if (irq_reg_0x70) + io_write(sd, 0x71, irq_reg_0x70); + if (irq_reg_0x6b) + io_write(sd, 0x6c, irq_reg_0x6b); + + v4l2_dbg(2, debug, sd, "%s: ", __func__); /* format change */ - fmt_change = io_read(sd, 0x43) & 0x98; - if (fmt_change) - io_write(sd, 0x44, fmt_change); - fmt_change_digital = DIGITAL_INPUT ? (io_read(sd, 0x6b) & 0xc0) : 0; - if (fmt_change_digital) - io_write(sd, 0x6c, fmt_change_digital); + fmt_change = irq_reg_0x43 & 0x98; + fmt_change_digital = is_digital_input(sd) ? (irq_reg_0x6b & 0xc0) : 0; + if (fmt_change || fmt_change_digital) { v4l2_dbg(1, debug, sd, "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", __func__, fmt_change, fmt_change_digital); - adv7604_g_input_status(sd, &input_status); - if (input_status != state->prev_input_status) { - v4l2_dbg(1, debug, sd, - "%s: input_status = 0x%x, prev_input_status = 0x%x\n", - __func__, input_status, state->prev_input_status); - state->prev_input_status = input_status; - v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); - } + v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); if (handled) *handled = true; } + /* HDMI/DVI mode */ + if (irq_reg_0x6b & 0x01) { + v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, + (io_read(sd, 0x6a) & 0x01) ? "HDMI" : "DVI"); + set_rgb_quantization_range(sd); + if (handled) + *handled = true; + } + /* tx 5v detect */ - tx_5v = io_read(sd, 0x70) & 0x10; + tx_5v = io_read(sd, 0x70) & 0x1e; if (tx_5v) { v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); io_write(sd, 0x71, tx_5v); @@ -1559,55 +1661,178 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) { struct adv7604_state *state = to_state(sd); + u8 *data = NULL; - if (edid->pad != 0) + if (edid->pad > ADV7604_EDID_PORT_D) return -EINVAL; if (edid->blocks == 0) return -EINVAL; - if (edid->start_block >= state->edid_blocks) + if (edid->blocks > 2) + return -EINVAL; + if (edid->start_block > 1) return -EINVAL; - if (edid->start_block + edid->blocks > state->edid_blocks) - edid->blocks = state->edid_blocks - edid->start_block; + if (edid->start_block == 1) + edid->blocks = 1; if (!edid->edid) return -EINVAL; - memcpy(edid->edid + edid->start_block * 128, - state->edid + edid->start_block * 128, + + if (edid->blocks > state->edid.blocks) + edid->blocks = state->edid.blocks; + + switch (edid->pad) { + case ADV7604_EDID_PORT_A: + case ADV7604_EDID_PORT_B: + case ADV7604_EDID_PORT_C: + case ADV7604_EDID_PORT_D: + if (state->edid.present & (1 << edid->pad)) + data = state->edid.edid; + break; + default: + return -EINVAL; + break; + } + if (!data) + return -ENODATA; + + memcpy(edid->edid, + data + edid->start_block * 128, edid->blocks * 128); return 0; } +static int get_edid_spa_location(const u8 *edid) +{ + u8 d; + + if ((edid[0x7e] != 1) || + (edid[0x80] != 0x02) || + (edid[0x81] != 0x03)) { + return -1; + } + + /* search Vendor Specific Data Block (tag 3) */ + d = edid[0x82] & 0x7f; + if (d > 4) { + int i = 0x84; + int end = 0x80 + d; + + do { + u8 tag = edid[i] >> 5; + u8 len = edid[i] & 0x1f; + + if ((tag == 3) && (len >= 5)) + return i + 4; + i += len + 1; + } while (i < end); + } + return -1; +} + static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) { struct adv7604_state *state = to_state(sd); + int spa_loc; + int tmp = 0; int err; + int i; - if (edid->pad != 0) + if (edid->pad > ADV7604_EDID_PORT_D) return -EINVAL; if (edid->start_block != 0) return -EINVAL; if (edid->blocks == 0) { - /* Pull down the hotplug pin */ - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); - /* Disables I2C access to internal EDID ram from DDC port */ - rep_write_and_or(sd, 0x77, 0xf0, 0x0); - state->edid_blocks = 0; + /* Disable hotplug and I2C access to EDID RAM from DDC port */ + state->edid.present &= ~(1 << edid->pad); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); + rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + /* Fall back to a 16:9 aspect ratio */ state->aspect_ratio.numerator = 16; state->aspect_ratio.denominator = 9; + + if (!state->edid.present) + state->edid.blocks = 0; + + v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n", + __func__, edid->pad, state->edid.present); return 0; } - if (edid->blocks > 2) + if (edid->blocks > 2) { + edid->blocks = 2; return -E2BIG; + } if (!edid->edid) return -EINVAL; - memcpy(state->edid, edid->edid, 128 * edid->blocks); - state->edid_blocks = edid->blocks; + + v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", + __func__, edid->pad, state->edid.present); + + /* Disable hotplug and I2C access to EDID RAM from DDC port */ + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); + rep_write_and_or(sd, 0x77, 0xf0, 0x00); + + spa_loc = get_edid_spa_location(edid->edid); + if (spa_loc < 0) + spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ + + switch (edid->pad) { + case ADV7604_EDID_PORT_A: + state->spa_port_a[0] = edid->edid[spa_loc]; + state->spa_port_a[1] = edid->edid[spa_loc + 1]; + break; + case ADV7604_EDID_PORT_B: + rep_write(sd, 0x70, edid->edid[spa_loc]); + rep_write(sd, 0x71, edid->edid[spa_loc + 1]); + break; + case ADV7604_EDID_PORT_C: + rep_write(sd, 0x72, edid->edid[spa_loc]); + rep_write(sd, 0x73, edid->edid[spa_loc + 1]); + break; + case ADV7604_EDID_PORT_D: + rep_write(sd, 0x74, edid->edid[spa_loc]); + rep_write(sd, 0x75, edid->edid[spa_loc + 1]); + break; + default: + return -EINVAL; + } + rep_write(sd, 0x76, spa_loc & 0xff); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); + + edid->edid[spa_loc] = state->spa_port_a[0]; + edid->edid[spa_loc + 1] = state->spa_port_a[1]; + + memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); + state->edid.blocks = edid->blocks; state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15], edid->edid[0x16]); - err = edid_write_block(sd, 128 * edid->blocks, state->edid); - if (err < 0) - v4l2_err(sd, "error %d writing edid\n", err); - return err; + state->edid.present |= 1 << edid->pad; + + err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); + if (err < 0) { + v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); + return err; + } + + /* adv7604 calculates the checksums and enables I2C access to internal + EDID RAM from DDC port. */ + rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + + for (i = 0; i < 1000; i++) { + if (rep_read(sd, 0x7d) & state->edid.present) + break; + mdelay(1); + } + if (i == 1000) { + v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); + return -EIO; + } + + + /* enable hotplug after 100 ms */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 10); + return 0; } /*********** avi info frame CEA-861-E **************/ @@ -1670,7 +1895,7 @@ static int adv7604_log_status(struct v4l2_subdev *sd) char *input_color_space_txt[16] = { "RGB limited range (16-235)", "RGB full range (0-255)", "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", - "XvYCC Bt.601", "XvYCC Bt.709", + "xvYCC Bt.601", "xvYCC Bt.709", "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "automatic" @@ -1689,16 +1914,20 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); - v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? - "HDMI" : (DIGITAL_INPUT ? "DVI-D" : "DVI-A")); - v4l2_info(sd, "EDID: %s\n", ((rep_read(sd, 0x7d) & 0x01) && - (rep_read(sd, 0x77) & 0x01)) ? "enabled" : "disabled "); + v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n", + ((rep_read(sd, 0x7d) & 0x01) ? "Yes" : "No"), + ((rep_read(sd, 0x7d) & 0x02) ? "Yes" : "No"), + ((rep_read(sd, 0x7d) & 0x04) ? "Yes" : "No"), + ((rep_read(sd, 0x7d) & 0x08) ? "Yes" : "No")); v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? "enabled" : "disabled"); v4l2_info(sd, "-----Signal status-----\n"); - v4l2_info(sd, "Cable detected (+5V power): %s\n", - (io_read(sd, 0x6f) & 0x10) ? "true" : "false"); + v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n", + ((io_read(sd, 0x6f) & 0x10) ? "Yes" : "No"), + ((io_read(sd, 0x6f) & 0x08) ? "Yes" : "No"), + ((io_read(sd, 0x6f) & 0x04) ? "Yes" : "No"), + ((io_read(sd, 0x6f) & 0x02) ? "Yes" : "No")); v4l2_info(sd, "TMDS signal detected: %s\n", no_signal_tmds(sd) ? "false" : "true"); v4l2_info(sd, "TMDS signal locked: %s\n", @@ -1744,11 +1973,14 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]); - if (!DIGITAL_INPUT) + if (!is_digital_input(sd)) return 0; v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); - v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); + v4l2_info(sd, "Digital video port selected: %c\n", + (hdmi_read(sd, 0x00) & 0x03) + 'A'); + v4l2_info(sd, "HDCP encrypted content: %s\n", + (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); v4l2_info(sd, "HDCP keys read: %s%s\n", (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no", (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); @@ -1894,10 +2126,16 @@ static int adv7604_core_init(struct v4l2_subdev *sd) pdata->replicate_av_codes << 1 | pdata->invert_cbcr << 0); - /* TODO from platform data */ cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ - io_write(sd, 0x06, 0xa6); /* positive VS and HS */ - io_write(sd, 0x14, 0x7f); /* Drive strength adjusted to max */ + + /* VS, HS polarities */ + io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | pdata->inv_hs_pol << 1); + + /* Adjust drive strength */ + io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 | + pdata->dr_str_clk << 2 | + pdata->dr_str_sync); + cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */ cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold - @@ -1907,6 +2145,11 @@ static int adv7604_core_init(struct v4l2_subdev *sd) cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution for digital formats */ + /* HDMI audio */ + hdmi_write_and_or(sd, 0x15, 0xfc, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */ + hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ + hdmi_write_and_or(sd, 0x68, 0xf9, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */ + /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ @@ -1917,8 +2160,8 @@ static int adv7604_core_init(struct v4l2_subdev *sd) io_write(sd, 0x40, 0xc2); /* Configure INT1 */ io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ - io_write(sd, 0x6e, 0xc0); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ - io_write(sd, 0x73, 0x10); /* Enable CABLE_DET_A_ST (+5v) interrupt */ + io_write(sd, 0x6e, 0xc1); /* Enable V_LOCKED, DE_REGEN_LCK, HDMI_MODE interrupts */ + io_write(sd, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */ return v4l2_ctrl_handler_setup(sd->ctrl_handler); } @@ -1964,6 +2207,8 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, static int adv7604_probe(struct i2c_client *client, const struct i2c_device_id *id) { + static const struct v4l2_dv_timings cea640x480 = + V4L2_DV_BT_CEA_640X480P59_94; struct adv7604_state *state; struct adv7604_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; @@ -1984,19 +2229,19 @@ static int adv7604_probe(struct i2c_client *client, /* initialize variables */ state->restart_stdi_once = true; - state->prev_input_status = ~0; + state->selected_input = ~0; /* platform data */ if (!pdata) { v4l_err(client, "No platform data!\n"); return -ENODEV; } - memcpy(&state->pdata, pdata, sizeof(state->pdata)); + state->pdata = *pdata; + state->timings = cea640x480; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7604_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - state->connector_hdmi = pdata->connector_hdmi; /* i2c access to adv7604? */ if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) { @@ -2020,7 +2265,7 @@ static int adv7604_probe(struct i2c_client *client, /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + V4L2_CID_DV_RX_POWER_PRESENT, 0, 0x0f, 0, 0); state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index b154f36740b4..9bbd6656fb8f 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -20,10 +20,13 @@ /* * References (c = chapter, p = page): - * REF_01 - Analog devices, ADV7842, Register Settings Recommendations, - * Revision 2.5, June 2010 - * REF_02 - Analog devices, Register map documentation, Documentation of - * the register maps, Software manual, Rev. F, June 2010 + * REF_01 - Analog devices, ADV7842, + * Register Settings Recommendations, Rev. 1.9, April 2011 + * REF_02 - Analog devices, Software User Guide, UG-206, + * ADV7842 I2C Register Maps, Rev. 0, November 2010 + * REF_03 - Analog devices, Hardware User Guide, UG-214, + * ADV7842 Fast Switching 2:1 HDMI 1.4 Receiver with 3D-Comb + * Decoder and Digitizer , Rev. 0, January 2011 */ @@ -61,6 +64,7 @@ MODULE_LICENSE("GPL"); */ struct adv7842_state { + struct adv7842_platform_data pdata; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -81,7 +85,7 @@ struct adv7842_state { bool is_cea_format; struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; - bool connector_hdmi; + bool restart_stdi_once; bool hdmi_port_a; /* i2c clients */ @@ -491,6 +495,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); } +static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val); +} + static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7842_state *state = to_state(sd); @@ -532,7 +541,7 @@ static void main_reset(struct v4l2_subdev *sd) adv_smbus_write_byte_no_check(client, 0xff, 0x80); - mdelay(2); + mdelay(5); } /* ----------------------------------------------------------------------- */ @@ -587,10 +596,10 @@ static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n", __func__, present); - if (present & 0x1) - mask |= 0x20; /* port A */ - if (present & 0x2) - mask |= 0x10; /* port B */ + if (present & (0x04 << ADV7842_EDID_PORT_A)) + mask |= 0x20; + if (present & (0x04 << ADV7842_EDID_PORT_B)) + mask |= 0x10; io_write_and_or(sd, 0x20, 0xcf, mask); } @@ -679,14 +688,12 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7842_state *state = to_state(sd); const u8 *val = state->hdmi_edid.edid; - u8 cur_mask = rep_read(sd, 0x77) & 0x0c; - u8 mask = port == 0 ? 0x4 : 0x8; int spa_loc = edid_spa_location(val); int err = 0; int i; - v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n", - __func__, port, spa_loc); + v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", + __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc); /* HPA disable on port A and B */ io_write_and_or(sd, 0x20, 0xcf, 0x00); @@ -694,6 +701,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) /* Disable I2C access to internal EDID ram from HDMI DDC ports */ rep_write_and_or(sd, 0x77, 0xf3, 0x00); + if (!state->hdmi_edid.present) + return 0; + /* edid segment pointer '0' for HDMI ports */ rep_write_and_or(sd, 0x77, 0xef, 0x00); @@ -703,44 +713,32 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) if (err) return err; - if (spa_loc > 0) { - if (port == 0) { - /* port A SPA */ - rep_write(sd, 0x72, val[spa_loc]); - rep_write(sd, 0x73, val[spa_loc + 1]); - } else { - /* port B SPA */ - rep_write(sd, 0x74, val[spa_loc]); - rep_write(sd, 0x75, val[spa_loc + 1]); - } - rep_write(sd, 0x76, spa_loc); + if (spa_loc < 0) + spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ + + if (port == ADV7842_EDID_PORT_A) { + rep_write(sd, 0x72, val[spa_loc]); + rep_write(sd, 0x73, val[spa_loc + 1]); } else { - /* default register values for SPA */ - if (port == 0) { - /* port A SPA */ - rep_write(sd, 0x72, 0); - rep_write(sd, 0x73, 0); - } else { - /* port B SPA */ - rep_write(sd, 0x74, 0); - rep_write(sd, 0x75, 0); - } - rep_write(sd, 0x76, 0xc0); + rep_write(sd, 0x74, val[spa_loc]); + rep_write(sd, 0x75, val[spa_loc + 1]); } - rep_write_and_or(sd, 0x77, 0xbf, 0x00); + rep_write(sd, 0x76, spa_loc & 0xff); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); /* Calculates the checksums and enables I2C access to internal * EDID ram from HDMI DDC ports */ - rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask); + rep_write_and_or(sd, 0x77, 0xf3, state->hdmi_edid.present); for (i = 0; i < 1000; i++) { - if (rep_read(sd, 0x7d) & mask) + if (rep_read(sd, 0x7d) & state->hdmi_edid.present) break; mdelay(1); } if (i == 1000) { - v4l_err(client, "error enabling edid on port %d\n", port); + v4l_err(client, "error enabling edid on port %c\n", + (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); return -EIO; } @@ -927,7 +925,7 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, cp_write(sd, 0x27, 0x00); cp_write(sd, 0x28, 0x00); cp_write(sd, 0x29, 0x00); - cp_write(sd, 0x8f, 0x00); + cp_write(sd, 0x8f, 0x40); cp_write(sd, 0x90, 0x00); cp_write(sd, 0xa5, 0x00); cp_write(sd, 0xa6, 0x00); @@ -1033,34 +1031,60 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); + v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n", + __func__, state->rgb_quantization_range); + switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: - /* automatic */ - if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { - /* receiving DVI-D signal */ - - /* ADV7842 selects RGB limited range regardless of - input format (CE/IT) in automatic mode */ - if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); - - } else { - /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); - } - } else { - /* receiving HDMI or analog signal, set automode */ + if (state->mode == ADV7842_MODE_RGB) { + /* Receiving analog RGB signal + * Set RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + break; + } + + if (state->mode == ADV7842_MODE_COMP) { + /* Receiving analog YPbPr signal + * Set automode */ io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + if (hdmi_read(sd, 0x05) & 0x80) { + /* Receiving HDMI signal + * Set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + /* Receiving DVI-D signal + * ADV7842 selects RGB limited range regardless of + * input format (CE/IT) in automatic mode */ + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); } break; case V4L2_DV_RGB_RANGE_LIMITED: - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + if (state->mode == ADV7842_MODE_COMP) { + /* YCrCb limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x20); + } else { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + } break; case V4L2_DV_RGB_RANGE_FULL: - /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + if (state->mode == ADV7842_MODE_COMP) { + /* YCrCb full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x60); + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } break; } } @@ -1298,7 +1322,7 @@ static int adv7842_dv_timings_cap(struct v4l2_subdev *sd, } /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings - if the format is listed in adv7604_timings[] */ + if the format is listed in adv7842_timings[] */ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { @@ -1314,119 +1338,106 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt = &timings->bt; struct stdi_readback stdi = { 0 }; + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + /* SDP block */ if (state->mode == ADV7842_MODE_SDP) return -ENODATA; /* read STDI */ if (read_stdi(sd, &stdi)) { + state->restart_stdi_once = true; v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); return -ENOLINK; } bt->interlaced = stdi.interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | - ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); - bt->vsync = stdi.lcvs; if (is_digital_input(sd)) { - bool lock = hdmi_read(sd, 0x04) & 0x02; - bool interlaced = hdmi_read(sd, 0x0b) & 0x20; - unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08); - unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a); - unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 + - hdmi_read(sd, 0x1f); - unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 + - hdmi_read(sd, 0x27)) / 2; - unsigned freq = (((hdmi_read(sd, 0x51) << 1) + - (hdmi_read(sd, 0x52) >> 7)) * 1000000) + - ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; - int i; + uint32_t freq; - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8); - } - - /* No lock? */ - if (!lock) { - v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__); - return -ENOLCK; - } - /* Interlaced? */ - if (interlaced) { - v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); - return -ERANGE; - } - - for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - - if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], - adv7842_get_dv_timings_cap(sd), - adv7842_check_dv_timings, NULL)) - continue; - if (w_total != htotal(bt) || h_total != vtotal(bt)) - continue; + timings->type = V4L2_DV_BT_656_1120; - if (w != bt->width || h != bt->height) - continue; + bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); + bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); + freq = (hdmi_read(sd, 0x06) * 1000000) + + ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; - if (abs(freq - bt->pixelclock) > 1000000) - continue; - *timings = v4l2_dv_timings_presets[i]; - return 0; + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8); } - - timings->type = V4L2_DV_BT_656_1120; - - bt->width = w; - bt->height = h; - bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ? - V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? - V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? - V4L2_DV_HSYNC_POS_POL : 0); - bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) + - (hdmi_read(sd, 0x52) >> 7)) * 1000000) + - ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; - bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 + + bt->pixelclock = freq; + bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + hdmi_read(sd, 0x21); - bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 + + bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + hdmi_read(sd, 0x23); - bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 + + bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 + hdmi_read(sd, 0x25); - bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 + - hdmi_read(sd, 0x2b)) / 2; - bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 + - hdmi_read(sd, 0x2d)) / 2; - bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 + - hdmi_read(sd, 0x2f)) / 2; - bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 + - hdmi_read(sd, 0x31)) / 2; - bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 + - hdmi_read(sd, 0x33)) / 2; - bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 + - hdmi_read(sd, 0x35)) / 2; - - bt->standards = 0; - bt->flags = 0; - } else { - /* Interlaced? */ - if (stdi.interlaced) { - v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); - return -ERANGE; + bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + + hdmi_read(sd, 0x2b)) / 2; + bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + + hdmi_read(sd, 0x2f)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + + hdmi_read(sd, 0x33)) / 2; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | + ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); + if (bt->interlaced == V4L2_DV_INTERLACED) { + bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + + hdmi_read(sd, 0x0c); + bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + + hdmi_read(sd, 0x2d)) / 2; + bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + + hdmi_read(sd, 0x31)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + + hdmi_read(sd, 0x35)) / 2; } - + adv7842_fill_optional_dv_timings_fields(sd, timings); + } else { + /* find format + * Since LCVS values are inaccurate [REF_03, p. 339-340], + * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. + */ + if (!stdi2dv_timings(sd, &stdi, timings)) + goto found; + stdi.lcvs += 1; + v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs); + if (!stdi2dv_timings(sd, &stdi, timings)) + goto found; + stdi.lcvs -= 2; + v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs); if (stdi2dv_timings(sd, &stdi, timings)) { + /* + * The STDI block may measure wrong values, especially + * for lcvs and lcf. If the driver can not find any + * valid timing, the STDI block is restarted to measure + * the video timings again. The function will return an + * error, but the restart of STDI will generate a new + * STDI interrupt and the format detection process will + * restart. + */ + if (state->restart_stdi_once) { + v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); + /* TODO restart STDI for Sync Channel 2 */ + /* enter one-shot mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x00); + /* trigger STDI restart */ + cp_write_and_or(sd, 0x86, 0xf9, 0x04); + /* reset to continuous mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x02); + state->restart_stdi_once = false; + return -ENOLINK; + } v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); return -ERANGE; } + state->restart_stdi_once = true; } +found: if (debug > 1) - v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ", - timings, true); + v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings:", + timings, true); return 0; } @@ -1437,9 +1448,16 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt; int err; + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + if (state->mode == ADV7842_MODE_SDP) return -ENODATA; + if (v4l2_match_dv_timings(&state->timings, timings, 0)) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + bt = &timings->bt; if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd), @@ -1450,7 +1468,7 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, state->timings = *timings; - cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + cp_write(sd, 0x91, bt->interlaced ? 0x40 : 0x00); /* Use prim_mode and vid_std when available */ err = configure_predefined_video_timings(sd, timings); @@ -1483,18 +1501,18 @@ static int adv7842_g_dv_timings(struct v4l2_subdev *sd, static void enable_input(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); + + set_rgb_quantization_range(sd); switch (state->mode) { case ADV7842_MODE_SDP: case ADV7842_MODE_COMP: case ADV7842_MODE_RGB: - /* enable */ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ break; case ADV7842_MODE_HDMI: - /* enable */ - hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ + hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */ break; default: v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", @@ -1505,9 +1523,9 @@ static void enable_input(struct v4l2_subdev *sd) static void disable_input(struct v4l2_subdev *sd) { - /* disable */ + hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio [REF_01, c. 2.2.2] */ + msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 8.29] */ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ - hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ } @@ -1575,9 +1593,6 @@ static void select_input(struct v4l2_subdev *sd, afe_write(sd, 0x00, 0x00); /* power up ADC */ afe_write(sd, 0xc8, 0x00); /* phase control */ - io_write(sd, 0x19, 0x83); /* LLC DLL phase */ - io_write(sd, 0x33, 0x40); /* LLC DLL enable */ - io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */ /* script says register 0xde, which don't exist in manual */ @@ -1611,8 +1626,6 @@ static void select_input(struct v4l2_subdev *sd, /* deinterlacer enabled and 3D comb */ sdp_write_and_or(sd, 0x12, 0xf6, 0x09); - sdp_write(sd, 0xdd, 0x08); /* free run auto */ - break; case ADV7842_MODE_COMP: @@ -1627,6 +1640,13 @@ static void select_input(struct v4l2_subdev *sd, afe_write(sd, 0x00, 0x00); /* power up ADC */ afe_write(sd, 0xc8, 0x00); /* phase control */ + if (state->mode == ADV7842_MODE_COMP) { + /* force to YCrCb */ + io_write_and_or(sd, 0x02, 0x0f, 0x60); + } else { + /* force to RGB */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } /* set ADI recommended settings for digitizer */ /* "ADV7842 Register Settings Recommendations @@ -1722,19 +1742,19 @@ static int adv7842_s_routing(struct v4l2_subdev *sd, switch (input) { case ADV7842_SELECT_HDMI_PORT_A: - /* TODO select HDMI_COMP or HDMI_GR */ state->mode = ADV7842_MODE_HDMI; state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; state->hdmi_port_a = true; break; case ADV7842_SELECT_HDMI_PORT_B: - /* TODO select HDMI_COMP or HDMI_GR */ state->mode = ADV7842_MODE_HDMI; state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; state->hdmi_port_a = false; break; case ADV7842_SELECT_VGA_COMP: - v4l2_info(sd, "%s: VGA component: todo\n", __func__); + state->mode = ADV7842_MODE_COMP; + state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; + break; case ADV7842_SELECT_VGA_RGB: state->mode = ADV7842_MODE_RGB; state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; @@ -1814,12 +1834,15 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable) io_write(sd, 0x78, 0x03); /* Enable SDP Standard Detection Change and SDP Video Detected */ io_write(sd, 0xa0, 0x09); + /* Enable HDMI_MODE interrupt */ + io_write(sd, 0x69, 0x08); } else { io_write(sd, 0x46, 0x0); io_write(sd, 0x5a, 0x0); io_write(sd, 0x73, 0x0); io_write(sd, 0x78, 0x0); io_write(sd, 0xa0, 0x0); + io_write(sd, 0x69, 0x0); } } @@ -1827,11 +1850,9 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7842_state *state = to_state(sd); u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp; - u8 irq_status[5]; - u8 irq_cfg = io_read(sd, 0x40); + u8 irq_status[6]; - /* disable irq-pin output */ - io_write(sd, 0x40, irq_cfg | 0x3); + adv7842_irq_enable(sd, false); /* read status */ irq_status[0] = io_read(sd, 0x43); @@ -1839,6 +1860,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) irq_status[2] = io_read(sd, 0x70); irq_status[3] = io_read(sd, 0x75); irq_status[4] = io_read(sd, 0x9d); + irq_status[5] = io_read(sd, 0x66); /* and clear */ if (irq_status[0]) @@ -1851,10 +1873,14 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) io_write(sd, 0x76, irq_status[3]); if (irq_status[4]) io_write(sd, 0x9e, irq_status[4]); + if (irq_status[5]) + io_write(sd, 0x67, irq_status[5]); - v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, + adv7842_irq_enable(sd, true); + + v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x, %x\n", __func__, irq_status[0], irq_status[1], irq_status[2], - irq_status[3], irq_status[4]); + irq_status[3], irq_status[4], irq_status[5]); /* format change CP */ fmt_change_cp = irq_status[0] & 0x9c; @@ -1871,25 +1897,72 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) else fmt_change_digital = 0; - /* notify */ + /* format change */ if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) { v4l2_dbg(1, debug, sd, "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", __func__, fmt_change_cp, fmt_change_digital, fmt_change_sdp); v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + if (handled) + *handled = true; } - /* 5v cable detect */ - if (irq_status[2]) + /* HDMI/DVI mode */ + if (irq_status[5] & 0x08) { + v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, + (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI"); + if (handled) + *handled = true; + } + + /* tx 5v detect */ + if (irq_status[2] & 0x3) { + v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__); adv7842_s_detect_tx_5v_ctrl(sd); + if (handled) + *handled = true; + } + return 0; +} + +static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + struct adv7842_state *state = to_state(sd); + u8 *data = NULL; - if (handled) - *handled = true; + if (edid->pad > ADV7842_EDID_PORT_VGA) + return -EINVAL; + if (edid->blocks == 0) + return -EINVAL; + if (edid->blocks > 2) + return -EINVAL; + if (edid->start_block > 1) + return -EINVAL; + if (edid->start_block == 1) + edid->blocks = 1; + if (!edid->edid) + return -EINVAL; - /* re-enable irq-pin output */ - io_write(sd, 0x40, irq_cfg); + switch (edid->pad) { + case ADV7842_EDID_PORT_A: + case ADV7842_EDID_PORT_B: + if (state->hdmi_edid.present & (0x04 << edid->pad)) + data = state->hdmi_edid.edid; + break; + case ADV7842_EDID_PORT_VGA: + if (state->vga_edid.present) + data = state->vga_edid.edid; + break; + default: + return -EINVAL; + } + if (!data) + return -ENODATA; + memcpy(edid->edid, + data + edid->start_block * 128, + edid->blocks * 128); return 0; } @@ -1898,7 +1971,7 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) struct adv7842_state *state = to_state(sd); int err = 0; - if (e->pad > 2) + if (e->pad > ADV7842_EDID_PORT_VGA) return -EINVAL; if (e->start_block != 0) return -EINVAL; @@ -1911,20 +1984,25 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], e->edid[0x16]); - if (e->pad == 2) { + switch (e->pad) { + case ADV7842_EDID_PORT_VGA: memset(&state->vga_edid.edid, 0, 256); state->vga_edid.present = e->blocks ? 0x1 : 0x0; memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); err = edid_write_vga_segment(sd); - } else { - u32 mask = 0x1<<e->pad; + break; + case ADV7842_EDID_PORT_A: + case ADV7842_EDID_PORT_B: memset(&state->hdmi_edid.edid, 0, 256); if (e->blocks) - state->hdmi_edid.present |= mask; + state->hdmi_edid.present |= 0x04 << e->pad; else - state->hdmi_edid.present &= ~mask; - memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks); + state->hdmi_edid.present &= ~(0x04 << e->pad); + memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); err = edid_write_hdmi_segment(sd, e->pad); + break; + default: + return -EINVAL; } if (err < 0) v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad); @@ -2156,7 +2234,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) static const char * const input_color_space_txt[16] = { "RGB limited range (16-235)", "RGB full range (0-255)", "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", - "XvYCC Bt.601", "XvYCC Bt.709", + "xvYCC Bt.601", "xvYCC Bt.709", "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "automatic" @@ -2175,8 +2253,6 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); - v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? - "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); v4l2_info(sd, "HDMI/DVI-D port selected: %s\n", state->hdmi_port_a ? "A" : "B"); v4l2_info(sd, "EDID A %s, B %s\n", @@ -2354,15 +2430,63 @@ static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) return 0; } +static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_adjustment *s) +{ + if (s && s->adjust) { + sdp_io_write(sd, 0x94, (s->hs_beg >> 8) & 0xf); + sdp_io_write(sd, 0x95, s->hs_beg & 0xff); + sdp_io_write(sd, 0x96, (s->hs_width >> 8) & 0xf); + sdp_io_write(sd, 0x97, s->hs_width & 0xff); + sdp_io_write(sd, 0x98, (s->de_beg >> 8) & 0xf); + sdp_io_write(sd, 0x99, s->de_beg & 0xff); + sdp_io_write(sd, 0x9a, (s->de_end >> 8) & 0xf); + sdp_io_write(sd, 0x9b, s->de_end & 0xff); + sdp_io_write(sd, 0xa8, s->vs_beg_o); + sdp_io_write(sd, 0xa9, s->vs_beg_e); + sdp_io_write(sd, 0xaa, s->vs_end_o); + sdp_io_write(sd, 0xab, s->vs_end_e); + sdp_io_write(sd, 0xac, s->de_v_beg_o); + sdp_io_write(sd, 0xad, s->de_v_beg_e); + sdp_io_write(sd, 0xae, s->de_v_end_o); + sdp_io_write(sd, 0xaf, s->de_v_end_e); + } else { + /* set to default */ + sdp_io_write(sd, 0x94, 0x00); + sdp_io_write(sd, 0x95, 0x00); + sdp_io_write(sd, 0x96, 0x00); + sdp_io_write(sd, 0x97, 0x20); + sdp_io_write(sd, 0x98, 0x00); + sdp_io_write(sd, 0x99, 0x00); + sdp_io_write(sd, 0x9a, 0x00); + sdp_io_write(sd, 0x9b, 0x00); + sdp_io_write(sd, 0xa8, 0x04); + sdp_io_write(sd, 0xa9, 0x04); + sdp_io_write(sd, 0xaa, 0x04); + sdp_io_write(sd, 0xab, 0x04); + sdp_io_write(sd, 0xac, 0x04); + sdp_io_write(sd, 0xad, 0x04); + sdp_io_write(sd, 0xae, 0x04); + sdp_io_write(sd, 0xaf, 0x04); + } +} + static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; v4l2_dbg(1, debug, sd, "%s:\n", __func__); if (state->mode != ADV7842_MODE_SDP) return -ENODATA; + if (norm & V4L2_STD_625_50) + adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_625); + else if (norm & V4L2_STD_525_60) + adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_525); + else + adv7842_s_sdp_io(sd, NULL); + if (norm & V4L2_STD_ALL) { state->norm = norm; return 0; @@ -2385,9 +2509,10 @@ static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) /* ----------------------------------------------------------------------- */ -static int adv7842_core_init(struct v4l2_subdev *sd, - const struct adv7842_platform_data *pdata) +static int adv7842_core_init(struct v4l2_subdev *sd) { + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; hdmi_write(sd, 0x48, (pdata->disable_pwrdnb ? 0x80 : 0) | (pdata->disable_cable_det_rst ? 0x40 : 0)); @@ -2400,7 +2525,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd, /* video format */ io_write(sd, 0x02, - pdata->inp_color_space << 4 | + 0xf0 | pdata->alt_gamma << 3 | pdata->op_656_range << 2 | pdata->rgb_out << 1 | @@ -2412,13 +2537,24 @@ static int adv7842_core_init(struct v4l2_subdev *sd, pdata->replicate_av_codes << 1 | pdata->invert_cbcr << 0); + /* HDMI audio */ + hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ + /* Drive strength */ - io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 | - pdata->drive_strength.clock<<2 | - pdata->drive_strength.sync); + io_write_and_or(sd, 0x14, 0xc0, + pdata->dr_str_data << 4 | + pdata->dr_str_clk << 2 | + pdata->dr_str_sync); /* HDMI free run */ - cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); + cp_write_and_or(sd, 0xba, 0xfc, pdata->hdmi_free_run_enable | + (pdata->hdmi_free_run_mode << 1)); + + /* SPD free run */ + sdp_write_and_or(sd, 0xdd, 0xf0, pdata->sdp_free_run_force | + (pdata->sdp_free_run_cbar_en << 1) | + (pdata->sdp_free_run_man_col_en << 2) | + (pdata->sdp_free_run_auto << 3)); /* TODO from platform data */ cp_write(sd, 0x69, 0x14); /* Enable CP CSC */ @@ -2431,18 +2567,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd, sdp_csc_coeff(sd, &pdata->sdp_csc_coeff); - if (pdata->sdp_io_sync.adjust) { - const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync; - sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf); - sdp_io_write(sd, 0x95, s->hs_beg & 0xff); - sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf); - sdp_io_write(sd, 0x97, s->hs_width & 0xff); - sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf); - sdp_io_write(sd, 0x99, s->de_beg & 0xff); - sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf); - sdp_io_write(sd, 0x9b, s->de_end & 0xff); - } - /* todo, improve settings for sdram */ if (pdata->sd_ram_size >= 128) { sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ @@ -2483,12 +2607,11 @@ static int adv7842_core_init(struct v4l2_subdev *sd, io_write_and_or(sd, 0x20, 0xcf, 0x00); /* LLC */ - /* Set phase to 16. TODO: get this from platform_data */ - io_write(sd, 0x19, 0x90); + io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase); io_write(sd, 0x33, 0x40); /* interrupts */ - io_write(sd, 0x40, 0xe2); /* Configure INT1 */ + io_write(sd, 0x40, 0xf2); /* Configure INT1 */ adv7842_irq_enable(sd, true); @@ -2588,6 +2711,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7842_state *state = to_state(sd); struct adv7842_platform_data *pdata = client->dev.platform_data; + struct v4l2_dv_timings timings; int ret = 0; if (!pdata) @@ -2610,7 +2734,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) adv7842_rewrite_i2c_addresses(sd, pdata); /* and re-init chip and state */ - adv7842_core_init(sd, pdata); + adv7842_core_init(sd); disable_input(sd); @@ -2618,11 +2742,15 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) enable_input(sd); - adv7842_s_dv_timings(sd, &state->timings); - edid_write_vga_segment(sd); - edid_write_hdmi_segment(sd, 0); - edid_write_hdmi_segment(sd, 1); + edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_A); + edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_B); + + timings = state->timings; + + memset(&state->timings, 0, sizeof(struct v4l2_dv_timings)); + + adv7842_s_dv_timings(sd, &timings); return ret; } @@ -2670,6 +2798,7 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { }; static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { + .get_edid = adv7842_get_edid, .set_edid = adv7842_set_edid, }; @@ -2712,8 +2841,9 @@ static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = { }; -static void adv7842_unregister_clients(struct adv7842_state *state) +static void adv7842_unregister_clients(struct v4l2_subdev *sd) { + struct adv7842_state *state = to_state(sd); if (state->i2c_avlink) i2c_unregister_device(state->i2c_avlink); if (state->i2c_cec) @@ -2736,21 +2866,79 @@ static void adv7842_unregister_clients(struct adv7842_state *state) i2c_unregister_device(state->i2c_cp); if (state->i2c_vdp) i2c_unregister_device(state->i2c_vdp); + + state->i2c_avlink = NULL; + state->i2c_cec = NULL; + state->i2c_infoframe = NULL; + state->i2c_sdp_io = NULL; + state->i2c_sdp = NULL; + state->i2c_afe = NULL; + state->i2c_repeater = NULL; + state->i2c_edid = NULL; + state->i2c_hdmi = NULL; + state->i2c_cp = NULL; + state->i2c_vdp = NULL; } -static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, +static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const char *desc, u8 addr, u8 io_reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_client *cp; io_write(sd, io_reg, addr << 1); - return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + + if (addr == 0) { + v4l2_err(sd, "no %s i2c addr configured\n", desc); + return NULL; + } + + cp = i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + if (!cp) + v4l2_err(sd, "register %s on i2c addr 0x%x failed\n", desc, addr); + + return cp; +} + +static int adv7842_register_clients(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; + + state->i2c_avlink = adv7842_dummy_client(sd, "avlink", pdata->i2c_avlink, 0xf3); + state->i2c_cec = adv7842_dummy_client(sd, "cec", pdata->i2c_cec, 0xf4); + state->i2c_infoframe = adv7842_dummy_client(sd, "infoframe", pdata->i2c_infoframe, 0xf5); + state->i2c_sdp_io = adv7842_dummy_client(sd, "sdp_io", pdata->i2c_sdp_io, 0xf2); + state->i2c_sdp = adv7842_dummy_client(sd, "sdp", pdata->i2c_sdp, 0xf1); + state->i2c_afe = adv7842_dummy_client(sd, "afe", pdata->i2c_afe, 0xf8); + state->i2c_repeater = adv7842_dummy_client(sd, "repeater", pdata->i2c_repeater, 0xf9); + state->i2c_edid = adv7842_dummy_client(sd, "edid", pdata->i2c_edid, 0xfa); + state->i2c_hdmi = adv7842_dummy_client(sd, "hdmi", pdata->i2c_hdmi, 0xfb); + state->i2c_cp = adv7842_dummy_client(sd, "cp", pdata->i2c_cp, 0xfd); + state->i2c_vdp = adv7842_dummy_client(sd, "vdp", pdata->i2c_vdp, 0xfe); + + if (!state->i2c_avlink || + !state->i2c_cec || + !state->i2c_infoframe || + !state->i2c_sdp_io || + !state->i2c_sdp || + !state->i2c_afe || + !state->i2c_repeater || + !state->i2c_edid || + !state->i2c_hdmi || + !state->i2c_cp || + !state->i2c_vdp) + return -1; + + return 0; } static int adv7842_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7842_state *state; + static const struct v4l2_dv_timings cea640x480 = + V4L2_DV_BT_CEA_640X480P59_94; struct adv7842_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; @@ -2775,13 +2963,17 @@ static int adv7842_probe(struct i2c_client *client, return -ENOMEM; } + /* platform data */ + state->pdata = *pdata; + state->timings = cea640x480; + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - state->connector_hdmi = pdata->connector_hdmi; state->mode = pdata->mode; - state->hdmi_port_a = true; + state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; + state->restart_stdi_once = true; /* i2c access to adv7842? */ rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | @@ -2843,21 +3035,7 @@ static int adv7842_probe(struct i2c_client *client, goto err_hdl; } - state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3); - state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4); - state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5); - state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2); - state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1); - state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8); - state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9); - state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa); - state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb); - state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd); - state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe); - if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || - !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe || - !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || - !state->i2c_cp || !state->i2c_vdp) { + if (adv7842_register_clients(sd) < 0) { err = -ENOMEM; v4l2_err(sd, "failed to create all i2c clients\n"); goto err_i2c; @@ -2879,7 +3057,7 @@ static int adv7842_probe(struct i2c_client *client, if (err) goto err_work_queues; - err = adv7842_core_init(sd, pdata); + err = adv7842_core_init(sd); if (err) goto err_entity; @@ -2893,7 +3071,7 @@ err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); err_i2c: - adv7842_unregister_clients(state); + adv7842_unregister_clients(sd); err_hdl: v4l2_ctrl_handler_free(hdl); return err; @@ -2912,7 +3090,7 @@ static int adv7842_remove(struct i2c_client *client) destroy_workqueue(state->work_queues); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - adv7842_unregister_clients(to_state(sd)); + adv7842_unregister_clients(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); return 0; } diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 3317a9ae3961..d98ca3aebe23 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -172,28 +172,28 @@ static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash, static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) { struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); + int rval = -EINVAL; mutex_lock(&flash->lock); if (ctrl->id == V4L2_CID_FLASH_FAULT) { - int rval; s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); if (rval < 0) - return rval; - if (rval & FAULT_SHORT_CIRCUIT) + goto out; + if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; - if (rval & FAULT_OVERTEMP) + if (reg_val & FAULT_OVERTEMP) fault |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; - if (rval & FAULT_TIMEOUT) + if (reg_val & FAULT_TIMEOUT) fault |= V4L2_FLASH_FAULT_TIMEOUT; ctrl->cur.val = fault; - return 0; } +out: mutex_unlock(&flash->lock); - return -EINVAL; + return rval; } static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) @@ -219,15 +219,19 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; case V4L2_CID_FLASH_STROBE: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { + rval = -EBUSY; + goto err_out; + } flash->led_mode = V4L2_FLASH_LED_MODE_FLASH; rval = lm3560_mode_ctrl(flash); break; case V4L2_CID_FLASH_STROBE_STOP: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { + rval = -EBUSY; + goto err_out; + } flash->led_mode = V4L2_FLASH_LED_MODE_NONE; rval = lm3560_mode_ctrl(flash); break; @@ -247,8 +251,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } - mutex_unlock(&flash->lock); err_out: + mutex_unlock(&flash->lock); return rval; } @@ -444,14 +448,14 @@ static int lm3560_probe(struct i2c_client *client, if (rval < 0) return rval; + i2c_set_clientdata(client, flash); + return 0; } static int lm3560_remove(struct i2c_client *client) { - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct lm3560_flash *flash = container_of(subdev, struct lm3560_flash, - subdev_led[LM3560_LED_MAX]); + struct lm3560_flash *flash = i2c_get_clientdata(client); unsigned int i; for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) { diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 846b15f0bf64..85ec3bacdf1c 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -459,13 +459,15 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, MT9M032_COLUMN_START_MAX); rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, MT9M032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, - MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, - MT9M032_ROW_SIZE_MAX); - - rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); + + rect.width = min_t(unsigned int, rect.width, + MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 1c2303d18bf4..e5ddf47030fd 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -519,11 +519,13 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, /* Clamp the width and height to avoid dividing by zero. */ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), + max_t(unsigned int, __crop->width / 7, + MT9P031_WINDOW_WIDTH_MIN), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), - __crop->height); + max_t(unsigned int, __crop->height / 8, + MT9P031_WINDOW_HEIGHT_MIN), + __crop->height); hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); @@ -565,15 +567,17 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, MT9P031_COLUMN_START_MAX); rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, MT9P031_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9P031_WINDOW_WIDTH_MIN, - MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9P031_WINDOW_HEIGHT_MIN, - MT9P031_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9P031_WINDOW_WIDTH_MIN, + MT9P031_WINDOW_WIDTH_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9P031_WINDOW_HEIGHT_MIN, + MT9P031_WINDOW_HEIGHT_MAX); + + rect.width = min_t(unsigned int, rect.width, + MT9P031_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 796463466ef0..d41c70eaf838 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -291,10 +291,12 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, /* Clamp the width and height to avoid dividing by zero. */ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + max_t(unsigned int, __crop->width / 8, + MT9T001_WINDOW_HEIGHT_MIN + 1), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + max_t(unsigned int, __crop->height / 8, + MT9T001_WINDOW_HEIGHT_MIN + 1), __crop->height); hratio = DIV_ROUND_CLOSEST(__crop->width, width); @@ -339,15 +341,17 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev, rect.top = clamp(ALIGN(crop->rect.top, 2), MT9T001_ROW_START_MIN, MT9T001_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9T001_WINDOW_WIDTH_MIN + 1, - MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9T001_WINDOW_HEIGHT_MIN + 1, - MT9T001_WINDOW_HEIGHT_MAX + 1); - - rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9T001_WINDOW_WIDTH_MIN + 1, + MT9T001_WINDOW_WIDTH_MAX + 1); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9T001_WINDOW_HEIGHT_MIN + 1, + MT9T001_WINDOW_HEIGHT_MAX + 1); + + rect.width = min_t(unsigned int, rect.width, + MT9T001_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 2c50effaa334..36c504b78f2c 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -27,14 +27,16 @@ #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -#define MT9V032_PIXEL_ARRAY_HEIGHT 492 -#define MT9V032_PIXEL_ARRAY_WIDTH 782 +/* The first four rows are black rows. The active area spans 753x481 pixels. */ +#define MT9V032_PIXEL_ARRAY_HEIGHT 485 +#define MT9V032_PIXEL_ARRAY_WIDTH 753 #define MT9V032_SYSCLK_FREQ_DEF 26600000 #define MT9V032_CHIP_VERSION 0x00 #define MT9V032_CHIP_ID_REV1 0x1311 #define MT9V032_CHIP_ID_REV3 0x1313 +#define MT9V034_CHIP_ID_REV1 0X1324 #define MT9V032_COLUMN_START 0x01 #define MT9V032_COLUMN_START_MIN 1 #define MT9V032_COLUMN_START_DEF 1 @@ -53,12 +55,15 @@ #define MT9V032_WINDOW_WIDTH_MAX 752 #define MT9V032_HORIZONTAL_BLANKING 0x05 #define MT9V032_HORIZONTAL_BLANKING_MIN 43 +#define MT9V034_HORIZONTAL_BLANKING_MIN 61 #define MT9V032_HORIZONTAL_BLANKING_DEF 94 #define MT9V032_HORIZONTAL_BLANKING_MAX 1023 #define MT9V032_VERTICAL_BLANKING 0x06 #define MT9V032_VERTICAL_BLANKING_MIN 4 +#define MT9V034_VERTICAL_BLANKING_MIN 2 #define MT9V032_VERTICAL_BLANKING_DEF 45 #define MT9V032_VERTICAL_BLANKING_MAX 3000 +#define MT9V034_VERTICAL_BLANKING_MAX 32288 #define MT9V032_CHIP_CONTROL 0x07 #define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) #define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) @@ -68,8 +73,10 @@ #define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a #define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b #define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 +#define MT9V034_TOTAL_SHUTTER_WIDTH_MIN 0 #define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 #define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 +#define MT9V034_TOTAL_SHUTTER_WIDTH_MAX 32765 #define MT9V032_RESET 0x0c #define MT9V032_READ_MODE 0x0d #define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) @@ -81,6 +88,8 @@ #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) #define MT9V032_PIXEL_OPERATION_MODE 0x0f +#define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) +#define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) #define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) #define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) #define MT9V032_ANALOG_GAIN 0x35 @@ -96,9 +105,12 @@ #define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) #define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 #define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 +#define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 0) +#define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 1) #define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) #define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) #define MT9V032_PIXEL_CLOCK 0x74 +#define MT9V034_PIXEL_CLOCK 0x72 #define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) #define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) #define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) @@ -120,12 +132,88 @@ #define MT9V032_AGC_ENABLE (1 << 1) #define MT9V032_THERMAL_INFO 0xc1 +enum mt9v032_model { + MT9V032_MODEL_V032_COLOR, + MT9V032_MODEL_V032_MONO, + MT9V032_MODEL_V034_COLOR, + MT9V032_MODEL_V034_MONO, +}; + +struct mt9v032_model_version { + unsigned int version; + const char *name; +}; + +struct mt9v032_model_data { + unsigned int min_row_time; + unsigned int min_hblank; + unsigned int min_vblank; + unsigned int max_vblank; + unsigned int min_shutter; + unsigned int max_shutter; + unsigned int pclk_reg; +}; + +struct mt9v032_model_info { + const struct mt9v032_model_data *data; + bool color; +}; + +static const struct mt9v032_model_version mt9v032_versions[] = { + { MT9V032_CHIP_ID_REV1, "MT9V032 rev1/2" }, + { MT9V032_CHIP_ID_REV3, "MT9V032 rev3" }, + { MT9V034_CHIP_ID_REV1, "MT9V034 rev1" }, +}; + +static const struct mt9v032_model_data mt9v032_model_data[] = { + { + /* MT9V032 revisions 1/2/3 */ + .min_row_time = 660, + .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, + .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, + .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, + .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, + .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, + .pclk_reg = MT9V032_PIXEL_CLOCK, + }, { + /* MT9V034 */ + .min_row_time = 690, + .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, + .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, + .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, + .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, + .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, + .pclk_reg = MT9V034_PIXEL_CLOCK, + }, +}; + +static const struct mt9v032_model_info mt9v032_models[] = { + [MT9V032_MODEL_V032_COLOR] = { + .data = &mt9v032_model_data[0], + .color = true, + }, + [MT9V032_MODEL_V032_MONO] = { + .data = &mt9v032_model_data[0], + .color = false, + }, + [MT9V032_MODEL_V034_COLOR] = { + .data = &mt9v032_model_data[1], + .color = true, + }, + [MT9V032_MODEL_V034_MONO] = { + .data = &mt9v032_model_data[1], + .color = false, + }, +}; + struct mt9v032 { struct v4l2_subdev subdev; struct media_pad pad; struct v4l2_mbus_framefmt format; struct v4l2_rect crop; + unsigned int hratio; + unsigned int vratio; struct v4l2_ctrl_handler ctrls; struct { @@ -139,6 +227,8 @@ struct mt9v032 { struct clk *clk; struct mt9v032_platform_data *pdata; + const struct mt9v032_model_info *model; + const struct mt9v032_model_version *version; u32 sysclk; u16 chip_control; @@ -210,12 +300,17 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) { struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); struct v4l2_rect *crop = &mt9v032->crop; + unsigned int min_hblank = mt9v032->model->data->min_hblank; + unsigned int hblank; - return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, - max_t(s32, mt9v032->hblank, 660 - crop->width)); -} + if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) + min_hblank += (mt9v032->hratio - 1) * 10; + min_hblank = max_t(unsigned int, (int)mt9v032->model->data->min_row_time - crop->width, + (int)min_hblank); + hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); -#define EXT_CLK 25000000 + return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); +} static int mt9v032_power_on(struct mt9v032 *mt9v032) { @@ -259,7 +354,7 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) /* Configure the pixel clock polarity */ if (mt9v032->pdata && mt9v032->pdata->clk_pol) { - ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, + ret = mt9v032_write(client, mt9v032->model->data->pclk_reg, MT9V032_PIXEL_CLOCK_INV_PXL_CLK); if (ret < 0) return ret; @@ -312,22 +407,20 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) | MT9V032_CHIP_CONTROL_SEQUENTIAL; struct i2c_client *client = v4l2_get_subdevdata(subdev); struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *format = &mt9v032->format; struct v4l2_rect *crop = &mt9v032->crop; - unsigned int hratio; - unsigned int vratio; + unsigned int hbin; + unsigned int vbin; int ret; if (!enable) return mt9v032_set_chip_control(mt9v032, mode, 0); /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - + hbin = fls(mt9v032->hratio) - 1; + vbin = fls(mt9v032->vratio) - 1; ret = mt9v032_write(client, MT9V032_READ_MODE, - (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | - (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); + hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | + vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT); if (ret < 0) return ret; @@ -370,12 +463,12 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + if (fse->index >= 3 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) return -EINVAL; - fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); fse->max_width = fse->min_width; - fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index); fse->max_height = fse->min_height; return 0; @@ -392,18 +485,30 @@ static int mt9v032_get_format(struct v4l2_subdev *subdev, return 0; } -static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032, - unsigned int hratio) +static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032) { struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); int ret; ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, - mt9v032->sysclk / hratio); + mt9v032->sysclk / mt9v032->hratio); if (ret < 0) dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); } +static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) +{ + /* Compute the power-of-two binning factor closest to the input size to + * output size ratio. Given that the output size is bounded by input/4 + * and input, a generic implementation would be an ineffective luxury. + */ + if (output * 3 > input * 2) + return 1; + if (output * 3 > input) + return 2; + return 4; +} + static int mt9v032_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format) @@ -420,22 +525,28 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, format->which); /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); + width = clamp(ALIGN(format->format.width, 2), + max_t(unsigned int, __crop->width / 4, + MT9V032_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp(ALIGN(format->format.height, 2), + max_t(unsigned int, __crop->height / 4, + MT9V032_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = mt9v032_calc_ratio(__crop->width, width); + vratio = mt9v032_calc_ratio(__crop->height, height); __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - mt9v032_configure_pixel_rate(mt9v032, hratio); + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mt9v032->hratio = hratio; + mt9v032->vratio = vratio; + mt9v032_configure_pixel_rate(mt9v032); + } format->format = *__format; @@ -471,15 +582,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, MT9V032_ROW_START_MIN, MT9V032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9V032_WINDOW_WIDTH_MIN, - MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9V032_WINDOW_HEIGHT_MIN, - MT9V032_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9V032_WINDOW_WIDTH_MIN, + MT9V032_WINDOW_WIDTH_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9V032_WINDOW_HEIGHT_MIN, + MT9V032_WINDOW_HEIGHT_MAX); + + rect.width = min_t(unsigned int, + rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, + rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); @@ -491,8 +604,11 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, crop->which); __format->width = rect.width; __format->height = rect.height; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) - mt9v032_configure_pixel_rate(mt9v032, 1); + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mt9v032->hratio = 1; + mt9v032->vratio = 1; + mt9v032_configure_pixel_rate(mt9v032); + } } *__crop = rect; @@ -641,7 +757,8 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) { struct i2c_client *client = v4l2_get_subdevdata(subdev); struct mt9v032 *mt9v032 = to_mt9v032(subdev); - s32 data; + unsigned int i; + s32 version; int ret; dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", @@ -654,25 +771,38 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) } /* Read and check the sensor version */ - data = mt9v032_read(client, MT9V032_CHIP_VERSION); - if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { - dev_err(&client->dev, "MT9V032 not detected, wrong version " - "0x%04x\n", data); + version = mt9v032_read(client, MT9V032_CHIP_VERSION); + if (version < 0) { + dev_err(&client->dev, "Failed reading chip version\n"); + return version; + } + + for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { + if (mt9v032_versions[i].version == version) { + mt9v032->version = &mt9v032_versions[i]; + break; + } + } + + if (mt9v032->version == NULL) { + dev_err(&client->dev, "Unsupported chip version 0x%04x\n", + version); return -ENODEV; } mt9v032_power_off(mt9v032); - dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", - client->addr); + dev_info(&client->dev, "%s detected at address 0x%02x\n", + mt9v032->version->name, client->addr); - mt9v032_configure_pixel_rate(mt9v032, 1); + mt9v032_configure_pixel_rate(mt9v032); return ret; } static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) { + struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; @@ -683,7 +813,12 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) crop->height = MT9V032_WINDOW_HEIGHT_DEF; format = v4l2_subdev_get_try_format(fh, 0); - format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + if (mt9v032->model->color) + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + else + format->code = V4L2_MBUS_FMT_Y10_1X10; + format->width = MT9V032_WINDOW_WIDTH_DEF; format->height = MT9V032_WINDOW_HEIGHT_DEF; format->field = V4L2_FIELD_NONE; @@ -755,6 +890,7 @@ static int mt9v032_probe(struct i2c_client *client, mutex_init(&mt9v032->power_lock); mt9v032->pdata = pdata; + mt9v032->model = (const void *)did->driver_data; v4l2_ctrl_handler_init(&mt9v032->ctrls, 10); @@ -767,16 +903,16 @@ static int mt9v032_probe(struct i2c_client *client, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, - MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, + V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter, + mt9v032->model->data->max_shutter, 1, MT9V032_TOTAL_SHUTTER_WIDTH_DEF); v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_HBLANK, MT9V032_HORIZONTAL_BLANKING_MIN, + V4L2_CID_HBLANK, mt9v032->model->data->min_hblank, MT9V032_HORIZONTAL_BLANKING_MAX, 1, MT9V032_HORIZONTAL_BLANKING_DEF); v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_VBLANK, MT9V032_VERTICAL_BLANKING_MIN, - MT9V032_VERTICAL_BLANKING_MAX, 1, + V4L2_CID_VBLANK, mt9v032->model->data->min_vblank, + mt9v032->model->data->max_vblank, 1, MT9V032_VERTICAL_BLANKING_DEF); mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls, &mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, @@ -819,12 +955,19 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; - mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + if (mt9v032->model->color) + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + else + mt9v032->format.code = V4L2_MBUS_FMT_Y10_1X10; + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; mt9v032->format.field = V4L2_FIELD_NONE; mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; + mt9v032->hratio = 1; + mt9v032->vratio = 1; + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF; mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; @@ -855,7 +998,10 @@ static int mt9v032_remove(struct i2c_client *client) } static const struct i2c_device_id mt9v032_id[] = { - { "mt9v032", 0 }, + { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, + { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, + { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, + { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v032_id); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c new file mode 100644 index 000000000000..77e10e0fd8d6 --- /dev/null +++ b/drivers/media/i2c/s5k5baf.c @@ -0,0 +1,2053 @@ +/* + * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2013, Samsung Electronics Co., Ltd. + * Andrzej Hajda <a.hajda@samsung.com> + * + * Based on S5K6AA driver authored by Sylwester Nawrocki + * Copyright (C) 2013, Samsung Electronics Co., Ltd. + * + * 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/delay.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/media.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-of.h> + +static int debug; +module_param(debug, int, 0644); + +#define S5K5BAF_DRIVER_NAME "s5k5baf" +#define S5K5BAF_DEFAULT_MCLK_FREQ 24000000U +#define S5K5BAF_CLK_NAME "mclk" + +#define S5K5BAF_FW_FILENAME "s5k5baf-cfg.bin" +#define S5K5BAF_FW_TAG "SF00" +#define S5K5BAG_FW_TAG_LEN 2 +#define S5K5BAG_FW_MAX_COUNT 16 + +#define S5K5BAF_CIS_WIDTH 1600 +#define S5K5BAF_CIS_HEIGHT 1200 +#define S5K5BAF_WIN_WIDTH_MIN 8 +#define S5K5BAF_WIN_HEIGHT_MIN 8 +#define S5K5BAF_GAIN_RED_DEF 127 +#define S5K5BAF_GAIN_GREEN_DEF 95 +#define S5K5BAF_GAIN_BLUE_DEF 180 +/* Default number of MIPI CSI-2 data lanes used */ +#define S5K5BAF_DEF_NUM_LANES 1 + +#define AHB_MSB_ADDR_PTR 0xfcfc + +/* + * Register interface pages (the most significant word of the address) + */ +#define PAGE_IF_HW 0xd000 +#define PAGE_IF_SW 0x7000 + +/* + * H/W register Interface (PAGE_IF_HW) + */ +#define REG_SW_LOAD_COMPLETE 0x0014 +#define REG_CMDWR_PAGE 0x0028 +#define REG_CMDWR_ADDR 0x002a +#define REG_CMDRD_PAGE 0x002c +#define REG_CMDRD_ADDR 0x002e +#define REG_CMD_BUF 0x0f12 +#define REG_SET_HOST_INT 0x1000 +#define REG_CLEAR_HOST_INT 0x1030 +#define REG_PATTERN_SET 0x3100 +#define REG_PATTERN_WIDTH 0x3118 +#define REG_PATTERN_HEIGHT 0x311a +#define REG_PATTERN_PARAM 0x311c + +/* + * S/W register interface (PAGE_IF_SW) + */ + +/* Firmware revision information */ +#define REG_FW_APIVER 0x012e +#define S5K5BAF_FW_APIVER 0x0001 +#define REG_FW_REVISION 0x0130 +#define REG_FW_SENSOR_ID 0x0152 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L 0x01b8 +#define REG_I_INCLK_FREQ_H 0x01ba +#define MIN_MCLK_FREQ_KHZ 6000U +#define MAX_MCLK_FREQ_KHZ 48000U +#define REG_I_USE_NPVI_CLOCKS 0x01c6 +#define NPVI_CLOCKS 1 +#define REG_I_USE_NMIPI_CLOCKS 0x01c8 +#define NMIPI_CLOCKS 1 +#define REG_I_BLOCK_INTERNAL_PLL_CALC 0x01ca + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) +#define SCLK_PVI_FREQ 24000 +#define SCLK_MIPI_FREQ 48000 +#define PCLK_MIN_FREQ 6000 +#define PCLK_MAX_FREQ 48000 +#define REG_I_USE_REGS_API 0x01de +#define REG_I_INIT_PARAMS_UPDATED 0x01e0 +#define REG_I_ERROR_INFO 0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS 0x01e4 +#define REG_USER_CONTRAST 0x01e6 +#define REG_USER_SATURATION 0x01e8 +#define REG_USER_SHARPBLUR 0x01ea + +#define REG_G_SPEC_EFFECTS 0x01ee +#define REG_G_ENABLE_PREV 0x01f0 +#define REG_G_ENABLE_PREV_CHG 0x01f2 +#define REG_G_NEW_CFG_SYNC 0x01f8 +#define REG_G_PREVREQ_IN_WIDTH 0x01fa +#define REG_G_PREVREQ_IN_HEIGHT 0x01fc +#define REG_G_PREVREQ_IN_XOFFS 0x01fe +#define REG_G_PREVREQ_IN_YOFFS 0x0200 +#define REG_G_PREVZOOM_IN_WIDTH 0x020a +#define REG_G_PREVZOOM_IN_HEIGHT 0x020c +#define REG_G_PREVZOOM_IN_XOFFS 0x020e +#define REG_G_PREVZOOM_IN_YOFFS 0x0210 +#define REG_G_INPUTS_CHANGE_REQ 0x021a +#define REG_G_ACTIVE_PREV_CFG 0x021c +#define REG_G_PREV_CFG_CHG 0x021e +#define REG_G_PREV_OPEN_AFTER_CH 0x0220 +#define REG_G_PREV_CFG_ERROR 0x0222 +#define CFG_ERROR_RANGE 0x0b +#define REG_G_PREV_CFG_BYPASS_CHANGED 0x022a +#define REG_G_ACTUAL_P_FR_TIME 0x023a +#define REG_G_ACTUAL_P_OUT_RATE 0x023c +#define REG_G_ACTUAL_C_FR_TIME 0x023e +#define REG_G_ACTUAL_C_OUT_RATE 0x0240 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x) ((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) +#define REG_P_FMT(n) PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) +#define REG_P_PVI_MASK(n) PREG(n, 0x024c) +#define PVI_MASK_MIPI 0x52 +#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) +#define CLK_PVI_INDEX 0 +#define CLK_MIPI_INDEX NPVI_CLOCKS +#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) +#define FR_RATE_DYNAMIC 0 +#define FR_RATE_FIXED 1 +#define FR_RATE_FIXED_ACCURATE 2 +#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) +#define FR_RATE_Q_DYNAMIC 0 +#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ +#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) +#define S5K5BAF_MIN_FR_TIME 333 /* x100 us */ +#define S5K5BAF_MAX_FR_TIME 6500 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_SATURATION(n) PREG(n, 0x0258) +#define REG_P_SHARP_BLUR(n) PREG(n, 0x025a) +#define REG_P_GLAMOUR(n) PREG(n, 0x025c) +#define REG_P_COLORTEMP(n) PREG(n, 0x025e) +#define REG_P_GAMMA_INDEX(n) PREG(n, 0x0260) +#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) +#define REG_P_CAP_MIRROR(n) PREG(n, 0x0264) +#define REG_P_CAP_ROTATION(n) PREG(n, 0x0266) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L 0x03bc +#define REG_SF_USR_EXPOSURE_H 0x03be +#define REG_SF_USR_EXPOSURE_CHG 0x03c0 +#define REG_SF_USR_TOT_GAIN 0x03c2 +#define REG_SF_USR_TOT_GAIN_CHG 0x03c4 +#define REG_SF_RGAIN 0x03c6 +#define REG_SF_RGAIN_CHG 0x03c8 +#define REG_SF_GGAIN 0x03ca +#define REG_SF_GGAIN_CHG 0x03cc +#define REG_SF_BGAIN 0x03ce +#define REG_SF_BGAIN_CHG 0x03d0 +#define REG_SF_WBGAIN_CHG 0x03d2 +#define REG_SF_FLICKER_QUANT 0x03d4 +#define REG_SF_FLICKER_QUANT_CHG 0x03d6 + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES 0x03f2 +#define REG_OIF_EN_PACKETS 0x03f4 +#define EN_PACKETS_CSI2 0xc3 +#define REG_OIF_CFG_CHG 0x03f6 + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN 0x03f8 +#define AALG_ALL_EN BIT(0) +#define AALG_AE_EN BIT(1) +#define AALG_DIVLEI_EN BIT(2) +#define AALG_WB_EN BIT(3) +#define AALG_USE_WB_FOR_ISP BIT(4) +#define AALG_FLICKER_EN BIT(5) +#define AALG_FIT_EN BIT(6) +#define AALG_WRHW_EN BIT(7) + +/* Pointers to color correction matrices */ +#define REG_PTR_CCM_HORIZON 0x06d0 +#define REG_PTR_CCM_INCANDESCENT 0x06d4 +#define REG_PTR_CCM_WARM_WHITE 0x06d8 +#define REG_PTR_CCM_COOL_WHITE 0x06dc +#define REG_PTR_CCM_DL50 0x06e0 +#define REG_PTR_CCM_DL65 0x06e4 +#define REG_PTR_CCM_OUTDOOR 0x06ec + +#define REG_ARR_CCM(n) (0x2800 + 36 * (n)) + +static const char * const s5k5baf_supply_names[] = { + "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ + "vddreg", /* Regulator input power supply 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0) */ + "vddio", /* I/O power supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V) */ +}; +#define S5K5BAF_NUM_SUPPLIES ARRAY_SIZE(s5k5baf_supply_names) + +struct s5k5baf_gpio { + int gpio; + int level; +}; + +enum s5k5baf_gpio_id { + STBY, + RST, + NUM_GPIOS, +}; + +#define PAD_CIS 0 +#define PAD_OUT 1 +#define NUM_CIS_PADS 1 +#define NUM_ISP_PADS 2 + +struct s5k5baf_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_P_FMT(x) register value */ + u16 reg_p_fmt; +}; + +struct s5k5baf_ctrls { + struct v4l2_ctrl_handler handler; + struct { /* Auto / manual white balance cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain_red; + struct v4l2_ctrl *gain_blue; + }; + struct { /* Mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct { /* Auto exposure / manual exposure and gain cluster */ + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; +}; + +enum { + S5K5BAF_FW_ID_PATCH, + S5K5BAF_FW_ID_CCM, + S5K5BAF_FW_ID_CIS, +}; + +struct s5k5baf_fw { + u16 count; + struct { + u16 id; + u16 offset; + } seq[0]; + u16 data[0]; +}; + +struct s5k5baf { + struct s5k5baf_gpio gpios[NUM_GPIOS]; + enum v4l2_mbus_type bus_type; + u8 nlanes; + struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES]; + + struct clk *clock; + u32 mclk_frequency; + + struct s5k5baf_fw *fw; + + struct v4l2_subdev cis_sd; + struct media_pad cis_pad; + + struct v4l2_subdev sd; + struct media_pad pads[NUM_ISP_PADS]; + + /* protects the struct members below */ + struct mutex lock; + + int error; + + struct v4l2_rect crop_sink; + struct v4l2_rect compose; + struct v4l2_rect crop_source; + /* index to s5k5baf_formats array */ + int pixfmt; + /* actual frame interval in 100us */ + u16 fiv; + /* requested frame interval in 100us */ + u16 req_fiv; + /* cache for REG_DBG_AUTOALG_EN register */ + u16 auto_alg; + + struct s5k5baf_ctrls ctrls; + + unsigned int streaming:1; + unsigned int apply_cfg:1; + unsigned int apply_crop:1; + unsigned int valid_auto_alg:1; + unsigned int power; +}; + +static const struct s5k5baf_pixfmt s5k5baf_formats[] = { + { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 }, + /* range 16-240 */ + { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 }, + { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, +}; + +static struct v4l2_rect s5k5baf_cis_rect = { + 0, 0, S5K5BAF_CIS_WIDTH, S5K5BAF_CIS_HEIGHT +}; + +/* Setfile contains set of I2C command sequences. Each sequence has its ID. + * setfile format: + * u8 magic[4]; + * u16 count; number of sequences + * struct { + * u16 id; sequence id + * u16 offset; sequence offset in data array + * } seq[count]; + * u16 data[*]; array containing sequences + * + */ +static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, + size_t count, const u16 *data) +{ + struct s5k5baf_fw *f; + u16 *d, i, *end; + int ret; + + if (count < S5K5BAG_FW_TAG_LEN + 1) { + dev_err(dev, "firmware file too short (%zu)\n", count); + return -EINVAL; + } + + ret = memcmp(data, S5K5BAF_FW_TAG, S5K5BAG_FW_TAG_LEN * sizeof(u16)); + if (ret != 0) { + dev_err(dev, "invalid firmware magic number\n"); + return -EINVAL; + } + + data += S5K5BAG_FW_TAG_LEN; + count -= S5K5BAG_FW_TAG_LEN; + + d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL); + + for (i = 0; i < count; ++i) + d[i] = le16_to_cpu(data[i]); + + f = (struct s5k5baf_fw *)d; + if (count < 1 + 2 * f->count) { + dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", + f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); + return -EINVAL; + } + end = d + count; + d += 1 + 2 * f->count; + + for (i = 0; i < f->count; ++i) { + if (f->seq[i].offset + d <= end) + continue; + dev_err(dev, "invalid firmware header (seq=%d)\n", i); + return -EINVAL; + } + + *fw = f; + + return 0; +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd; +} + +static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) +{ + return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; +} + +static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) +{ + if (s5k5baf_is_cis_subdev(sd)) + return container_of(sd, struct s5k5baf, cis_sd); + else + return container_of(sd, struct s5k5baf, sd); +} + +static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + __be16 w, r; + struct i2c_msg msg[] = { + { .addr = c->addr, .flags = 0, + .len = 2, .buf = (u8 *)&w }, + { .addr = c->addr, .flags = I2C_M_RD, + .len = 2, .buf = (u8 *)&r }, + }; + int ret; + + if (state->error) + return 0; + + w = cpu_to_be16(addr); + ret = i2c_transfer(c->adapter, msg, 2); + r = be16_to_cpu(r); + + v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r); + + if (ret != 2) { + v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); + state->error = ret; + } + return r; +} + +static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) +{ + u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF }; + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + int ret; + + if (state->error) + return; + + ret = i2c_master_send(c, buf, 4); + v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val); + + if (ret != 4) { + v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret); + state->error = ret; + } +} + +static u16 s5k5baf_read(struct s5k5baf *state, u16 addr) +{ + s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr); + return s5k5baf_i2c_read(state, REG_CMD_BUF); +} + +static void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val) +{ + s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); + s5k5baf_i2c_write(state, REG_CMD_BUF, val); +} + +static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, + u16 count, const u16 *seq) +{ + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + __be16 buf[65]; + + s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); + if (state->error) + return; + + v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, + min(2 * count, 64), seq); + + buf[0] = __constant_cpu_to_be16(REG_CMD_BUF); + + while (count > 0) { + int n = min_t(int, count, ARRAY_SIZE(buf) - 1); + int ret, i; + + for (i = 1; i <= n; ++i) + buf[i] = cpu_to_be16(*seq++); + + i *= 2; + ret = i2c_master_send(c, (char *)buf, i); + if (ret != i) { + v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); + state->error = ret; + break; + } + + count -= n; + } +} + +#define s5k5baf_write_seq(state, addr, seq...) \ + s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \ + (const u16 []){ seq }); + +/* add items count at the beginning of the list */ +#define NSEQ(seq...) sizeof((char[]){ seq }), seq + +/* + * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c + * @nseq: sequence of u16 words in format: + * (N, address, value[1]...value[N-1])*,0 + * Ex.: + * u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 }; + * ret = s5k5baf_write_nseq(c, seq); + */ +static void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq) +{ + int count; + + while ((count = *nseq++)) { + u16 addr = *nseq++; + --count; + + s5k5baf_write_arr_seq(state, addr, count, nseq); + nseq += count; + } +} + +static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout); + u16 reg; + + s5k5baf_write(state, addr, 1); + do { + reg = s5k5baf_read(state, addr); + if (state->error || !reg) + return; + usleep_range(5000, 10000); + } while (time_is_after_jiffies(end)); + + v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr); + state->error = -ETIMEDOUT; +} + +static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) +{ + struct s5k5baf_fw *fw = state->fw; + u16 *data; + int i; + + if (fw == NULL) + return NULL; + + data = fw->data + 2 * fw->count; + + for (i = 0; i < fw->count; ++i) { + if (fw->seq[i].id == seq_id) + return data + fw->seq[i].offset; + } + + return NULL; +} + +static void s5k5baf_hw_patch(struct s5k5baf *state) +{ + u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH); + + if (seq) + s5k5baf_write_nseq(state, seq); +} + +static void s5k5baf_hw_set_clocks(struct s5k5baf *state) +{ + unsigned long mclk = state->mclk_frequency / 1000; + u16 status; + static const u16 nseq_clk_cfg[] = { + NSEQ(REG_I_USE_NPVI_CLOCKS, + NPVI_CLOCKS, NMIPI_CLOCKS, 0, + SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4, + SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4), + NSEQ(REG_I_USE_REGS_API, 1), + 0 + }; + + s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16); + s5k5baf_write_nseq(state, nseq_clk_cfg); + + s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED); + status = s5k5baf_read(state, REG_I_ERROR_INFO); + if (!state->error && status) { + v4l2_err(&state->sd, "error configuring PLL (%d)\n", status); + state->error = -EINVAL; + } +} + +/* set custom color correction matrices for various illuminations */ +static void s5k5baf_hw_set_ccm(struct s5k5baf *state) +{ + u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM); + + if (seq) + s5k5baf_write_nseq(state, seq); +} + +/* CIS sensor tuning, based on undocumented android driver code */ +static void s5k5baf_hw_set_cis(struct s5k5baf *state) +{ + u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS); + + if (!seq) + return; + + s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW); + s5k5baf_write_nseq(state, seq); + s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); +} + +static void s5k5baf_hw_sync_cfg(struct s5k5baf *state) +{ + s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1); + if (state->apply_crop) { + s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1); + s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1); + } + s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC); +} +/* Set horizontal and vertical image flipping */ +static void s5k5baf_hw_set_mirror(struct s5k5baf *state) +{ + u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1); + + s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip); + if (state->streaming) + s5k5baf_hw_sync_cfg(state); +} + +static void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable) +{ + u16 cur_alg, new_alg; + + if (!state->valid_auto_alg) + cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN); + else + cur_alg = state->auto_alg; + + new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg); + + if (new_alg != cur_alg) + s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg); + + if (state->error) + return; + + state->valid_auto_alg = 1; + state->auto_alg = new_alg; +} + +/* Configure auto/manual white balance and R/G/B gains */ +static void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb) +{ + struct s5k5baf_ctrls *ctrls = &state->ctrls; + + if (!awb) + s5k5baf_write_seq(state, REG_SF_RGAIN, + ctrls->gain_red->val, 1, + S5K5BAF_GAIN_GREEN_DEF, 1, + ctrls->gain_blue->val, 1, + 1); + + s5k5baf_hw_set_alg(state, AALG_WB_EN, awb); +} + +/* Program FW with exposure time, 'exposure' in us units */ +static void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure) +{ + unsigned int time = exposure / 10; + + s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L, + time & 0xffff, time >> 16, 1); +} + +static void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain) +{ + s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1); +} + +/* Set auto/manual exposure and total gain */ +static void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value) +{ + if (value == V4L2_EXPOSURE_AUTO) { + s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true); + } else { + unsigned int exp_time = state->ctrls.exposure->val; + + s5k5baf_hw_set_user_exposure(state, exp_time); + s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val); + s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false); + } +} + +static void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v) +{ + if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { + s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true); + } else { + /* The V4L2_CID_LINE_FREQUENCY control values match + * the register values */ + s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1); + s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false); + } +} + +static void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val) +{ + static const u16 colorfx[] = { + [V4L2_COLORFX_NONE] = 0, + [V4L2_COLORFX_BW] = 1, + [V4L2_COLORFX_NEGATIVE] = 2, + [V4L2_COLORFX_SEPIA] = 3, + [V4L2_COLORFX_SKY_BLUE] = 4, + [V4L2_COLORFX_SKETCH] = 5, + }; + + s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]); +} + +static int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf) +{ + int i, c = -1; + + for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) { + if (mf->colorspace != s5k5baf_formats[i].colorspace) + continue; + if (mf->code == s5k5baf_formats[i].code) + return i; + if (c < 0) + c = i; + } + return (c < 0) ? 0 : c; +} + +static int s5k5baf_clear_error(struct s5k5baf *state) +{ + int ret = state->error; + + state->error = 0; + return ret; +} + +static int s5k5baf_hw_set_video_bus(struct s5k5baf *state) +{ + u16 en_pkts; + + if (state->bus_type == V4L2_MBUS_CSI2) + en_pkts = EN_PACKETS_CSI2; + else + en_pkts = 0; + + s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES, + state->nlanes, en_pkts, 1); + + return s5k5baf_clear_error(state); +} + +static u16 s5k5baf_get_cfg_error(struct s5k5baf *state) +{ + u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR); + if (err) + s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0); + return err; +} + +static void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv) +{ + s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv); + s5k5baf_hw_sync_cfg(state); +} + +static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) +{ + u16 err, fiv; + int n; + + fiv = s5k5baf_read(state, REG_G_ACTUAL_P_FR_TIME); + if (state->error) + return; + + for (n = 5; n > 0; --n) { + s5k5baf_hw_set_fiv(state, fiv); + err = s5k5baf_get_cfg_error(state); + if (state->error) + return; + switch (err) { + case CFG_ERROR_RANGE: + ++fiv; + break; + case 0: + state->fiv = fiv; + v4l2_info(&state->sd, + "found valid frame interval: %d00us\n", fiv); + return; + default: + v4l2_err(&state->sd, + "error setting frame interval: %d\n", err); + state->error = -EINVAL; + } + }; + v4l2_err(&state->sd, "cannot find correct frame interval\n"); + state->error = -ERANGE; +} + +static void s5k5baf_hw_validate_cfg(struct s5k5baf *state) +{ + u16 err; + + err = s5k5baf_get_cfg_error(state); + if (state->error) + return; + + switch (err) { + case 0: + state->apply_cfg = 1; + return; + case CFG_ERROR_RANGE: + s5k5baf_hw_find_min_fiv(state); + if (!state->error) + state->apply_cfg = 1; + return; + default: + v4l2_err(&state->sd, + "error setting format: %d\n", err); + state->error = -EINVAL; + } +} + +static void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v, + const struct v4l2_rect *n, + const struct v4l2_rect *d) +{ + r->left = v->left * n->width / d->width; + r->top = v->top * n->height / d->height; + r->width = v->width * n->width / d->width; + r->height = v->height * n->height / d->height; +} + +static int s5k5baf_hw_set_crop_rects(struct s5k5baf *state) +{ + struct v4l2_rect *p, r; + u16 err; + int ret; + + p = &state->crop_sink; + s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height, + p->left, p->top); + + s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink, + &state->compose); + s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height, + r.left, r.top); + + s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ); + s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED); + err = s5k5baf_get_cfg_error(state); + ret = s5k5baf_clear_error(state); + if (ret < 0) + return ret; + + switch (err) { + case 0: + break; + case CFG_ERROR_RANGE: + /* retry crop with frame interval set to max */ + s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME); + err = s5k5baf_get_cfg_error(state); + ret = s5k5baf_clear_error(state); + if (ret < 0) + return ret; + if (err) { + v4l2_err(&state->sd, + "crop error on max frame interval: %d\n", err); + state->error = -EINVAL; + } + s5k5baf_hw_set_fiv(state, state->req_fiv); + s5k5baf_hw_validate_cfg(state); + break; + default: + v4l2_err(&state->sd, "crop error: %d\n", err); + return -EINVAL; + } + + if (!state->apply_cfg) + return 0; + + p = &state->crop_source; + s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height); + s5k5baf_hw_set_fiv(state, state->req_fiv); + s5k5baf_hw_validate_cfg(state); + + return s5k5baf_clear_error(state); +} + +static void s5k5baf_hw_set_config(struct s5k5baf *state) +{ + u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt; + struct v4l2_rect *r = &state->crop_source; + + s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), + r->width, r->height, reg_fmt, + PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2, + PVI_MASK_MIPI, CLK_MIPI_INDEX, + FR_RATE_FIXED, FR_RATE_Q_DYNAMIC, + state->req_fiv, S5K5BAF_MIN_FR_TIME); + s5k5baf_hw_sync_cfg(state); + s5k5baf_hw_validate_cfg(state); +} + + +static void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id) +{ + s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800); + s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511); + s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0); + s5k5baf_i2c_write(state, REG_PATTERN_SET, id); +} + +static void s5k5baf_gpio_assert(struct s5k5baf *state, int id) +{ + struct s5k5baf_gpio *gpio = &state->gpios[id]; + + gpio_set_value(gpio->gpio, gpio->level); +} + +static void s5k5baf_gpio_deassert(struct s5k5baf *state, int id) +{ + struct s5k5baf_gpio *gpio = &state->gpios[id]; + + gpio_set_value(gpio->gpio, !gpio->level); +} + +static int s5k5baf_power_on(struct s5k5baf *state) +{ + int ret; + + ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies); + if (ret < 0) + goto err; + + ret = clk_set_rate(state->clock, state->mclk_frequency); + if (ret < 0) + goto err_reg_dis; + + ret = clk_prepare_enable(state->clock); + if (ret < 0) + goto err_reg_dis; + + v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n", + clk_get_rate(state->clock)); + + s5k5baf_gpio_deassert(state, STBY); + usleep_range(50, 100); + s5k5baf_gpio_deassert(state, RST); + return 0; + +err_reg_dis: + regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies); +err: + v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret); + return ret; +} + +static int s5k5baf_power_off(struct s5k5baf *state) +{ + int ret; + + state->streaming = 0; + state->apply_cfg = 0; + state->apply_crop = 0; + + s5k5baf_gpio_assert(state, RST); + s5k5baf_gpio_assert(state, STBY); + + if (!IS_ERR(state->clock)) + clk_disable_unprepare(state->clock); + + ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, + state->supplies); + if (ret < 0) + v4l2_err(&state->sd, "failed to disable regulators\n"); + + return 0; +} + +static void s5k5baf_hw_init(struct s5k5baf *state) +{ + s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW); + s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0); + s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1); + s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW); + s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); +} + +/* + * V4L2 subdev core and video operations + */ + +static void s5k5baf_initialize_data(struct s5k5baf *state) +{ + state->pixfmt = 0; + state->req_fiv = 10000 / 15; + state->fiv = state->req_fiv; + state->valid_auto_alg = 0; +} + +static int s5k5baf_load_setfile(struct s5k5baf *state) +{ + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev); + if (ret < 0) { + dev_warn(&c->dev, "firmware file (%s) not loaded\n", + S5K5BAF_FW_FILENAME); + return ret; + } + + ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, + (u16 *)fw->data); + + release_firmware(fw); + + return ret; +} + +static int s5k5baf_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5k5baf *state = to_s5k5baf(sd); + int ret = 0; + + mutex_lock(&state->lock); + + if (!on != state->power) + goto out; + + if (on) { + if (state->fw == NULL) + s5k5baf_load_setfile(state); + + s5k5baf_initialize_data(state); + ret = s5k5baf_power_on(state); + if (ret < 0) + goto out; + + s5k5baf_hw_init(state); + s5k5baf_hw_patch(state); + s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1); + s5k5baf_hw_set_clocks(state); + + ret = s5k5baf_hw_set_video_bus(state); + if (ret < 0) + goto out; + + s5k5baf_hw_set_cis(state); + s5k5baf_hw_set_ccm(state); + + ret = s5k5baf_clear_error(state); + if (!ret) + state->power++; + } else { + s5k5baf_power_off(state); + state->power--; + } + +out: + mutex_unlock(&state->lock); + + if (!ret && on) + ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); + + return ret; +} + +static void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable) +{ + s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1); +} + +static int s5k5baf_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k5baf *state = to_s5k5baf(sd); + int ret; + + mutex_lock(&state->lock); + + if (state->streaming == !!on) { + ret = 0; + goto out; + } + + if (on) { + s5k5baf_hw_set_config(state); + ret = s5k5baf_hw_set_crop_rects(state); + if (ret < 0) + goto out; + s5k5baf_hw_set_stream(state, 1); + s5k5baf_i2c_write(state, 0xb0cc, 0x000b); + } else { + s5k5baf_hw_set_stream(state, 0); + } + ret = s5k5baf_clear_error(state); + if (!ret) + state->streaming = !state->streaming; + +out: + mutex_unlock(&state->lock); + + return ret; +} + +static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k5baf *state = to_s5k5baf(sd); + + mutex_lock(&state->lock); + fi->interval.numerator = state->fiv; + fi->interval.denominator = 10000; + mutex_unlock(&state->lock); + + return 0; +} + +static void s5k5baf_set_frame_interval(struct s5k5baf *state, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_fract *i = &fi->interval; + + if (fi->interval.denominator == 0) + state->req_fiv = S5K5BAF_MAX_FR_TIME; + else + state->req_fiv = clamp_t(u32, + i->numerator * 10000 / i->denominator, + S5K5BAF_MIN_FR_TIME, + S5K5BAF_MAX_FR_TIME); + + state->fiv = state->req_fiv; + if (state->apply_cfg) { + s5k5baf_hw_set_fiv(state, state->req_fiv); + s5k5baf_hw_validate_cfg(state); + } + *i = (struct v4l2_fract){ state->fiv, 10000 }; + if (state->fiv == state->req_fiv) + v4l2_info(&state->sd, "frame interval changed to %d00us\n", + state->fiv); +} + +static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k5baf *state = to_s5k5baf(sd); + + mutex_lock(&state->lock); + s5k5baf_set_frame_interval(state, fi); + mutex_unlock(&state->lock); + return 0; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || + fie->pad != PAD_CIS) + return -EINVAL; + + v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN, + S5K5BAF_CIS_WIDTH, 1, + &fie->height, S5K5BAF_WIN_HEIGHT_MIN, + S5K5BAF_CIS_HEIGHT, 1, 0); + + fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index; + fie->interval.denominator = 10000; + + return 0; +} + +static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == PAD_CIS) { + if (code->index > 0) + return -EINVAL; + code->code = V4L2_MBUS_FMT_FIXED; + return 0; + } + + if (code->index >= ARRAY_SIZE(s5k5baf_formats)) + return -EINVAL; + + code->code = s5k5baf_formats[code->index].code; + return 0; +} + +static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i; + + if (fse->index > 0) + return -EINVAL; + + if (fse->pad == PAD_CIS) { + fse->code = V4L2_MBUS_FMT_FIXED; + fse->min_width = S5K5BAF_CIS_WIDTH; + fse->max_width = S5K5BAF_CIS_WIDTH; + fse->min_height = S5K5BAF_CIS_HEIGHT; + fse->max_height = S5K5BAF_CIS_HEIGHT; + return 0; + } + + i = ARRAY_SIZE(s5k5baf_formats); + while (--i) + if (fse->code == s5k5baf_formats[i].code) + break; + fse->code = s5k5baf_formats[i].code; + fse->min_width = S5K5BAF_WIN_WIDTH_MIN; + fse->max_width = S5K5BAF_CIS_WIDTH; + fse->max_height = S5K5BAF_WIN_HEIGHT_MIN; + fse->min_height = S5K5BAF_CIS_HEIGHT; + + return 0; +} + +static void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf) +{ + mf->width = S5K5BAF_CIS_WIDTH; + mf->height = S5K5BAF_CIS_HEIGHT; + mf->code = V4L2_MBUS_FMT_FIXED; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; +} + +static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) +{ + int pixfmt; + + v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN, + S5K5BAF_CIS_WIDTH, 1, + &mf->height, S5K5BAF_WIN_HEIGHT_MIN, + S5K5BAF_CIS_HEIGHT, 1, 0); + + pixfmt = s5k5baf_find_pixfmt(mf); + + mf->colorspace = s5k5baf_formats[pixfmt].colorspace; + mf->code = s5k5baf_formats[pixfmt].code; + mf->field = V4L2_FIELD_NONE; + + return pixfmt; +} + +static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k5baf *state = to_s5k5baf(sd); + const struct s5k5baf_pixfmt *pixfmt; + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + + mf = &fmt->format; + if (fmt->pad == PAD_CIS) { + s5k5baf_try_cis_format(mf); + return 0; + } + mf->field = V4L2_FIELD_NONE; + mutex_lock(&state->lock); + pixfmt = &s5k5baf_formats[state->pixfmt]; + mf->width = state->crop_source.width; + mf->height = state->crop_source.height; + mf->code = pixfmt->code; + mf->colorspace = pixfmt->colorspace; + mutex_unlock(&state->lock); + + return 0; +} + +static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct s5k5baf *state = to_s5k5baf(sd); + const struct s5k5baf_pixfmt *pixfmt; + int ret = 0; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(fh, fmt->pad) = *mf; + return 0; + } + + if (fmt->pad == PAD_CIS) { + s5k5baf_try_cis_format(mf); + return 0; + } + + mutex_lock(&state->lock); + + if (state->streaming) { + mutex_unlock(&state->lock); + return -EBUSY; + } + + state->pixfmt = s5k5baf_try_isp_format(mf); + pixfmt = &s5k5baf_formats[state->pixfmt]; + mf->code = pixfmt->code; + mf->colorspace = pixfmt->colorspace; + mf->width = state->crop_source.width; + mf->height = state->crop_source.height; + + mutex_unlock(&state->lock); + return ret; +} + +enum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID }; + +static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) +{ + switch (target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + return pad ? R_COMPOSE : R_CIS; + case V4L2_SEL_TGT_CROP: + return pad ? R_CROP_SOURCE : R_CROP_SINK; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + return pad ? R_INVALID : R_CROP_SINK; + case V4L2_SEL_TGT_COMPOSE: + return pad ? R_INVALID : R_COMPOSE; + default: + return R_INVALID; + } +} + +static int s5k5baf_is_bound_target(u32 target) +{ + return target == V4L2_SEL_TGT_CROP_BOUNDS || + target == V4L2_SEL_TGT_COMPOSE_BOUNDS; +} + +static int s5k5baf_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + static enum selection_rect rtype; + struct s5k5baf *state = to_s5k5baf(sd); + + rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); + + switch (rtype) { + case R_INVALID: + return -EINVAL; + case R_CIS: + sel->r = s5k5baf_cis_rect; + return 0; + default: + break; + } + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + if (rtype == R_COMPOSE) + sel->r = *v4l2_subdev_get_try_compose(fh, sel->pad); + else + sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + return 0; + } + + mutex_lock(&state->lock); + switch (rtype) { + case R_CROP_SINK: + sel->r = state->crop_sink; + break; + case R_COMPOSE: + sel->r = state->compose; + break; + case R_CROP_SOURCE: + sel->r = state->crop_source; + break; + default: + break; + } + if (s5k5baf_is_bound_target(sel->target)) { + sel->r.left = 0; + sel->r.top = 0; + } + mutex_unlock(&state->lock); + + return 0; +} + +/* bounds range [start, start+len) to [0, max) and aligns to 2 */ +static void s5k5baf_bound_range(u32 *start, u32 *len, u32 max) +{ + if (*len > max) + *len = max; + if (*start + *len > max) + *start = max - *len; + *start &= ~1; + *len &= ~1; + if (*len < S5K5BAF_WIN_WIDTH_MIN) + *len = S5K5BAF_WIN_WIDTH_MIN; +} + +static void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height) +{ + s5k5baf_bound_range(&r->left, &r->width, width); + s5k5baf_bound_range(&r->top, &r->height, height); +} + +static void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects, + enum selection_rect first, + struct v4l2_rect *v) +{ + struct v4l2_rect *r, *br; + enum selection_rect i = first; + + *rects[first] = *v; + do { + r = rects[i]; + br = rects[i - 1]; + s5k5baf_bound_rect(r, br->width, br->height); + } while (++i != R_INVALID); + *v = *rects[first]; +} + +static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, + const struct v4l2_rect *r2) +{ + return !memcmp(r1, r2, sizeof(*r1)); +} + +static int s5k5baf_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + static enum selection_rect rtype; + struct s5k5baf *state = to_s5k5baf(sd); + struct v4l2_rect **rects; + int ret = 0; + + rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); + if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target)) + return -EINVAL; + + /* allow only scaling on compose */ + if (rtype == R_COMPOSE) { + sel->r.left = 0; + sel->r.top = 0; + } + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + rects = (struct v4l2_rect * []) { + &s5k5baf_cis_rect, + v4l2_subdev_get_try_crop(fh, PAD_CIS), + v4l2_subdev_get_try_compose(fh, PAD_CIS), + v4l2_subdev_get_try_crop(fh, PAD_OUT) + }; + s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); + return 0; + } + + rects = (struct v4l2_rect * []) { + &s5k5baf_cis_rect, + &state->crop_sink, + &state->compose, + &state->crop_source + }; + mutex_lock(&state->lock); + if (state->streaming) { + /* adjust sel->r to avoid output resolution change */ + if (rtype < R_CROP_SOURCE) { + if (sel->r.width < state->crop_source.width) + sel->r.width = state->crop_source.width; + if (sel->r.height < state->crop_source.height) + sel->r.height = state->crop_source.height; + } else { + sel->r.width = state->crop_source.width; + sel->r.height = state->crop_source.height; + } + } + s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); + if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) || + !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect)) + state->apply_crop = 1; + if (state->streaming) + ret = s5k5baf_hw_set_crop_rects(state); + mutex_unlock(&state->lock); + + return ret; +} + +static const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = { + .enum_mbus_code = s5k5baf_enum_mbus_code, + .enum_frame_size = s5k5baf_enum_frame_size, + .get_fmt = s5k5baf_get_fmt, + .set_fmt = s5k5baf_set_fmt, +}; + +static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { + .enum_mbus_code = s5k5baf_enum_mbus_code, + .enum_frame_size = s5k5baf_enum_frame_size, + .enum_frame_interval = s5k5baf_enum_frame_interval, + .get_fmt = s5k5baf_get_fmt, + .set_fmt = s5k5baf_set_fmt, + .get_selection = s5k5baf_get_selection, + .set_selection = s5k5baf_set_selection, +}; + +static const struct v4l2_subdev_video_ops s5k5baf_video_ops = { + .g_frame_interval = s5k5baf_g_frame_interval, + .s_frame_interval = s5k5baf_s_frame_interval, + .s_stream = s5k5baf_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct s5k5baf *state = to_s5k5baf(sd); + int ret; + + v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); + + mutex_lock(&state->lock); + + if (state->power == 0) + goto unlock; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + s5k5baf_hw_set_awb(state, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val); + break; + + case V4L2_CID_COLORFX: + s5k5baf_hw_set_colorfx(state, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + s5k5baf_hw_set_auto_exposure(state, ctrl->val); + break; + + case V4L2_CID_HFLIP: + s5k5baf_hw_set_mirror(state); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + s5k5baf_hw_set_anti_flicker(state, ctrl->val); + break; + + case V4L2_CID_SATURATION: + s5k5baf_write(state, REG_USER_SATURATION, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val); + break; + + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val); + if (state->apply_cfg) + s5k5baf_hw_sync_cfg(state); + break; + + case V4L2_CID_TEST_PATTERN: + s5k5baf_hw_set_test_pattern(state, ctrl->val); + break; + } +unlock: + ret = s5k5baf_clear_error(state); + mutex_unlock(&state->lock); + return ret; +} + +static const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = { + .s_ctrl = s5k5baf_s_ctrl, +}; + +static const char * const s5k5baf_test_pattern_menu[] = { + "Disabled", + "Blank", + "Bars", + "Gradients", + "Textile", + "Textile2", + "Squares" +}; + +static int s5k5baf_initialize_ctrls(struct s5k5baf *state) +{ + const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops; + struct s5k5baf_ctrls *ctrls = &state->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) { + v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret); + return ret; + } + + /* Auto white balance cluster */ + ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, + 0, 255, 1, S5K5BAF_GAIN_RED_DEF); + ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, + 0, 255, 1, S5K5BAF_GAIN_BLUE_DEF); + v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time: x 1 us */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 6000000U, 1, 100000U); + /* Total gain: 256 <=> 1x */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 0, 256, 1, 256); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 256, 1, 0); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1, + 0, 0, s5k5baf_test_pattern_menu); + + if (hdl->error) { + v4l2_err(&state->sd, "error creating controls (%d)\n", + hdl->error); + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + state->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(fh, PAD_CIS); + s5k5baf_try_cis_format(mf); + + if (s5k5baf_is_cis_subdev(sd)) + return 0; + + mf = v4l2_subdev_get_try_format(fh, PAD_OUT); + mf->colorspace = s5k5baf_formats[0].colorspace; + mf->code = s5k5baf_formats[0].code; + mf->width = s5k5baf_cis_rect.width; + mf->height = s5k5baf_cis_rect.height; + mf->field = V4L2_FIELD_NONE; + + *v4l2_subdev_get_try_crop(fh, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_compose(fh, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_get_try_crop(fh, PAD_OUT) = s5k5baf_cis_rect; + + return 0; +} + +static int s5k5baf_check_fw_revision(struct s5k5baf *state) +{ + u16 api_ver = 0, fw_rev = 0, s_id = 0; + int ret; + + api_ver = s5k5baf_read(state, REG_FW_APIVER); + fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff; + s_id = s5k5baf_read(state, REG_FW_SENSOR_ID); + ret = s5k5baf_clear_error(state); + if (ret < 0) + return ret; + + v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n", + api_ver, fw_rev, s_id); + + if (api_ver != S5K5BAF_FW_APIVER) { + v4l2_err(&state->sd, "FW API version not supported\n"); + return -ENODEV; + } + + return 0; +} + +static int s5k5baf_registered(struct v4l2_subdev *sd) +{ + struct s5k5baf *state = to_s5k5baf(sd); + int ret; + + ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd); + if (ret < 0) + v4l2_err(sd, "failed to register subdev %s\n", + state->cis_sd.name); + else + ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS, + &state->sd.entity, PAD_CIS, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + return ret; +} + +static void s5k5baf_unregistered(struct v4l2_subdev *sd) +{ + struct s5k5baf *state = to_s5k5baf(sd); + v4l2_device_unregister_subdev(&state->cis_sd); +} + +static const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = { + .pad = &s5k5baf_cis_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = { + .open = s5k5baf_open, +}; + +static const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = { + .registered = s5k5baf_registered, + .unregistered = s5k5baf_unregistered, + .open = s5k5baf_open, +}; + +static const struct v4l2_subdev_core_ops s5k5baf_core_ops = { + .s_power = s5k5baf_set_power, + .log_status = v4l2_ctrl_subdev_log_status, +}; + +static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { + .core = &s5k5baf_core_ops, + .pad = &s5k5baf_pad_ops, + .video = &s5k5baf_video_ops, +}; + +static int s5k5baf_configure_gpios(struct s5k5baf *state) +{ + static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + struct s5k5baf_gpio *g = state->gpios; + int ret, i; + + for (i = 0; i < NUM_GPIOS; ++i) { + int flags = GPIOF_DIR_OUT; + if (g[i].level) + flags |= GPIOF_INIT_HIGH; + ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]); + if (ret < 0) { + v4l2_err(c, "failed to request gpio %s\n", name[i]); + return ret; + } + } + return 0; +} + +static int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev) +{ + static const char * const names[] = { + "stbyn-gpios", + "rstn-gpios", + }; + struct device_node *node = dev->of_node; + enum of_gpio_flags flags; + int ret, i; + + for (i = 0; i < NUM_GPIOS; ++i) { + ret = of_get_named_gpio_flags(node, names[i], 0, &flags); + if (ret < 0) { + dev_err(dev, "no %s GPIO pin provided\n", names[i]); + return ret; + } + gpios[i].gpio = ret; + gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW); + } + + return 0; +} + +static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) +{ + struct device_node *node = dev->of_node; + struct device_node *node_ep; + struct v4l2_of_endpoint ep; + int ret; + + if (!node) { + dev_err(dev, "no device-tree node provided\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "clock-frequency", + &state->mclk_frequency); + if (ret < 0) { + state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; + dev_info(dev, "using default %u Hz clock frequency\n", + state->mclk_frequency); + } + + ret = s5k5baf_parse_gpios(state->gpios, dev); + if (ret < 0) + return ret; + + node_ep = v4l2_of_get_next_endpoint(node, NULL); + if (!node_ep) { + dev_err(dev, "no endpoint defined at node %s\n", + node->full_name); + return -EINVAL; + } + + v4l2_of_parse_endpoint(node_ep, &ep); + of_node_put(node_ep); + state->bus_type = ep.bus_type; + + switch (state->bus_type) { + case V4L2_MBUS_CSI2: + state->nlanes = ep.bus.mipi_csi2.num_data_lanes; + break; + case V4L2_MBUS_PARALLEL: + break; + default: + dev_err(dev, "unsupported bus in endpoint defined at node %s\n", + node->full_name); + return -EINVAL; + } + + return 0; +} + +static int s5k5baf_configure_subdevs(struct s5k5baf *state, + struct i2c_client *c) +{ + struct v4l2_subdev *sd; + int ret; + + sd = &state->cis_sd; + v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops); + sd->owner = THIS_MODULE; + v4l2_set_subdevdata(sd, state); + snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x", + i2c_adapter_id(c->adapter), c->addr); + + sd->internal_ops = &s5k5baf_cis_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0); + if (ret < 0) + goto err; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops); + snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x", + i2c_adapter_id(c->adapter), c->addr); + + sd->internal_ops = &s5k5baf_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; + state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0); + + if (!ret) + return 0; + + media_entity_cleanup(&state->cis_sd.entity); +err: + dev_err(&c->dev, "cannot init media entity %s\n", sd->name); + return ret; +} + +static int s5k5baf_configure_regulators(struct s5k5baf *state) +{ + struct i2c_client *c = v4l2_get_subdevdata(&state->sd); + int ret; + int i; + + for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++) + state->supplies[i].supply = s5k5baf_supply_names[i]; + + ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES, + state->supplies); + if (ret < 0) + v4l2_err(c, "failed to get regulators\n"); + return ret; +} + +static int s5k5baf_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + struct s5k5baf *state; + int ret; + + state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + state->crop_sink = s5k5baf_cis_rect; + state->compose = s5k5baf_cis_rect; + state->crop_source = s5k5baf_cis_rect; + + ret = s5k5baf_parse_device_node(state, &c->dev); + if (ret < 0) + return ret; + + ret = s5k5baf_configure_subdevs(state, c); + if (ret < 0) + return ret; + + ret = s5k5baf_configure_gpios(state); + if (ret < 0) + goto err_me; + + ret = s5k5baf_configure_regulators(state); + if (ret < 0) + goto err_me; + + state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); + if (IS_ERR(state->clock)) { + ret = -EPROBE_DEFER; + goto err_me; + } + + ret = s5k5baf_power_on(state); + if (ret < 0) { + ret = -EPROBE_DEFER; + goto err_me; + } + s5k5baf_hw_init(state); + ret = s5k5baf_check_fw_revision(state); + + s5k5baf_power_off(state); + if (ret < 0) + goto err_me; + + ret = s5k5baf_initialize_ctrls(state); + if (ret < 0) + goto err_me; + + ret = v4l2_async_register_subdev(&state->sd); + if (ret < 0) + goto err_ctrl; + + return 0; + +err_ctrl: + v4l2_ctrl_handler_free(state->sd.ctrl_handler); +err_me: + media_entity_cleanup(&state->sd.entity); + media_entity_cleanup(&state->cis_sd.entity); + return ret; +} + +static int s5k5baf_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct s5k5baf *state = to_s5k5baf(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + + sd = &state->cis_sd; + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + + return 0; +} + +static const struct i2c_device_id s5k5baf_id[] = { + { S5K5BAF_DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, s5k5baf_id); + +static const struct of_device_id s5k5baf_of_match[] = { + { .compatible = "samsung,s5k5baf" }, + { } +}; +MODULE_DEVICE_TABLE(of, s5k5baf_of_match); + +static struct i2c_driver s5k5baf_i2c_driver = { + .driver = { + .of_match_table = s5k5baf_of_match, + .name = S5K5BAF_DRIVER_NAME + }, + .probe = s5k5baf_probe, + .remove = s5k5baf_remove, + .id_table = s5k5baf_id, +}; + +module_i2c_driver(s5k5baf_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver"); +MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 70bc72e795d0..2960b5a8362a 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -150,14 +150,14 @@ static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) /* ---------------------------------------------------------------------- */ -static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) +static bool block_from_buf(struct saa6588 *s, unsigned char *buf) { int i; if (s->rd_index == s->wr_index) { if (debug > 2) dprintk(PREFIX "Read: buffer empty.\n"); - return 0; + return false; } if (debug > 2) { @@ -166,8 +166,7 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) dprintk("0x%02x ", s->buffer[i]); } - if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) - return -EFAULT; + memcpy(buf, &s->buffer[s->rd_index], 3); s->rd_index += 3; if (s->rd_index >= s->buf_size) @@ -177,22 +176,22 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) if (debug > 2) dprintk("%d blocks total.\n", s->block_count); - return 1; + return true; } static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) { - unsigned long flags; - unsigned char __user *buf_ptr = a->buffer; - unsigned int i; + unsigned char buf[3]; + unsigned long flags; unsigned int rd_blocks; + unsigned int i; a->result = 0; if (!a->buffer) return; - while (!s->data_available_for_read) { + while (!a->nonblocking && !s->data_available_for_read) { int ret = wait_event_interruptible(s->read_queue, s->data_available_for_read); if (ret == -ERESTARTSYS) { @@ -201,24 +200,31 @@ static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) } } - spin_lock_irqsave(&s->lock, flags); rd_blocks = a->block_count; + spin_lock_irqsave(&s->lock, flags); if (rd_blocks > s->block_count) rd_blocks = s->block_count; + spin_unlock_irqrestore(&s->lock, flags); - if (!rd_blocks) { - spin_unlock_irqrestore(&s->lock, flags); + if (!rd_blocks) return; - } for (i = 0; i < rd_blocks; i++) { - if (block_to_user_buf(s, buf_ptr)) { - buf_ptr += 3; - a->result++; - } else + bool got_block; + + spin_lock_irqsave(&s->lock, flags); + got_block = block_from_buf(s, buf); + spin_unlock_irqrestore(&s->lock, flags); + if (!got_block) break; + if (copy_to_user(buf_ptr, buf, 3)) { + a->result = -EFAULT; + return; + } + buf_ptr += 3; + a->result += 3; } - a->result *= 3; + spin_lock_irqsave(&s->lock, flags); s->data_available_for_read = (s->block_count > 0); spin_unlock_irqrestore(&s->lock, flags); } @@ -394,14 +400,11 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct saa6588_command *a = arg; switch (cmd) { - /* --- open() for /dev/radio --- */ - case SAA6588_CMD_OPEN: - a->result = 0; /* return error if chip doesn't work ??? */ - break; /* --- close() for /dev/radio --- */ case SAA6588_CMD_CLOSE: s->data_available_for_read = 1; wake_up_interruptible(&s->read_queue); + s->data_available_for_read = 0; a->result = 0; break; /* --- read() for /dev/radio --- */ @@ -411,9 +414,8 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) /* --- poll() for /dev/radio --- */ case SAA6588_CMD_POLL: a->result = 0; - if (s->data_available_for_read) { + if (s->data_available_for_read) a->result |= POLLIN | POLLRDNORM; - } poll_wait(a->instance, &s->read_queue, a->event_list); break; diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 8ac4b1f2322d..8272c0b9c5bf 100644 --- a/drivers/media/pci/saa7134/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -33,11 +33,11 @@ #include <linux/i2c.h> #include <linux/types.h> #include <linux/videodev2.h> +#include <linux/init.h> +#include <linux/crc32.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-common.h> -#include <linux/init.h> -#include <linux/crc32.h> #define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 #define MPEG_VIDEO_MAX_BITRATE_MAX 27000 @@ -124,7 +124,7 @@ static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) /* ---------------------------------------------------------------------- */ -static u8 PAT[] = { +static const u8 PAT[] = { 0xc2, /* i2c register */ 0x00, /* table number for encoder */ @@ -150,7 +150,7 @@ static u8 PAT[] = { 0x00, 0x00, 0x00, 0x00 /* CRC32 */ }; -static u8 PMT[] = { +static const u8 PMT[] = { 0xc2, /* i2c register */ 0x01, /* table number for encoder */ @@ -179,7 +179,7 @@ static u8 PMT[] = { 0x00, 0x00, 0x00, 0x00 /* CRC32 */ }; -static u8 PMT_AC3[] = { +static const u8 PMT_AC3[] = { 0xc2, /* i2c register */ 0x01, /* table number for encoder(1) */ 0x47, /* sync */ @@ -212,7 +212,7 @@ static u8 PMT_AC3[] = { 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ }; -static struct saa6752hs_mpeg_params param_defaults = +static const struct saa6752hs_mpeg_params param_defaults = { .ts_pid_pmt = 16, .ts_pid_video = 260, @@ -643,13 +643,6 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { .init = saa6752hs_init, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .s_std = saa6752hs_s_std, }; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index ae66d91bf713..8741cae9c9f2 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -399,7 +399,6 @@ static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order >= ARRAY_SIZE(smiapp_csi_data_formats)); - BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); dev_dbg(&client->dev, "new pixel order %s\n", pixel_order_str[pixel_order]); @@ -2028,8 +2027,8 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, sel->r.width = min(sel->r.width, src_size->width); sel->r.height = min(sel->r.height, src_size->height); - sel->r.left = min(sel->r.left, src_size->width - sel->r.width); - sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); + sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); *crops[sel->pad] = sel->r; @@ -2121,8 +2120,8 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.left = max(0, sel->r.left & ~1); sel->r.top = max(0, sel->r.top & ~1); - sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); - sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + sel->r.width = SMIAPP_ALIGN_DIM(sel->r.width, sel->flags); + sel->r.height = SMIAPP_ALIGN_DIM(sel->r.height, sel->flags); sel->r.width = max_t(unsigned int, sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 6f4056668bbc..ccf59406a172 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -208,8 +208,8 @@ struct mt9m111 { struct mt9m111_context *ctx; struct v4l2_rect rect; /* cropping rectangle */ struct v4l2_clk *clk; - int width; /* output */ - int height; /* sizes */ + unsigned int width; /* output */ + unsigned int height; /* sizes */ struct mutex power_lock; /* lock to protect power_count */ int power_count; const struct mt9m111_datafmt *fmt; diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c deleted file mode 100644 index 9252529fc5dd..000000000000 --- a/drivers/media/i2c/tcm825x.c +++ /dev/null @@ -1,937 +0,0 @@ -/* - * drivers/media/i2c/tcm825x.c - * - * TCM825X camera sensor driver. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from David Cohen <david.cohen@indt.org.br> - * - * This driver was based on ov9640 sensor driver from MontaVista - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/i2c.h> -#include <linux/module.h> -#include <media/v4l2-int-device.h> - -#include "tcm825x.h" - -/* - * The sensor has two fps modes: the lower one just gives half the fps - * at the same xclk than the high one. - */ -#define MAX_FPS 30 -#define MIN_FPS 8 -#define MAX_HALF_FPS (MAX_FPS / 2) -#define HIGH_FPS_MODE_LOWER_LIMIT 14 -#define DEFAULT_FPS MAX_HALF_FPS - -struct tcm825x_sensor { - const struct tcm825x_platform_data *platform_data; - struct v4l2_int_device *v4l2_int_device; - struct i2c_client *i2c_client; - struct v4l2_pix_format pix; - struct v4l2_fract timeperframe; -}; - -/* list of image formats supported by TCM825X sensor */ -static const struct v4l2_fmtdesc tcm825x_formats[] = { - { - .description = "YUYV (YUV 4:2:2), packed", - .pixelformat = V4L2_PIX_FMT_UYVY, - }, { - /* Note: V4L2 defines RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 - * - * We interpret RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 - */ - .description = "RGB565, le", - .pixelformat = V4L2_PIX_FMT_RGB565, - }, -}; - -#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) - -/* - * TCM825X register configuration for all combinations of pixel format and - * image size - */ -static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; -static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; -static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; -static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; -static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; -static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; - -static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; -static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; - -/* Our own specific controls */ -#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE -#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 -#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 -#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 -#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 -#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME - -/* Video controls */ -static struct vcontrol { - struct v4l2_queryctrl qc; - u16 reg; - u16 start_bit; -} video_control[] = { - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 63, - .step = 1, - }, - .reg = TCM825X_AG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MRG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MBG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_AWBSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure Time", - .minimum = 0, - .maximum = 0x1fff, - .step = 1, - }, - .reg = TCM825X_ESRSPD_U, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Image", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_H_INV, - .start_bit = 6, - }, - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_V_INV, - .start_bit = 7, - }, - /* Private controls */ - { - { - .id = V4L2_CID_ALC, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Luminance Control", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_ALCSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_H_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Horizontal Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_HDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_V_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Vertical Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_VDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_LENS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Lens Shading Compensation", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - }, - .reg = TCM825X_LENS, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_MAX_EXPOSURE_TIME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Maximum Exposure Time", - .minimum = 0, - .maximum = 0x3, - .step = 1, - }, - .reg = TCM825X_ESRLIM, - .start_bit = 5, - }, -}; - - -static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = -{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; - -static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = -{ &yuv422, &rgb565 }; - -/* - * Read a value from a register in an TCM825X sensor device. The value is - * returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_read_reg(struct i2c_client *client, int reg) -{ - int err; - struct i2c_msg msg[2]; - u8 reg_buf, data_buf = 0; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = ®_buf; - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = &data_buf; - - reg_buf = reg; - - err = i2c_transfer(client->adapter, msg, 2); - if (err < 0) - return err; - return data_buf; -} - -/* - * Write a value to a register in an TCM825X sensor device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) -{ - int err; - struct i2c_msg msg[1]; - unsigned char data[2]; - - if (!client->adapter) - return -ENODEV; - - msg->addr = client->addr; - msg->flags = 0; - msg->len = 2; - msg->buf = data; - data[0] = reg; - data[1] = val; - err = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) - return 0; - return err; -} - -static int __tcm825x_write_reg_mask(struct i2c_client *client, - u8 reg, u8 val, u8 mask) -{ - int rc; - - /* need to do read - modify - write */ - rc = tcm825x_read_reg(client, reg); - if (rc < 0) - return rc; - - rc &= (~mask); /* Clear the masked bits */ - val &= mask; /* Enforce mask on value */ - val |= rc; - - /* write the new value to the register */ - rc = tcm825x_write_reg(client, reg, val); - if (rc) - return rc; - - return 0; -} - -#define tcm825x_write_reg_mask(client, regmask, val) \ - __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ - TCM825X_MASK((regmask))) - - -/* - * Initialize a list of TCM825X registers. - * The list of registers is terminated by the pair of values - * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_default_regs(struct i2c_client *client, - const struct tcm825x_reg *reglist) -{ - int err; - const struct tcm825x_reg *next = reglist; - - while (!((next->reg == TCM825X_REG_TERM) - && (next->val == TCM825X_VAL_TERM))) { - err = tcm825x_write_reg(client, next->reg, next->val); - if (err) { - dev_err(&client->dev, "register writing failed\n"); - return err; - } - next++; - } - - return 0; -} - -static struct vcontrol *find_vctrl(int id) -{ - int i; - - if (id < V4L2_CID_BASE) - return NULL; - - for (i = 0; i < ARRAY_SIZE(video_control); i++) - if (video_control[i].qc.id == id) - return &video_control[i]; - - return NULL; -} - -/* - * Find the best match for a requested image capture size. The best match - * is chosen as the nearest match that has the same number or fewer pixels - * as the requested size, or the smallest image size if the requested size - * has fewer pixels than the smallest image. - */ -static enum image_size tcm825x_find_size(struct v4l2_int_device *s, - unsigned int width, - unsigned int height) -{ - enum image_size isize; - unsigned long pixels = width * height; - struct tcm825x_sensor *sensor = s->priv; - - for (isize = subQCIF; isize < VGA; isize++) { - if (tcm825x_sizes[isize + 1].height - * tcm825x_sizes[isize + 1].width > pixels) { - dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); - - return isize; - } - } - - dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); - - return VGA; -} - -/* - * Configure the TCM825X for current image size, pixel format, and - * frame period. fper is the frame period (in seconds) expressed as a - * fraction. Returns zero if successful, or non-zero otherwise. The - * actual frame period is returned in fper. - */ -static int tcm825x_configure(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &sensor->pix; - enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); - struct v4l2_fract *fper = &sensor->timeperframe; - enum pixel_format pfmt; - int err; - u32 tgt_fps; - u8 val; - - /* common register initialization */ - err = tcm825x_write_default_regs( - sensor->i2c_client, sensor->platform_data->default_regs()); - if (err) - return err; - - /* configure image size */ - val = tcm825x_siz_reg[isize]->val; - dev_dbg(&sensor->i2c_client->dev, - "configuring image size %d\n", isize); - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_siz_reg[isize]->reg, val); - if (err) - return err; - - /* configure pixel format */ - switch (pix->pixelformat) { - default: - case V4L2_PIX_FMT_RGB565: - pfmt = RGB565; - break; - case V4L2_PIX_FMT_UYVY: - pfmt = YUV422; - break; - } - - dev_dbg(&sensor->i2c_client->dev, - "configuring pixel format %d\n", pfmt); - val = tcm825x_fmt_reg[pfmt]->val; - - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_fmt_reg[pfmt]->reg, val); - if (err) - return err; - - /* - * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be - * set. Frame rate will be halved from the normal. - */ - tgt_fps = fper->denominator / fper->numerator; - if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { - val = tcm825x_read_reg(sensor->i2c_client, 0x02); - val |= 0x80; - tcm825x_write_reg(sensor->i2c_client, 0x02, val); - } - - return 0; -} - -static int ioctl_queryctrl(struct v4l2_int_device *s, - struct v4l2_queryctrl *qc) -{ - struct vcontrol *control; - - control = find_vctrl(qc->id); - - if (control == NULL) - return -EINVAL; - - *qc = control->qc; - - return 0; -} - -static int ioctl_g_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - int val, r; - struct vcontrol *lvc; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - - val_upper = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_U)); - if (val_upper < 0) - return val_upper; - val_lower = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_L)); - if (val_lower < 0) - return val_lower; - - vc->value = ((val_upper & 0x1f) << 8) | (val_lower); - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); - if (r < 0) - return r; - val = r & TCM825X_MASK(lvc->reg); - val >>= lvc->start_bit; - - if (val < 0) - return val; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - vc->value = val; - return 0; -} - -static int ioctl_s_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - struct vcontrol *lvc; - int val = vc->value; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); - val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_U, val_upper)) - return -EIO; - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_L, val_lower)) - return -EIO; - - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - val = val << lvc->start_bit; - if (tcm825x_write_reg_mask(client, lvc->reg, val)) - return -EIO; - - return 0; -} - -static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (index >= TCM825X_NUM_CAPTURE_FORMATS) - return -EINVAL; - break; - - default: - return -EINVAL; - } - - fmt->flags = tcm825x_formats[index].flags; - strlcpy(fmt->description, tcm825x_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = tcm825x_formats[index].pixelformat; - - return 0; -} - -static int ioctl_try_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - enum image_size isize; - int ifmt; - struct v4l2_pix_format *pix = &f->fmt.pix; - - isize = tcm825x_find_size(s, pix->width, pix->height); - dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", - isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); - - pix->width = tcm825x_sizes[isize].width; - pix->height = tcm825x_sizes[isize].height; - - for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) - if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) - break; - - if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) - ifmt = 0; /* Default = YUV 4:2:2 */ - - pix->pixelformat = tcm825x_formats[ifmt].pixelformat; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; - pix->sizeimage = pix->bytesperline * pix->height; - pix->priv = 0; - dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", - pix->pixelformat); - - switch (pix->pixelformat) { - case V4L2_PIX_FMT_UYVY: - default: - pix->colorspace = V4L2_COLORSPACE_JPEG; - break; - case V4L2_PIX_FMT_RGB565: - pix->colorspace = V4L2_COLORSPACE_SRGB; - break; - } - - return 0; -} - -static int ioctl_s_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - int rval; - - rval = ioctl_try_fmt_cap(s, f); - if (rval) - return rval; - - rval = tcm825x_configure(s); - - sensor->pix = *pix; - - return rval; -} - -static int ioctl_g_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - - f->fmt.pix = sensor->pix; - - return 0; -} - -static int ioctl_g_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_captureparm *cparm = &a->parm.capture; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(a, 0, sizeof(*a)); - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = sensor->timeperframe; - - return 0; -} - -static int ioctl_s_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; - u32 tgt_fps; /* target frames per secound */ - int rval; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if ((timeperframe->numerator == 0) - || (timeperframe->denominator == 0)) { - timeperframe->denominator = DEFAULT_FPS; - timeperframe->numerator = 1; - } - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - if (tgt_fps > MAX_FPS) { - timeperframe->denominator = MAX_FPS; - timeperframe->numerator = 1; - } else if (tgt_fps < MIN_FPS) { - timeperframe->denominator = MIN_FPS; - timeperframe->numerator = 1; - } - - sensor->timeperframe = *timeperframe; - - rval = tcm825x_configure(s); - - return rval; -} - -static int ioctl_s_power(struct v4l2_int_device *s, int on) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->power_set(on); -} - -/* - * Given the image capture format in pix, the nominal frame period in - * timeperframe, calculate the required xclk frequency. - * - * TCM825X input frequency characteristics are: - * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz - */ - -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &sensor->timeperframe; - u32 tgt_xclk; /* target xclk */ - u32 tgt_fps; /* target frames per secound */ - int rval; - - rval = sensor->platform_data->ifparm(p); - if (rval) - return rval; - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? - (2457 * tgt_fps) / MAX_HALF_FPS : - (2457 * tgt_fps) / MAX_FPS; - tgt_xclk *= 10000; - - tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); - tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); - - p->u.bt656.clock_curr = tgt_xclk; - - return 0; -} - -static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->needs_reset(s, buf, &sensor->pix); -} - -static int ioctl_reset(struct v4l2_int_device *s) -{ - return -EBUSY; -} - -static int ioctl_init(struct v4l2_int_device *s) -{ - return tcm825x_configure(s); -} - -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - int r; - - r = tcm825x_read_reg(sensor->i2c_client, 0x01); - if (r < 0) - return r; - if (r == 0) { - dev_err(&sensor->i2c_client->dev, "device not detected\n"); - return -EIO; - } - return 0; -} - -static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { - { vidioc_int_dev_init_num, - (v4l2_int_ioctl_func *)ioctl_dev_init }, - { vidioc_int_dev_exit_num, - (v4l2_int_ioctl_func *)ioctl_dev_exit }, - { vidioc_int_s_power_num, - (v4l2_int_ioctl_func *)ioctl_s_power }, - { vidioc_int_g_ifparm_num, - (v4l2_int_ioctl_func *)ioctl_g_ifparm }, - { vidioc_int_g_needs_reset_num, - (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, - { vidioc_int_reset_num, - (v4l2_int_ioctl_func *)ioctl_reset }, - { vidioc_int_init_num, - (v4l2_int_ioctl_func *)ioctl_init }, - { vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, - { vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, - { vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, - { vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, - { vidioc_int_g_parm_num, - (v4l2_int_ioctl_func *)ioctl_g_parm }, - { vidioc_int_s_parm_num, - (v4l2_int_ioctl_func *)ioctl_s_parm }, - { vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *)ioctl_queryctrl }, - { vidioc_int_g_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_g_ctrl }, - { vidioc_int_s_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_s_ctrl }, -}; - -static struct v4l2_int_slave tcm825x_slave = { - .ioctls = tcm825x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), -}; - -static struct tcm825x_sensor tcm825x; - -static struct v4l2_int_device tcm825x_int_device = { - .module = THIS_MODULE, - .name = TCM825X_NAME, - .priv = &tcm825x, - .type = v4l2_int_type_slave, - .u = { - .slave = &tcm825x_slave, - }, -}; - -static int tcm825x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct tcm825x_sensor *sensor = &tcm825x; - - if (i2c_get_clientdata(client)) - return -EBUSY; - - sensor->platform_data = client->dev.platform_data; - - if (sensor->platform_data == NULL - || !sensor->platform_data->is_okay()) - return -ENODEV; - - sensor->v4l2_int_device = &tcm825x_int_device; - - sensor->i2c_client = client; - i2c_set_clientdata(client, sensor); - - /* Make the default capture format QVGA RGB565 */ - sensor->pix.width = tcm825x_sizes[QVGA].width; - sensor->pix.height = tcm825x_sizes[QVGA].height; - sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; - - return v4l2_int_device_register(sensor->v4l2_int_device); -} - -static int tcm825x_remove(struct i2c_client *client) -{ - struct tcm825x_sensor *sensor = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ - - v4l2_int_device_unregister(sensor->v4l2_int_device); - - return 0; -} - -static const struct i2c_device_id tcm825x_id[] = { - { "tcm825x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tcm825x_id); - -static struct i2c_driver tcm825x_i2c_driver = { - .driver = { - .name = TCM825X_NAME, - }, - .probe = tcm825x_probe, - .remove = tcm825x_remove, - .id_table = tcm825x_id, -}; - -static struct tcm825x_sensor tcm825x = { - .timeperframe = { - .numerator = 1, - .denominator = DEFAULT_FPS, - }, -}; - -static int __init tcm825x_init(void) -{ - int rval; - - rval = i2c_add_driver(&tcm825x_i2c_driver); - if (rval) - printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", - __func__); - - return rval; -} - -static void __exit tcm825x_exit(void) -{ - i2c_del_driver(&tcm825x_i2c_driver); -} - -/* - * FIXME: Menelaus isn't ready (?) at module_init stage, so use - * late_initcall for now. - */ -late_initcall(tcm825x_init); -module_exit(tcm825x_exit); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); -MODULE_DESCRIPTION("TCM825x camera sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h deleted file mode 100644 index 8ebab953963f..000000000000 --- a/drivers/media/i2c/tcm825x.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/media/i2c/tcm825x.h - * - * Register definitions for the TCM825X CameraChip. - * - * Author: David Cohen (david.cohen@indt.org.br) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - * This file was based on ov9640.h from MontaVista - */ - -#ifndef TCM825X_H -#define TCM825X_H - -#include <linux/videodev2.h> - -#include <media/v4l2-int-device.h> - -#define TCM825X_NAME "tcm825x" - -#define TCM825X_MASK(x) x & 0x00ff -#define TCM825X_ADDR(x) (x & 0xff00) >> 8 - -/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ -#define TCM825X_I2C_ADDR 0x3d - -/* - * define register offsets for the TCM825X sensor chip - * OFFSET(8 bits) + MASK(8 bits) - * MASK bit 4 and 3 are used when the register uses more than one address - */ -#define TCM825X_FPS 0x0280 -#define TCM825X_ACF 0x0240 -#define TCM825X_DOUTBUF 0x020C -#define TCM825X_DCLKP 0x0202 -#define TCM825X_ACFDET 0x0201 -#define TCM825X_DOUTSW 0x0380 -#define TCM825X_DATAHZ 0x0340 -#define TCM825X_PICSIZ 0x033c -#define TCM825X_PICFMT 0x0302 -#define TCM825X_V_INV 0x0480 -#define TCM825X_H_INV 0x0440 -#define TCM825X_ESRLSW 0x0430 -#define TCM825X_V_LENGTH 0x040F -#define TCM825X_ALCSW 0x0580 -#define TCM825X_ESRLIM 0x0560 -#define TCM825X_ESRSPD_U 0x051F -#define TCM825X_ESRSPD_L 0x06FF -#define TCM825X_AG 0x07FF -#define TCM825X_ESRSPD2 0x06FF -#define TCM825X_ALCMODE 0x0830 -#define TCM825X_ALCH 0x080F -#define TCM825X_ALCL 0x09FF -#define TCM825X_AWBSW 0x0A80 -#define TCM825X_MRG 0x0BFF -#define TCM825X_MBG 0x0CFF -#define TCM825X_GAMSW 0x0D80 -#define TCM825X_HDTG 0x0EFF -#define TCM825X_VDTG 0x0FFF -#define TCM825X_HDTCORE 0x10F0 -#define TCM825X_VDTCORE 0x100F -#define TCM825X_CONT 0x11FF -#define TCM825X_BRIGHT 0x12FF -#define TCM825X_VHUE 0x137F -#define TCM825X_UHUE 0x147F -#define TCM825X_VGAIN 0x153F -#define TCM825X_UGAIN 0x163F -#define TCM825X_UVCORE 0x170F -#define TCM825X_SATU 0x187F -#define TCM825X_MHMODE 0x1980 -#define TCM825X_MHLPFSEL 0x1940 -#define TCM825X_YMODE 0x1930 -#define TCM825X_MIXHG 0x1907 -#define TCM825X_LENS 0x1A3F -#define TCM825X_AGLIM 0x1BE0 -#define TCM825X_LENSRPOL 0x1B10 -#define TCM825X_LENSRGAIN 0x1B0F -#define TCM825X_ES100S 0x1CFF -#define TCM825X_ES120S 0x1DFF -#define TCM825X_DMASK 0x1EC0 -#define TCM825X_CODESW 0x1E20 -#define TCM825X_CODESEL 0x1E10 -#define TCM825X_TESPIC 0x1E04 -#define TCM825X_PICSEL 0x1E03 -#define TCM825X_HNUM 0x20FF -#define TCM825X_VOUTPH 0x287F -#define TCM825X_ESROUT 0x327F -#define TCM825X_ESROUT2 0x33FF -#define TCM825X_AGOUT 0x34FF -#define TCM825X_DGOUT 0x353F -#define TCM825X_AGSLOW1 0x39C0 -#define TCM825X_FLLSMODE 0x3930 -#define TCM825X_FLLSLIM 0x390F -#define TCM825X_DETSEL 0x3AF0 -#define TCM825X_ACDETNC 0x3A0F -#define TCM825X_AGSLOW2 0x3BC0 -#define TCM825X_DG 0x3B3F -#define TCM825X_REJHLEV 0x3CFF -#define TCM825X_ALCLOCK 0x3D80 -#define TCM825X_FPSLNKSW 0x3D40 -#define TCM825X_ALCSPD 0x3D30 -#define TCM825X_REJH 0x3D03 -#define TCM825X_SHESRSW 0x3E80 -#define TCM825X_ESLIMSEL 0x3E40 -#define TCM825X_SHESRSPD 0x3E30 -#define TCM825X_ELSTEP 0x3E0C -#define TCM825X_ELSTART 0x3E03 -#define TCM825X_AGMIN 0x3FFF -#define TCM825X_PREGRG 0x423F -#define TCM825X_PREGBG 0x433F -#define TCM825X_PRERG 0x443F -#define TCM825X_PREBG 0x453F -#define TCM825X_MSKBR 0x477F -#define TCM825X_MSKGR 0x487F -#define TCM825X_MSKRB 0x497F -#define TCM825X_MSKGB 0x4A7F -#define TCM825X_MSKRG 0x4B7F -#define TCM825X_MSKBG 0x4C7F -#define TCM825X_HDTCSW 0x4D80 -#define TCM825X_VDTCSW 0x4D40 -#define TCM825X_DTCYL 0x4D3F -#define TCM825X_HDTPSW 0x4E80 -#define TCM825X_VDTPSW 0x4E40 -#define TCM825X_DTCGAIN 0x4E3F -#define TCM825X_DTLLIMSW 0x4F10 -#define TCM825X_DTLYLIM 0x4F0F -#define TCM825X_YLCUTLMSK 0x5080 -#define TCM825X_YLCUTL 0x503F -#define TCM825X_YLCUTHMSK 0x5180 -#define TCM825X_YLCUTH 0x513F -#define TCM825X_UVSKNC 0x527F -#define TCM825X_UVLJ 0x537F -#define TCM825X_WBGMIN 0x54FF -#define TCM825X_WBGMAX 0x55FF -#define TCM825X_WBSPDUP 0x5603 -#define TCM825X_ALLAREA 0x5820 -#define TCM825X_WBLOCK 0x5810 -#define TCM825X_WB2SP 0x580F -#define TCM825X_KIZUSW 0x5920 -#define TCM825X_PBRSW 0x5910 -#define TCM825X_ABCSW 0x5903 -#define TCM825X_PBDLV 0x5AFF -#define TCM825X_PBC1LV 0x5BFF - -#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) - -#define TCM825X_BYTES_PER_PIXEL 2 - -#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ -#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ - -/* define a structure for tcm825x register initialization values */ -struct tcm825x_reg { - u8 val; - u16 reg; -}; - -enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; -enum pixel_format { YUV422 = 0, RGB565 }; -#define NUM_IMAGE_SIZES 6 -#define NUM_PIXEL_FORMATS 2 - -#define TCM825X_XCLK_MIN 11900000 -#define TCM825X_XCLK_MAX 25000000 - -struct capture_size { - unsigned long width; - unsigned long height; -}; - -struct tcm825x_platform_data { - /* Is the sensor usable? Doesn't yet mean it's there, but you - * can try! */ - int (*is_okay)(void); - /* Set power state, zero is off, non-zero is on. */ - int (*power_set)(int power); - /* Default registers written after power-on or reset. */ - const struct tcm825x_reg *(*default_regs)(void); - int (*needs_reset)(struct v4l2_int_device *s, void *buf, - struct v4l2_pix_format *fmt); - int (*ifparm)(struct v4l2_ifparm *p); - int (*is_upside_down)(void); -}; - -/* Array of image sizes supported by TCM825X. These must be ordered from - * smallest image size to largest. - */ -static const struct capture_size tcm825x_sizes[] = { - { 128, 96 }, /* subQCIF */ - { 160, 120 }, /* QQVGA */ - { 176, 144 }, /* QCIF */ - { 320, 240 }, /* QVGA */ - { 352, 288 }, /* CIF */ - { 640, 480 }, /* VGA */ -}; - -#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 89c0b13463b7..542d2528b3f9 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -58,21 +58,17 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[1]; int rc; - - buffer[0] = addr; - - rc = i2c_master_send(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - msleep(10); - - rc = i2c_master_recv(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; + struct i2c_msg msg[] = { + { .addr = c->addr, .flags = 0, + .buf = &addr, .len = 1 }, + { .addr = c->addr, .flags = I2C_M_RD, + .buf = buffer, .len = 1 } + }; + + rc = i2c_transfer(c->adapter, msg, 2); + if (rc < 0 || rc != 2) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 2)\n", rc); + return rc < 0 ? rc : -EIO; } v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); @@ -867,7 +863,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) struct v4l2_rect rect = a->c; struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; - int hmax; + unsigned int hmax; v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", __func__, rect.left, rect.top, rect.width, rect.height); @@ -877,9 +873,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) /* tvp5150 has some special limits */ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp(rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); + rect.width = clamp_t(unsigned int, rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); /* Calculate height based on current standard */ @@ -893,9 +889,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) else hmax = TVP5150_V_MAX_OTHERS; - rect.height = clamp(rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); + rect.height = clamp_t(unsigned int, rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 25bdd9312fea..23f4f65fccd7 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -503,6 +503,7 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) return &container_of(ctrl->handler, struct vs6624, hdl)->sd; } +#ifdef CONFIG_VIDEO_ADV_DEBUG static int vs6624_read(struct v4l2_subdev *sd, u16 index) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -515,6 +516,7 @@ static int vs6624_read(struct v4l2_subdev *sd, u16 index) return buf[0]; } +#endif static int vs6624_write(struct v4l2_subdev *sd, u16 index, u8 value) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 2c286c307145..37c334edc7e8 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -235,6 +235,8 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + DECLARE_BITMAP(active, entity->num_pads); + DECLARE_BITMAP(has_no_links, entity->num_pads); unsigned int i; entity->stream_count++; @@ -248,21 +250,46 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, if (!entity->ops || !entity->ops->link_validate) continue; + bitmap_zero(active, entity->num_pads); + bitmap_fill(has_no_links, entity->num_pads); + for (i = 0; i < entity->num_links; i++) { struct media_link *link = &entity->links[i]; - - /* Is this pad part of an enabled link? */ - if (!(link->flags & MEDIA_LNK_FL_ENABLED)) - continue; - - /* Are we the sink or not? */ - if (link->sink->entity != entity) + struct media_pad *pad = link->sink->entity == entity + ? link->sink : link->source; + + /* Mark that a pad is connected by a link. */ + bitmap_clear(has_no_links, pad->index, 1); + + /* + * Pads that either do not need to connect or + * are connected through an enabled link are + * fine. + */ + if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) || + link->flags & MEDIA_LNK_FL_ENABLED) + bitmap_set(active, pad->index, 1); + + /* + * Link validation will only take place for + * sink ends of the link that are enabled. + */ + if (link->sink != pad || + !(link->flags & MEDIA_LNK_FL_ENABLED)) continue; ret = entity->ops->link_validate(link); if (ret < 0 && ret != -ENOIOCTLCMD) goto error; } + + /* Either no links or validated links are fine. */ + bitmap_or(active, active, has_no_links, entity->num_pads); + + if (!bitmap_full(active, entity->num_pads)) { + ret = -EPIPE; + goto error; + } } mutex_unlock(&mdev->graph_mutex); diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index d85cb0ace4dc..6662b495b22c 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -2426,7 +2426,7 @@ struct tvcard bttv_tvcards[] = { }, /* ---- card 0x87---------------------------------- */ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = { - /* Michael Krufky <mkrufky@m1k.net> */ + /* Michael Krufky <mkrufky@linuxtv.org> */ .name = "DViCO FusionHDTV 5 Lite", .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ .tuner_addr = ADDR_UNSET, diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 92a06fd85865..afcd53bfcf8e 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1126,9 +1126,9 @@ bttv_crop_calc_limits(struct bttv_crop *c) c->min_scaled_height = 32; } else { c->min_scaled_width = - (max(48, c->rect.width >> 4) + 3) & ~3; + (max_t(unsigned int, 48, c->rect.width >> 4) + 3) & ~3; c->min_scaled_height = - max(32, c->rect.height >> 4); + max_t(unsigned int, 32, c->rect.height >> 4); } c->max_scaled_width = c->rect.width & ~3; @@ -2024,7 +2024,7 @@ limit_scaled_size_lock (struct bttv_fh * fh, /* We cannot scale up. When the scaled image is larger than crop.rect we adjust the crop.rect as required by the V4L2 spec, hence cropcap.bounds are our limit. */ - max_width = min(b->width, (__s32) MAX_HACTIVE); + max_width = min_t(unsigned int, b->width, MAX_HACTIVE); max_height = b->height; /* We cannot capture the same line as video and VBI data. @@ -3266,7 +3266,9 @@ static ssize_t radio_read(struct file *file, char __user *data, struct bttv_fh *fh = file->private_data; struct bttv *btv = fh->btv; struct saa6588_command cmd; - cmd.block_count = count/3; + + cmd.block_count = count / 3; + cmd.nonblocking = file->f_flags & O_NONBLOCK; cmd.buffer = data; cmd.instance = file; cmd.result = -ENODEV; diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c index 922e8233fd0b..3f364b7062b9 100644 --- a/drivers/media/pci/bt8xx/bttv-gpio.c +++ b/drivers/media/pci/bt8xx/bttv-gpio.c @@ -98,7 +98,7 @@ int bttv_sub_add_device(struct bttv_core *core, char *name) err = device_register(&sub->dev); if (0 != err) { - kfree(sub); + put_device(&sub->dev); return err; } pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index c1f8cc6f14b2..716bdc57fac6 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -327,13 +327,16 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) struct i2c_client *c; u8 eedata[256]; + memset(tv, 0, sizeof(*tv)); + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return; strlcpy(c->name, "cx18 tveeprom tmp", sizeof(c->name)); c->adapter = &cx->i2c_adap[0]; c->addr = 0xa0 >> 1; - memset(tv, 0, sizeof(*tv)); if (tveeprom_read(c, eedata, sizeof(eedata))) goto ret; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 6e91e84d6bf9..b1e08c3e55cd 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -618,7 +618,7 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, * Only boards with eeprom and byte 1 at eeprom=1 have it */ -static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = { +static const struct pci_device_id cx25821_audio_pci_tbl[] = { {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0,} }; diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index b762c5b2ca10..e81173c41e5a 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1361,7 +1361,7 @@ static void cx25821_finidev(struct pci_dev *pci_dev) kfree(dev); } -static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = { +static const struct pci_device_id cx25821_pci_tbl[] = { { /* CX25821 Athena */ .vendor = 0x14f1, diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index 400eb1c42d3f..d014206e7176 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -931,9 +931,9 @@ error: */ static void cx88_audio_finidev(struct pci_dev *pci) { - struct cx88_audio_dev *card = pci_get_drvdata(pci); + struct snd_card *card = pci_get_drvdata(pci); - snd_card_free((void *)card); + snd_card_free(card); devno--; } diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 15b90d6e9130..7883393571e5 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -6,6 +6,7 @@ config VIDEO_SAA7134 select VIDEO_TVEEPROM select CRC32 select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA6752HS if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Philips SAA713x based TV cards. diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index 35375480ed4d..58de9b085689 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -4,7 +4,7 @@ saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o saa7134-y += saa7134-video.o saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o -obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o +obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index d45e7f6ff332..c9b2350e92c8 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -2590,7 +2590,7 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180] = { - /* Michael Krufky <mkrufky@m1k.net> + /* Michael Krufky <mkrufky@linuxtv.org> * Uses Alps Electric TDHU2, containing NXT2004 ATSC Decoder * AFAIK, there is no analog demod, thus, * no support for analog television. diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 27d7ee709c58..1362b4aab473 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -751,6 +751,7 @@ static int saa7134_hwfini(struct saa7134_dev *dev) saa7134_input_fini(dev); saa7134_vbi_fini(dev); saa7134_tvaudio_fini(dev); + saa7134_video_fini(dev); return 0; } @@ -802,7 +803,6 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; - 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); @@ -1008,13 +1008,13 @@ static int saa7134_initdev(struct pci_dev *pci_dev, /* load i2c helpers */ if (card_is_empress(dev)) { - struct v4l2_subdev *sd = + dev->empress_sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "saa6752hs", saa7134_boards[dev->board].empress_addr, NULL); - if (sd) - sd->grp_id = GRP_EMPRESS; + if (dev->empress_sd) + dev->empress_sd->grp_id = GRP_EMPRESS; } if (saa7134_boards[dev->board].rds_addr) { @@ -1046,6 +1046,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); + dev->video_dev->ctrl_handler = &dev->ctrl_handler; err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[dev->nr]); if (err < 0) { @@ -1057,6 +1058,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->name, video_device_node_name(dev->video_dev)); dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); + dev->vbi_dev->ctrl_handler = &dev->ctrl_handler; err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, vbi_nr[dev->nr]); @@ -1067,6 +1069,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, if (card_has_radio(dev)) { dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio"); + dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler; err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, radio_nr[dev->nr]); if (err < 0) diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 3022eb2a7925..0a9047e754b9 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -23,12 +23,12 @@ #include <linux/kernel.h> #include <linux/delay.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> + #include "saa7134-reg.h" #include "saa7134.h" -#include <media/saa6752hs.h> -#include <media/v4l2-common.h> - /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); @@ -85,52 +85,54 @@ static int ts_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct saa7134_dev *dev = video_drvdata(file); - int err; + struct saa7134_fh *fh; - dprintk("open dev=%s\n", video_device_node_name(vdev)); - err = -EBUSY; - if (!mutex_trylock(&dev->empress_tsq.vb_lock)) - return err; - if (atomic_read(&dev->empress_users)) - goto done; + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + v4l2_fh_init(&fh->fh, vdev); + file->private_data = fh; + fh->is_empress = true; + v4l2_fh_add(&fh->fh); /* Unmute audio */ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); - atomic_inc(&dev->empress_users); - file->private_data = dev; - err = 0; - -done: - mutex_unlock(&dev->empress_tsq.vb_lock); - return err; + return 0; } static int ts_release(struct file *file) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh = file->private_data; - videobuf_stop(&dev->empress_tsq); - videobuf_mmap_free(&dev->empress_tsq); + if (res_check(fh, RESOURCE_EMPRESS)) { + videobuf_stop(&dev->empress_tsq); + videobuf_mmap_free(&dev->empress_tsq); - /* stop the encoder */ - ts_reset_encoder(dev); + /* stop the encoder */ + ts_reset_encoder(dev); - /* Mute audio */ - saa_writeb(SAA7134_AUDIO_MUTE_CTRL, - saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); - - atomic_dec(&dev->empress_users); + /* Mute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); return 0; } static ssize_t ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); + if (res_locked(dev, RESOURCE_EMPRESS)) + return -EBUSY; if (!dev->empress_started) ts_init_encoder(dev); @@ -142,68 +144,27 @@ ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) static unsigned int ts_poll(struct file *file, struct poll_table_struct *wait) { - struct saa7134_dev *dev = file->private_data; + unsigned long req_events = poll_requested_events(wait); + struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh = file->private_data; + unsigned int rc = 0; - return videobuf_poll_stream(file, &dev->empress_tsq, wait); + if (v4l2_event_pending(&fh->fh)) + rc = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); + return rc | videobuf_poll_stream(file, &dev->empress_tsq, wait); } static int ts_mmap(struct file *file, struct vm_area_struct * vma) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_mmap_mapper(&dev->empress_tsq, vma); } -/* - * This function is _not_ called directly, but from - * video_generic_ioctl (and maybe others). userspace - * copying is done already, arg is a kernel pointer. - */ - -static int empress_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct saa7134_dev *dev = file->private_data; - - 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_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int empress_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(i->name, "CCIR656"); - - return 0; -} - -static int empress_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int empress_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i != 0) - return -EINVAL; - - return 0; -} - static int empress_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -219,7 +180,7 @@ static int empress_enum_fmt_vid_cap(struct file *file, void *priv, static int empress_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt); @@ -236,7 +197,7 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv, static int empress_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); @@ -254,7 +215,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, static int empress_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); @@ -269,175 +230,6 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int empress_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_reqbufs(&dev->empress_tsq, p); -} - -static int empress_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_querybuf(&dev->empress_tsq, b); -} - -static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_qbuf(&dev->empress_tsq, b); -} - -static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_dqbuf(&dev->empress_tsq, b, - file->f_flags & O_NONBLOCK); -} - -static int empress_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_streamon(&dev->empress_tsq); -} - -static int empress_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_dev *dev = file->private_data; - - return videobuf_streamoff(&dev->empress_tsq); -} - -static int empress_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7134_dev *dev = file->private_data; - int err; - - /* count == 0 is abused in saa6752hs.c, so that special - case is handled here explicitly. */ - if (ctrls->count == 0) - return 0; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - err = saa_call_empress(dev, core, s_ext_ctrls, ctrls); - ts_init_encoder(dev); - - return err; -} - -static int empress_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7134_dev *dev = file->private_data; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return saa_call_empress(dev, core, g_ext_ctrls, ctrls); -} - -static int empress_g_ctrl(struct file *file, void *priv, - struct v4l2_control *c) -{ - struct saa7134_dev *dev = file->private_data; - - return saa7134_g_ctrl_internal(dev, NULL, c); -} - -static int empress_s_ctrl(struct file *file, void *priv, - struct v4l2_control *c) -{ - struct saa7134_dev *dev = file->private_data; - - return saa7134_s_ctrl_internal(dev, NULL, c); -} - -static int empress_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - /* Must be sorted from low to high control ID! */ - static const u32 user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_MUTE, - V4L2_CID_HFLIP, - 0 - }; - - /* Must be sorted from low to high control ID! */ - static const u32 mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_STREAM_PID_PMT, - V4L2_CID_MPEG_STREAM_PID_AUDIO, - V4L2_CID_MPEG_STREAM_PID_VIDEO, - V4L2_CID_MPEG_STREAM_PID_PCR, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0 - }; - static const u32 *ctrl_classes[] = { - user_ctrls, - mpeg_ctrls, - NULL - }; - struct saa7134_dev *dev = file->private_data; - - c->id = v4l2_ctrl_next(ctrl_classes, c->id); - if (c->id == 0) - return -EINVAL; - if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) - return v4l2_ctrl_query_fill(c, 0, 0, 0, 0); - if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) - return saa7134_queryctrl(file, priv, c); - return saa_call_empress(dev, core, queryctrl, c); -} - -static int empress_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *c) -{ - struct saa7134_dev *dev = file->private_data; - - if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return saa_call_empress(dev, core, querymenu, c); -} - -static int empress_s_std(struct file *file, void *priv, v4l2_std_id id) -{ - struct saa7134_dev *dev = file->private_data; - - return saa7134_s_std_internal(dev, NULL, id); -} - -static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct saa7134_dev *dev = file->private_data; - - *id = dev->tvnorm->id; - return 0; -} - static const struct v4l2_file_operations ts_fops = { .owner = THIS_MODULE, @@ -450,28 +242,29 @@ static const struct v4l2_file_operations ts_fops = }; static const struct v4l2_ioctl_ops ts_ioctl_ops = { - .vidioc_querycap = empress_querycap, + .vidioc_querycap = saa7134_querycap, .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, - .vidioc_reqbufs = empress_reqbufs, - .vidioc_querybuf = empress_querybuf, - .vidioc_qbuf = empress_qbuf, - .vidioc_dqbuf = empress_dqbuf, - .vidioc_streamon = empress_streamon, - .vidioc_streamoff = empress_streamoff, - .vidioc_s_ext_ctrls = empress_s_ext_ctrls, - .vidioc_g_ext_ctrls = empress_g_ext_ctrls, - .vidioc_enum_input = empress_enum_input, - .vidioc_g_input = empress_g_input, - .vidioc_s_input = empress_s_input, - .vidioc_queryctrl = empress_queryctrl, - .vidioc_querymenu = empress_querymenu, - .vidioc_g_ctrl = empress_g_ctrl, - .vidioc_s_ctrl = empress_s_ctrl, - .vidioc_s_std = empress_s_std, - .vidioc_g_std = empress_g_std, + .vidioc_reqbufs = saa7134_reqbufs, + .vidioc_querybuf = saa7134_querybuf, + .vidioc_qbuf = saa7134_qbuf, + .vidioc_dqbuf = saa7134_dqbuf, + .vidioc_streamon = saa7134_streamon, + .vidioc_streamoff = saa7134_streamoff, + .vidioc_g_frequency = saa7134_g_frequency, + .vidioc_s_frequency = saa7134_s_frequency, + .vidioc_g_tuner = saa7134_g_tuner, + .vidioc_s_tuner = saa7134_s_tuner, + .vidioc_enum_input = saa7134_enum_input, + .vidioc_g_input = saa7134_g_input, + .vidioc_s_input = saa7134_s_input, + .vidioc_s_std = saa7134_s_std, + .vidioc_g_std = saa7134_g_std, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* ----------------------------------------------------------- */ @@ -501,9 +294,26 @@ static void empress_signal_change(struct saa7134_dev *dev) schedule_work(&dev->empress_workqueue); } +static bool empress_ctrl_filter(const struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_PRIVATE_INVERT: + case V4L2_CID_PRIVATE_AUTOMUTE: + return true; + default: + return false; + } +} static int empress_init(struct saa7134_dev *dev) { + struct v4l2_ctrl_handler *hdl = &dev->empress_ctrl_handler; int err; dprintk("%s: %s\n",dev->name,__func__); @@ -516,6 +326,16 @@ static int empress_init(struct saa7134_dev *dev) snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), "%s empress (%s)", dev->name, saa7134_boards[dev->board].name); + set_bit(V4L2_FL_USE_FH_PRIO, &dev->empress_dev->flags); + v4l2_ctrl_handler_init(hdl, 21); + v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter); + if (dev->empress_sd) + v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL); + if (hdl->error) { + video_device_release(dev->empress_dev); + return hdl->error; + } + dev->empress_dev->ctrl_handler = hdl; INIT_WORK(&dev->empress_workqueue, empress_signal_update); @@ -551,6 +371,7 @@ static int empress_fini(struct saa7134_dev *dev) return 0; flush_work(&dev->empress_workqueue); video_unregister_device(dev->empress_dev); + v4l2_ctrl_handler_free(&dev->empress_ctrl_handler); dev->empress_dev = NULL; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index e9aa94b807f1..d4da18d049f3 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -117,8 +117,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); struct saa7134_tvnorm *norm = dev->tvnorm; unsigned int lines, llength, size; @@ -141,7 +140,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.width = llength; buf->vb.height = lines; buf->vb.size = size; - buf->pt = &fh->pt_vbi; + buf->pt = &dev->pt_vbi; err = videobuf_iolock(q,&buf->vb,NULL); if (err) @@ -166,8 +165,7 @@ static int buffer_prepare(struct videobuf_queue *q, static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = q->priv_data; int llength,lines; lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1; @@ -181,8 +179,7 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); saa7134_buffer_queue(dev,&dev->vbi_q,buf); diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index fb60da85bc2c..eb472b5b26a0 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -27,11 +27,13 @@ #include <linux/slab.h> #include <linux/sort.h> -#include "saa7134-reg.h" -#include "saa7134.h" #include <media/v4l2-common.h> +#include <media/v4l2-event.h> #include <media/saa6588.h> +#include "saa7134-reg.h" +#include "saa7134.h" + /* ------------------------------------------------------------------ */ unsigned int video_debug; @@ -369,117 +371,6 @@ static struct saa7134_tvnorm tvnorms[] = { }; #define TVNORMS ARRAY_SIZE(tvnorms) -#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) -#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) -#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3) -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4) - -static const struct v4l2_queryctrl no_ctrl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; -static const struct v4l2_queryctrl video_ctrls[] = { - /* --- video --- */ - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 68, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_HFLIP, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - /* --- 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 = -15, - .maximum = 15, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- private --- */ - { - .id = V4L2_CID_PRIVATE_INVERT, - .name = "Invert", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_Y_ODD, - .name = "y offset odd field", - .minimum = 0, - .maximum = 128, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_Y_EVEN, - .name = "y offset even field", - .minimum = 0, - .maximum = 128, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_AUTOMUTE, - .name = "automute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - } -}; -static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls); - -static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id) -{ - unsigned int i; - - for (i = 0; i < CTRLS; i++) - if (video_ctrls[i].id == id) - return video_ctrls+i; - return NULL; -} - static struct saa7134_format* format_by_fourcc(unsigned int fourcc) { unsigned int i; @@ -514,16 +405,6 @@ static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int return 1; } -static int res_check(struct saa7134_fh *fh, unsigned int bit) -{ - return (fh->resources & bit); -} - -static int res_locked(struct saa7134_dev *dev, unsigned int bit) -{ - return (dev->resources & bit); -} - static void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) { @@ -868,7 +749,7 @@ static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win, bool return 0; } -static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +static int start_preview(struct saa7134_dev *dev) { unsigned long base,control,bpl; int err; @@ -923,7 +804,7 @@ static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) return 0; } -static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +static int stop_preview(struct saa7134_dev *dev) { dev->ovenable = 0; saa7134_set_dmabits(dev); @@ -1018,8 +899,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); unsigned int size; int err; @@ -1057,7 +937,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.size = size; buf->vb.field = field; buf->fmt = dev->fmt; - buf->pt = &fh->pt_cap; + buf->pt = &dev->pt_cap; dev->video_q.curr = NULL; err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); @@ -1082,8 +962,7 @@ static int buffer_prepare(struct videobuf_queue *q, static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = q->priv_data; *size = dev->fmt->depth * dev->width * dev->height >> 3; if (0 == *count) @@ -1094,10 +973,10 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct saa7134_fh *fh = q->priv_data; + struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf); + saa7134_buffer_queue(dev, &dev->video_q, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -1116,133 +995,56 @@ static struct videobuf_queue_ops video_qops = { /* ------------------------------------------------------------------ */ -int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) +static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) { - const struct v4l2_queryctrl* ctrl; - - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) - return -EINVAL; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = dev->ctl_bright; - break; - case V4L2_CID_HUE: - c->value = dev->ctl_hue; - break; - case V4L2_CID_CONTRAST: - c->value = dev->ctl_contrast; - break; - case V4L2_CID_SATURATION: - c->value = dev->ctl_saturation; - break; - case V4L2_CID_AUDIO_MUTE: - c->value = dev->ctl_mute; - break; - case V4L2_CID_AUDIO_VOLUME: - c->value = dev->ctl_volume; - break; - case V4L2_CID_PRIVATE_INVERT: - c->value = dev->ctl_invert; - break; - case V4L2_CID_HFLIP: - c->value = dev->ctl_mirror; - break; - case V4L2_CID_PRIVATE_Y_EVEN: - c->value = dev->ctl_y_even; - break; - case V4L2_CID_PRIVATE_Y_ODD: - c->value = dev->ctl_y_odd; - break; - case V4L2_CID_PRIVATE_AUTOMUTE: - c->value = dev->ctl_automute; - break; - default: - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal); - -static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) -{ - struct saa7134_fh *fh = priv; - - return saa7134_g_ctrl_internal(fh->dev, fh, c); -} - -int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) -{ - const struct v4l2_queryctrl* ctrl; + struct saa7134_dev *dev = container_of(ctrl->handler, struct saa7134_dev, ctrl_handler); unsigned long flags; int restart_overlay = 0; - int err; - err = -EINVAL; - - mutex_lock(&dev->lock); - - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) - goto error; - - dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER: - if (c->value < ctrl->minimum) - c->value = ctrl->minimum; - if (c->value > ctrl->maximum) - c->value = ctrl->maximum; - break; - default: - /* nothing */; - } - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - dev->ctl_bright = c->value; - saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); + dev->ctl_bright = ctrl->val; + saa_writeb(SAA7134_DEC_LUMA_BRIGHT, ctrl->val); break; case V4L2_CID_HUE: - dev->ctl_hue = c->value; - saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); + dev->ctl_hue = ctrl->val; + saa_writeb(SAA7134_DEC_CHROMA_HUE, ctrl->val); break; case V4L2_CID_CONTRAST: - dev->ctl_contrast = c->value; + dev->ctl_contrast = ctrl->val; saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); break; case V4L2_CID_SATURATION: - dev->ctl_saturation = c->value; + dev->ctl_saturation = ctrl->val; saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); break; case V4L2_CID_AUDIO_MUTE: - dev->ctl_mute = c->value; + dev->ctl_mute = ctrl->val; saa7134_tvaudio_setmute(dev); break; case V4L2_CID_AUDIO_VOLUME: - dev->ctl_volume = c->value; + dev->ctl_volume = ctrl->val; saa7134_tvaudio_setvolume(dev,dev->ctl_volume); break; case V4L2_CID_PRIVATE_INVERT: - dev->ctl_invert = c->value; + dev->ctl_invert = ctrl->val; saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); break; case V4L2_CID_HFLIP: - dev->ctl_mirror = c->value; + dev->ctl_mirror = ctrl->val; restart_overlay = 1; break; case V4L2_CID_PRIVATE_Y_EVEN: - dev->ctl_y_even = c->value; + dev->ctl_y_even = ctrl->val; restart_overlay = 1; break; case V4L2_CID_PRIVATE_Y_ODD: - dev->ctl_y_odd = c->value; + dev->ctl_y_odd = ctrl->val; restart_overlay = 1; break; case V4L2_CID_PRIVATE_AUTOMUTE: @@ -1252,7 +1054,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &dev->tda9887_conf; - dev->ctl_automute = c->value; + dev->ctl_automute = ctrl->val; if (dev->tda9887_conf) { if (dev->ctl_automute) dev->tda9887_conf |= TDA9887_AUTOMUTE; @@ -1264,27 +1066,15 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str break; } default: - goto error; + return -EINVAL; } - if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { - spin_lock_irqsave(&dev->slock,flags); - stop_preview(dev,fh); - start_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock,flags); + if (restart_overlay && res_locked(dev, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev); + start_preview(dev); + spin_unlock_irqrestore(&dev->slock, flags); } - err = 0; - -error: - mutex_unlock(&dev->lock); - return err; -} -EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal); - -static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) -{ - struct saa7134_fh *fh = f; - - return saa7134_s_ctrl_internal(fh->dev, fh, c); + return 0; } /* ------------------------------------------------------------------ */ @@ -1292,15 +1082,16 @@ static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) static struct videobuf_queue *saa7134_queue(struct file *file) { struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_fh *fh = file->private_data; struct videobuf_queue *q = NULL; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: - q = &fh->cap; + q = fh->is_empress ? &dev->empress_tsq : &dev->cap; break; case VFL_TYPE_VBI: - q = &fh->vbi; + q = &dev->vbi; break; default: BUG(); @@ -1311,9 +1102,10 @@ static struct videobuf_queue *saa7134_queue(struct file *file) static int saa7134_resource(struct file *file) { struct video_device *vdev = video_devdata(file); + struct saa7134_fh *fh = file->private_data; if (vdev->vfl_type == VFL_TYPE_GRABBER) - return RESOURCE_VIDEO; + return fh->is_empress ? RESOURCE_EMPRESS : RESOURCE_VIDEO; if (vdev->vfl_type == VFL_TYPE_VBI) return RESOURCE_VBI; @@ -1335,22 +1127,6 @@ static int video_open(struct file *file) v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; - fh->dev = dev; - - videobuf_queue_sg_init(&fh->cap, &video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct saa7134_buf), - fh, NULL); - videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct saa7134_buf), - fh, NULL); - saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); - saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); if (vdev->vfl_type == VFL_TYPE_RADIO) { /* switch to radio mode */ @@ -1369,17 +1145,18 @@ static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_fh *fh = file->private_data; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: - if (res_locked(fh->dev,RESOURCE_VIDEO)) + if (res_locked(dev, RESOURCE_VIDEO)) return -EBUSY; return videobuf_read_one(saa7134_queue(file), data, count, ppos, file->f_flags & O_NONBLOCK); case VFL_TYPE_VBI: - if (!res_get(fh->dev,fh,RESOURCE_VBI)) + if (!res_get(dev, fh, RESOURCE_VBI)) return -EBUSY; return videobuf_read_stream(saa7134_queue(file), data, count, ppos, 1, @@ -1394,52 +1171,59 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) static unsigned int video_poll(struct file *file, struct poll_table_struct *wait) { + unsigned long req_events = poll_requested_events(wait); struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; unsigned int rc = 0; + if (v4l2_event_pending(&fh->fh)) + rc = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); + if (vdev->vfl_type == VFL_TYPE_VBI) - return videobuf_poll_stream(file, &fh->vbi, wait); + return rc | videobuf_poll_stream(file, &dev->vbi, wait); - if (res_check(fh,RESOURCE_VIDEO)) { - mutex_lock(&fh->cap.vb_lock); - if (!list_empty(&fh->cap.stream)) - buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); + if (res_check(fh, RESOURCE_VIDEO)) { + mutex_lock(&dev->cap.vb_lock); + if (!list_empty(&dev->cap.stream)) + buf = list_entry(dev->cap.stream.next, struct videobuf_buffer, stream); } else { - mutex_lock(&fh->cap.vb_lock); - if (UNSET == fh->cap.read_off) { + mutex_lock(&dev->cap.vb_lock); + if (UNSET == dev->cap.read_off) { /* need to capture a new frame */ - if (res_locked(fh->dev,RESOURCE_VIDEO)) + if (res_locked(dev, RESOURCE_VIDEO)) goto err; - if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field)) + if (0 != dev->cap.ops->buf_prepare(&dev->cap, + dev->cap.read_buf, dev->cap.field)) goto err; - fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); - fh->cap.read_off = 0; + dev->cap.ops->buf_queue(&dev->cap, dev->cap.read_buf); + dev->cap.read_off = 0; } - buf = fh->cap.read_buf; + buf = dev->cap.read_buf; } if (!buf) goto err; poll_wait(file, &buf->done, wait); - if (buf->state == VIDEOBUF_DONE || - buf->state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; - mutex_unlock(&fh->cap.vb_lock); + if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) + rc |= POLLIN | POLLRDNORM; + mutex_unlock(&dev->cap.vb_lock); return rc; err: - mutex_unlock(&fh->cap.vb_lock); - return POLLERR; + mutex_unlock(&dev->cap.vb_lock); + return rc | POLLERR; } static int video_release(struct file *file) { struct video_device *vdev = video_devdata(file); - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh = file->private_data; struct saa6588_command cmd; unsigned long flags; @@ -1448,26 +1232,28 @@ static int video_release(struct file *file) /* turn off overlay */ if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); - stop_preview(dev,fh); + stop_preview(dev); spin_unlock_irqrestore(&dev->slock,flags); - res_free(dev,fh,RESOURCE_OVERLAY); + res_free(dev, fh, RESOURCE_OVERLAY); } /* stop video capture */ if (res_check(fh, RESOURCE_VIDEO)) { pm_qos_remove_request(&dev->qos_request); - videobuf_streamoff(&fh->cap); - res_free(dev,fh,RESOURCE_VIDEO); + videobuf_streamoff(&dev->cap); + res_free(dev, fh, RESOURCE_VIDEO); + videobuf_mmap_free(&dev->cap); } - if (fh->cap.read_buf) { - buffer_release(&fh->cap,fh->cap.read_buf); - kfree(fh->cap.read_buf); + if (dev->cap.read_buf) { + buffer_release(&dev->cap, dev->cap.read_buf); + kfree(dev->cap.read_buf); } /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { - videobuf_stop(&fh->vbi); - res_free(dev,fh,RESOURCE_VBI); + videobuf_stop(&dev->vbi); + res_free(dev, fh, RESOURCE_VBI); + videobuf_mmap_free(&dev->vbi); } /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/ @@ -1480,12 +1266,6 @@ static int video_release(struct file *file) if (vdev->vfl_type == VFL_TYPE_RADIO) saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd); - /* free stuff */ - videobuf_mmap_free(&fh->cap); - videobuf_mmap_free(&fh->vbi); - saa7134_pgtable_free(dev->pci,&fh->pt_cap); - saa7134_pgtable_free(dev->pci,&fh->pt_vbi); - v4l2_fh_del(&fh->fh); v4l2_fh_exit(&fh->fh); file->private_data = NULL; @@ -1501,11 +1281,11 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma) static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct saa6588_command cmd; cmd.block_count = count/3; + cmd.nonblocking = file->f_flags & O_NONBLOCK; cmd.buffer = data; cmd.instance = file; cmd.result = -ENODEV; @@ -1517,16 +1297,16 @@ static ssize_t radio_read(struct file *file, char __user *data, static unsigned int radio_poll(struct file *file, poll_table *wait) { - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct saa6588_command cmd; + unsigned int rc = v4l2_ctrl_poll(file, wait); cmd.instance = file; cmd.event_list = wait; - cmd.result = -ENODEV; + cmd.result = 0; saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); - return cmd.result; + return rc | cmd.result; } /* ------------------------------------------------------------------ */ @@ -1534,8 +1314,7 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_tvnorm *norm = dev->tvnorm; memset(&f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); @@ -1555,12 +1334,11 @@ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.field = fh->cap.field; + f->fmt.pix.field = dev->cap.field; f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * dev->fmt->depth) >> 3; @@ -1574,8 +1352,7 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct v4l2_clip __user *clips = f->fmt.win.clips; u32 clipcount = f->fmt.win.clipcount; int err = 0; @@ -1607,8 +1384,7 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_format *fmt; enum v4l2_field field; unsigned int maxw, maxh; @@ -1659,8 +1435,7 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (saa7134_no_overlay > 0) { printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); @@ -1675,8 +1450,7 @@ static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); int err; err = saa7134_try_fmt_vid_cap(file, priv, f); @@ -1686,15 +1460,14 @@ static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; - fh->cap.field = f->fmt.pix.field; + dev->cap.field = f->fmt.pix.field; return 0; } static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); int err; unsigned long flags; @@ -1719,10 +1492,10 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, return -EFAULT; } - if (res_check(fh, RESOURCE_OVERLAY)) { + if (res_check(priv, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); - stop_preview(dev, fh); - start_preview(dev, fh); + stop_preview(dev); + start_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); } @@ -1730,26 +1503,9 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, return 0; } -int saa7134_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) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - ctrl = ctrl_by_id(c->id); - *c = (NULL != ctrl) ? *ctrl : no_ctrl; - return 0; -} -EXPORT_SYMBOL_GPL(saa7134_queryctrl); - -static int saa7134_enum_input(struct file *file, void *priv, - struct v4l2_input *i) +int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); unsigned int n; n = i->index; @@ -1769,27 +1525,27 @@ static int saa7134_enum_input(struct file *file, void *priv, if (0 != (v1 & 0x40)) i->status |= V4L2_IN_ST_NO_H_LOCK; if (0 != (v2 & 0x40)) - i->status |= V4L2_IN_ST_NO_SYNC; + i->status |= V4L2_IN_ST_NO_SIGNAL; if (0 != (v2 & 0x0e)) i->status |= V4L2_IN_ST_MACROVISION; } i->std = SAA7134_NORMS; return 0; } +EXPORT_SYMBOL_GPL(saa7134_enum_input); -static int saa7134_g_input(struct file *file, void *priv, unsigned int *i) +int saa7134_g_input(struct file *file, void *priv, unsigned int *i) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); *i = dev->ctl_input; return 0; } +EXPORT_SYMBOL_GPL(saa7134_g_input); -static int saa7134_s_input(struct file *file, void *priv, unsigned int i) +int saa7134_s_input(struct file *file, void *priv, unsigned int i) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (i >= SAA7134_INPUT_MAX) return -EINVAL; @@ -1800,13 +1556,14 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) mutex_unlock(&dev->lock); return 0; } +EXPORT_SYMBOL_GPL(saa7134_s_input); -static int saa7134_querycap(struct file *file, void *priv, +int saa7134_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); + struct saa7134_fh *fh = priv; u32 radio_caps, video_caps, vbi_caps; unsigned int tuner_type = dev->tuner_type; @@ -1825,7 +1582,7 @@ static int saa7134_querycap(struct file *file, void *priv, radio_caps |= V4L2_CAP_RDS_CAPTURE; video_caps = V4L2_CAP_VIDEO_CAPTURE; - if (saa7134_no_overlay <= 0) + if (saa7134_no_overlay <= 0 && !fh->is_empress) video_caps |= V4L2_CAP_VIDEO_OVERLAY; vbi_caps = V4L2_CAP_VBI_CAPTURE; @@ -1851,14 +1608,17 @@ static int saa7134_querycap(struct file *file, void *priv, return 0; } +EXPORT_SYMBOL_GPL(saa7134_querycap); -int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id id) +int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) { + struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh = priv; unsigned long flags; unsigned int i; v4l2_std_id fixup; - if (!fh && res_locked(dev, RESOURCE_OVERLAY)) { + if (fh->is_empress && res_locked(dev, RESOURCE_OVERLAY)) { /* Don't change the std from the mpeg device if overlay is active. */ return -EBUSY; @@ -1898,15 +1658,15 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ id = tvnorms[i].id; mutex_lock(&dev->lock); - if (fh && res_check(fh, RESOURCE_OVERLAY)) { + if (!fh->is_empress && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); - stop_preview(dev, fh); + stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); set_tvnorm(dev, &tvnorms[i]); spin_lock_irqsave(&dev->slock, flags); - start_preview(dev, fh); + start_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); } else set_tvnorm(dev, &tvnorms[i]); @@ -1915,29 +1675,21 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ mutex_unlock(&dev->lock); return 0; } -EXPORT_SYMBOL_GPL(saa7134_s_std_internal); +EXPORT_SYMBOL_GPL(saa7134_s_std); -static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) +int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct saa7134_fh *fh = priv; - - return saa7134_s_std_internal(fh->dev, fh, id); -} - -static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); *id = dev->tvnorm->id; return 0; } +EXPORT_SYMBOL_GPL(saa7134_g_std); static int saa7134_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) @@ -1959,8 +1711,7 @@ static int saa7134_cropcap(struct file *file, void *priv, static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) @@ -1971,22 +1722,17 @@ static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct v4l2_rect *b = &dev->crop_bounds; struct v4l2_rect *c = &dev->crop_current; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - if (crop->c.height < 0) - return -EINVAL; - if (crop->c.width < 0) - return -EINVAL; - if (res_locked(fh->dev, RESOURCE_OVERLAY)) + if (res_locked(dev, RESOURCE_OVERLAY)) return -EBUSY; - if (res_locked(fh->dev, RESOURCE_VIDEO)) + if (res_locked(dev, RESOURCE_VIDEO)) return -EBUSY; *c = crop->c; @@ -2006,11 +1752,10 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr return 0; } -static int saa7134_g_tuner(struct file *file, void *priv, +int saa7134_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); int n; if (0 != t->index) @@ -2037,12 +1782,12 @@ static int saa7134_g_tuner(struct file *file, void *priv, t->signal = 0xffff; return 0; } +EXPORT_SYMBOL_GPL(saa7134_g_tuner); -static int saa7134_s_tuner(struct file *file, void *priv, +int saa7134_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); int rx, mode; if (0 != t->index) @@ -2058,12 +1803,12 @@ static int saa7134_s_tuner(struct file *file, void *priv, return 0; } +EXPORT_SYMBOL_GPL(saa7134_s_tuner); -static int saa7134_g_frequency(struct file *file, void *priv, +int saa7134_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (0 != f->tuner) return -EINVAL; @@ -2072,12 +1817,12 @@ static int saa7134_g_frequency(struct file *file, void *priv, return 0; } +EXPORT_SYMBOL_GPL(saa7134_g_frequency); -static int saa7134_s_frequency(struct file *file, void *priv, +int saa7134_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (0 != f->tuner) return -EINVAL; @@ -2089,6 +1834,7 @@ static int saa7134_s_frequency(struct file *file, void *priv, mutex_unlock(&dev->lock); return 0; } +EXPORT_SYMBOL_GPL(saa7134_s_frequency); static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) @@ -2126,8 +1872,7 @@ static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv, static int saa7134_g_fbuf(struct file *file, void *f, struct v4l2_framebuffer *fb) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); *fb = dev->ovbuf; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; @@ -2138,8 +1883,7 @@ static int saa7134_g_fbuf(struct file *file, void *f, static int saa7134_s_fbuf(struct file *file, void *f, const struct v4l2_framebuffer *fb) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_format *fmt; if (!capable(CAP_SYS_ADMIN) && @@ -2160,10 +1904,9 @@ static int saa7134_s_fbuf(struct file *file, void *f, return 0; } -static int saa7134_overlay(struct file *file, void *f, unsigned int on) +static int saa7134_overlay(struct file *file, void *priv, unsigned int on) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); unsigned long flags; if (on) { @@ -2172,54 +1915,57 @@ static int saa7134_overlay(struct file *file, void *f, unsigned int on) return -EINVAL; } - if (!res_get(dev, fh, RESOURCE_OVERLAY)) + if (!res_get(dev, priv, RESOURCE_OVERLAY)) return -EBUSY; spin_lock_irqsave(&dev->slock, flags); - start_preview(dev, fh); + start_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); } if (!on) { - if (!res_check(fh, RESOURCE_OVERLAY)) + if (!res_check(priv, RESOURCE_OVERLAY)) return -EINVAL; spin_lock_irqsave(&dev->slock, flags); - stop_preview(dev, fh); + stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); - res_free(dev, fh, RESOURCE_OVERLAY); + res_free(dev, priv, RESOURCE_OVERLAY); } return 0; } -static int saa7134_reqbufs(struct file *file, void *priv, +int saa7134_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { return videobuf_reqbufs(saa7134_queue(file), p); } +EXPORT_SYMBOL_GPL(saa7134_reqbufs); -static int saa7134_querybuf(struct file *file, void *priv, +int saa7134_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { return videobuf_querybuf(saa7134_queue(file), b); } +EXPORT_SYMBOL_GPL(saa7134_querybuf); -static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { return videobuf_qbuf(saa7134_queue(file), b); } +EXPORT_SYMBOL_GPL(saa7134_qbuf); -static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { return videobuf_dqbuf(saa7134_queue(file), b, file->f_flags & O_NONBLOCK); } +EXPORT_SYMBOL_GPL(saa7134_dqbuf); -static int saa7134_streamon(struct file *file, void *priv, +int saa7134_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); int res = saa7134_resource(file); - if (!res_get(dev, fh, res)) + if (!res_get(dev, priv, res)) return -EBUSY; /* The SAA7134 has a 1K FIFO; the datasheet suggests that when @@ -2229,36 +1975,37 @@ static int saa7134_streamon(struct file *file, void *priv, * Unfortunately, I lack register-level documentation to check the * Linux FIFO setup and confirm the perfect value. */ - pm_qos_add_request(&dev->qos_request, - PM_QOS_CPU_DMA_LATENCY, - 20); + if (res != RESOURCE_EMPRESS) + pm_qos_add_request(&dev->qos_request, + PM_QOS_CPU_DMA_LATENCY, 20); return videobuf_streamon(saa7134_queue(file)); } +EXPORT_SYMBOL_GPL(saa7134_streamon); -static int saa7134_streamoff(struct file *file, void *priv, +int saa7134_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { + struct saa7134_dev *dev = video_drvdata(file); int err; - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; int res = saa7134_resource(file); - pm_qos_remove_request(&dev->qos_request); + if (res != RESOURCE_EMPRESS) + pm_qos_remove_request(&dev->qos_request); err = videobuf_streamoff(saa7134_queue(file)); if (err < 0) return err; - res_free(dev, fh, res); + res_free(dev, priv, res); return 0; } +EXPORT_SYMBOL_GPL(saa7134_streamoff); #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *priv, struct v4l2_dbg_register *reg) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); reg->val = saa_readb(reg->reg & 0xffffff); reg->size = 1; @@ -2268,8 +2015,7 @@ static int vidioc_g_register (struct file *file, void *priv, static int vidioc_s_register (struct file *file, void *priv, const struct v4l2_dbg_register *reg) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); saa_writeb(reg->reg & 0xffffff, reg->val); return 0; @@ -2279,8 +2025,7 @@ static int vidioc_s_register (struct file *file, void *priv, static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -2299,8 +2044,7 @@ static int radio_g_tuner(struct file *file, void *priv, static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -2309,50 +2053,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - 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) -{ - 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_ctrl; - return 0; -} - static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, @@ -2387,9 +2087,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = saa7134_enum_input, .vidioc_g_input = saa7134_g_input, .vidioc_s_input = saa7134_s_input, - .vidioc_queryctrl = saa7134_queryctrl, - .vidioc_g_ctrl = saa7134_g_ctrl, - .vidioc_s_ctrl = saa7134_s_ctrl, .vidioc_streamon = saa7134_streamon, .vidioc_streamoff = saa7134_streamoff, .vidioc_g_tuner = saa7134_g_tuner, @@ -2405,6 +2102,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct v4l2_file_operations radio_fops = { @@ -2419,16 +2119,11 @@ static const struct v4l2_file_operations radio_fops = { static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = saa7134_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, .vidioc_s_tuner = radio_s_tuner, - .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 = saa7134_g_ctrl, - .vidioc_s_ctrl = saa7134_s_ctrl, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* ----------------------------------------------------------- */ @@ -2447,8 +2142,55 @@ struct video_device saa7134_radio_template = { .ioctl_ops = &radio_ioctl_ops, }; +static const struct v4l2_ctrl_ops saa7134_ctrl_ops = { + .s_ctrl = saa7134_s_ctrl, +}; + +static const struct v4l2_ctrl_config saa7134_ctrl_invert = { + .ops = &saa7134_ctrl_ops, + .id = V4L2_CID_PRIVATE_INVERT, + .name = "Invert", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config saa7134_ctrl_y_odd = { + .ops = &saa7134_ctrl_ops, + .id = V4L2_CID_PRIVATE_Y_ODD, + .name = "Y Offset Odd Field", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 128, + .step = 1, +}; + +static const struct v4l2_ctrl_config saa7134_ctrl_y_even = { + .ops = &saa7134_ctrl_ops, + .id = V4L2_CID_PRIVATE_Y_EVEN, + .name = "Y Offset Even Field", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 128, + .step = 1, +}; + +static const struct v4l2_ctrl_config saa7134_ctrl_automute = { + .ops = &saa7134_ctrl_ops, + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "Automute", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + int saa7134_video_init1(struct saa7134_dev *dev) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + /* sanitycheck insmod options */ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) gbuffers = 2; @@ -2456,17 +2198,38 @@ int saa7134_video_init1(struct saa7134_dev *dev) gbufsize = gbufsize_max; gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; - /* put some sensible defaults into the data structures ... */ - dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value; - dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value; - dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value; - dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value; - dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value; - dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value; - dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value; - dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value; - - if (dev->tda9887_conf && dev->ctl_automute) + v4l2_ctrl_handler_init(hdl, 11); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 68); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); + v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_invert, NULL); + v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_odd, NULL); + v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_even, NULL); + v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_automute, NULL); + if (hdl->error) + return hdl->error; + if (card_has_radio(dev)) { + hdl = &dev->radio_ctrl_handler; + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, + v4l2_ctrl_radio_filter); + if (hdl->error) + return hdl->error; + } + dev->ctl_mute = 1; + + if (dev->tda9887_conf && saa7134_ctrl_automute.def) dev->tda9887_conf |= TDA9887_AUTOMUTE; dev->automute = 0; @@ -2489,9 +2252,34 @@ int saa7134_video_init1(struct saa7134_dev *dev) if (saa7134_boards[dev->board].video_out) saa7134_videoport_init(dev); + videobuf_queue_sg_init(&dev->cap, &video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct saa7134_buf), + dev, NULL); + videobuf_queue_sg_init(&dev->vbi, &saa7134_vbi_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct saa7134_buf), + dev, NULL); + saa7134_pgtable_alloc(dev->pci, &dev->pt_cap); + saa7134_pgtable_alloc(dev->pci, &dev->pt_vbi); + return 0; } +void saa7134_video_fini(struct saa7134_dev *dev) +{ + /* free stuff */ + saa7134_pgtable_free(dev->pci, &dev->pt_cap); + saa7134_pgtable_free(dev->pci, &dev->pt_vbi); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + if (card_has_radio(dev)) + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); +} + int saa7134_videoport_init(struct saa7134_dev *dev) { /* enable video output */ @@ -2533,6 +2321,7 @@ int saa7134_video_init2(struct saa7134_dev *dev) /* init video hw */ set_tvnorm(dev,&tvnorms[0]); video_mux(dev,0); + v4l2_ctrl_handler_setup(&dev->ctrl_handler); saa7134_tvaudio_setmute(dev); saa7134_tvaudio_setvolume(dev,dev->ctl_volume); return 0; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 8d1453a48014..2474e848f2c0 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -37,6 +37,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> #include <media/tuner.h> #include <media/rc-core.h> #include <media/ir-kbd-i2c.h> @@ -410,12 +411,18 @@ struct saa7134_board { #define card(dev) (saa7134_boards[dev->board]) #define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) +#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_USER_SAA7134_BASE + 0) +#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_USER_SAA7134_BASE + 1) +#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_USER_SAA7134_BASE + 2) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_USER_SAA7134_BASE + 3) + /* ----------------------------------------------------------- */ /* device / file handle status */ #define RESOURCE_OVERLAY 1 #define RESOURCE_VIDEO 2 #define RESOURCE_VBI 4 +#define RESOURCE_EMPRESS 8 #define INTERLACE_AUTO 0 #define INTERLACE_ON 1 @@ -470,16 +477,8 @@ struct saa7134_dmaqueue { /* video filehandle status */ struct saa7134_fh { struct v4l2_fh fh; - struct saa7134_dev *dev; + bool is_empress; unsigned int resources; - - /* video capture */ - struct videobuf_queue cap; - struct saa7134_pgtable pt_cap; - - /* vbi capture */ - struct videobuf_queue vbi; - struct saa7134_pgtable pt_vbi; }; /* dmasound dsp status */ @@ -589,7 +588,11 @@ struct saa7134_dev { /* video+ts+vbi capture */ struct saa7134_dmaqueue video_q; + struct videobuf_queue cap; + struct saa7134_pgtable pt_cap; struct saa7134_dmaqueue vbi_q; + struct videobuf_queue vbi; + struct saa7134_pgtable pt_vbi; unsigned int video_fieldcount; unsigned int vbi_fieldcount; struct saa7134_format *fmt; @@ -599,6 +602,7 @@ struct saa7134_dev { /* various v4l controls */ struct saa7134_tvnorm *tvnorm; /* video */ struct saa7134_tvaudio *tvaudio; + struct v4l2_ctrl_handler ctrl_handler; unsigned int ctl_input; int ctl_bright; int ctl_contrast; @@ -626,6 +630,7 @@ struct saa7134_dev { int last_carrier; int nosignal; unsigned int insuspend; + struct v4l2_ctrl_handler radio_ctrl_handler; /* I2C keyboard data */ struct IR_i2c_init_data init_data; @@ -638,10 +643,11 @@ struct saa7134_dev { /* SAA7134_MPEG_EMPRESS only */ struct video_device *empress_dev; + struct v4l2_subdev *empress_sd; struct videobuf_queue empress_tsq; - atomic_t empress_users; struct work_struct empress_workqueue; int empress_started; + struct v4l2_ctrl_handler empress_ctrl_handler; #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) /* SAA7134_MPEG_DVB only */ @@ -699,6 +705,16 @@ struct saa7134_dev { _rc; \ }) +static inline int res_check(struct saa7134_fh *fh, unsigned int bit) +{ + return fh->resources & bit; +} + +static inline int res_locked(struct saa7134_dev *dev, unsigned int bit) +{ + return dev->resources & bit; +} + /* ----------------------------------------------------------- */ /* saa7134-core.c */ @@ -761,10 +777,31 @@ extern unsigned int video_debug; extern struct video_device saa7134_video_template; 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(struct file *file, void *priv, v4l2_std_id id); +int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); +int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); +int saa7134_g_input(struct file *file, void *priv, unsigned int *i); +int saa7134_s_input(struct file *file, void *priv, unsigned int i); +int saa7134_querycap(struct file *file, void *priv, + struct v4l2_capability *cap); +int saa7134_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t); +int saa7134_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t); +int saa7134_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +int saa7134_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f); +int saa7134_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p); +int saa7134_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int saa7134_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); +int saa7134_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type); int saa7134_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); @@ -773,6 +810,7 @@ int saa7134_video_init1(struct saa7134_dev *dev); int saa7134_video_init2(struct saa7134_dev *dev); void saa7134_irq_video_signalchange(struct saa7134_dev *dev); void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); +void saa7134_video_fini(struct saa7134_dev *dev); /* ----------------------------------------------------------- */ diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 77edc113e485..e5cfb6cfa18d 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1303,7 +1303,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) #endif -static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { +static const struct pci_device_id sta2x11_vip_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, {0,} }; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index d7f0249e4050..b2a4403940c5 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -36,7 +36,8 @@ source "drivers/media/platform/blackfin/Kconfig" config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT - depends on VIDEO_DEV && ARCH_SHMOBILE && I2C + depends on VIDEO_DEV && I2C + depends on ARCH_SHMOBILE || COMPILE_TEST select VIDEOBUF_DMA_CONTIG help Support for the Video Output Unit (VOU) on SuperH SoCs. @@ -90,13 +91,6 @@ config VIDEO_M32R_AR_M64278 To compile this driver as a module, choose M here: the module will be called arv. -config VIDEO_OMAP2 - tristate "OMAP2 Camera Capture Interface driver" - depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE - select VIDEOBUF_DMA_SG - ---help--- - This is a v4l2 driver for the TI OMAP2 camera capture interface - config VIDEO_OMAP3 tristate "OMAP 3 Camera support" depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 1348ba1faf92..e5269da91906 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -2,8 +2,6 @@ # Makefile for the video capture/playback device drivers. # -omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o - obj-$(CONFIG_VIDEO_VINO) += indycam.o obj-$(CONFIG_VIDEO_VINO) += vino.o @@ -14,7 +12,6 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ -obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index eac472b5ae83..b02aba488826 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -347,7 +347,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) /* If buffer queue is empty, return error */ if (list_empty(&layer->dma_queue)) { v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); - return -EINVAL; + return -ENOBUFS; } /* Get the next frame from the buffer queue */ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 52ac5e6c8625..735ec47601a9 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -277,7 +277,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) if (list_empty(&common->dma_queue)) { spin_unlock_irqrestore(&common->irqlock, flags); vpif_dbg(1, debug, "buffer queue is empty\n"); - return -EIO; + return -ENOBUFS; } /* Get the next frame from the buffer queue */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index c31bcf129a5d..9d115cdc6bdb 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -239,7 +239,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) if (list_empty(&common->dma_queue)) { spin_unlock_irqrestore(&common->irqlock, flags); vpif_err("buffer queue is empty\n"); - return -EIO; + return -ENOBUFS; } /* Get the next frame from the buffer queue */ diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index d2d3b4b61435..01ed1ecdff7e 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -1,7 +1,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PM_RUNTIME + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (PLAT_S5P || ARCH_EXYNOS) help Say Y here to enable camera host interface devices for diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index fb27ff7e1e07..8a712ca91d11 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -549,7 +549,7 @@ static int fimc_capture_release(struct file *file) vc->streaming = false; } - ret = vb2_fop_release(file); + ret = _vb2_fop_release(file, NULL); if (close) { clear_bit(ST_CAPT_BUSY, &fimc->state); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index f7915695c907..da2fc86cc524 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -998,41 +998,46 @@ static int fimc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, res->start, fimc_irq_handler, 0, dev_name(dev), fimc); - if (ret) { + if (ret < 0) { dev_err(dev, "failed to install irq (%d)\n", ret); - goto err_clk; + goto err_sclk; } ret = fimc_initialize_capture_subdev(fimc); - if (ret) - goto err_clk; + if (ret < 0) + goto err_sclk; platform_set_drvdata(pdev, fimc); pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) - goto err_sd; + + if (!pm_runtime_enabled(dev)) { + ret = clk_enable(fimc->clock[CLK_GATE]); + if (ret < 0) + goto err_sd; + } + /* Initialize contiguous memory allocator */ fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); - goto err_pm; + goto err_gclk; } dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); - - pm_runtime_put(dev); return 0; -err_pm: - pm_runtime_put(dev); + +err_gclk: + if (!pm_runtime_enabled(dev)) + clk_disable(fimc->clock[CLK_GATE]); err_sd: fimc_unregister_capture_subdev(fimc); -err_clk: +err_sclk: clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); return ret; } +#ifdef CONFIG_PM_RUNTIME static int fimc_runtime_resume(struct device *dev) { struct fimc_dev *fimc = dev_get_drvdata(dev); @@ -1065,6 +1070,7 @@ static int fimc_runtime_suspend(struct device *dev) dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); return ret; } +#endif #ifdef CONFIG_PM_SLEEP static int fimc_resume(struct device *dev) diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 3d376faec777..1790fb4e32ea 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -481,7 +481,6 @@ struct fimc_ctrls { * @flags: additional flags for image conversion * @state: flags to keep track of user configuration * @fimc_dev: the FIMC device this context applies to - * @m2m_ctx: memory-to-memory device context * @fh: v4l2 file handle * @ctrls: v4l2 controls structure */ @@ -502,7 +501,6 @@ struct fimc_ctx { u32 flags; u32 state; struct fimc_dev *fimc_dev; - struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_fh fh; struct fimc_ctrls ctrls; }; diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index f758e2694fa3..2628733c4e10 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -33,47 +33,23 @@ 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) { + do { cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); status = INTMSR0_GET_INTMSD(0, cfg); - if (timeout == 0) { + + if (--timeout == 0) { dev_warn(&is->pdev->dev, "%s timeout\n", __func__); - return -ETIME; + return -ETIMEDOUT; } - timeout--; udelay(1); - } + } while (status != 0); + return 0; } diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h index 5fa2fda46742..1d9d4ffc6ad5 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.h +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h @@ -145,7 +145,6 @@ 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); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 9770fa98d6a1..13a4228952e3 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -781,6 +781,9 @@ static int fimc_is_debugfs_create(struct fimc_is *is) return is->debugfs_entry == NULL ? -EIO : 0; } +static int fimc_is_runtime_resume(struct device *dev); +static int fimc_is_runtime_suspend(struct device *dev); + static int fimc_is_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -835,14 +838,20 @@ static int fimc_is_probe(struct platform_device *pdev) } pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = fimc_is_runtime_resume(dev); + if (ret < 0) + goto err_irq; + } + ret = pm_runtime_get_sync(dev); if (ret < 0) - goto err_irq; + goto err_pm; is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { ret = PTR_ERR(is->alloc_ctx); - goto err_irq; + goto err_pm; } /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes @@ -867,10 +876,13 @@ static int fimc_is_probe(struct platform_device *pdev) 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_vb: + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); +err_pm: + if (!pm_runtime_enabled(dev)) + fimc_is_runtime_suspend(dev); err_irq: free_irq(is->irq, is); err_clk: @@ -919,10 +931,13 @@ static int fimc_is_suspend(struct device *dev) static int fimc_is_remove(struct platform_device *pdev) { - struct fimc_is *is = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct fimc_is *is = dev_get_drvdata(dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + if (!pm_runtime_status_suspended(dev)) + fimc_is_runtime_suspend(dev); free_irq(is->irq, is); fimc_is_unregister_subdevs(is); vb2_dma_contig_cleanup_ctx(is->alloc_ctx); diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index 72a343e3b5e8..d0dc7ee04452 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -133,7 +133,7 @@ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) int i = ARRAY_SIZE(src_pixfmt_map); u32 cfg; - while (--i >= 0) { + while (--i) { if (src_pixfmt_map[i][0] == pixelcode) break; } @@ -240,7 +240,7 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); int i = ARRAY_SIZE(pixcode); - while (--i >= 0) + while (--i) if (pixcode[i][0] == f->fmt->mbus_code) break; cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e5798f70d149..779ec3cd259d 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -546,7 +546,7 @@ static int fimc_lite_release(struct file *file) mutex_unlock(&entity->parent->graph_mutex); } - vb2_fop_release(file); + _vb2_fop_release(file, NULL); pm_runtime_put(&fimc->pdev->dev); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); @@ -1549,42 +1549,46 @@ static int fimc_lite_probe(struct platform_device *pdev) 0, dev_name(dev), fimc); if (ret) { dev_err(dev, "Failed to install irq (%d)\n", ret); - goto err_clk; + goto err_clk_put; } /* The video node will be created within the subdev's registered() op */ ret = fimc_lite_create_capture_subdev(fimc); if (ret) - goto err_clk; + goto err_clk_put; platform_set_drvdata(pdev, fimc); pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) - goto err_sd; + + if (!pm_runtime_enabled(dev)) { + ret = clk_enable(fimc->clock); + if (ret < 0) + goto err_sd; + } fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); - goto err_pm; + goto err_clk_dis; } - pm_runtime_put(dev); - fimc_lite_set_default_config(fimc); dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", fimc->index); return 0; -err_pm: - pm_runtime_put(dev); + +err_clk_dis: + if (!pm_runtime_enabled(dev)) + clk_disable(fimc->clock); err_sd: fimc_lite_unregister_capture_subdev(fimc); -err_clk: +err_clk_put: fimc_lite_clk_put(fimc); return ret; } +#ifdef CONFIG_PM_RUNTIME static int fimc_lite_runtime_resume(struct device *dev) { struct fimc_lite *fimc = dev_get_drvdata(dev); @@ -1600,6 +1604,7 @@ static int fimc_lite_runtime_suspend(struct device *dev) clk_disable(fimc->clock); return 0; } +#endif #ifdef CONFIG_PM_SLEEP static int fimc_lite_resume(struct device *dev) diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 8d33b68c76ba..9da95bd14820 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -44,17 +44,17 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) { struct vb2_buffer *src_vb, *dst_vb; - if (!ctx || !ctx->m2m_ctx) + if (!ctx || !ctx->fh.m2m_ctx) return; - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (src_vb && dst_vb) { v4l2_m2m_buf_done(src_vb, vb_state); v4l2_m2m_buf_done(dst_vb, vb_state); v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, - ctx->m2m_ctx); + ctx->fh.m2m_ctx); } } @@ -123,12 +123,12 @@ static void fimc_device_run(void *priv) fimc_prepare_dma_offset(ctx, df); } - src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); if (ret) goto dma_unlock; - dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); if (ret) goto dma_unlock; @@ -219,31 +219,15 @@ static int fimc_buf_prepare(struct vb2_buffer *vb) static void fimc_buf_queue(struct vb2_buffer *vb) { struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -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); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } static struct vb2_ops fimc_qops = { .queue_setup = fimc_queue_setup, .buf_prepare = fimc_buf_prepare, .buf_queue = fimc_buf_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .stop_streaming = stop_streaming, .start_streaming = start_streaming, }; @@ -385,7 +369,7 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, if (ret) return ret; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (vb2_is_busy(vq)) { v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type); @@ -410,56 +394,6 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, return 0; } -static int fimc_m2m_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *reqbufs) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int fimc_m2m_querybuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_qbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_dqbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_expbuf(struct file *file, void *fh, - struct v4l2_exportbuffer *eb) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); -} - - -static int fimc_m2m_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - static int fimc_m2m_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cr) { @@ -598,13 +532,13 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, - .vidioc_reqbufs = fimc_m2m_reqbufs, - .vidioc_querybuf = fimc_m2m_querybuf, - .vidioc_qbuf = fimc_m2m_qbuf, - .vidioc_dqbuf = fimc_m2m_dqbuf, - .vidioc_expbuf = fimc_m2m_expbuf, - .vidioc_streamon = fimc_m2m_streamon, - .vidioc_streamoff = fimc_m2m_streamoff, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_crop = fimc_m2m_g_crop, .vidioc_s_crop = fimc_m2m_s_crop, .vidioc_cropcap = fimc_m2m_cropcap @@ -624,6 +558,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, 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; + src_vq->lock = &ctx->fimc_dev->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -636,6 +571,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, 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; + dst_vq->lock = &ctx->fimc_dev->lock; return vb2_queue_init(dst_vq); } @@ -708,9 +644,9 @@ static int fimc_m2m_open(struct file *file) ctx->out_path = FIMC_IO_DMA; ctx->scaler.enabled = 1; - ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); goto error_c; } @@ -725,7 +661,7 @@ static int fimc_m2m_open(struct file *file) return 0; error_m2m_ctx: - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); error_c: fimc_ctrls_delete(ctx); error_fh: @@ -747,7 +683,7 @@ static int fimc_m2m_release(struct file *file) mutex_lock(&fimc->lock); - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); fimc_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -760,45 +696,13 @@ static int fimc_m2m_release(struct file *file) return 0; } -static unsigned int fimc_m2m_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - mutex_unlock(&fimc->lock); - - return ret; -} - - -static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&fimc->lock); - - return ret; -} - static const struct v4l2_file_operations fimc_m2m_fops = { .owner = THIS_MODULE, .open = fimc_m2m_open, .release = fimc_m2m_release, - .poll = fimc_m2m_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = fimc_m2m_mmap, + .mmap = v4l2_m2m_fop_mmap, }; static struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 9fc2af6a0446..f3c3591fdc5d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -91,7 +91,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29) #define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) #define S5PCSIS_INTSRC_ODD (0x3 << 28) -#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28) +#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xf << 28) #define S5PCSIS_INTSRC_FRAME_START (1 << 27) #define S5PCSIS_INTSRC_FRAME_END (1 << 26) #define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12) @@ -790,6 +790,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, #define s5pcsis_parse_dt(pdev, state) (-ENOSYS) #endif +static int s5pcsis_pm_resume(struct device *dev, bool runtime); static const struct of_device_id s5pcsis_of_match[]; static int s5pcsis_probe(struct platform_device *pdev) @@ -902,13 +903,21 @@ 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); + if (!pm_runtime_enabled(dev)) { + ret = s5pcsis_pm_resume(dev, true); + if (ret < 0) + goto e_m_ent; + } 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_m_ent: + media_entity_cleanup(&state->sd.entity); e_clkdis: clk_disable(state->clock[CSIS_CLK_MUX]); e_clkput: @@ -1014,7 +1023,7 @@ static int s5pcsis_remove(struct platform_device *pdev) struct csis_state *state = sd_to_csis_state(sd); pm_runtime_disable(&pdev->dev); - s5pcsis_pm_suspend(&pdev->dev, false); + s5pcsis_pm_suspend(&pdev->dev, true); clk_disable(state->clock[CSIS_CLK_MUX]); pm_runtime_set_suspended(&pdev->dev); s5pcsis_clk_put(state); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 6a232239ee8c..dbf0ce38a8e7 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1580,7 +1580,7 @@ static int viu_of_probe(struct platform_device *op) } /* enable VIU clock */ - clk = devm_clk_get(&op->dev, "viu_clk"); + clk = devm_clk_get(&op->dev, "ipg"); if (IS_ERR(clk)) { dev_err(&op->dev, "failed to lookup the clock!\n"); ret = PTR_ERR(clk); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 65cab70fefcb..6bb86b581a34 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -918,7 +918,7 @@ static int deinterlace_open(struct file *file) return ret; } - ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + + ctx->xt = kzalloc(sizeof(struct dma_interleaved_template) + sizeof(struct data_chunk), GFP_KERNEL); if (!ctx->xt) { kfree(ctx); diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 8df5975b700a..08e24379b794 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -177,8 +177,6 @@ struct m2mtest_ctx { enum v4l2_colorspace colorspace; - struct v4l2_m2m_ctx *m2m_ctx; - /* Source and destination queue data */ struct m2mtest_q_data q_data[2]; }; @@ -342,8 +340,8 @@ static int job_ready(void *priv) { struct m2mtest_ctx *ctx = priv; - if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen - || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) { + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen + || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) { dprintk(ctx->dev, "Not enough buffers available\n"); return 0; } @@ -359,21 +357,6 @@ static void job_abort(void *priv) ctx->aborting = 1; } -static void m2mtest_lock(void *priv) -{ - struct m2mtest_ctx *ctx = priv; - struct m2mtest_dev *dev = ctx->dev; - mutex_lock(&dev->dev_mutex); -} - -static void m2mtest_unlock(void *priv) -{ - struct m2mtest_ctx *ctx = priv; - struct m2mtest_dev *dev = ctx->dev; - mutex_unlock(&dev->dev_mutex); -} - - /* device_run() - prepares and starts the device * * This simulates all the immediate preparations required before starting @@ -386,8 +369,8 @@ static void device_run(void *priv) struct m2mtest_dev *dev = ctx->dev; struct vb2_buffer *src_buf, *dst_buf; - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); device_process(ctx, src_buf, dst_buf); @@ -409,8 +392,8 @@ static void device_isr(unsigned long priv) return; } - 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_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); curr_ctx->num_processed++; @@ -423,7 +406,7 @@ static void device_isr(unsigned long priv) || curr_ctx->aborting) { dprintk(curr_ctx->dev, "Finishing transaction\n"); curr_ctx->num_processed = 0; - v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); + v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->fh.m2m_ctx); } else { device_run(curr_ctx); } @@ -491,7 +474,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; struct m2mtest_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -594,7 +577,7 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) struct m2mtest_q_data *q_data; struct vb2_queue *vq; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -648,52 +631,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl) { struct m2mtest_ctx *ctx = @@ -748,14 +685,14 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -818,27 +755,15 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb) static void m2mtest_buf_queue(struct vb2_buffer *vb) { struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void m2mtest_wait_prepare(struct vb2_queue *q) -{ - struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); - m2mtest_unlock(ctx); -} - -static void m2mtest_wait_finish(struct vb2_queue *q) -{ - struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); - m2mtest_lock(ctx); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } static struct vb2_ops m2mtest_qops = { .queue_setup = m2mtest_queue_setup, .buf_prepare = m2mtest_buf_prepare, .buf_queue = m2mtest_buf_queue, - .wait_prepare = m2mtest_wait_prepare, - .wait_finish = m2mtest_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -853,6 +778,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->ops = &m2mtest_qops; src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -865,6 +791,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->ops = &m2mtest_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; return vb2_queue_init(dst_vq); } @@ -936,10 +863,10 @@ static int m2mtest_open(struct file *file) ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - rc = PTR_ERR(ctx->m2m_ctx); + if (IS_ERR(ctx->fh.m2m_ctx)) { + rc = PTR_ERR(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(hdl); kfree(ctx); @@ -949,7 +876,8 @@ static int m2mtest_open(struct file *file) v4l2_fh_add(&ctx->fh); atomic_inc(&dev->num_inst); - dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); + dprintk(dev, "Created instance: %p, m2m_ctx: %p\n", + ctx, ctx->fh.m2m_ctx); open_unlock: mutex_unlock(&dev->dev_mutex); @@ -967,7 +895,7 @@ static int m2mtest_release(struct file *file) v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->hdl); mutex_lock(&dev->dev_mutex); - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); mutex_unlock(&dev->dev_mutex); kfree(ctx); @@ -976,34 +904,13 @@ static int m2mtest_release(struct file *file) return 0; } -static unsigned int m2mtest_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct m2mtest_ctx *ctx = file2ctx(file); - - return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -} - -static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct m2mtest_dev *dev = video_drvdata(file); - struct m2mtest_ctx *ctx = file2ctx(file); - int res; - - if (mutex_lock_interruptible(&dev->dev_mutex)) - return -ERESTARTSYS; - res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&dev->dev_mutex); - return res; -} - static const struct v4l2_file_operations m2mtest_fops = { .owner = THIS_MODULE, .open = m2mtest_open, .release = m2mtest_release, - .poll = m2mtest_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = m2mtest_mmap, + .mmap = v4l2_m2m_fop_mmap, }; static struct video_device m2mtest_videodev = { @@ -1019,8 +926,6 @@ static struct v4l2_m2m_ops m2m_ops = { .device_run = device_run, .job_ready = job_ready, .job_abort = job_abort, - .lock = m2mtest_lock, - .unlock = m2mtest_unlock, }; static int m2mtest_probe(struct platform_device *pdev) @@ -1133,4 +1038,3 @@ static int __init m2mtest_init(void) module_init(m2mtest_init); module_exit(m2mtest_exit); - diff --git a/drivers/media/platform/omap24xxcam-dma.c b/drivers/media/platform/omap24xxcam-dma.c deleted file mode 100644 index 9c00776d6583..000000000000 --- a/drivers/media/platform/omap24xxcam-dma.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam-dma.c - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.com> and - * David Cohen <david.cohen@indt.org.br>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/kernel.h> -#include <linux/io.h> -#include <linux/scatterlist.h> - -#include "omap24xxcam.h" - -/* - * - * DMA hardware. - * - */ - -/* Ack all interrupt on CSR and IRQSTATUS_L0 */ -static void omap24xxcam_dmahw_ack_all(void __iomem *base) -{ - u32 csr; - int i; - - for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) { - csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i)); - /* ack interrupt in CSR */ - omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr); - } - omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf); -} - -/* Ack dmach on CSR and IRQSTATUS_L0 */ -static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach) -{ - u32 csr; - - csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach)); - /* ack interrupt in CSR */ - omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr); - /* ack interrupt in IRQSTATUS */ - omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach)); - - return csr; -} - -static int omap24xxcam_dmahw_running(void __iomem *base, int dmach) -{ - return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; -} - -static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach, - dma_addr_t start, u32 len) -{ - omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), - CAMDMA_CCR_SEL_SRC_DST_SYNC - | CAMDMA_CCR_BS - | CAMDMA_CCR_DST_AMODE_POST_INC - | CAMDMA_CCR_SRC_AMODE_POST_INC - | CAMDMA_CCR_FS - | CAMDMA_CCR_WR_ACTIVE - | CAMDMA_CCR_RD_ACTIVE - | CAMDMA_CCR_SYNCHRO_CAMERA); - omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0); - omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len); - omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1); - omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach), - CAMDMA_CSDP_WRITE_MODE_POSTED - | CAMDMA_CSDP_DST_BURST_EN_32 - | CAMDMA_CSDP_DST_PACKED - | CAMDMA_CSDP_SRC_BURST_EN_32 - | CAMDMA_CSDP_SRC_PACKED - | CAMDMA_CSDP_DATA_TYPE_8BITS); - omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0); - omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start); - omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0); - omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD); - omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0); - omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0); - omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), - CAMDMA_CSR_MISALIGNED_ERR - | CAMDMA_CSR_SECURE_ERR - | CAMDMA_CSR_TRANS_ERR - | CAMDMA_CSR_BLOCK - | CAMDMA_CSR_DROP); - omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), - CAMDMA_CICR_MISALIGNED_ERR_IE - | CAMDMA_CICR_SECURE_ERR_IE - | CAMDMA_CICR_TRANS_ERR_IE - | CAMDMA_CICR_BLOCK_IE - | CAMDMA_CICR_DROP_IE); -} - -static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach) -{ - omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), - CAMDMA_CCR_SEL_SRC_DST_SYNC - | CAMDMA_CCR_BS - | CAMDMA_CCR_DST_AMODE_POST_INC - | CAMDMA_CCR_SRC_AMODE_POST_INC - | CAMDMA_CCR_ENABLE - | CAMDMA_CCR_FS - | CAMDMA_CCR_SYNCHRO_CAMERA); -} - -static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach, - int free_dmach) -{ - int prev_dmach, ch; - - if (dmach == 0) - prev_dmach = NUM_CAMDMA_CHANNELS - 1; - else - prev_dmach = dmach - 1; - omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach), - CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach); - /* Did we chain the DMA transfer before the previous one - * finished? - */ - ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS; - while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch)) - & CAMDMA_CCR_ENABLE)) { - if (ch == dmach) { - /* The previous transfer has ended and this one - * hasn't started, so we must not have chained - * to the previous one in time. We'll have to - * start it now. - */ - omap24xxcam_dmahw_transfer_start(base, dmach); - break; - } else - ch = (ch + 1) % NUM_CAMDMA_CHANNELS; - } -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. The DMA - * controller may not be idle after this routine completes, because - * the completion routines might start new transfers. - */ -static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach) -{ - /* mask all interrupts from this channel */ - omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); - /* unlink this channel */ - omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0, - CAMDMA_CLNK_CTRL_ENABLE_LNK); - /* disable this channel */ - omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); -} - -static void omap24xxcam_dmahw_init(void __iomem *base) -{ - omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, - CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY - | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE - | CAMDMA_OCP_SYSCONFIG_AUTOIDLE); - - omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10, - CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH); - - omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf); -} - -/* - * - * Individual DMA channel handling. - * - */ - -/* Start a DMA transfer from the camera to memory. - * Returns zero if the transfer was successfully started, or non-zero if all - * DMA channels are already in use or starting is currently inhibited. - */ -static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start, - u32 len, dma_callback_t callback, void *arg) -{ - unsigned long flags; - int dmach; - - spin_lock_irqsave(&dma->lock, flags); - - if (!dma->free_dmach || atomic_read(&dma->dma_stop)) { - spin_unlock_irqrestore(&dma->lock, flags); - return -EBUSY; - } - - dmach = dma->next_dmach; - - dma->ch_state[dmach].callback = callback; - dma->ch_state[dmach].arg = arg; - - omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len); - - /* We're ready to start the DMA transfer. */ - - if (dma->free_dmach < NUM_CAMDMA_CHANNELS) { - /* A transfer is already in progress, so try to chain to it. */ - omap24xxcam_dmahw_transfer_chain(dma->base, dmach, - dma->free_dmach); - } else { - /* No transfer is in progress, so we'll just start this one - * now. - */ - omap24xxcam_dmahw_transfer_start(dma->base, dmach); - } - - dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS; - dma->free_dmach--; - - spin_unlock_irqrestore(&dma->lock, flags); - - return 0; -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. The DMA - * controller may not be idle after this routine completes, because - * the completion routines might start new transfers. - */ -static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr) -{ - unsigned long flags; - int dmach, i, free_dmach; - dma_callback_t callback; - void *arg; - - spin_lock_irqsave(&dma->lock, flags); - - /* stop any DMA transfers in progress */ - dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS; - for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) { - omap24xxcam_dmahw_abort_ch(dma->base, dmach); - dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS; - } - - /* We have to be careful here because the callback routine - * might start a new DMA transfer, and we only want to abort - * transfers that were started before this routine was called. - */ - free_dmach = dma->free_dmach; - while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) && - (free_dmach < NUM_CAMDMA_CHANNELS)) { - dmach = (dma->next_dmach + dma->free_dmach) - % NUM_CAMDMA_CHANNELS; - callback = dma->ch_state[dmach].callback; - arg = dma->ch_state[dmach].arg; - dma->free_dmach++; - free_dmach++; - if (callback) { - /* leave interrupts disabled during callback */ - spin_unlock(&dma->lock); - (*callback) (dma, csr, arg); - spin_lock(&dma->lock); - } - } - - spin_unlock_irqrestore(&dma->lock, flags); -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. If the completion - * routines attempt to start a new DMA transfer it will fail, so the - * DMA controller will be idle after this routine completes. - */ -static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr) -{ - atomic_inc(&dma->dma_stop); - omap24xxcam_dma_abort(dma, csr); - atomic_dec(&dma->dma_stop); -} - -/* Camera DMA interrupt service routine. */ -void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma) -{ - int dmach; - dma_callback_t callback; - void *arg; - u32 csr; - const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR - | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR - | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - - spin_lock(&dma->lock); - - if (dma->free_dmach == NUM_CAMDMA_CHANNELS) { - /* A camera DMA interrupt occurred while all channels - * are idle, so we'll acknowledge the interrupt in the - * IRQSTATUS register and exit. - */ - omap24xxcam_dmahw_ack_all(dma->base); - spin_unlock(&dma->lock); - return; - } - - while (dma->free_dmach < NUM_CAMDMA_CHANNELS) { - dmach = (dma->next_dmach + dma->free_dmach) - % NUM_CAMDMA_CHANNELS; - if (omap24xxcam_dmahw_running(dma->base, dmach)) { - /* This buffer hasn't finished yet, so we're done. */ - break; - } - csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach); - if (csr & csr_error) { - /* A DMA error occurred, so stop all DMA - * transfers in progress. - */ - spin_unlock(&dma->lock); - omap24xxcam_dma_stop(dma, csr); - return; - } else { - callback = dma->ch_state[dmach].callback; - arg = dma->ch_state[dmach].arg; - dma->free_dmach++; - if (callback) { - spin_unlock(&dma->lock); - (*callback) (dma, csr, arg); - spin_lock(&dma->lock); - } - } - } - - spin_unlock(&dma->lock); - - omap24xxcam_sgdma_process( - container_of(dma, struct omap24xxcam_sgdma, dma)); -} - -void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) -{ - unsigned long flags; - - spin_lock_irqsave(&dma->lock, flags); - - omap24xxcam_dmahw_init(dma->base); - - spin_unlock_irqrestore(&dma->lock, flags); -} - -static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, - void __iomem *base) -{ - int ch; - - /* group all channels on DMA IRQ0 and unmask irq */ - spin_lock_init(&dma->lock); - dma->base = base; - dma->free_dmach = NUM_CAMDMA_CHANNELS; - dma->next_dmach = 0; - for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) { - dma->ch_state[ch].callback = NULL; - dma->ch_state[ch].arg = NULL; - } -} - -/* - * - * Scatter-gather DMA. - * - * High-level DMA construct for transferring whole picture frames to - * memory that is discontinuous. - * - */ - -/* DMA completion routine for the scatter-gather DMA fragments. */ -static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr, - void *arg) -{ - struct omap24xxcam_sgdma *sgdma = - container_of(dma, struct omap24xxcam_sgdma, dma); - int sgslot = (int)arg; - struct sgdma_state *sg_state; - const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR - | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR - | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - - spin_lock(&sgdma->lock); - - /* We got an interrupt, we can remove the timer */ - del_timer(&sgdma->reset_timer); - - sg_state = sgdma->sg_state + sgslot; - if (!sg_state->queued_sglist) { - spin_unlock(&sgdma->lock); - printk(KERN_ERR "%s: sgdma completed when none queued!\n", - __func__); - return; - } - - sg_state->csr |= csr; - if (!--sg_state->queued_sglist) { - /* Queue for this sglist is empty, so check to see if we're - * done. - */ - if ((sg_state->next_sglist == sg_state->sglen) - || (sg_state->csr & csr_error)) { - sgdma_callback_t callback = sg_state->callback; - void *arg = sg_state->arg; - u32 sg_csr = sg_state->csr; - /* All done with this sglist */ - sgdma->free_sgdma++; - if (callback) { - spin_unlock(&sgdma->lock); - (*callback) (sgdma, sg_csr, arg); - return; - } - } - } - - spin_unlock(&sgdma->lock); -} - -/* Start queued scatter-gather DMA transfers. */ -void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma) -{ - unsigned long flags; - int queued_sgdma, sgslot; - struct sgdma_state *sg_state; - const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR - | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR - | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - - spin_lock_irqsave(&sgdma->lock, flags); - - queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma; - sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; - while (queued_sgdma > 0) { - sg_state = sgdma->sg_state + sgslot; - while ((sg_state->next_sglist < sg_state->sglen) && - !(sg_state->csr & csr_error)) { - const struct scatterlist *sglist; - unsigned int len; - - sglist = sg_state->sglist + sg_state->next_sglist; - /* try to start the next DMA transfer */ - if (sg_state->next_sglist + 1 == sg_state->sglen) { - /* - * On the last sg, we handle the case where - * cam->img.pix.sizeimage % PAGE_ALIGN != 0 - */ - len = sg_state->len - sg_state->bytes_read; - } else { - len = sg_dma_len(sglist); - } - - if (omap24xxcam_dma_start(&sgdma->dma, - sg_dma_address(sglist), - len, - omap24xxcam_sgdma_callback, - (void *)sgslot)) { - /* DMA start failed */ - spin_unlock_irqrestore(&sgdma->lock, flags); - return; - } else { - unsigned long expires; - /* DMA start was successful */ - sg_state->next_sglist++; - sg_state->bytes_read += len; - sg_state->queued_sglist++; - - /* We start the reset timer */ - expires = jiffies + HZ; - mod_timer(&sgdma->reset_timer, expires); - } - } - queued_sgdma--; - sgslot = (sgslot + 1) % NUM_SG_DMA; - } - - spin_unlock_irqrestore(&sgdma->lock, flags); -} - -/* - * Queue a scatter-gather DMA transfer from the camera to memory. - * Returns zero if the transfer was successfully queued, or non-zero - * if all of the scatter-gather slots are already in use. - */ -int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, - const struct scatterlist *sglist, int sglen, - int len, sgdma_callback_t callback, void *arg) -{ - unsigned long flags; - struct sgdma_state *sg_state; - - if ((sglen < 0) || ((sglen > 0) && !sglist)) - return -EINVAL; - - spin_lock_irqsave(&sgdma->lock, flags); - - if (!sgdma->free_sgdma) { - spin_unlock_irqrestore(&sgdma->lock, flags); - return -EBUSY; - } - - sg_state = sgdma->sg_state + sgdma->next_sgdma; - - sg_state->sglist = sglist; - sg_state->sglen = sglen; - sg_state->next_sglist = 0; - sg_state->bytes_read = 0; - sg_state->len = len; - sg_state->queued_sglist = 0; - sg_state->csr = 0; - sg_state->callback = callback; - sg_state->arg = arg; - - sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA; - sgdma->free_sgdma--; - - spin_unlock_irqrestore(&sgdma->lock, flags); - - omap24xxcam_sgdma_process(sgdma); - - return 0; -} - -/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress. - * Any queued scatter-gather DMA transactions that have not yet been started - * will remain queued. The DMA controller will be idle after this routine - * completes. When the scatter-gather queue is restarted, the next - * scatter-gather DMA transfer will begin at the start of a new transaction. - */ -void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) -{ - unsigned long flags; - int sgslot; - struct sgdma_state *sg_state; - u32 csr = CAMDMA_CSR_TRANS_ERR; - - /* stop any DMA transfers in progress */ - omap24xxcam_dma_stop(&sgdma->dma, csr); - - spin_lock_irqsave(&sgdma->lock, flags); - - if (sgdma->free_sgdma < NUM_SG_DMA) { - sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; - sg_state = sgdma->sg_state + sgslot; - if (sg_state->next_sglist != 0) { - /* This DMA transfer was in progress, so abort it. */ - sgdma_callback_t callback = sg_state->callback; - void *arg = sg_state->arg; - sgdma->free_sgdma++; - if (callback) { - /* leave interrupts masked */ - spin_unlock(&sgdma->lock); - (*callback) (sgdma, csr, arg); - spin_lock(&sgdma->lock); - } - } - } - - spin_unlock_irqrestore(&sgdma->lock, flags); -} - -void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - void __iomem *base, - void (*reset_callback)(unsigned long data), - unsigned long reset_callback_data) -{ - int sg; - - spin_lock_init(&sgdma->lock); - sgdma->free_sgdma = NUM_SG_DMA; - sgdma->next_sgdma = 0; - for (sg = 0; sg < NUM_SG_DMA; sg++) { - sgdma->sg_state[sg].sglen = 0; - sgdma->sg_state[sg].next_sglist = 0; - sgdma->sg_state[sg].bytes_read = 0; - sgdma->sg_state[sg].queued_sglist = 0; - sgdma->sg_state[sg].csr = 0; - sgdma->sg_state[sg].callback = NULL; - sgdma->sg_state[sg].arg = NULL; - } - - omap24xxcam_dma_init(&sgdma->dma, base); - setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data); -} diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c deleted file mode 100644 index d2b440c842b3..000000000000 --- a/drivers/media/platform/omap24xxcam.c +++ /dev/null @@ -1,1888 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam.c - * - * OMAP 2 camera block driver. - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007-2008 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/videodev2.h> -#include <linux/pci.h> /* needed for videobufs */ -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/module.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> - -#include "omap24xxcam.h" - -#define OMAP24XXCAM_VERSION "0.0.1" - -#define RESET_TIMEOUT_NS 10000 - -static void omap24xxcam_reset(struct omap24xxcam_device *cam); -static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam); -static void omap24xxcam_device_unregister(struct v4l2_int_device *s); -static int omap24xxcam_remove(struct platform_device *pdev); - -/* module parameters */ -static int video_nr = -1; /* video device minor (-1 ==> auto assign) */ -/* - * Maximum amount of memory to use for capture buffers. - * Default is 4800KB, enough to double-buffer SXGA. - */ -static int capture_mem = 1280 * 960 * 2 * 2; - -static struct v4l2_int_device omap24xxcam; - -/* - * - * Clocks. - * - */ - -static void omap24xxcam_clock_put(struct omap24xxcam_device *cam) -{ - if (cam->ick != NULL && !IS_ERR(cam->ick)) - clk_put(cam->ick); - if (cam->fck != NULL && !IS_ERR(cam->fck)) - clk_put(cam->fck); - - cam->ick = cam->fck = NULL; -} - -static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) -{ - int rval = 0; - - cam->fck = clk_get(cam->dev, "fck"); - if (IS_ERR(cam->fck)) { - dev_err(cam->dev, "can't get camera fck"); - rval = PTR_ERR(cam->fck); - omap24xxcam_clock_put(cam); - return rval; - } - - cam->ick = clk_get(cam->dev, "ick"); - if (IS_ERR(cam->ick)) { - dev_err(cam->dev, "can't get camera ick"); - rval = PTR_ERR(cam->ick); - omap24xxcam_clock_put(cam); - } - - return rval; -} - -static void omap24xxcam_clock_on(struct omap24xxcam_device *cam) -{ - clk_enable(cam->fck); - clk_enable(cam->ick); -} - -static void omap24xxcam_clock_off(struct omap24xxcam_device *cam) -{ - clk_disable(cam->fck); - clk_disable(cam->ick); -} - -/* - * - * Camera core - * - */ - -/* - * Set xclk. - * - * To disable xclk, use value zero. - */ -static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam, - u32 xclk) -{ - if (xclk) { - u32 divisor = CAM_MCLK / xclk; - - if (divisor == 1) - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, - CC_CTRL_XCLK, - CC_CTRL_XCLK_DIV_BYPASS); - else - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, - CC_CTRL_XCLK, divisor); - } else - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, - CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW); -} - -static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam) -{ - /* - * Setting the camera core AUTOIDLE bit causes problems with frame - * synchronization, so we will clear the AUTOIDLE bit instead. - */ - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG, - CC_SYSCONFIG_AUTOIDLE); - - /* program the camera interface DMA packet size */ - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA, - CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1)); - - /* enable camera core error interrupts */ - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE, - CC_IRQENABLE_FW_ERR_IRQ - | CC_IRQENABLE_FSC_ERR_IRQ - | CC_IRQENABLE_SSC_ERR_IRQ - | CC_IRQENABLE_FIFO_OF_IRQ); -} - -/* - * Enable the camera core. - * - * Data transfer to the camera DMA starts from next starting frame. - */ -static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam) -{ - - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, - cam->cc_ctrl); -} - -/* - * Disable camera core. - * - * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The - * core internal state machines will be reset. Use - * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current - * frame completely. - */ -static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam) -{ - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, - CC_CTRL_CC_RST); -} - -/* Interrupt service routine for camera core interrupts. */ -static void omap24xxcam_core_isr(struct omap24xxcam_device *cam) -{ - u32 cc_irqstatus; - const u32 cc_irqstatus_err = - CC_IRQSTATUS_FW_ERR_IRQ - | CC_IRQSTATUS_FSC_ERR_IRQ - | CC_IRQSTATUS_SSC_ERR_IRQ - | CC_IRQSTATUS_FIFO_UF_IRQ - | CC_IRQSTATUS_FIFO_OF_IRQ; - - cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET, - CC_IRQSTATUS); - omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS, - cc_irqstatus); - - if (cc_irqstatus & cc_irqstatus_err - && !atomic_read(&cam->in_reset)) { - dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n", - cc_irqstatus); - omap24xxcam_reset(cam); - } -} - -/* - * - * videobuf_buffer handling. - * - * Memory for mmapped videobuf_buffers is not allocated - * conventionally, but by several kmalloc allocations and then - * creating the scatterlist on our own. User-space buffers are handled - * normally. - * - */ - -/* - * Free the memory-mapped buffer memory allocated for a - * videobuf_buffer and the associated scatterlist. - */ -static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - size_t alloc_size; - struct page *page; - int i; - - if (dma->sglist == NULL) - return; - - i = dma->sglen; - while (i) { - i--; - alloc_size = sg_dma_len(&dma->sglist[i]); - page = sg_page(&dma->sglist[i]); - do { - ClearPageReserved(page++); - } while (alloc_size -= PAGE_SIZE); - __free_pages(sg_page(&dma->sglist[i]), - get_order(sg_dma_len(&dma->sglist[i]))); - } - - kfree(dma->sglist); - dma->sglist = NULL; -} - -/* Release all memory related to the videobuf_queue. */ -static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq) -{ - int i; - - mutex_lock(&vbq->vb_lock); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == vbq->bufs[i]) - continue; - if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory) - continue; - vbq->ops->buf_release(vbq, vbq->bufs[i]); - omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); - kfree(vbq->bufs[i]); - vbq->bufs[i] = NULL; - } - - mutex_unlock(&vbq->vb_lock); - - videobuf_mmap_free(vbq); -} - -/* - * Allocate physically as contiguous as possible buffer for video - * frame and allocate and build DMA scatter-gather list for it. - */ -static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb) -{ - unsigned int order; - size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */ - struct page *page; - int max_pages, err = 0, i = 0; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - /* - * allocate maximum size scatter-gather list. Note this is - * overhead. We may not use as many entries as we allocate - */ - max_pages = vb->bsize >> PAGE_SHIFT; - dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL); - if (dma->sglist == NULL) { - err = -ENOMEM; - goto out; - } - - while (size) { - order = get_order(size); - /* - * do not over-allocate even if we would get larger - * contiguous chunk that way - */ - if ((PAGE_SIZE << order) > size) - order--; - - /* try to allocate as many contiguous pages as possible */ - page = alloc_pages(GFP_KERNEL, order); - /* if allocation fails, try to allocate smaller amount */ - while (page == NULL) { - order--; - page = alloc_pages(GFP_KERNEL, order); - if (page == NULL && !order) { - err = -ENOMEM; - goto out; - } - } - size -= (PAGE_SIZE << order); - - /* append allocated chunk of pages into scatter-gather list */ - sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0); - dma->sglen++; - i++; - - alloc_size = (PAGE_SIZE << order); - - /* clear pages before giving them to user space */ - memset(page_address(page), 0, alloc_size); - - /* mark allocated pages reserved */ - do { - SetPageReserved(page++); - } while (alloc_size -= PAGE_SIZE); - } - /* - * REVISIT: not fully correct to assign nr_pages == sglen but - * video-buf is passing nr_pages for e.g. unmap_sg calls - */ - dma->nr_pages = dma->sglen; - dma->direction = PCI_DMA_FROMDEVICE; - - return 0; - -out: - omap24xxcam_vbq_free_mmap_buffer(vb); - return err; -} - -static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq, - unsigned int count) -{ - int i, err = 0; - struct omap24xxcam_fh *fh = - container_of(vbq, struct omap24xxcam_fh, vbq); - - mutex_lock(&vbq->vb_lock); - - for (i = 0; i < count; i++) { - err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]); - if (err) - goto out; - dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n", - videobuf_to_dma(vbq->bufs[i])->sglen, i); - } - - mutex_unlock(&vbq->vb_lock); - - return 0; -out: - while (i) { - i--; - omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); - } - - mutex_unlock(&vbq->vb_lock); - - return err; -} - -/* - * This routine is called from interrupt context when a scatter-gather DMA - * transfer of a videobuf_buffer completes. - */ -static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, - u32 csr, void *arg) -{ - struct omap24xxcam_device *cam = - container_of(sgdma, struct omap24xxcam_device, sgdma); - struct omap24xxcam_fh *fh = cam->streaming->private_data; - struct videobuf_buffer *vb = (struct videobuf_buffer *)arg; - const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR - | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR - | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - unsigned long flags; - - spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - if (--cam->sgdma_in_queue == 0) - omap24xxcam_core_disable(cam); - spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - - v4l2_get_timestamp(&vb->ts); - vb->field_count = atomic_add_return(2, &fh->field_count); - if (csr & csr_error) { - vb->state = VIDEOBUF_ERROR; - if (!atomic_read(&fh->cam->in_reset)) { - dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr); - omap24xxcam_reset(cam); - } - } else - vb->state = VIDEOBUF_DONE; - wake_up(&vb->done); -} - -static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - /* wait for buffer, especially to get out of the sgdma queue */ - videobuf_waiton(vbq, vb, 0, 0); - if (vb->memory == V4L2_MEMORY_MMAP) { - dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen, - dma->direction); - dma->direction = DMA_NONE; - } else { - videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb)); - videobuf_dma_free(videobuf_to_dma(vb)); - } - - vb->state = VIDEOBUF_NEEDS_INIT; -} - -/* - * Limit the number of available kernel image capture buffers based on the - * number requested, the currently selected image size, and the maximum - * amount of memory permitted for kernel capture buffers. - */ -static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, - unsigned int *size) -{ - struct omap24xxcam_fh *fh = vbq->priv_data; - - if (*cnt <= 0) - *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ - - if (*cnt > VIDEO_MAX_FRAME) - *cnt = VIDEO_MAX_FRAME; - - *size = fh->pix.sizeimage; - - /* accessing fh->cam->capture_mem is ok, it's constant */ - if (*size * *cnt > fh->cam->capture_mem) - *cnt = fh->cam->capture_mem / *size; - - return 0; -} - -static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq, - struct videobuf_dmabuf *dma) -{ - int err = 0; - - dma->direction = PCI_DMA_FROMDEVICE; - if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) { - kfree(dma->sglist); - dma->sglist = NULL; - dma->sglen = 0; - err = -EIO; - } - - return err; -} - -static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct omap24xxcam_fh *fh = vbq->priv_data; - int err = 0; - - /* - * Accessing pix here is okay since it's constant while - * streaming is on (and we only get called then). - */ - if (vb->baddr) { - /* This is a userspace buffer. */ - if (fh->pix.sizeimage > vb->bsize) { - /* The buffer isn't big enough. */ - err = -EINVAL; - } else - vb->size = fh->pix.sizeimage; - } else { - if (vb->state != VIDEOBUF_NEEDS_INIT) { - /* - * We have a kernel bounce buffer that has - * already been allocated. - */ - if (fh->pix.sizeimage > vb->size) { - /* - * The image size has been changed to - * a larger size since this buffer was - * allocated, so we need to free and - * reallocate it. - */ - omap24xxcam_vbq_release(vbq, vb); - vb->size = fh->pix.sizeimage; - } - } else { - /* We need to allocate a new kernel bounce buffer. */ - vb->size = fh->pix.sizeimage; - } - } - - if (err) - return err; - - vb->width = fh->pix.width; - vb->height = fh->pix.height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - if (vb->memory == V4L2_MEMORY_MMAP) - /* - * we have built the scatter-gather list by ourself so - * do the scatter-gather mapping as well - */ - err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb)); - else - err = videobuf_iolock(vbq, vb, NULL); - } - - if (!err) - vb->state = VIDEOBUF_PREPARED; - else - omap24xxcam_vbq_release(vbq, vb); - - return err; -} - -static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq, - struct videobuf_buffer *vb) -{ - struct omap24xxcam_fh *fh = vbq->priv_data; - struct omap24xxcam_device *cam = fh->cam; - enum videobuf_state state = vb->state; - unsigned long flags; - int err; - - /* - * FIXME: We're marking the buffer active since we have no - * pretty way of marking it active exactly when the - * scatter-gather transfer starts. - */ - vb->state = VIDEOBUF_ACTIVE; - - err = omap24xxcam_sgdma_queue(&fh->cam->sgdma, - videobuf_to_dma(vb)->sglist, - videobuf_to_dma(vb)->sglen, vb->size, - omap24xxcam_vbq_complete, vb); - - if (!err) { - spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - if (++cam->sgdma_in_queue == 1 - && !atomic_read(&cam->in_reset)) - omap24xxcam_core_enable(cam); - spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - } else { - /* - * Oops. We're not supposed to get any errors here. - * The only way we could get an error is if we ran out - * of scatter-gather DMA slots, but we are supposed to - * have at least as many scatter-gather DMA slots as - * video buffers so that can't happen. - */ - dev_err(cam->dev, "failed to queue a video buffer for dma!\n"); - dev_err(cam->dev, "likely a bug in the driver!\n"); - vb->state = state; - } -} - -static struct videobuf_queue_ops omap24xxcam_vbq_ops = { - .buf_setup = omap24xxcam_vbq_setup, - .buf_prepare = omap24xxcam_vbq_prepare, - .buf_queue = omap24xxcam_vbq_queue, - .buf_release = omap24xxcam_vbq_release, -}; - -/* - * - * OMAP main camera system - * - */ - -/* - * Reset camera block to power-on state. - */ -static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam) -{ - int max_loop = RESET_TIMEOUT_NS; - - /* Reset whole camera subsystem */ - omap24xxcam_reg_out(cam->mmio_base, - CAM_SYSCONFIG, - CAM_SYSCONFIG_SOFTRESET); - - /* Wait till it's finished */ - while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) - & CAM_SYSSTATUS_RESETDONE) - && --max_loop) { - ndelay(1); - } - - if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) - & CAM_SYSSTATUS_RESETDONE)) - dev_err(cam->dev, "camera soft reset timeout\n"); -} - -/* - * (Re)initialise the camera block. - */ -static void omap24xxcam_hwinit(struct omap24xxcam_device *cam) -{ - omap24xxcam_poweron_reset(cam); - - /* set the camera subsystem autoidle bit */ - omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG, - CAM_SYSCONFIG_AUTOIDLE); - - /* set the camera MMU autoidle bit */ - omap24xxcam_reg_out(cam->mmio_base, - CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG, - CAMMMU_SYSCONFIG_AUTOIDLE); - - omap24xxcam_core_hwinit(cam); - - omap24xxcam_dma_hwinit(&cam->sgdma.dma); -} - -/* - * Callback for dma transfer stalling. - */ -static void omap24xxcam_stalled_dma_reset(unsigned long data) -{ - struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data; - - if (!atomic_read(&cam->in_reset)) { - dev_dbg(cam->dev, "dma stalled, resetting camera\n"); - omap24xxcam_reset(cam); - } -} - -/* - * Stop capture. Mark we're doing a reset, stop DMA transfers and - * core. (No new scatter-gather transfers will be queued whilst - * in_reset is non-zero.) - * - * If omap24xxcam_capture_stop is called from several places at - * once, only the first call will have an effect. Similarly, the last - * call omap24xxcam_streaming_cont will have effect. - * - * Serialisation is ensured by using cam->core_enable_disable_lock. - */ -static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam) -{ - unsigned long flags; - - spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - - if (atomic_inc_return(&cam->in_reset) != 1) { - spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - return; - } - - omap24xxcam_core_disable(cam); - - spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - - omap24xxcam_sgdma_sync(&cam->sgdma); -} - -/* - * Reset and continue streaming. - * - * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL - * register is supposed to be sufficient to recover from a camera - * interface error, but it doesn't seem to be enough. If we only do - * that then subsequent image captures are out of sync by either one - * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the - * entire camera subsystem prevents the problem with frame - * synchronization. - */ -static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam) -{ - unsigned long flags; - - spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - - if (atomic_read(&cam->in_reset) != 1) - goto out; - - omap24xxcam_hwinit(cam); - - omap24xxcam_sensor_if_enable(cam); - - omap24xxcam_sgdma_process(&cam->sgdma); - - if (cam->sgdma_in_queue) - omap24xxcam_core_enable(cam); - -out: - atomic_dec(&cam->in_reset); - spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); -} - -static ssize_t -omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct omap24xxcam_device *cam = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive"); -} -static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL); - -/* - * Stop capture and restart it. I.e. reset the camera during use. - */ -static void omap24xxcam_reset(struct omap24xxcam_device *cam) -{ - omap24xxcam_capture_stop(cam); - omap24xxcam_capture_cont(cam); -} - -/* - * The main interrupt handler. - */ -static irqreturn_t omap24xxcam_isr(int irq, void *arg) -{ - struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg; - u32 irqstatus; - unsigned int irqhandled = 0; - - irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS); - - if (irqstatus & - (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1 - | CAM_IRQSTATUS_DMA_IRQ0)) { - omap24xxcam_dma_isr(&cam->sgdma.dma); - irqhandled = 1; - } - if (irqstatus & CAM_IRQSTATUS_CC_IRQ) { - omap24xxcam_core_isr(cam); - irqhandled = 1; - } - if (irqstatus & CAM_IRQSTATUS_MMU_IRQ) - dev_err(cam->dev, "unhandled camera MMU interrupt!\n"); - - return IRQ_RETVAL(irqhandled); -} - -/* - * - * Sensor handling. - * - */ - -/* - * Enable the external sensor interface. Try to negotiate interface - * parameters with the sensor and start using the new ones. The calls - * to sensor_if_enable and sensor_if_disable need not to be balanced. - */ -static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam) -{ - int rval; - struct v4l2_ifparm p; - - rval = vidioc_int_g_ifparm(cam->sdev, &p); - if (rval) { - dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval); - return rval; - } - - cam->if_type = p.if_type; - - cam->cc_ctrl = CC_CTRL_CC_EN; - - switch (p.if_type) { - case V4L2_IF_TYPE_BT656: - if (p.u.bt656.frame_start_on_rising_vs) - cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO; - if (p.u.bt656.bt_sync_correct) - cam->cc_ctrl |= CC_CTRL_BT_CORRECT; - if (p.u.bt656.swap) - cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM; - if (p.u.bt656.latch_clk_inv) - cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL; - if (p.u.bt656.nobt_hs_inv) - cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL; - if (p.u.bt656.nobt_vs_inv) - cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL; - - switch (p.u.bt656.mode) { - case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT: - cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8; - break; - case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT: - cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10; - break; - case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT: - cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12; - break; - case V4L2_IF_TYPE_BT656_MODE_BT_8BIT: - cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8; - break; - case V4L2_IF_TYPE_BT656_MODE_BT_10BIT: - cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10; - break; - default: - dev_err(cam->dev, - "bt656 interface mode %d not supported\n", - p.u.bt656.mode); - return -EINVAL; - } - /* - * The clock rate that the sensor wants has changed. - * We have to adjust the xclk from OMAP 2 side to - * match the sensor's wish as closely as possible. - */ - if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) { - u32 xclk = p.u.bt656.clock_curr; - u32 divisor; - - if (xclk == 0) - return -EINVAL; - - if (xclk > CAM_MCLK) - xclk = CAM_MCLK; - - divisor = CAM_MCLK / xclk; - if (divisor * xclk < CAM_MCLK) - divisor++; - if (CAM_MCLK / divisor < p.u.bt656.clock_min - && divisor > 1) - divisor--; - if (divisor > 30) - divisor = 30; - - xclk = CAM_MCLK / divisor; - - if (xclk < p.u.bt656.clock_min - || xclk > p.u.bt656.clock_max) - return -EINVAL; - - cam->if_u.bt656.xclk = xclk; - } - omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk); - break; - default: - /* FIXME: how about other interfaces? */ - dev_err(cam->dev, "interface type %d not supported\n", - p.if_type); - return -EINVAL; - } - - return 0; -} - -static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam) -{ - switch (cam->if_type) { - case V4L2_IF_TYPE_BT656: - omap24xxcam_core_xclk_set(cam, 0); - break; - } -} - -/* - * Initialise the sensor hardware. - */ -static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam) -{ - int err = 0; - struct v4l2_int_device *sdev = cam->sdev; - - omap24xxcam_clock_on(cam); - err = omap24xxcam_sensor_if_enable(cam); - if (err) { - dev_err(cam->dev, "sensor interface could not be enabled at " - "initialisation, %d\n", err); - cam->sdev = NULL; - goto out; - } - - /* power up sensor during sensor initialization */ - vidioc_int_s_power(sdev, 1); - - err = vidioc_int_dev_init(sdev); - if (err) { - dev_err(cam->dev, "cannot initialize sensor, error %d\n", err); - /* Sensor init failed --- it's nonexistent to us! */ - cam->sdev = NULL; - goto out; - } - - dev_info(cam->dev, "sensor is %s\n", sdev->name); - -out: - omap24xxcam_sensor_if_disable(cam); - omap24xxcam_clock_off(cam); - - vidioc_int_s_power(sdev, 0); - - return err; -} - -static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam) -{ - if (cam->sdev) - vidioc_int_dev_exit(cam->sdev); -} - -static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam) -{ - omap24xxcam_sensor_if_disable(cam); - omap24xxcam_clock_off(cam); - vidioc_int_s_power(cam->sdev, 0); -} - -/* - * Power-up and configure camera sensor. It's ready for capturing now. - */ -static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam) -{ - int rval; - - omap24xxcam_clock_on(cam); - - omap24xxcam_sensor_if_enable(cam); - - rval = vidioc_int_s_power(cam->sdev, 1); - if (rval) - goto out; - - rval = vidioc_int_init(cam->sdev); - if (rval) - goto out; - - return 0; - -out: - omap24xxcam_sensor_disable(cam); - - return rval; -} - -static void omap24xxcam_sensor_reset_work(struct work_struct *work) -{ - struct omap24xxcam_device *cam = - container_of(work, struct omap24xxcam_device, - sensor_reset_work); - - if (atomic_read(&cam->reset_disable)) - return; - - omap24xxcam_capture_stop(cam); - - if (vidioc_int_reset(cam->sdev) == 0) { - vidioc_int_init(cam->sdev); - } else { - /* Can't reset it by vidioc_int_reset. */ - omap24xxcam_sensor_disable(cam); - omap24xxcam_sensor_enable(cam); - } - - omap24xxcam_capture_cont(cam); -} - -/* - * - * IOCTL interface. - * - */ - -static int vidioc_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - - strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver)); - strlcpy(cap->card, cam->vfd->name, sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - rval = vidioc_int_enum_fmt_cap(cam->sdev, f); - - return rval; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - rval = vidioc_int_g_fmt_cap(cam->sdev, f); - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - if (cam->streaming) { - rval = -EBUSY; - goto out; - } - - rval = vidioc_int_s_fmt_cap(cam->sdev, f); - -out: - mutex_unlock(&cam->mutex); - - if (!rval) { - mutex_lock(&ofh->vbq.vb_lock); - ofh->pix = f->fmt.pix; - mutex_unlock(&ofh->vbq.vb_lock); - } - - memset(f, 0, sizeof(*f)); - vidioc_g_fmt_vid_cap(file, fh, f); - - return rval; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - rval = vidioc_int_try_fmt_cap(cam->sdev, f); - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *b) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - if (cam->streaming) { - mutex_unlock(&cam->mutex); - return -EBUSY; - } - - omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); - mutex_unlock(&cam->mutex); - - rval = videobuf_reqbufs(&ofh->vbq, b); - - /* - * Either videobuf_reqbufs failed or the buffers are not - * memory-mapped (which would need special attention). - */ - if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) - goto out; - - rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval); - if (rval) - omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); - -out: - return rval; -} - -static int vidioc_querybuf(struct file *file, void *fh, - struct v4l2_buffer *b) -{ - struct omap24xxcam_fh *ofh = fh; - - return videobuf_querybuf(&ofh->vbq, b); -} - -static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct omap24xxcam_fh *ofh = fh; - - return videobuf_qbuf(&ofh->vbq, b); -} - -static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - struct videobuf_buffer *vb; - int rval; - -videobuf_dqbuf_again: - rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); - if (rval) - goto out; - - vb = ofh->vbq.bufs[b->index]; - - mutex_lock(&cam->mutex); - /* _needs_reset returns -EIO if reset is required. */ - rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr); - mutex_unlock(&cam->mutex); - if (rval == -EIO) - schedule_work(&cam->sensor_reset_work); - else - rval = 0; - -out: - /* - * This is a hack. We don't want to show -EIO to the user - * space. Requeue the buffer and try again if we're not doing - * this in non-blocking mode. - */ - if (rval == -EIO) { - videobuf_qbuf(&ofh->vbq, b); - if (!(file->f_flags & O_NONBLOCK)) - goto videobuf_dqbuf_again; - /* - * We don't have a videobuf_buffer now --- maybe next - * time... - */ - rval = -EAGAIN; - } - - return rval; -} - -static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - if (cam->streaming) { - rval = -EBUSY; - goto out; - } - - rval = omap24xxcam_sensor_if_enable(cam); - if (rval) { - dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n"); - goto out; - } - - rval = videobuf_streamon(&ofh->vbq); - if (!rval) { - cam->streaming = file; - sysfs_notify(&cam->dev->kobj, NULL, "streaming"); - } - -out: - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - struct videobuf_queue *q = &ofh->vbq; - int rval; - - atomic_inc(&cam->reset_disable); - - flush_work(&cam->sensor_reset_work); - - rval = videobuf_streamoff(q); - if (!rval) { - mutex_lock(&cam->mutex); - cam->streaming = NULL; - mutex_unlock(&cam->mutex); - sysfs_notify(&cam->dev->kobj, NULL, "streaming"); - } - - atomic_dec(&cam->reset_disable); - - return rval; -} - -static int vidioc_enum_input(struct file *file, void *fh, - struct v4l2_input *inp) -{ - if (inp->index > 0) - return -EINVAL; - - strlcpy(inp->name, "camera", sizeof(inp->name)); - inp->type = V4L2_INPUT_TYPE_CAMERA; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *a) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - rval = vidioc_int_queryctrl(cam->sdev, a); - - return rval; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, - struct v4l2_control *a) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - rval = vidioc_int_g_ctrl(cam->sdev, a); - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, - struct v4l2_control *a) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - rval = vidioc_int_s_ctrl(cam->sdev, a); - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) { - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - int rval; - - mutex_lock(&cam->mutex); - rval = vidioc_int_g_parm(cam->sdev, a); - mutex_unlock(&cam->mutex); - - return rval; -} - -static int vidioc_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct omap24xxcam_fh *ofh = fh; - struct omap24xxcam_device *cam = ofh->cam; - struct v4l2_streamparm old_streamparm; - int rval; - - mutex_lock(&cam->mutex); - if (cam->streaming) { - rval = -EBUSY; - goto out; - } - - old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rval = vidioc_int_g_parm(cam->sdev, &old_streamparm); - if (rval) - goto out; - - rval = vidioc_int_s_parm(cam->sdev, a); - if (rval) - goto out; - - rval = omap24xxcam_sensor_if_enable(cam); - /* - * Revert to old streaming parameters if enabling sensor - * interface with the new ones failed. - */ - if (rval) - vidioc_int_s_parm(cam->sdev, &old_streamparm); - -out: - mutex_unlock(&cam->mutex); - - return rval; -} - -/* - * - * File operations. - * - */ - -static unsigned int omap24xxcam_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct omap24xxcam_fh *fh = file->private_data; - struct omap24xxcam_device *cam = fh->cam; - struct videobuf_buffer *vb; - - mutex_lock(&cam->mutex); - if (cam->streaming != file) { - mutex_unlock(&cam->mutex); - return POLLERR; - } - mutex_unlock(&cam->mutex); - - mutex_lock(&fh->vbq.vb_lock); - if (list_empty(&fh->vbq.stream)) { - mutex_unlock(&fh->vbq.vb_lock); - return POLLERR; - } - vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); - mutex_unlock(&fh->vbq.vb_lock); - - poll_wait(file, &vb->done, wait); - - if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - - return 0; -} - -static int omap24xxcam_mmap_buffers(struct file *file, - struct vm_area_struct *vma) -{ - struct omap24xxcam_fh *fh = file->private_data; - struct omap24xxcam_device *cam = fh->cam; - struct videobuf_queue *vbq = &fh->vbq; - unsigned int first, last, size, i, j; - int err = 0; - - mutex_lock(&cam->mutex); - if (cam->streaming) { - mutex_unlock(&cam->mutex); - return -EBUSY; - } - mutex_unlock(&cam->mutex); - mutex_lock(&vbq->vb_lock); - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (NULL == vbq->bufs[first]) - continue; - if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory) - continue; - if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) - break; - } - - /* look for last buffer to map */ - for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { - if (NULL == vbq->bufs[last]) - continue; - if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory) - continue; - size += vbq->bufs[last]->bsize; - if (size == (vma->vm_end - vma->vm_start)) - break; - } - - size = 0; - for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]); - - for (j = 0; j < dma->sglen; j++) { - err = remap_pfn_range( - vma, vma->vm_start + size, - page_to_pfn(sg_page(&dma->sglist[j])), - sg_dma_len(&dma->sglist[j]), vma->vm_page_prot); - if (err) - goto out; - size += sg_dma_len(&dma->sglist[j]); - } - } - -out: - mutex_unlock(&vbq->vb_lock); - - return err; -} - -static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct omap24xxcam_fh *fh = file->private_data; - int rval; - - /* let the video-buf mapper check arguments and set-up structures */ - rval = videobuf_mmap_mapper(&fh->vbq, vma); - if (rval) - return rval; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - /* do mapping to our allocated buffers */ - rval = omap24xxcam_mmap_buffers(file, vma); - /* - * In case of error, free vma->vm_private_data allocated by - * videobuf_mmap_mapper. - */ - if (rval) - kfree(vma->vm_private_data); - - return rval; -} - -static int omap24xxcam_open(struct file *file) -{ - struct omap24xxcam_device *cam = omap24xxcam.priv; - struct omap24xxcam_fh *fh; - struct v4l2_format format; - - if (!cam || !cam->vfd) - return -ENODEV; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - mutex_lock(&cam->mutex); - if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) { - mutex_unlock(&cam->mutex); - goto out_try_module_get; - } - - if (atomic_inc_return(&cam->users) == 1) { - omap24xxcam_hwinit(cam); - if (omap24xxcam_sensor_enable(cam)) { - mutex_unlock(&cam->mutex); - goto out_omap24xxcam_sensor_enable; - } - } - mutex_unlock(&cam->mutex); - - fh->cam = cam; - mutex_lock(&cam->mutex); - vidioc_int_g_fmt_cap(cam->sdev, &format); - mutex_unlock(&cam->mutex); - /* FIXME: how about fh->pix when there are more users? */ - fh->pix = format.fmt.pix; - - file->private_data = fh; - - spin_lock_init(&fh->vbq_lock); - - videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL, - &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; - -out_omap24xxcam_sensor_enable: - omap24xxcam_poweron_reset(cam); - module_put(cam->sdev->module); - -out_try_module_get: - kfree(fh); - - return -ENODEV; -} - -static int omap24xxcam_release(struct file *file) -{ - struct omap24xxcam_fh *fh = file->private_data; - struct omap24xxcam_device *cam = fh->cam; - - atomic_inc(&cam->reset_disable); - - flush_work(&cam->sensor_reset_work); - - /* stop streaming capture */ - videobuf_streamoff(&fh->vbq); - - mutex_lock(&cam->mutex); - if (cam->streaming == file) { - cam->streaming = NULL; - mutex_unlock(&cam->mutex); - sysfs_notify(&cam->dev->kobj, NULL, "streaming"); - } else { - mutex_unlock(&cam->mutex); - } - - atomic_dec(&cam->reset_disable); - - omap24xxcam_vbq_free_mmap_buffers(&fh->vbq); - - /* - * Make sure the reset work we might have scheduled is not - * pending! It may be run *only* if we have users. (And it may - * not be scheduled anymore since streaming is already - * disabled.) - */ - flush_work(&cam->sensor_reset_work); - - mutex_lock(&cam->mutex); - if (atomic_dec_return(&cam->users) == 0) { - omap24xxcam_sensor_disable(cam); - omap24xxcam_poweron_reset(cam); - } - mutex_unlock(&cam->mutex); - - file->private_data = NULL; - - module_put(cam->sdev->module); - kfree(fh); - - return 0; -} - -static struct v4l2_file_operations omap24xxcam_fops = { - .ioctl = video_ioctl2, - .poll = omap24xxcam_poll, - .mmap = omap24xxcam_mmap, - .open = omap24xxcam_open, - .release = omap24xxcam_release, -}; - -/* - * - * Power management. - * - */ - -#ifdef CONFIG_PM -static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - - if (atomic_read(&cam->users) == 0) - return 0; - - if (!atomic_read(&cam->reset_disable)) - omap24xxcam_capture_stop(cam); - - omap24xxcam_sensor_disable(cam); - omap24xxcam_poweron_reset(cam); - - return 0; -} - -static int omap24xxcam_resume(struct platform_device *pdev) -{ - struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - - if (atomic_read(&cam->users) == 0) - return 0; - - omap24xxcam_hwinit(cam); - omap24xxcam_sensor_enable(cam); - - if (!atomic_read(&cam->reset_disable)) - omap24xxcam_capture_cont(cam); - - return 0; -} -#endif /* CONFIG_PM */ - -static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_try_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_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_g_parm = vidioc_g_parm, - .vidioc_s_parm = vidioc_s_parm, -}; - -/* - * - * Camera device (i.e. /dev/video). - * - */ - -static int omap24xxcam_device_register(struct v4l2_int_device *s) -{ - struct omap24xxcam_device *cam = s->u.slave->master->priv; - struct video_device *vfd; - int rval; - - /* We already have a slave. */ - if (cam->sdev) - return -EBUSY; - - cam->sdev = s; - - if (device_create_file(cam->dev, &dev_attr_streaming) != 0) { - dev_err(cam->dev, "could not register sysfs entry\n"); - rval = -EBUSY; - goto err; - } - - /* initialize the video_device struct */ - vfd = cam->vfd = video_device_alloc(); - if (!vfd) { - dev_err(cam->dev, "could not allocate video device struct\n"); - rval = -ENOMEM; - goto err; - } - vfd->release = video_device_release; - - vfd->v4l2_dev = &cam->v4l2_dev; - - strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); - vfd->fops = &omap24xxcam_fops; - vfd->ioctl_ops = &omap24xxcam_ioctl_fops; - - omap24xxcam_hwinit(cam); - - rval = omap24xxcam_sensor_init(cam); - if (rval) - goto err; - - if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) { - dev_err(cam->dev, "could not register V4L device\n"); - rval = -EBUSY; - goto err; - } - - omap24xxcam_poweron_reset(cam); - - dev_info(cam->dev, "registered device %s\n", - video_device_node_name(vfd)); - - return 0; - -err: - omap24xxcam_device_unregister(s); - - return rval; -} - -static void omap24xxcam_device_unregister(struct v4l2_int_device *s) -{ - struct omap24xxcam_device *cam = s->u.slave->master->priv; - - omap24xxcam_sensor_exit(cam); - - if (cam->vfd) { - if (!video_is_registered(cam->vfd)) { - /* - * The device was never registered, so release the - * video_device struct directly. - */ - video_device_release(cam->vfd); - } else { - /* - * The unregister function will release the - * video_device struct as well as - * unregistering it. - */ - video_unregister_device(cam->vfd); - } - cam->vfd = NULL; - } - - device_remove_file(cam->dev, &dev_attr_streaming); - - cam->sdev = NULL; -} - -static struct v4l2_int_master omap24xxcam_master = { - .attach = omap24xxcam_device_register, - .detach = omap24xxcam_device_unregister, -}; - -static struct v4l2_int_device omap24xxcam = { - .module = THIS_MODULE, - .name = CAM_NAME, - .type = v4l2_int_type_master, - .u = { - .master = &omap24xxcam_master - }, -}; - -/* - * - * Driver initialisation and deinitialisation. - * - */ - -static int omap24xxcam_probe(struct platform_device *pdev) -{ - struct omap24xxcam_device *cam; - struct resource *mem; - int irq; - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) { - dev_err(&pdev->dev, "could not allocate memory\n"); - goto err; - } - - platform_set_drvdata(pdev, cam); - - cam->dev = &pdev->dev; - - if (v4l2_device_register(&pdev->dev, &cam->v4l2_dev)) { - dev_err(&pdev->dev, "v4l2_device_register failed\n"); - goto err; - } - - /* - * Impose a lower limit on the amount of memory allocated for - * capture. We require at least enough memory to double-buffer - * QVGA (300KB). - */ - if (capture_mem < 320 * 240 * 2 * 2) - capture_mem = 320 * 240 * 2 * 2; - cam->capture_mem = capture_mem; - - /* request the mem region for the camera registers */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(cam->dev, "no mem resource?\n"); - goto err; - } - if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { - dev_err(cam->dev, - "cannot reserve camera register I/O region\n"); - goto err; - } - cam->mmio_base_phys = mem->start; - cam->mmio_size = resource_size(mem); - - /* map the region */ - cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); - if (!cam->mmio_base) { - dev_err(cam->dev, "cannot map camera register I/O region\n"); - goto err; - } - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(cam->dev, "no irq for camera?\n"); - goto err; - } - - /* install the interrupt service routine */ - if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) { - dev_err(cam->dev, - "could not install interrupt service routine\n"); - goto err; - } - cam->irq = irq; - - if (omap24xxcam_clock_get(cam)) - goto err; - - INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work); - - mutex_init(&cam->mutex); - spin_lock_init(&cam->core_enable_disable_lock); - - omap24xxcam_sgdma_init(&cam->sgdma, - cam->mmio_base + CAMDMA_REG_OFFSET, - omap24xxcam_stalled_dma_reset, - (unsigned long)cam); - - omap24xxcam.priv = cam; - - if (v4l2_int_device_register(&omap24xxcam)) - goto err; - - return 0; - -err: - omap24xxcam_remove(pdev); - return -ENODEV; -} - -static int omap24xxcam_remove(struct platform_device *pdev) -{ - struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - - if (!cam) - return 0; - - if (omap24xxcam.priv != NULL) - v4l2_int_device_unregister(&omap24xxcam); - omap24xxcam.priv = NULL; - - omap24xxcam_clock_put(cam); - - if (cam->irq) { - free_irq(cam->irq, cam); - cam->irq = 0; - } - - if (cam->mmio_base) { - iounmap((void *)cam->mmio_base); - cam->mmio_base = 0; - } - - if (cam->mmio_base_phys) { - release_mem_region(cam->mmio_base_phys, cam->mmio_size); - cam->mmio_base_phys = 0; - } - - v4l2_device_unregister(&cam->v4l2_dev); - - kfree(cam); - - return 0; -} - -static struct platform_driver omap24xxcam_driver = { - .probe = omap24xxcam_probe, - .remove = omap24xxcam_remove, -#ifdef CONFIG_PM - .suspend = omap24xxcam_suspend, - .resume = omap24xxcam_resume, -#endif - .driver = { - .name = CAM_NAME, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(omap24xxcam_driver); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); -MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(OMAP24XXCAM_VERSION); -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, - "Minor number for video device (-1 ==> auto assign)"); -module_param(capture_mem, int, 0); -MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture " - "buffers (default 4800kiB)"); diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h deleted file mode 100644 index 7f6f79155537..000000000000 --- a/drivers/media/platform/omap24xxcam.h +++ /dev/null @@ -1,596 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam.h - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef OMAP24XXCAM_H -#define OMAP24XXCAM_H - -#include <media/videobuf-dma-sg.h> -#include <media/v4l2-int-device.h> -#include <media/v4l2-device.h> - -/* - * - * General driver related definitions. - * - */ - -#define CAM_NAME "omap24xxcam" - -#define CAM_MCLK 96000000 - -/* number of bytes transferred per DMA request */ -#define DMA_THRESHOLD 32 - -/* - * NUM_CAMDMA_CHANNELS is the number of logical channels provided by - * the camera DMA controller. - */ -#define NUM_CAMDMA_CHANNELS 4 - -/* - * NUM_SG_DMA is the number of scatter-gather DMA transfers that can - * be queued. (We don't have any overlay sglists now.) - */ -#define NUM_SG_DMA (VIDEO_MAX_FRAME) - -/* - * - * Register definitions. - * - */ - -/* subsystem register block offsets */ -#define CC_REG_OFFSET 0x00000400 -#define CAMDMA_REG_OFFSET 0x00000800 -#define CAMMMU_REG_OFFSET 0x00000C00 - -/* define camera subsystem register offsets */ -#define CAM_REVISION 0x000 -#define CAM_SYSCONFIG 0x010 -#define CAM_SYSSTATUS 0x014 -#define CAM_IRQSTATUS 0x018 -#define CAM_GPO 0x040 -#define CAM_GPI 0x050 - -/* define camera core register offsets */ -#define CC_REVISION 0x000 -#define CC_SYSCONFIG 0x010 -#define CC_SYSSTATUS 0x014 -#define CC_IRQSTATUS 0x018 -#define CC_IRQENABLE 0x01C -#define CC_CTRL 0x040 -#define CC_CTRL_DMA 0x044 -#define CC_CTRL_XCLK 0x048 -#define CC_FIFODATA 0x04C -#define CC_TEST 0x050 -#define CC_GENPAR 0x054 -#define CC_CCPFSCR 0x058 -#define CC_CCPFECR 0x05C -#define CC_CCPLSCR 0x060 -#define CC_CCPLECR 0x064 -#define CC_CCPDFR 0x068 - -/* define camera dma register offsets */ -#define CAMDMA_REVISION 0x000 -#define CAMDMA_IRQSTATUS_L0 0x008 -#define CAMDMA_IRQSTATUS_L1 0x00C -#define CAMDMA_IRQSTATUS_L2 0x010 -#define CAMDMA_IRQSTATUS_L3 0x014 -#define CAMDMA_IRQENABLE_L0 0x018 -#define CAMDMA_IRQENABLE_L1 0x01C -#define CAMDMA_IRQENABLE_L2 0x020 -#define CAMDMA_IRQENABLE_L3 0x024 -#define CAMDMA_SYSSTATUS 0x028 -#define CAMDMA_OCP_SYSCONFIG 0x02C -#define CAMDMA_CAPS_0 0x064 -#define CAMDMA_CAPS_2 0x06C -#define CAMDMA_CAPS_3 0x070 -#define CAMDMA_CAPS_4 0x074 -#define CAMDMA_GCR 0x078 -#define CAMDMA_CCR(n) (0x080 + (n)*0x60) -#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60) -#define CAMDMA_CICR(n) (0x088 + (n)*0x60) -#define CAMDMA_CSR(n) (0x08C + (n)*0x60) -#define CAMDMA_CSDP(n) (0x090 + (n)*0x60) -#define CAMDMA_CEN(n) (0x094 + (n)*0x60) -#define CAMDMA_CFN(n) (0x098 + (n)*0x60) -#define CAMDMA_CSSA(n) (0x09C + (n)*0x60) -#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60) -#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60) -#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60) -#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60) -#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60) -#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60) -#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60) -#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60) -#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60) -#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60) - -/* define camera mmu register offsets */ -#define CAMMMU_REVISION 0x000 -#define CAMMMU_SYSCONFIG 0x010 -#define CAMMMU_SYSSTATUS 0x014 -#define CAMMMU_IRQSTATUS 0x018 -#define CAMMMU_IRQENABLE 0x01C -#define CAMMMU_WALKING_ST 0x040 -#define CAMMMU_CNTL 0x044 -#define CAMMMU_FAULT_AD 0x048 -#define CAMMMU_TTB 0x04C -#define CAMMMU_LOCK 0x050 -#define CAMMMU_LD_TLB 0x054 -#define CAMMMU_CAM 0x058 -#define CAMMMU_RAM 0x05C -#define CAMMMU_GFLUSH 0x060 -#define CAMMMU_FLUSH_ENTRY 0x064 -#define CAMMMU_READ_CAM 0x068 -#define CAMMMU_READ_RAM 0x06C -#define CAMMMU_EMU_FAULT_AD 0x070 - -/* Define bit fields within selected registers */ -#define CAM_REVISION_MAJOR (15 << 4) -#define CAM_REVISION_MAJOR_SHIFT 4 -#define CAM_REVISION_MINOR (15 << 0) -#define CAM_REVISION_MINOR_SHIFT 0 - -#define CAM_SYSCONFIG_SOFTRESET (1 << 1) -#define CAM_SYSCONFIG_AUTOIDLE (1 << 0) - -#define CAM_SYSSTATUS_RESETDONE (1 << 0) - -#define CAM_IRQSTATUS_CC_IRQ (1 << 4) -#define CAM_IRQSTATUS_MMU_IRQ (1 << 3) -#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2) -#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1) -#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0) - -#define CAM_GPO_CAM_S_P_EN (1 << 1) -#define CAM_GPO_CAM_CCP_MODE (1 << 0) - -#define CAM_GPI_CC_DMA_REQ1 (1 << 24) -#define CAP_GPI_CC_DMA_REQ0 (1 << 23) -#define CAP_GPI_CAM_MSTANDBY (1 << 21) -#define CAP_GPI_CAM_WAIT (1 << 20) -#define CAP_GPI_CAM_S_DATA (1 << 17) -#define CAP_GPI_CAM_S_CLK (1 << 16) -#define CAP_GPI_CAM_P_DATA (0xFFF << 3) -#define CAP_GPI_CAM_P_DATA_SHIFT 3 -#define CAP_GPI_CAM_P_VS (1 << 2) -#define CAP_GPI_CAM_P_HS (1 << 1) -#define CAP_GPI_CAM_P_CLK (1 << 0) - -#define CC_REVISION_MAJOR (15 << 4) -#define CC_REVISION_MAJOR_SHIFT 4 -#define CC_REVISION_MINOR (15 << 0) -#define CC_REVISION_MINOR_SHIFT 0 - -#define CC_SYSCONFIG_SIDLEMODE (3 << 3) -#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) -#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) -#define CC_SYSCONFIG_SOFTRESET (1 << 1) -#define CC_SYSCONFIG_AUTOIDLE (1 << 0) - -#define CC_SYSSTATUS_RESETDONE (1 << 0) - -#define CC_IRQSTATUS_FS_IRQ (1 << 19) -#define CC_IRQSTATUS_LE_IRQ (1 << 18) -#define CC_IRQSTATUS_LS_IRQ (1 << 17) -#define CC_IRQSTATUS_FE_IRQ (1 << 16) -#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10) -#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9) -#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8) -#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4) -#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3) -#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2) -#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1) -#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0) - -#define CC_IRQENABLE_FS_IRQ (1 << 19) -#define CC_IRQENABLE_LE_IRQ (1 << 18) -#define CC_IRQENABLE_LS_IRQ (1 << 17) -#define CC_IRQENABLE_FE_IRQ (1 << 16) -#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10) -#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9) -#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8) -#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4) -#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3) -#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2) -#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1) -#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0) - -#define CC_CTRL_CC_ONE_SHOT (1 << 20) -#define CC_CTRL_CC_IF_SYNCHRO (1 << 19) -#define CC_CTRL_CC_RST (1 << 18) -#define CC_CTRL_CC_FRAME_TRIG (1 << 17) -#define CC_CTRL_CC_EN (1 << 16) -#define CC_CTRL_NOBT_SYNCHRO (1 << 13) -#define CC_CTRL_BT_CORRECT (1 << 12) -#define CC_CTRL_PAR_ORDERCAM (1 << 11) -#define CC_CTRL_PAR_CLK_POL (1 << 10) -#define CC_CTRL_NOBT_HS_POL (1 << 9) -#define CC_CTRL_NOBT_VS_POL (1 << 8) -#define CC_CTRL_PAR_MODE (7 << 1) -#define CC_CTRL_PAR_MODE_SHIFT 1 -#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1) -#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1) -#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1) -#define CC_CTRL_PAR_MODE_BT8 (4 << 1) -#define CC_CTRL_PAR_MODE_BT10 (5 << 1) -#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1) -#define CC_CTRL_CCP_MODE (1 << 0) - -#define CC_CTRL_DMA_EN (1 << 8) -#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0) -#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0 - -#define CC_CTRL_XCLK_DIV (0x1F << 0) -#define CC_CTRL_XCLK_DIV_SHIFT 0 -#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0) -#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0) -#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0) - -#define CC_TEST_FIFO_RD_POINTER (0xFF << 24) -#define CC_TEST_FIFO_RD_POINTER_SHIFT 24 -#define CC_TEST_FIFO_WR_POINTER (0xFF << 16) -#define CC_TEST_FIFO_WR_POINTER_SHIFT 16 -#define CC_TEST_FIFO_LEVEL (0xFF << 8) -#define CC_TEST_FIFO_LEVEL_SHIFT 8 -#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0) -#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0 - -#define CC_GENPAR_FIFO_DEPTH (7 << 0) -#define CC_GENPAR_FIFO_DEPTH_SHIFT 0 - -#define CC_CCPDFR_ALPHA (0xFF << 8) -#define CC_CCPDFR_ALPHA_SHIFT 8 -#define CC_CCPDFR_DATAFORMAT (15 << 0) -#define CC_CCPDFR_DATAFORMAT_SHIFT 0 -#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0) -#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0) -#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0) -#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0) -#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0) -#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0) -#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0) -#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0) -#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0) - -#define CAMDMA_REVISION_MAJOR (15 << 4) -#define CAMDMA_REVISION_MAJOR_SHIFT 4 -#define CAMDMA_REVISION_MINOR (15 << 0) -#define CAMDMA_REVISION_MINOR_SHIFT 0 - -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12) -#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9) -#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8) -#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3) -#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1) -#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0) - -#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0) - -#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16) -#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16 -#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0) -#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0 - -#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24) -#define CAMDMA_CCR_PREFETCH (1 << 23) -#define CAMDMA_CCR_SUPERVISOR (1 << 22) -#define CAMDMA_CCR_SECURE (1 << 21) -#define CAMDMA_CCR_BS (1 << 18) -#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17) -#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16) -#define CAMDMA_CCR_DST_AMODE (3 << 14) -#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14) -#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14) -#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14) -#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14) -#define CAMDMA_CCR_SRC_AMODE (3 << 12) -#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12) -#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12) -#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12) -#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12) -#define CAMDMA_CCR_WR_ACTIVE (1 << 10) -#define CAMDMA_CCR_RD_ACTIVE (1 << 9) -#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8) -#define CAMDMA_CCR_ENABLE (1 << 7) -#define CAMDMA_CCR_PRIO (1 << 6) -#define CAMDMA_CCR_FS (1 << 5) -#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0)) -#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01 - -#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15) -#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0) -#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0 - -#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11) -#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10) -#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9) -#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8) -#define CAMDMA_CICR_PACKET_IE (1 << 7) -#define CAMDMA_CICR_BLOCK_IE (1 << 5) -#define CAMDMA_CICR_LAST_IE (1 << 4) -#define CAMDMA_CICR_FRAME_IE (1 << 3) -#define CAMDMA_CICR_HALF_IE (1 << 2) -#define CAMDMA_CICR_DROP_IE (1 << 1) - -#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11) -#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10) -#define CAMDMA_CSR_SECURE_ERR (1 << 9) -#define CAMDMA_CSR_TRANS_ERR (1 << 8) -#define CAMDMA_CSR_PACKET (1 << 7) -#define CAMDMA_CSR_SYNC (1 << 6) -#define CAMDMA_CSR_BLOCK (1 << 5) -#define CAMDMA_CSR_LAST (1 << 4) -#define CAMDMA_CSR_FRAME (1 << 3) -#define CAMDMA_CSR_HALF (1 << 2) -#define CAMDMA_CSR_DROP (1 << 1) - -#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21) -#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20) -#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19) -#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18) -#define CAMDMA_CSDP_WRITE_MODE (3 << 16) -#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16) -#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16) -#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16) -#define CAMDMA_CSDP_DST_BURST_EN (3 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14) -#define CAMDMA_CSDP_DST_PACKED (1 << 13) -#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9) -#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9) -#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7) -#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7) -#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7) -#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7) -#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7) -#define CAMDMA_CSDP_SRC_PACKED (1 << 6) -#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2) -#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2) -#define CAMDMA_CSDP_DATA_TYPE (3 << 0) -#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0) -#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0) -#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0) - -#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0) - -/* - * - * Declarations. - * - */ - -/* forward declarations */ -struct omap24xxcam_sgdma; -struct omap24xxcam_dma; - -typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam, - u32 status, void *arg); -typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam, - u32 status, void *arg); - -struct channel_state { - dma_callback_t callback; - void *arg; -}; - -/* sgdma state for each of the possible videobuf_buffers + 2 overlays */ -struct sgdma_state { - const struct scatterlist *sglist; - int sglen; /* number of sglist entries */ - int next_sglist; /* index of next sglist entry to process */ - unsigned int bytes_read; /* number of bytes read */ - unsigned int len; /* total length of sglist (excluding - * bytes due to page alignment) */ - int queued_sglist; /* number of sglist entries queued for DMA */ - u32 csr; /* DMA return code */ - sgdma_callback_t callback; - void *arg; -}; - -/* physical DMA channel management */ -struct omap24xxcam_dma { - spinlock_t lock; /* Lock for the whole structure. */ - - void __iomem *base; /* base address for dma controller */ - - /* While dma_stop!=0, an attempt to start a new DMA transfer will - * fail. - */ - atomic_t dma_stop; - int free_dmach; /* number of dma channels free */ - int next_dmach; /* index of next dma channel to use */ - struct channel_state ch_state[NUM_CAMDMA_CHANNELS]; -}; - -/* scatter-gather DMA (scatterlist stuff) management */ -struct omap24xxcam_sgdma { - struct omap24xxcam_dma dma; - - spinlock_t lock; /* Lock for the fields below. */ - int free_sgdma; /* number of free sg dma slots */ - int next_sgdma; /* index of next sg dma slot to use */ - struct sgdma_state sg_state[NUM_SG_DMA]; - - /* Reset timer data */ - struct timer_list reset_timer; -}; - -/* per-device data structure */ -struct omap24xxcam_device { - /*** mutex ***/ - /* - * mutex serialises access to this structure. Also camera - * opening and releasing is synchronised by this. - */ - struct mutex mutex; - - struct v4l2_device v4l2_dev; - - /*** general driver state information ***/ - atomic_t users; - /* - * Lock to serialise core enabling and disabling and access to - * sgdma_in_queue. - */ - spinlock_t core_enable_disable_lock; - /* - * Number or sgdma requests in scatter-gather queue, protected - * by the lock above. - */ - int sgdma_in_queue; - /* - * Sensor interface parameters: interface type, CC_CTRL - * register value and interface specific data. - */ - int if_type; - union { - struct parallel { - u32 xclk; - } bt656; - } if_u; - u32 cc_ctrl; - - /*** subsystem structures ***/ - struct omap24xxcam_sgdma sgdma; - - /*** hardware resources ***/ - unsigned int irq; - void __iomem *mmio_base; - unsigned long mmio_base_phys; - unsigned long mmio_size; - - /*** interfaces and device ***/ - struct v4l2_int_device *sdev; - struct device *dev; - struct video_device *vfd; - - /*** camera and sensor reset related stuff ***/ - struct work_struct sensor_reset_work; - /* - * We're in the middle of a reset. Don't enable core if this - * is non-zero! This exists to help decisionmaking in a case - * where videobuf_qbuf is called while we are in the middle of - * a reset. - */ - atomic_t in_reset; - /* - * Non-zero if we don't want any resets for now. Used to - * prevent reset work to run when we're about to stop - * streaming. - */ - atomic_t reset_disable; - - /*** video device parameters ***/ - int capture_mem; - - /*** camera module clocks ***/ - struct clk *fck; - struct clk *ick; - - /*** capture data ***/ - /* file handle, if streaming is on */ - struct file *streaming; -}; - -/* Per-file handle data. */ -struct omap24xxcam_fh { - spinlock_t vbq_lock; /* spinlock for the videobuf queue */ - struct videobuf_queue vbq; - struct v4l2_pix_format pix; /* serialise pix by vbq->lock */ - atomic_t field_count; /* field counter for videobuf_buffer */ - /* accessing cam here doesn't need serialisation: it's constant */ - struct omap24xxcam_device *cam; -}; - -/* - * - * Register I/O functions. - * - */ - -static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset) -{ - return readl(base + offset); -} - -static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset, - u32 val) -{ - writel(val, base + offset); - return val; -} - -static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset, - u32 val, u32 mask) -{ - u32 __iomem *addr = base + offset; - u32 new_val = (readl(addr) & ~mask) | (val & mask); - - writel(new_val, addr); - return new_val; -} - -/* - * - * Function prototypes. - * - */ - -/* dma prototypes */ - -void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma); -void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma); - -/* sgdma prototypes */ - -void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma); -int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, - const struct scatterlist *sglist, int sglen, - int len, sgdma_callback_t callback, void *arg); -void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); -void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - void __iomem *base, - void (*reset_callback)(unsigned long data), - unsigned long reset_callback_data); -void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); - -#endif diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index fdbdeae3900d..5807185262fe 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -873,15 +873,12 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, unsigned long flags; int ret; - /* If the preview engine crashed it might not respond to read/write - * operations on the L4 bus. This would result in a bus fault and a - * kernel oops. Refuse to start streaming in that case. This check must - * be performed before the loop below to avoid starting entities if the - * pipeline won't start anyway (those entities would then likely fail to - * stop, making the problem worse). + /* Refuse to start streaming if an entity included in the pipeline has + * crashed. This check must be performed before the loop below to avoid + * starting entities if the pipeline won't start anyway (those entities + * would then likely fail to stop, making the problem worse). */ - if ((pipe->entities & isp->crashed) & - (1U << isp->isp_prev.subdev.entity.id)) + if (pipe->entities & isp->crashed) return -EIO; spin_lock_irqsave(&pipe->lock, flags); @@ -1014,13 +1011,23 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) else ret = 0; + /* Handle stop failures. An entity that fails to stop can + * usually just be restarted. Flag the stop failure nonetheless + * to trigger an ISP reset the next time the device is released, + * just in case. + * + * The preview engine is a special case. A failure to stop can + * mean a hardware crash. When that happens the preview engine + * won't respond to read/write operations on the L4 bus anymore, + * resulting in a bus fault and a kernel oops next time it gets + * accessed. Mark it as crashed to prevent pipelines including + * it from being started. + */ if (ret) { dev_info(isp->dev, "Unable to stop %s\n", subdev->name); - /* If the entity failed to stopped, assume it has - * crashed. Mark it as such, the ISP will be reset when - * applications will release it. - */ - isp->crashed |= 1U << subdev->entity.id; + isp->stop_failure = true; + if (subdev == &isp->isp_prev.subdev) + isp->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } @@ -1057,6 +1064,23 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, } /* + * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline + * @pipe: ISP pipeline + * + * Cancelling a stream mark all buffers on all video nodes in the pipeline as + * erroneous and makes sure no new buffer can be queued. This function is called + * when a fatal error that prevents any further operation on the pipeline + * occurs. + */ +void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe) +{ + if (pipe->input) + omap3isp_video_cancel_stream(pipe->input); + if (pipe->output) + omap3isp_video_cancel_stream(pipe->output); +} + +/* * isp_pipeline_resume - Resume streaming on a pipeline * @pipe: ISP pipeline * @@ -1208,6 +1232,7 @@ static int isp_reset(struct isp_device *isp) udelay(1); } + isp->stop_failure = false; isp->crashed = 0; return 0; } @@ -1619,7 +1644,7 @@ void omap3isp_put(struct isp_device *isp) /* Reset the ISP if an entity has failed to stop. This is the * only way to recover from such conditions. */ - if (isp->crashed) + if (isp->crashed || isp->stop_failure) isp_reset(isp); isp_disable_clocks(isp); } @@ -2130,28 +2155,13 @@ static int isp_map_mem_resource(struct platform_device *pdev, /* request the mem region for the camera registers */ mem = platform_get_resource(pdev, IORESOURCE_MEM, res); - if (!mem) { - dev_err(isp->dev, "no mem resource?\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(isp->dev, mem->start, resource_size(mem), - pdev->name)) { - dev_err(isp->dev, - "cannot reserve camera register I/O region\n"); - return -ENODEV; - } - isp->mmio_base_phys[res] = mem->start; - isp->mmio_size[res] = resource_size(mem); /* map the region */ - isp->mmio_base[res] = devm_ioremap_nocache(isp->dev, - isp->mmio_base_phys[res], - isp->mmio_size[res]); - if (!isp->mmio_base[res]) { - dev_err(isp->dev, "cannot map camera register I/O region\n"); - return -ENODEV; - } + isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem); + if (IS_ERR(isp->mmio_base[res])) + return PTR_ERR(isp->mmio_base[res]); + + isp->mmio_base_phys[res] = mem->start; return 0; } diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index d1e857e41731..081f5ec5a663 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -152,9 +152,9 @@ struct isp_xclk { * regions. * @mmio_base_phys: Array with physical L4 bus addresses for ISP register * regions. - * @mmio_size: Array with ISP register regions size in bytes. * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. + * @stop_failure: Indicates that an entity failed to stop. * @crashed: Bitmask of crashed entities (indexed by entity ID) * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. @@ -188,11 +188,11 @@ struct isp_device { void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; - resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; /* ISP Obj */ spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ + bool stop_failure; u32 crashed; int has_context; int ref_count; @@ -238,6 +238,7 @@ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, enum isp_pipeline_stream_state state); +void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe); void omap3isp_configure_bridge(struct isp_device *isp, enum ccdc_input_entity input, const struct isp_parallel_platform_data *pdata, diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 907a205da5a5..5db2c88b9ad8 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1516,6 +1516,8 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) if (ccdc_sbl_wait_idle(ccdc, 1000)) { dev_info(isp->dev, "CCDC won't become idle!\n"); + isp->crashed |= 1U << ccdc->subdev.entity.id; + omap3isp_pipeline_cancel_stream(pipe); goto done; } @@ -2484,7 +2486,8 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) v4l2_set_subdevdata(sd, ccdc); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; - pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index e71651429dda..e84fe0543e47 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -1076,7 +1076,8 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) v4l2_set_subdevdata(sd, ccp2); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccp2_media_ops; diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 6db245d84bbb..620560828a48 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -1245,7 +1245,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; me->ops = &csi2_media_ops; ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index cd8831aebdeb..1c776c1186f1 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2283,7 +2283,8 @@ static int preview_init_entities(struct isp_prev_device *prev) v4l2_ctrl_handler_setup(&prev->ctrls); sd->ctrl_handler = &prev->ctrls; - pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &preview_media_ops; diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index e15f01342058..5f0f8fab1d17 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -553,8 +553,10 @@ static void isp_video_buffer_query(struct isp_video_buffer *buf, switch (buf->state) { case ISP_BUF_STATE_ERROR: vbuf->flags |= V4L2_BUF_FLAG_ERROR; + /* Fallthrough */ case ISP_BUF_STATE_DONE: vbuf->flags |= V4L2_BUF_FLAG_DONE; + break; case ISP_BUF_STATE_QUEUED: case ISP_BUF_STATE_ACTIVE: vbuf->flags |= V4L2_BUF_FLAG_QUEUED; diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index d11fb261d530..0d36b8bc9f98 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -1532,6 +1532,20 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } +static int resizer_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct isp_pipeline *pipe = to_isp_pipeline(&sd->entity); + + omap3isp_resizer_max_rate(res, &pipe->max_rate); + + return v4l2_subdev_link_validate_default(sd, link, + source_fmt, sink_fmt); +} + /* * resizer_init_formats - Initialize formats on all pads * @sd: ISP resizer V4L2 subdevice @@ -1570,6 +1584,7 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { .set_fmt = resizer_set_format, .get_selection = resizer_get_selection, .set_selection = resizer_set_selection, + .link_validate = resizer_link_validate, }; /* subdev operations */ @@ -1701,7 +1716,8 @@ static int resizer_init_entities(struct isp_res_device *res) v4l2_set_subdevdata(sd, res); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 61e17f9bd8b9..a75407c3a726 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1067,7 +1067,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_set_subdevdata(subdev, stat); - stat->pad.flags = MEDIA_PAD_FL_SINK; + stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; me->ops = NULL; return media_entity_init(me, 1, &stat->pad, 0); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index f6304bb074f5..856fdf554035 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -278,55 +278,6 @@ static int isp_video_get_graph_data(struct isp_video *video, return 0; } -/* - * Validate a pipeline by checking both ends of all links for format - * discrepancies. - * - * Compute the minimum time per frame value as the maximum of time per frame - * limits reported by every block in the pipeline. - * - * Return 0 if all formats match, or -EPIPE if at least one link is found with - * different formats on its two ends or if the pipeline doesn't start with a - * video source (either a subdev with no input pad, or a non-subdev entity). - */ -static int isp_video_validate_pipeline(struct isp_pipeline *pipe) -{ - struct isp_device *isp = pipe->output->isp; - struct media_pad *pad; - struct v4l2_subdev *subdev; - - subdev = isp_video_remote_subdev(pipe->output, NULL); - if (subdev == NULL) - return -EPIPE; - - while (1) { - /* Retrieve the sink format */ - pad = &subdev->entity.pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - /* Update the maximum frame rate */ - if (subdev == &isp->isp_res.subdev) - omap3isp_resizer_max_rate(&isp->isp_res, - &pipe->max_rate); - - /* Retrieve the source format. Return an error if no source - * entity can be found, and stop checking the pipeline if the - * source entity isn't a subdev. - */ - pad = media_entity_remote_pad(pad); - if (pad == NULL) - return -EPIPE; - - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - subdev = media_entity_to_v4l2_subdev(pad->entity); - } - - return 0; -} - static int __isp_video_get_format(struct isp_video *video, struct v4l2_format *format) { @@ -460,6 +411,15 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf) struct isp_video *video = vfh->video; unsigned long addr; + /* Refuse to prepare the buffer is the video node has registered an + * error. We don't need to take any lock here as the operation is + * inherently racy. The authoritative check will be performed in the + * queue handler, which can't return an error, this check is just a best + * effort to notify userspace as early as possible. + */ + if (unlikely(video->error)) + return -EIO; + addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); if (IS_ERR_VALUE(addr)) return -EIO; @@ -496,6 +456,12 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf) unsigned int empty; unsigned int start; + if (unlikely(video->error)) { + buf->state = ISP_BUF_STATE_ERROR; + wake_up(&buf->wait); + return; + } + empty = list_empty(&video->dmaqueue); list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); @@ -618,6 +584,36 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) } /* + * omap3isp_video_cancel_stream - Cancel stream on a video node + * @video: ISP video object + * + * Cancelling a stream mark all buffers on the video node as erroneous and makes + * sure no new buffer can be queued. + */ +void omap3isp_video_cancel_stream(struct isp_video *video) +{ + struct isp_video_queue *queue = video->queue; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + + while (!list_empty(&video->dmaqueue)) { + struct isp_video_buffer *buf; + + buf = list_first_entry(&video->dmaqueue, + struct isp_video_buffer, irqlist); + list_del(&buf->irqlist); + + buf->state = ISP_BUF_STATE_ERROR; + wake_up(&buf->wait); + } + + video->error = true; + + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +/* * omap3isp_video_resume - Perform resume operation on the buffers * @video: ISP video object * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise @@ -1051,11 +1047,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_check_format; - /* Validate the pipeline and update its state. */ - ret = isp_video_validate_pipeline(pipe); - if (ret < 0) - goto err_check_format; - pipe->error = false; spin_lock_irqsave(&pipe->lock, flags); @@ -1159,6 +1150,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) omap3isp_video_queue_streamoff(&vfh->queue); video->queue = NULL; video->streaming = 0; + video->error = false; if (video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, false); @@ -1332,11 +1324,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name) switch (video->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: direction = "output"; - video->pad.flags = MEDIA_PAD_FL_SINK; + video->pad.flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: direction = "input"; - video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->pad.flags = MEDIA_PAD_FL_SOURCE + | MEDIA_PAD_FL_MUST_CONNECT; video->video.vfl_dir = VFL_DIR_TX; break; diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 1ad470ec2b9d..4e194076cc60 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -178,6 +178,7 @@ struct isp_video { /* Pipeline state */ struct isp_pipeline pipe; struct mutex stream_lock; /* pipeline and stream states */ + bool error; /* Video buffers queue */ struct isp_video_queue *queue; @@ -207,6 +208,7 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev); void omap3isp_video_unregister(struct isp_video *video); struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video); +void omap3isp_video_cancel_stream(struct isp_video *video); void omap3isp_video_resume(struct isp_video *video, int continuous); struct media_pad *omap3isp_video_remote_pad(struct isp_video *video); diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 0b2948376aee..0fcf7d75e841 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -136,10 +136,9 @@ static int g2d_buf_prepare(struct vb2_buffer *vb) static void g2d_buf_queue(struct vb2_buffer *vb) { struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } - static struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, @@ -159,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, 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; + src_vq->lock = &ctx->dev->mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -171,6 +171,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, 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; + dst_vq->lock = &ctx->dev->mutex; return vb2_queue_init(dst_vq); } @@ -253,9 +254,9 @@ static int g2d_open(struct file *file) kfree(ctx); return -ERESTARTSYS; } - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); mutex_unlock(&dev->mutex); kfree(ctx); return ret; @@ -324,7 +325,7 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) struct vb2_queue *vq; struct g2d_frame *frm; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; frm = get_frame(ctx, f->type); @@ -384,7 +385,7 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) ret = vidioc_try_fmt(file, prv, f); if (ret) return ret; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (vb2_is_busy(vq)) { v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type); return -EBUSY; @@ -410,72 +411,6 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) return 0; } -static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait) -{ - struct g2d_ctx *ctx = fh2ctx(file->private_data); - struct g2d_dev *dev = ctx->dev; - unsigned int res; - - mutex_lock(&dev->mutex); - res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - mutex_unlock(&dev->mutex); - return res; -} - -static int g2d_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct g2d_ctx *ctx = fh2ctx(file->private_data); - struct g2d_dev *dev = ctx->dev; - int ret; - - if (mutex_lock_interruptible(&dev->mutex)) - return -ERESTARTSYS; - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&dev->mutex); - return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct g2d_ctx *ctx = priv; - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - static int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cr) { @@ -551,20 +486,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c return 0; } -static void g2d_lock(void *prv) -{ - struct g2d_ctx *ctx = prv; - struct g2d_dev *dev = ctx->dev; - mutex_lock(&dev->mutex); -} - -static void g2d_unlock(void *prv) -{ - struct g2d_ctx *ctx = prv; - struct g2d_dev *dev = ctx->dev; - mutex_unlock(&dev->mutex); -} - static void job_abort(void *prv) { struct g2d_ctx *ctx = prv; @@ -589,8 +510,8 @@ static void device_run(void *prv) dev->curr = ctx; - src = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); clk_enable(dev->gate); g2d_reset(dev); @@ -631,8 +552,8 @@ static irqreturn_t g2d_isr(int irq, void *prv) BUG_ON(ctx == NULL); - src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); BUG_ON(src == NULL); BUG_ON(dst == NULL); @@ -642,7 +563,7 @@ static irqreturn_t g2d_isr(int irq, void *prv) 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); + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); dev->curr = NULL; wake_up(&dev->irq_queue); @@ -653,9 +574,9 @@ static const struct v4l2_file_operations g2d_fops = { .owner = THIS_MODULE, .open = g2d_open, .release = g2d_release, - .poll = g2d_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = g2d_mmap, + .mmap = v4l2_m2m_fop_mmap, }; static const struct v4l2_ioctl_ops g2d_ioctl_ops = { @@ -671,14 +592,13 @@ static const struct v4l2_ioctl_ops g2d_ioctl_ops = { .vidioc_try_fmt_vid_out = vidioc_try_fmt, .vidioc_s_fmt_vid_out = vidioc_s_fmt, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_crop = vidioc_g_crop, .vidioc_s_crop = vidioc_s_crop, @@ -697,8 +617,6 @@ static struct video_device g2d_videodev = { static struct v4l2_m2m_ops g2d_m2m_ops = { .device_run = device_run, .job_abort = job_abort, - .lock = g2d_lock, - .unlock = g2d_unlock, }; static const struct of_device_id exynos_g2d_match[]; diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index 300ca05ba404..b0e52ab7ecdb 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -57,7 +57,6 @@ struct g2d_frame { struct g2d_ctx { struct v4l2_fh fh; struct g2d_dev *dev; - struct v4l2_m2m_ctx *m2m_ctx; struct g2d_frame in; struct g2d_frame out; struct v4l2_ctrl *ctrl_hflip; diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile index d18cb5edd2d5..a1a9169254c3 100644 --- a/drivers/media/platform/s5p-jpeg/Makefile +++ b/drivers/media/platform/s5p-jpeg/Makefile @@ -1,2 +1,2 @@ -s5p-jpeg-objs := jpeg-core.o +s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos4.o jpeg-hw-s5p.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 9b88a4601007..7d68d0b9966a 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1,9 +1,10 @@ /* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Author: Jacek Anaszewski <j.anaszewski@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 @@ -17,6 +18,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -28,70 +30,234 @@ #include <media/videobuf2-dma-contig.h> #include "jpeg-core.h" -#include "jpeg-hw.h" +#include "jpeg-hw-s5p.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-regs.h" -static struct s5p_jpeg_fmt formats_enc[] = { +static struct s5p_jpeg_fmt sjpeg_formats[] = { { .name = "JPEG JFIF", .fourcc = V4L2_PIX_FMT_JPEG, + .flags = SJPEG_FMT_FLAG_ENC_CAPTURE | + SJPEG_FMT_FLAG_DEC_OUTPUT | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_FLAG_EXYNOS4, + }, + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, .colplanes = 1, - .types = MEM2MEM_CAPTURE, + .h_align = 4, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, }, { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .colplanes = 1, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, }, { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, }, -}; -#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc) - -static struct s5p_jpeg_fmt formats_dec[] = { { - .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, + .name = "RGB565", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .colplanes = 1, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "ARGB8888, 32 bpp", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .colplanes = 1, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "YUV 4:4:4 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV24, + .depth = 24, + .colplanes = 2, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "YUV 4:4:4 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV42, + .depth = 24, + .colplanes = 2, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "YUV 4:2:2 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV61, + .depth = 16, + .colplanes = 2, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .name = "YUV 4:2:2 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV16, + .depth = 16, + .colplanes = 2, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, - .colplanes = 3, + .colplanes = 2, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .colplanes = 2, .h_align = 4, .v_align = 4, - .types = MEM2MEM_CAPTURE, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, }, { - .name = "YUV 4:2:2 packed, YCbYCr", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .colplanes = 1, - .h_align = 4, - .v_align = 3, - .types = MEM2MEM_CAPTURE, + .name = "YUV 4:2:0 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 12, + .colplanes = 2, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, }, { - .name = "JPEG JFIF", - .fourcc = V4L2_PIX_FMT_JPEG, + .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .colplanes = 3, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .name = "Gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, }, }; -#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec) +#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats) static const unsigned char qtbl_luminance[4][64] = { - {/* level 1 - high quality */ - 8, 6, 6, 8, 12, 14, 16, 17, - 6, 6, 6, 8, 10, 13, 12, 15, - 6, 6, 7, 8, 13, 14, 18, 24, - 8, 8, 8, 14, 13, 19, 24, 35, - 12, 10, 13, 13, 20, 26, 34, 39, - 14, 13, 14, 19, 26, 34, 39, 39, - 16, 12, 18, 24, 34, 39, 39, 39, - 17, 15, 24, 35, 39, 39, 39, 39 + {/*level 0 - high compression quality */ + 20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68 + }, + {/* level 1 */ + 16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64 }, {/* level 2 */ 12, 8, 8, 12, 17, 21, 24, 23, @@ -103,38 +269,38 @@ static const unsigned char qtbl_luminance[4][64] = { 24, 18, 27, 36, 51, 59, 59, 59, 23, 23, 36, 53, 59, 59, 59, 59 }, - {/* level 3 */ - 16, 11, 11, 16, 23, 27, 31, 30, - 11, 12, 12, 15, 20, 23, 23, 30, - 11, 12, 13, 16, 23, 26, 35, 47, - 16, 15, 16, 23, 26, 37, 47, 64, - 23, 20, 23, 26, 39, 51, 64, 64, - 27, 23, 26, 37, 51, 64, 64, 64, - 31, 23, 35, 47, 64, 64, 64, 64, - 30, 30, 47, 64, 64, 64, 64, 64 - }, - {/*level 4 - low quality */ - 20, 16, 25, 39, 50, 46, 62, 68, - 16, 18, 23, 38, 38, 53, 65, 68, - 25, 23, 31, 38, 53, 65, 68, 68, - 39, 38, 38, 53, 65, 68, 68, 68, - 50, 38, 53, 65, 68, 68, 68, 68, - 46, 53, 65, 68, 68, 68, 68, 68, - 62, 65, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 + {/* level 3 - low compression quality */ + 8, 6, 6, 8, 12, 14, 16, 17, + 6, 6, 6, 8, 10, 13, 12, 15, + 6, 6, 7, 8, 13, 14, 18, 24, + 8, 8, 8, 14, 13, 19, 24, 35, + 12, 10, 13, 13, 20, 26, 34, 39, + 14, 13, 14, 19, 26, 34, 39, 39, + 16, 12, 18, 24, 34, 39, 39, 39, + 17, 15, 24, 35, 39, 39, 39, 39 } }; static const unsigned char qtbl_chrominance[4][64] = { - {/* level 1 - high quality */ - 9, 8, 9, 11, 14, 17, 19, 24, - 8, 10, 9, 11, 14, 13, 17, 22, - 9, 9, 13, 14, 13, 15, 23, 26, - 11, 11, 14, 14, 15, 20, 26, 33, - 14, 14, 13, 15, 20, 24, 33, 39, - 17, 13, 15, 20, 24, 32, 39, 39, - 19, 17, 23, 26, 33, 39, 39, 39, - 24, 22, 26, 33, 39, 39, 39, 39 + {/*level 0 - high compression quality */ + 21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68 + }, + {/* level 1 */ + 17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64 }, {/* level 2 */ 13, 11, 13, 16, 20, 20, 29, 37, @@ -146,25 +312,15 @@ static const unsigned char qtbl_chrominance[4][64] = { 29, 26, 35, 40, 50, 59, 59, 59, 37, 32, 40, 50, 59, 59, 59, 59 }, - {/* level 3 */ - 17, 15, 17, 21, 20, 26, 38, 48, - 15, 19, 18, 17, 20, 26, 35, 43, - 17, 18, 20, 22, 26, 30, 46, 53, - 21, 17, 22, 28, 30, 39, 53, 64, - 20, 20, 26, 30, 39, 48, 64, 64, - 26, 26, 30, 39, 48, 63, 64, 64, - 38, 35, 46, 53, 64, 64, 64, 64, - 48, 43, 53, 64, 64, 64, 64, 64 - }, - {/*level 4 - low quality */ - 21, 25, 32, 38, 54, 68, 68, 68, - 25, 28, 24, 38, 54, 68, 68, 68, - 32, 24, 32, 43, 66, 68, 68, 68, - 38, 38, 43, 53, 68, 68, 68, 68, - 54, 54, 66, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 + {/* level 3 - low compression quality */ + 9, 8, 9, 11, 14, 17, 19, 24, + 8, 10, 9, 11, 14, 13, 17, 22, + 9, 9, 13, 14, 13, 15, 23, 26, + 11, 11, 14, 14, 15, 20, 26, 33, + 14, 14, 13, 15, 20, 24, 33, 39, + 17, 13, 15, 20, 24, 32, 39, 39, + 19, 17, 23, 26, 33, 39, 39, 39, + 24, 22, 26, 33, 39, 39, 39, 39 } }; @@ -202,6 +358,106 @@ static const unsigned char hactblg0[162] = { 0xf9, 0xfa }; +/* + * Fourcc downgrade schema lookup tables for 422 and 420 + * chroma subsampling - fourcc on each position maps on the + * fourcc from the table fourcc_to_dwngrd_schema_id which allows + * to get the most suitable fourcc counterpart for the given + * downgraded subsampling property. + */ +static const u32 subs422_fourcc_dwngrd_schema[] = { + V4L2_PIX_FMT_NV16, + V4L2_PIX_FMT_NV61, +}; + +static const u32 subs420_fourcc_dwngrd_schema[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, +}; + +/* + * Lookup table for translation of a fourcc to the position + * of its downgraded counterpart in the *fourcc_dwngrd_schema + * tables. + */ +static const u32 fourcc_to_dwngrd_schema_id[] = { + V4L2_PIX_FMT_NV24, + V4L2_PIX_FMT_NV42, + V4L2_PIX_FMT_NV16, + V4L2_PIX_FMT_NV61, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_YVYU, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_GREY, +}; + +static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) { + if (fourcc_to_dwngrd_schema_id[i] == fourcc) + return i; + } + + return -EINVAL; +} + +static int s5p_jpeg_adjust_fourcc_to_subsampling( + enum v4l2_jpeg_chroma_subsampling subs, + u32 in_fourcc, + u32 *out_fourcc, + struct s5p_jpeg_ctx *ctx) +{ + int dwngrd_sch_id; + + if (ctx->subsampling != V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { + dwngrd_sch_id = + s5p_jpeg_get_dwngrd_sch_id_by_fourcc(in_fourcc); + if (dwngrd_sch_id < 0) + return -EINVAL; + } + + switch (ctx->subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: + *out_fourcc = V4L2_PIX_FMT_GREY; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + if (dwngrd_sch_id > + ARRAY_SIZE(subs420_fourcc_dwngrd_schema) - 1) + return -EINVAL; + *out_fourcc = subs420_fourcc_dwngrd_schema[dwngrd_sch_id]; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + if (dwngrd_sch_id > + ARRAY_SIZE(subs422_fourcc_dwngrd_schema) - 1) + return -EINVAL; + *out_fourcc = subs422_fourcc_dwngrd_schema[dwngrd_sch_id]; + break; + default: + *out_fourcc = V4L2_PIX_FMT_GREY; + break; + } + + return 0; +} + +static int exynos4x12_decoded_subsampling[] = { + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + V4L2_JPEG_CHROMA_SUBSAMPLING_444, + V4L2_JPEG_CHROMA_SUBSAMPLING_422, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, +}; + static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) { return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); @@ -212,8 +468,24 @@ static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) return container_of(fh, struct s5p_jpeg_ctx, fh); } -static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, - unsigned long tab, int len) +static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) +{ + WARN_ON(ctx->subsampling > 3); + + if (ctx->jpeg->variant->version == SJPEG_S5P) { + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + return ctx->subsampling; + } else { + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_420; + return exynos4x12_decoded_subsampling[ctx->subsampling]; + } +} + +static inline void s5p_jpeg_set_qtbl(void __iomem *regs, + const unsigned char *qtbl, + unsigned long tab, int len) { int i; @@ -221,22 +493,25 @@ static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, writel((unsigned int)qtbl[i], regs + tab + (i * 0x04)); } -static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality) { /* this driver fills quantisation table 0 with data for luma */ - jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0), - ARRAY_SIZE(qtbl_luminance[quality])); + s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality], + S5P_JPG_QTBL_CONTENT(0), + ARRAY_SIZE(qtbl_luminance[quality])); } -static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality) { /* this driver fills quantisation table 1 with data for chroma */ - jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1), - ARRAY_SIZE(qtbl_chrominance[quality])); + s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality], + S5P_JPG_QTBL_CONTENT(1), + ARRAY_SIZE(qtbl_chrominance[quality])); } -static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl, - unsigned long tab, int len) +static inline void s5p_jpeg_set_htbl(void __iomem *regs, + const unsigned char *htbl, + unsigned long tab, int len) { int i; @@ -244,28 +519,84 @@ static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl, writel((unsigned int)htbl[i], regs + tab + (i * 0x04)); } -static inline void jpeg_set_hdctbl(void __iomem *regs) +static inline void s5p_jpeg_set_hdctbl(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0)); + s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), + ARRAY_SIZE(hdctbl0)); } -static inline void jpeg_set_hdctblg(void __iomem *regs) +static inline void s5p_jpeg_set_hdctblg(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0)); + s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), + ARRAY_SIZE(hdctblg0)); } -static inline void jpeg_set_hactbl(void __iomem *regs) +static inline void s5p_jpeg_set_hactbl(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0)); + s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), + ARRAY_SIZE(hactbl0)); } -static inline void jpeg_set_hactblg(void __iomem *regs) +static inline void s5p_jpeg_set_hactblg(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0)); + s5p_jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), + ARRAY_SIZE(hactblg0)); +} + +static inline void exynos4_jpeg_set_tbl(void __iomem *regs, + const unsigned char *tbl, + unsigned long tab, int len) +{ + int i; + unsigned int dword; + + for (i = 0; i < len; i += 4) { + dword = tbl[i] | + (tbl[i + 1] << 8) | + (tbl[i + 2] << 16) | + (tbl[i + 3] << 24); + writel(dword, regs + tab + i); + } +} + +static inline void exynos4_jpeg_set_qtbl_lum(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 0 with data for luma */ + exynos4_jpeg_set_tbl(regs, qtbl_luminance[quality], + EXYNOS4_QTBL_CONTENT(0), + ARRAY_SIZE(qtbl_luminance[quality])); +} + +static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 1 with data for chroma */ + exynos4_jpeg_set_tbl(regs, qtbl_chrominance[quality], + EXYNOS4_QTBL_CONTENT(1), + ARRAY_SIZE(qtbl_chrominance[quality])); +} + +void exynos4_jpeg_set_huff_tbl(void __iomem *base) +{ + exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL, + ARRAY_SIZE(hdctbl0)); + exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCCL, + ARRAY_SIZE(hdctbl0)); + exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCLV, + ARRAY_SIZE(hdctblg0)); + exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCCV, + ARRAY_SIZE(hdctblg0)); + exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACLL, + ARRAY_SIZE(hactbl0)); + exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACCL, + ARRAY_SIZE(hactbl0)); + exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACLV, + ARRAY_SIZE(hactblg0)); + exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACCV, + ARRAY_SIZE(hactblg0)); } /* @@ -276,8 +607,8 @@ static inline void jpeg_set_hactblg(void __iomem *regs) static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, - __u32 pixelformat); +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, + __u32 pixelformat, unsigned int fmt_type); static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx); static int s5p_jpeg_open(struct file *file) @@ -285,7 +616,7 @@ static int s5p_jpeg_open(struct file *file) struct s5p_jpeg *jpeg = video_drvdata(file); struct video_device *vfd = video_devdata(file); struct s5p_jpeg_ctx *ctx; - struct s5p_jpeg_fmt *out_fmt; + struct s5p_jpeg_fmt *out_fmt, *cap_fmt; int ret = 0; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -306,24 +637,31 @@ static int s5p_jpeg_open(struct file *file) ctx->jpeg = jpeg; if (vfd == jpeg->vfd_encoder) { ctx->mode = S5P_JPEG_ENCODE; - out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565); + out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_RGB565, + FMT_TYPE_OUTPUT); + cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, + FMT_TYPE_CAPTURE); } else { ctx->mode = S5P_JPEG_DECODE; - out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG); + out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, + FMT_TYPE_OUTPUT); + cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV, + FMT_TYPE_CAPTURE); } - ret = s5p_jpeg_controls_create(ctx); - if (ret < 0) - goto error; - - ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); goto error; } ctx->out_q.fmt = out_fmt; - ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV); + ctx->cap_q.fmt = cap_fmt; + + ret = s5p_jpeg_controls_create(ctx); + if (ret < 0) + goto error; + mutex_unlock(&jpeg->lock); return 0; @@ -342,49 +680,23 @@ static int s5p_jpeg_release(struct file *file) struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); mutex_lock(&jpeg->lock); - v4l2_m2m_ctx_release(ctx->m2m_ctx); - mutex_unlock(&jpeg->lock); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); kfree(ctx); - - return 0; -} - -static unsigned int s5p_jpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - unsigned int res; - - mutex_lock(&jpeg->lock); - res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); mutex_unlock(&jpeg->lock); - return res; -} - -static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - int ret; - if (mutex_lock_interruptible(&jpeg->lock)) - return -ERESTARTSYS; - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&jpeg->lock); - return ret; + return 0; } static const struct v4l2_file_operations s5p_jpeg_fops = { .owner = THIS_MODULE, .open = s5p_jpeg_open, .release = s5p_jpeg_release, - .poll = s5p_jpeg_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = s5p_jpeg_mmap, + .mmap = v4l2_m2m_fop_mmap, }; /* @@ -427,10 +739,11 @@ static void skip(struct s5p_jpeg_buffer *buf, long len) } static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, - unsigned long buffer, unsigned long size) + unsigned long buffer, unsigned long size, + struct s5p_jpeg_ctx *ctx) { int c, components, notfound; - unsigned int height, width, word; + unsigned int height, width, word, subsampling = 0; long length; struct s5p_jpeg_buffer jpeg_buffer; @@ -469,7 +782,15 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, break; notfound = 0; - skip(&jpeg_buffer, components * 3); + if (components == 1) { + subsampling = 0x33; + } else { + skip(&jpeg_buffer, 1); + subsampling = get_byte(&jpeg_buffer); + skip(&jpeg_buffer, 1); + } + + skip(&jpeg_buffer, components * 2); break; /* skip payload-less markers */ @@ -491,6 +812,24 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, result->w = width; result->h = height; result->size = components; + + switch (subsampling) { + case 0x11: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444; + break; + case 0x21: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422; + break; + case 0x22: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420; + break; + case 0x33: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + break; + default: + return false; + } + return !notfound; } @@ -521,13 +860,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, return 0; } -static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, +static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, struct v4l2_fmtdesc *f, u32 type) { int i, num = 0; for (i = 0; i < n; ++i) { - if (formats[i].types & type) { + if (sjpeg_formats[i].flags & type) { /* index-th format of type type found ? */ if (num == f->index) break; @@ -541,8 +880,8 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, if (i >= n) return -EINVAL; - strlcpy(f->description, formats[i].name, sizeof(f->description)); - f->pixelformat = formats[i].fourcc; + strlcpy(f->description, sjpeg_formats[i].name, sizeof(f->description)); + f->pixelformat = sjpeg_formats[i].fourcc; return 0; } @@ -553,10 +892,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, - MEM2MEM_CAPTURE); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_CAPTURE); - return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_CAPTURE); } static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, @@ -565,10 +905,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, - MEM2MEM_OUTPUT); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_OUTPUT); - return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_OUTPUT); } static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, @@ -589,7 +930,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct v4l2_pix_format *pix = &f->fmt.pix; struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); - vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -615,29 +956,35 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) return 0; } -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, - u32 pixelformat) +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, + u32 pixelformat, unsigned int fmt_type) { - unsigned int k; - struct s5p_jpeg_fmt *formats; - int n; + unsigned int k, fmt_flag, ver_flag; - if (mode == S5P_JPEG_ENCODE) { - formats = formats_enc; - n = NUM_FORMATS_ENC; - } else { - formats = formats_dec; - n = NUM_FORMATS_DEC; - } + if (ctx->mode == S5P_JPEG_ENCODE) + fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? + SJPEG_FMT_FLAG_ENC_OUTPUT : + SJPEG_FMT_FLAG_ENC_CAPTURE; + else + fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? + SJPEG_FMT_FLAG_DEC_OUTPUT : + SJPEG_FMT_FLAG_DEC_CAPTURE; + + if (ctx->jpeg->variant->version == SJPEG_S5P) + ver_flag = SJPEG_FMT_FLAG_S5P; + else + ver_flag = SJPEG_FMT_FLAG_EXYNOS4; - for (k = 0; k < n; k++) { - struct s5p_jpeg_fmt *fmt = &formats[k]; - if (fmt->fourcc == pixelformat) + for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { + struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; + if (fmt->fourcc == pixelformat && + fmt->flags & fmt_flag && + fmt->flags & ver_flag) { return fmt; + } } return NULL; - } static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, @@ -673,7 +1020,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, /* V4L2 specification suggests the driver corrects the format struct * if any of the dimensions is unsupported */ - if (q_type == MEM2MEM_OUTPUT) + if (q_type == FMT_TYPE_OUTPUT) jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, 0, &pix->height, S5P_JPEG_MIN_HEIGHT, @@ -695,7 +1042,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, bpl = pix->width; /* planar */ if (fmt->colplanes == 1 && /* packed */ - (bpl << 3) * fmt->depth < pix->width) + (bpl << 3) / fmt->depth < pix->width) bpl = (pix->width * fmt->depth) >> 3; pix->bytesperline = bpl; @@ -709,17 +1056,41 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format *pix = &f->fmt.pix; struct s5p_jpeg_fmt *fmt; + int ret; - fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); - if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { + fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, + FMT_TYPE_CAPTURE); + if (!fmt) { v4l2_err(&ctx->jpeg->v4l2_dev, "Fourcc format (0x%08x) invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } - return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE); + /* + * The exynos4x12 device requires resulting YUV image + * subsampling not to be lower than the input jpeg subsampling. + * If this requirement is not met then downgrade the requested + * capture format to the one with subsampling equal to the input jpeg. + */ + if ((ctx->jpeg->variant->version != SJPEG_S5P) && + (ctx->mode == S5P_JPEG_DECODE) && + (fmt->flags & SJPEG_FMT_NON_RGB) && + (fmt->subsampling < ctx->subsampling)) { + ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling, + fmt->fourcc, + &pix->pixelformat, + ctx); + if (ret < 0) + pix->pixelformat = V4L2_PIX_FMT_GREY; + + fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, + FMT_TYPE_CAPTURE); + } + + return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE); } static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, @@ -728,15 +1099,16 @@ static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); - if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { + fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, + FMT_TYPE_OUTPUT); + if (!fmt) { v4l2_err(&ctx->jpeg->v4l2_dev, "Fourcc format (0x%08x) invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } - return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT); + return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT); } static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) @@ -744,8 +1116,10 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) struct vb2_queue *vq; struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_ctrl *ctrl_subs; + unsigned int f_type; - vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -757,7 +1131,10 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) return -EBUSY; } - q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat); + f_type = V4L2_TYPE_IS_OUTPUT(f->type) ? + FMT_TYPE_OUTPUT : FMT_TYPE_CAPTURE; + + q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type); q_data->w = pix->width; q_data->h = pix->height; if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) @@ -765,6 +1142,13 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) else q_data->size = pix->sizeimage; + if (f_type == FMT_TYPE_OUTPUT) { + ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING); + if (ctrl_subs) + v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling); + } + return 0; } @@ -792,60 +1176,14 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } -static int s5p_jpeg_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int s5p_jpeg_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int s5p_jpeg_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - static int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + ctx->jpeg->variant->version != SJPEG_S5P) return -EINVAL; /* For JPEG blob active == default == bounds */ @@ -884,12 +1222,7 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: spin_lock_irqsave(&jpeg->slock, flags); - - WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY); - if (ctx->subsampling > 2) - ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; - else - ctrl->val = ctx->subsampling; + ctrl->val = s5p_jpeg_to_user_subsampling(ctx); spin_unlock_irqrestore(&jpeg->slock, flags); break; } @@ -897,6 +1230,40 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return 0; } +static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) { + if (ctx->jpeg->variant->version == SJPEG_S5P) + goto error_free; + /* + * The exynos4x12 device requires input raw image fourcc + * to be V4L2_PIX_FMT_GREY if gray jpeg format + * is to be set. + */ + if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY && + ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { + ret = -EINVAL; + goto error_free; + } + /* + * The exynos4x12 device requires resulting jpeg subsampling + * not to be lower than the input raw image subsampling. + */ + if (ctx->out_q.fmt->subsampling > ctrl->val) + ctrl->val = ctx->out_q.fmt->subsampling; + } + +error_free: + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + return ret; +} + static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); @@ -906,7 +1273,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val; + ctx->compr_quality = ctrl->val; break; case V4L2_CID_JPEG_RESTART_INTERVAL: ctx->restart_interval = ctrl->val; @@ -922,6 +1289,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = { .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl, + .try_ctrl = s5p_jpeg_try_ctrl, .s_ctrl = s5p_jpeg_s_ctrl, }; @@ -929,18 +1297,20 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) { unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */ struct v4l2_ctrl *ctrl; + int ret; v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); if (ctx->mode == S5P_JPEG_ENCODE) { v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, V4L2_CID_JPEG_COMPRESSION_QUALITY, - 0, 3, 1, 3); + 0, 3, 1, S5P_JPEG_COMPR_QUAL_WORST); v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, V4L2_CID_JPEG_RESTART_INTERVAL, 0, 3, 0xffff, 0); - mask = ~0x06; /* 422, 420 */ + if (ctx->jpeg->variant->version == SJPEG_S5P) + mask = ~0x06; /* 422, 420 */ } ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, @@ -948,13 +1318,24 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, V4L2_JPEG_CHROMA_SUBSAMPLING_422); - if (ctx->ctrl_handler.error) - return ctx->ctrl_handler.error; + if (ctx->ctrl_handler.error) { + ret = ctx->ctrl_handler.error; + goto error_free; + } if (ctx->mode == S5P_JPEG_DECODE) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; - return 0; + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (ret < 0) + goto error_free; + + return ret; + +error_free: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ret; } static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { @@ -972,14 +1353,13 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap, .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out, - .vidioc_reqbufs = s5p_jpeg_reqbufs, - .vidioc_querybuf = s5p_jpeg_querybuf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_qbuf = s5p_jpeg_qbuf, - .vidioc_dqbuf = s5p_jpeg_dqbuf, - - .vidioc_streamon = s5p_jpeg_streamon, - .vidioc_streamoff = s5p_jpeg_streamoff, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = s5p_jpeg_g_selection, }; @@ -995,74 +1375,181 @@ static void s5p_jpeg_device_run(void *priv) struct s5p_jpeg_ctx *ctx = priv; struct s5p_jpeg *jpeg = ctx->jpeg; struct vb2_buffer *src_buf, *dst_buf; - unsigned long src_addr, dst_addr; + unsigned long src_addr, dst_addr, flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - jpeg_reset(jpeg->regs); - jpeg_poweron(jpeg->regs); - jpeg_proc_mode(jpeg->regs, ctx->mode); + s5p_jpeg_reset(jpeg->regs); + s5p_jpeg_poweron(jpeg->regs); + s5p_jpeg_proc_mode(jpeg->regs, ctx->mode); if (ctx->mode == S5P_JPEG_ENCODE) { if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565) - jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565); + s5p_jpeg_input_raw_mode(jpeg->regs, + S5P_JPEG_RAW_IN_565); else - jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422); - jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); - jpeg_dri(jpeg->regs, ctx->restart_interval); - jpeg_x(jpeg->regs, ctx->out_q.w); - jpeg_y(jpeg->regs, ctx->out_q.h); - jpeg_imgadr(jpeg->regs, src_addr); - jpeg_jpgadr(jpeg->regs, dst_addr); + s5p_jpeg_input_raw_mode(jpeg->regs, + S5P_JPEG_RAW_IN_422); + s5p_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + s5p_jpeg_dri(jpeg->regs, ctx->restart_interval); + s5p_jpeg_x(jpeg->regs, ctx->out_q.w); + s5p_jpeg_y(jpeg->regs, ctx->out_q.h); + s5p_jpeg_imgadr(jpeg->regs, src_addr); + s5p_jpeg_jpgadr(jpeg->regs, dst_addr); /* ultimately comes from sizeimage from userspace */ - jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); + s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); /* JPEG RGB to YCbCr conversion matrix */ - jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); - jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); - jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); - jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); - jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); - jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); - jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); - jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); - jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); + s5p_jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); + s5p_jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); + s5p_jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); + s5p_jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); + s5p_jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); + s5p_jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); + s5p_jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); + s5p_jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); + s5p_jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); /* * JPEG IP allows storing 4 quantization tables * We fill table 0 for luma and table 1 for chroma */ - jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); - jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); /* use table 0 for Y */ - jpeg_qtbl(jpeg->regs, 1, 0); + s5p_jpeg_qtbl(jpeg->regs, 1, 0); /* use table 1 for Cb and Cr*/ - jpeg_qtbl(jpeg->regs, 2, 1); - jpeg_qtbl(jpeg->regs, 3, 1); + s5p_jpeg_qtbl(jpeg->regs, 2, 1); + s5p_jpeg_qtbl(jpeg->regs, 3, 1); /* Y, Cb, Cr use Huffman table 0 */ - jpeg_htbl_ac(jpeg->regs, 1); - jpeg_htbl_dc(jpeg->regs, 1); - jpeg_htbl_ac(jpeg->regs, 2); - jpeg_htbl_dc(jpeg->regs, 2); - jpeg_htbl_ac(jpeg->regs, 3); - jpeg_htbl_dc(jpeg->regs, 3); + s5p_jpeg_htbl_ac(jpeg->regs, 1); + s5p_jpeg_htbl_dc(jpeg->regs, 1); + s5p_jpeg_htbl_ac(jpeg->regs, 2); + s5p_jpeg_htbl_dc(jpeg->regs, 2); + s5p_jpeg_htbl_ac(jpeg->regs, 3); + s5p_jpeg_htbl_dc(jpeg->regs, 3); } else { /* S5P_JPEG_DECODE */ - jpeg_rst_int_enable(jpeg->regs, true); - jpeg_data_num_int_enable(jpeg->regs, true); - jpeg_final_mcu_num_int_enable(jpeg->regs, true); + s5p_jpeg_rst_int_enable(jpeg->regs, true); + s5p_jpeg_data_num_int_enable(jpeg->regs, true); + s5p_jpeg_final_mcu_num_int_enable(jpeg->regs, true); if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) - jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); else - jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); - jpeg_jpgadr(jpeg->regs, src_addr); - jpeg_imgadr(jpeg->regs, dst_addr); + s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); + s5p_jpeg_jpgadr(jpeg->regs, src_addr); + s5p_jpeg_imgadr(jpeg->regs, dst_addr); } - jpeg_start(jpeg->regs); + s5p_jpeg_start(jpeg->regs); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +} + +static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct s5p_jpeg_fmt *fmt; + struct vb2_buffer *vb; + struct s5p_jpeg_addr jpeg_addr; + u32 pix_size, padding_bytes = 0; + + pix_size = ctx->cap_q.w * ctx->cap_q.h; + + if (ctx->mode == S5P_JPEG_ENCODE) { + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + fmt = ctx->out_q.fmt; + if (ctx->out_q.w % 2 && fmt->h_align > 0) + padding_bytes = ctx->out_q.h; + } else { + fmt = ctx->cap_q.fmt; + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + } + + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (fmt->colplanes == 2) { + jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; + } else if (fmt->colplanes == 3) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + if (fmt->fourcc == V4L2_PIX_FMT_YUV420) + jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; + else + jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; + } + + exynos4_jpeg_set_frame_buf_address(jpeg->regs, &jpeg_addr); +} + +static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb; + unsigned int jpeg_addr = 0; + + if (ctx->mode == S5P_JPEG_ENCODE) + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr); +} + +static void exynos4_jpeg_device_run(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned int bitstream_size; + unsigned long flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + if (ctx->mode == S5P_JPEG_ENCODE) { + exynos4_jpeg_sw_reset(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs); + exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); + + exynos4_jpeg_set_huff_tbl(jpeg->regs); + + /* + * JPEG IP allows storing 4 quantization tables + * We fill table 0 for luma and table 1 for chroma + */ + exynos4_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + exynos4_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + + exynos4_jpeg_set_encode_tbl_select(jpeg->regs, + ctx->compr_quality); + exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, + ctx->cap_q.h); + + exynos4_jpeg_set_enc_out_fmt(jpeg->regs, ctx->subsampling); + exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->out_q.fmt->fourcc); + exynos4_jpeg_set_img_addr(ctx); + exynos4_jpeg_set_jpeg_addr(ctx); + exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } else { + exynos4_jpeg_sw_reset(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs); + exynos4_jpeg_set_img_addr(ctx); + exynos4_jpeg_set_jpeg_addr(ctx); + exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->cap_q.fmt->fourcc); + + bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); + + exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size); + } + + exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); } static int s5p_jpeg_job_ready(void *priv) @@ -1082,6 +1569,12 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { .device_run = s5p_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, .job_abort = s5p_jpeg_job_abort, +} +; +static struct v4l2_m2m_ops exynos_jpeg_m2m_ops = { + .device_run = exynos4_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, + .job_abort = s5p_jpeg_job_abort, }; /* @@ -1149,7 +1642,7 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp, (unsigned long)vb2_plane_vaddr(vb, 0), min((unsigned long)ctx->out_q.size, - vb2_get_plane_payload(vb, 0))); + vb2_get_plane_payload(vb, 0)), ctx); if (!ctx->hdr_parsed) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); return; @@ -1162,30 +1655,9 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) q_data = &ctx->cap_q; q_data->w = tmp.w; q_data->h = tmp.h; - - jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH, - S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align, - &q_data->h, S5P_JPEG_MIN_HEIGHT, - S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align - ); - q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; } - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void s5p_jpeg_wait_prepare(struct vb2_queue *vq) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->jpeg->lock); -} - -static void s5p_jpeg_wait_finish(struct vb2_queue *vq) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_lock(&ctx->jpeg->lock); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) @@ -1211,8 +1683,8 @@ static struct vb2_ops s5p_jpeg_qops = { .queue_setup = s5p_jpeg_queue_setup, .buf_prepare = s5p_jpeg_buf_prepare, .buf_queue = s5p_jpeg_buf_queue, - .wait_prepare = s5p_jpeg_wait_prepare, - .wait_finish = s5p_jpeg_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = s5p_jpeg_start_streaming, .stop_streaming = s5p_jpeg_stop_streaming, }; @@ -1230,6 +1702,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &s5p_jpeg_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->jpeg->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -1242,6 +1715,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &s5p_jpeg_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->jpeg->lock; return vb2_queue_init(dst_vq); } @@ -1267,26 +1741,27 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); if (curr_ctx->mode == S5P_JPEG_ENCODE) - enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs); - timer_elapsed = jpeg_timer_stat(jpeg->regs); - op_completed = jpeg_result_stat_ok(jpeg->regs); + enc_jpeg_too_large = s5p_jpeg_enc_stream_stat(jpeg->regs); + timer_elapsed = s5p_jpeg_timer_stat(jpeg->regs); + op_completed = s5p_jpeg_result_stat_ok(jpeg->regs); if (curr_ctx->mode == S5P_JPEG_DECODE) - op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs); + op_completed = op_completed && + s5p_jpeg_stream_stat_ok(jpeg->regs); if (enc_jpeg_too_large) { state = VB2_BUF_STATE_ERROR; - jpeg_clear_enc_stream_stat(jpeg->regs); + s5p_jpeg_clear_enc_stream_stat(jpeg->regs); } else if (timer_elapsed) { state = VB2_BUF_STATE_ERROR; - jpeg_clear_timer_stat(jpeg->regs); + s5p_jpeg_clear_timer_stat(jpeg->regs); } else if (!op_completed) { state = VB2_BUF_STATE_ERROR; } else { - payload_size = jpeg_compressed_size(jpeg->regs); + payload_size = s5p_jpeg_compressed_size(jpeg->regs); } dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; @@ -1296,16 +1771,79 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(dst_buf, 0, payload_size); v4l2_m2m_buf_done(dst_buf, state); - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx); + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs); + curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs); spin_unlock(&jpeg->slock); - jpeg_clear_int(jpeg->regs); + s5p_jpeg_clear_int(jpeg->regs); return IRQ_HANDLED; } +static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) +{ + unsigned int int_status; + struct vb2_buffer *src_vb, *dst_vb; + struct s5p_jpeg *jpeg = priv; + struct s5p_jpeg_ctx *curr_ctx; + unsigned long payload_size = 0; + + spin_lock(&jpeg->slock); + + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + int_status = exynos4_jpeg_get_int_status(jpeg->regs); + + if (int_status) { + switch (int_status & 0x1f) { + case 0x1: + jpeg->irq_ret = ERR_PROT; + break; + case 0x2: + jpeg->irq_ret = OK_ENC_OR_DEC; + break; + case 0x4: + jpeg->irq_ret = ERR_DEC_INVALID_FORMAT; + break; + case 0x8: + jpeg->irq_ret = ERR_MULTI_SCAN; + break; + case 0x10: + jpeg->irq_ret = ERR_FRAME; + break; + default: + jpeg->irq_ret = ERR_UNKNOWN; + break; + } + } else { + jpeg->irq_ret = ERR_UNKNOWN; + } + + if (jpeg->irq_ret == OK_ENC_OR_DEC) { + if (curr_ctx->mode == S5P_JPEG_ENCODE) { + payload_size = exynos4_jpeg_get_stream_size(jpeg->regs); + vb2_set_plane_payload(dst_vb, 0, payload_size); + } + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + } else { + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); + } + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); + + spin_unlock(&jpeg->slock); + return IRQ_HANDLED; +} + +static void *jpeg_get_drv_data(struct platform_device *pdev); + /* * ============================================================================ * Driver basic infrastructure @@ -1316,13 +1854,19 @@ static int s5p_jpeg_probe(struct platform_device *pdev) { struct s5p_jpeg *jpeg; struct resource *res; + struct v4l2_m2m_ops *samsung_jpeg_m2m_ops; int ret; + if (!pdev->dev.of_node) + return -ENODEV; + /* JPEG IP abstraction struct */ jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); if (!jpeg) return -ENOMEM; + jpeg->variant = jpeg_get_drv_data(pdev); + mutex_init(&jpeg->lock); spin_lock_init(&jpeg->slock); jpeg->dev = &pdev->dev; @@ -1341,8 +1885,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return ret; } - ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0, - dev_name(&pdev->dev), jpeg); + ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq, + 0, dev_name(&pdev->dev), jpeg); if (ret) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq); return ret; @@ -1356,7 +1900,6 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return ret; } dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); - clk_prepare_enable(jpeg->clk); /* v4l2 device */ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); @@ -1365,8 +1908,13 @@ static int s5p_jpeg_probe(struct platform_device *pdev) goto clk_get_rollback; } + if (jpeg->variant->version == SJPEG_S5P) + samsung_jpeg_m2m_ops = &s5p_jpeg_m2m_ops; + else + samsung_jpeg_m2m_ops = &exynos_jpeg_m2m_ops; + /* mem2mem device */ - jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops); + jpeg->m2m_dev = v4l2_m2m_init(samsung_jpeg_m2m_ops); if (IS_ERR(jpeg->m2m_dev)) { v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(jpeg->m2m_dev); @@ -1387,8 +1935,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = -ENOMEM; goto vb2_allocator_rollback; } - strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME, - sizeof(jpeg->vfd_encoder->name)); + snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name), + "%s-enc", S5P_JPEG_M2M_NAME); jpeg->vfd_encoder->fops = &s5p_jpeg_fops; jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops; jpeg->vfd_encoder->minor = -1; @@ -1415,8 +1963,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = -ENOMEM; goto enc_vdev_register_rollback; } - strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME, - sizeof(jpeg->vfd_decoder->name)); + snprintf(jpeg->vfd_decoder->name, sizeof(jpeg->vfd_decoder->name), + "%s-dec", S5P_JPEG_M2M_NAME); jpeg->vfd_decoder->fops = &s5p_jpeg_fops; jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops; jpeg->vfd_decoder->minor = -1; @@ -1464,7 +2012,6 @@ device_register_rollback: v4l2_device_unregister(&jpeg->v4l2_dev); clk_get_rollback: - clk_disable_unprepare(jpeg->clk); clk_put(jpeg->clk); return ret; @@ -1484,7 +2031,9 @@ static int s5p_jpeg_remove(struct platform_device *pdev) v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); - clk_disable_unprepare(jpeg->clk); + if (!pm_runtime_status_suspended(&pdev->dev)) + clk_disable_unprepare(jpeg->clk); + clk_put(jpeg->clk); return 0; @@ -1492,41 +2041,119 @@ static int s5p_jpeg_remove(struct platform_device *pdev) static int s5p_jpeg_runtime_suspend(struct device *dev) { + struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + + clk_disable_unprepare(jpeg->clk); + return 0; } static int s5p_jpeg_runtime_resume(struct device *dev) { struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + unsigned long flags; + int ret; + + ret = clk_prepare_enable(jpeg->clk); + if (ret < 0) + return ret; + + spin_lock_irqsave(&jpeg->slock, flags); + /* * JPEG IP allows storing two Huffman tables for each component - * We fill table 0 for each component + * We fill table 0 for each component and do this here only + * for S5PC210 device as Exynos4x12 requires programming its + * Huffman tables each time the encoding process is initialized. */ - jpeg_set_hdctbl(jpeg->regs); - jpeg_set_hdctblg(jpeg->regs); - jpeg_set_hactbl(jpeg->regs); - jpeg_set_hactblg(jpeg->regs); + if (jpeg->variant->version == SJPEG_S5P) { + s5p_jpeg_set_hdctbl(jpeg->regs); + s5p_jpeg_set_hdctblg(jpeg->regs); + s5p_jpeg_set_hactbl(jpeg->regs); + s5p_jpeg_set_hactblg(jpeg->regs); + } + + spin_unlock_irqrestore(&jpeg->slock, flags); + return 0; } +static int s5p_jpeg_suspend(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return s5p_jpeg_runtime_suspend(dev); +} + +static int s5p_jpeg_resume(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return s5p_jpeg_runtime_resume(dev); +} + static const struct dev_pm_ops s5p_jpeg_pm_ops = { - .runtime_suspend = s5p_jpeg_runtime_suspend, - .runtime_resume = s5p_jpeg_runtime_resume, + SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume) + SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static struct s5p_jpeg_variant s5p_jpeg_drvdata = { + .version = SJPEG_S5P, + .jpeg_irq = s5p_jpeg_irq, +}; + +static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { + .version = SJPEG_EXYNOS4, + .jpeg_irq = exynos4_jpeg_irq, +}; + +static const struct of_device_id samsung_jpeg_match[] = { + { + .compatible = "samsung,s5pv210-jpeg", + .data = &s5p_jpeg_drvdata, + }, { + .compatible = "samsung,exynos4210-jpeg", + .data = &s5p_jpeg_drvdata, + }, { + .compatible = "samsung,exynos4212-jpeg", + .data = &exynos4_jpeg_drvdata, + }, + {}, }; +MODULE_DEVICE_TABLE(of, samsung_jpeg_match); + +static void *jpeg_get_drv_data(struct platform_device *pdev) +{ + struct s5p_jpeg_variant *driver_data = NULL; + const struct of_device_id *match; + + match = of_match_node(of_match_ptr(samsung_jpeg_match), + pdev->dev.of_node); + if (match) + driver_data = (struct s5p_jpeg_variant *)match->data; + + return driver_data; +} +#endif + static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, .remove = s5p_jpeg_remove, .driver = { - .owner = THIS_MODULE, - .name = S5P_JPEG_M2M_NAME, - .pm = &s5p_jpeg_pm_ops, + .of_match_table = of_match_ptr(samsung_jpeg_match), + .owner = THIS_MODULE, + .name = S5P_JPEG_M2M_NAME, + .pm = &s5p_jpeg_pm_ops, }, }; module_platform_driver(s5p_jpeg_driver); MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>"); +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); MODULE_DESCRIPTION("Samsung JPEG codec driver"); MODULE_LICENSE("GPL"); - diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 8a4013e3aee7..f482dbf55d5f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -13,6 +13,7 @@ #ifndef JPEG_CORE_H_ #define JPEG_CORE_H_ +#include <linux/interrupt.h> #include <media/v4l2-device.h> #include <media/v4l2-fh.h> #include <media/v4l2-ctrls.h> @@ -43,8 +44,45 @@ #define DHP 0xde /* Flags that indicate a format can be used for capture/output */ -#define MEM2MEM_CAPTURE (1 << 0) -#define MEM2MEM_OUTPUT (1 << 1) +#define SJPEG_FMT_FLAG_ENC_CAPTURE (1 << 0) +#define SJPEG_FMT_FLAG_ENC_OUTPUT (1 << 1) +#define SJPEG_FMT_FLAG_DEC_CAPTURE (1 << 2) +#define SJPEG_FMT_FLAG_DEC_OUTPUT (1 << 3) +#define SJPEG_FMT_FLAG_S5P (1 << 4) +#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 5) +#define SJPEG_FMT_RGB (1 << 6) +#define SJPEG_FMT_NON_RGB (1 << 7) + +#define S5P_JPEG_ENCODE 0 +#define S5P_JPEG_DECODE 1 + +#define FMT_TYPE_OUTPUT 0 +#define FMT_TYPE_CAPTURE 1 + +#define SJPEG_SUBSAMPLING_444 0x11 +#define SJPEG_SUBSAMPLING_422 0x21 +#define SJPEG_SUBSAMPLING_420 0x22 + +/* Version numbers */ + +#define SJPEG_S5P 1 +#define SJPEG_EXYNOS4 2 + +enum exynos4_jpeg_result { + OK_ENC_OR_DEC, + ERR_PROT, + ERR_DEC_INVALID_FORMAT, + ERR_MULTI_SCAN, + ERR_FRAME, + ERR_UNKNOWN, +}; + +enum exynos4_jpeg_img_quality_level { + QUALITY_LEVEL_1 = 0, /* high */ + QUALITY_LEVEL_2, + QUALITY_LEVEL_3, + QUALITY_LEVEL_4, /* low */ +}; /** * struct s5p_jpeg - JPEG IP abstraction @@ -71,9 +109,16 @@ struct s5p_jpeg { void __iomem *regs; unsigned int irq; + enum exynos4_jpeg_result irq_ret; struct clk *clk; struct device *dev; void *alloc_ctx; + struct s5p_jpeg_variant *variant; +}; + +struct s5p_jpeg_variant { + unsigned int version; + irqreturn_t (*jpeg_irq)(int irq, void *priv); }; /** @@ -84,16 +129,18 @@ struct s5p_jpeg { * @colplanes: number of color planes (1 for packed formats) * @h_align: horizontal alignment order (align to 2^h_align) * @v_align: vertical alignment order (align to 2^v_align) - * @types: types of queue this format is applicable to + * @flags: flags describing format applicability */ struct s5p_jpeg_fmt { char *name; u32 fourcc; int depth; int colplanes; + int memplanes; int h_align; int v_align; - u32 types; + int subsampling; + u32 flags; }; /** @@ -115,7 +162,6 @@ struct s5p_jpeg_q_data { * @jpeg: JPEG IP device for this context * @mode: compression (encode) operation or decompression (decode) * @compr_quality: destination image quality in compression (encode) mode - * @m2m_ctx: mem2mem device context * @out_q: source (output) queue information * @cap_fmt: destination (capture) queue queue information * @hdr_parsed: set if header has been parsed during decompression @@ -127,7 +173,6 @@ struct s5p_jpeg_ctx { unsigned short compr_quality; unsigned short restart_interval; unsigned short subsampling; - struct v4l2_m2m_ctx *m2m_ctx; struct s5p_jpeg_q_data out_q; struct s5p_jpeg_q_data cap_q; struct v4l2_fh fh; @@ -147,4 +192,16 @@ struct s5p_jpeg_buffer { unsigned long data; }; +/** + * struct s5p_jpeg_addr - JPEG converter physical address set for DMA + * @y: luminance plane physical address + * @cb: Cb plane physical address + * @cr: Cr plane physical address + */ +struct s5p_jpeg_addr { + u32 y; + u32 cb; + u32 cr; +}; + #endif /* JPEG_CORE_H */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c new file mode 100644 index 000000000000..da8d6a1a984f --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c @@ -0,0 +1,279 @@ +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + * + * Register interface file for JPEG driver on Exynos4x12. + * + * 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 "jpeg-core.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-regs.h" + +void exynos4_jpeg_sw_reset(void __iomem *base) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG); + writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); + + ndelay(100000); + + writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG); + /* set exynos4_jpeg mod register */ + if (mode == S5P_JPEG_DECODE) { + writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | + EXYNOS4_DEC_MODE, + base + EXYNOS4_JPEG_CNTL_REG); + } else {/* encode */ + writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | + EXYNOS4_ENC_MODE, + base + EXYNOS4_JPEG_CNTL_REG); + } +} + +void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_IMG_FMT_REG) & + EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */ + + switch (img_fmt) { + case V4L2_PIX_FMT_GREY: + reg = reg | EXYNOS4_ENC_GRAY_IMG | EXYNOS4_GRAY_IMG_IP; + break; + case V4L2_PIX_FMT_RGB32: + reg = reg | EXYNOS4_ENC_RGB_IMG | + EXYNOS4_RGB_IP_RGB_32BIT_IMG; + break; + case V4L2_PIX_FMT_RGB565: + reg = reg | EXYNOS4_ENC_RGB_IMG | + EXYNOS4_RGB_IP_RGB_16BIT_IMG; + break; + case V4L2_PIX_FMT_NV24: + reg = reg | EXYNOS4_ENC_YUV_444_IMG | + EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | + EXYNOS4_SWAP_CHROMA_CBCR; + break; + case V4L2_PIX_FMT_NV42: + reg = reg | EXYNOS4_ENC_YUV_444_IMG | + EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | + EXYNOS4_SWAP_CHROMA_CRCB; + break; + case V4L2_PIX_FMT_YUYV: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | + EXYNOS4_SWAP_CHROMA_CBCR; + break; + + case V4L2_PIX_FMT_YVYU: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | + EXYNOS4_SWAP_CHROMA_CRCB; + break; + case V4L2_PIX_FMT_NV16: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | + EXYNOS4_SWAP_CHROMA_CBCR; + break; + case V4L2_PIX_FMT_NV61: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | + EXYNOS4_SWAP_CHROMA_CRCB; + break; + case V4L2_PIX_FMT_NV12: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | + EXYNOS4_SWAP_CHROMA_CBCR; + break; + case V4L2_PIX_FMT_NV21: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | + EXYNOS4_SWAP_CHROMA_CRCB; + break; + case V4L2_PIX_FMT_YUV420: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_3P_IMG | + EXYNOS4_SWAP_CHROMA_CBCR; + break; + default: + break; + + } + + writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_IMG_FMT_REG) & + ~EXYNOS4_ENC_FMT_MASK; /* clear enc format */ + + switch (out_fmt) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: + reg = reg | EXYNOS4_ENC_FMT_GRAY; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_444: + reg = reg | EXYNOS4_ENC_FMT_YUV_444; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + reg = reg | EXYNOS4_ENC_FMT_YUV_422; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + reg = reg | EXYNOS4_ENC_FMT_YUV_420; + break; + + default: + break; + } + + writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void exynos4_jpeg_set_interrupt(void __iomem *base) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; + writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); +} + +unsigned int exynos4_jpeg_get_int_status(void __iomem *base) +{ + unsigned int int_status; + + int_status = readl(base + EXYNOS4_INT_STATUS_REG); + + return int_status; +} + +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base) +{ + unsigned int fifo_status; + + fifo_status = readl(base + EXYNOS4_FIFO_STATUS_REG); + + return fifo_status; +} + +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~EXYNOS4_HUF_TBL_EN; + + if (value == 1) + writel(reg | EXYNOS4_HUF_TBL_EN, + base + EXYNOS4_JPEG_CNTL_REG); + else + writel(reg | ~EXYNOS4_HUF_TBL_EN, + base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN); + + if (value == 1) + writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); + else + writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, + unsigned int address) +{ + writel(address, base + EXYNOS4_OUT_MEM_BASE_REG); +} + +void exynos4_jpeg_set_stream_size(void __iomem *base, + unsigned int x_value, unsigned int y_value) +{ + writel(0x0, base + EXYNOS4_JPEG_IMG_SIZE_REG); /* clear */ + writel(EXYNOS4_X_SIZE(x_value) | EXYNOS4_Y_SIZE(y_value), + base + EXYNOS4_JPEG_IMG_SIZE_REG); +} + +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, + struct s5p_jpeg_addr *exynos4_jpeg_addr) +{ + writel(exynos4_jpeg_addr->y, base + EXYNOS4_IMG_BA_PLANE_1_REG); + writel(exynos4_jpeg_addr->cb, base + EXYNOS4_IMG_BA_PLANE_2_REG); + writel(exynos4_jpeg_addr->cr, base + EXYNOS4_IMG_BA_PLANE_3_REG); +} + +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, + enum exynos4_jpeg_img_quality_level level) +{ + unsigned int reg; + + reg = EXYNOS4_Q_TBL_COMP1_0 | EXYNOS4_Q_TBL_COMP2_1 | + EXYNOS4_Q_TBL_COMP3_1 | + EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 | + EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 | + EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1; + + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt) +{ + if (fmt == V4L2_PIX_FMT_GREY) + writel(0xd2, base + EXYNOS4_HUFF_CNT_REG); + else + writel(0x1a2, base + EXYNOS4_HUFF_CNT_REG); +} + +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base) +{ + unsigned int size; + + size = readl(base + EXYNOS4_BITSTREAM_SIZE_REG); + return size; +} + +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size) +{ + writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG); +} + +void exynos4_jpeg_get_frame_size(void __iomem *base, + unsigned int *width, unsigned int *height) +{ + *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) & + EXYNOS4_DECODED_SIZE_MASK); + *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) & + EXYNOS4_DECODED_SIZE_MASK; +} + +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base) +{ + return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) & + EXYNOS4_JPEG_DECODED_IMG_FMT_MASK; +} + +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size) +{ + writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG); +} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h new file mode 100644 index 000000000000..c228d28a4bc7 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + * + * Header file of the register interface for JPEG driver on Exynos4x12. + * + * 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 JPEG_HW_EXYNOS4_H_ +#define JPEG_HW_EXYNOS4_H_ + +void exynos4_jpeg_sw_reset(void __iomem *base); +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode); +void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt); +void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt); +void exynos4_jpeg_set_enc_tbl(void __iomem *base); +void exynos4_jpeg_set_interrupt(void __iomem *base); +unsigned int exynos4_jpeg_get_int_status(void __iomem *base); +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value); +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value); +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, + unsigned int address); +void exynos4_jpeg_set_stream_size(void __iomem *base, + unsigned int x_value, unsigned int y_value); +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, + struct s5p_jpeg_addr *jpeg_addr); +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, + enum exynos4_jpeg_img_quality_level level); +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); +void exynos4_jpeg_get_frame_size(void __iomem *base, + unsigned int *width, unsigned int *height); +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base); +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base); +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size); + +#endif /* JPEG_HW_EXYNOS4_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index b47e887b6138..52407d790726 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -9,27 +9,15 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef JPEG_HW_H_ -#define JPEG_HW_H_ #include <linux/io.h> #include <linux/videodev2.h> -#include "jpeg-hw.h" +#include "jpeg-core.h" #include "jpeg-regs.h" +#include "jpeg-hw-s5p.h" -#define S5P_JPEG_MIN_WIDTH 32 -#define S5P_JPEG_MIN_HEIGHT 32 -#define S5P_JPEG_MAX_WIDTH 8192 -#define S5P_JPEG_MAX_HEIGHT 8192 -#define S5P_JPEG_ENCODE 0 -#define S5P_JPEG_DECODE 1 -#define S5P_JPEG_RAW_IN_565 0 -#define S5P_JPEG_RAW_IN_422 1 -#define S5P_JPEG_RAW_OUT_422 0 -#define S5P_JPEG_RAW_OUT_420 1 - -static inline void jpeg_reset(void __iomem *regs) +void s5p_jpeg_reset(void __iomem *regs) { unsigned long reg; @@ -42,12 +30,12 @@ static inline void jpeg_reset(void __iomem *regs) } } -static inline void jpeg_poweron(void __iomem *regs) +void s5p_jpeg_poweron(void __iomem *regs) { writel(S5P_POWER_ON, regs + S5P_JPGCLKCON); } -static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) { unsigned long reg, m; @@ -63,7 +51,7 @@ static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) writel(reg, regs + S5P_JPGCMOD); } -static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16) +void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16) { unsigned long reg; @@ -75,7 +63,7 @@ static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16) writel(reg, regs + S5P_JPGCMOD); } -static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode) { unsigned long reg, m; @@ -90,7 +78,7 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) writel(reg, regs + S5P_JPGMOD); } -static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) { unsigned long reg, m; @@ -105,12 +93,12 @@ static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) writel(reg, regs + S5P_JPGMOD); } -static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs) +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs) { return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; } -static inline void jpeg_dri(void __iomem *regs, unsigned int dri) +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri) { unsigned long reg; @@ -125,7 +113,7 @@ static inline void jpeg_dri(void __iomem *regs, unsigned int dri) writel(reg, regs + S5P_JPGDRI_L); } -static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) { unsigned long reg; @@ -135,7 +123,7 @@ static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) writel(reg, regs + S5P_JPG_QTBL); } -static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t) +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t) { unsigned long reg; @@ -146,7 +134,7 @@ static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t) writel(reg, regs + S5P_JPG_HTBL); } -static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t) +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t) { unsigned long reg; @@ -157,7 +145,7 @@ static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t) writel(reg, regs + S5P_JPG_HTBL); } -static inline void jpeg_y(void __iomem *regs, unsigned int y) +void s5p_jpeg_y(void __iomem *regs, unsigned int y) { unsigned long reg; @@ -172,7 +160,7 @@ static inline void jpeg_y(void __iomem *regs, unsigned int y) writel(reg, regs + S5P_JPGY_L); } -static inline void jpeg_x(void __iomem *regs, unsigned int x) +void s5p_jpeg_x(void __iomem *regs, unsigned int x) { unsigned long reg; @@ -187,7 +175,7 @@ static inline void jpeg_x(void __iomem *regs, unsigned int x) writel(reg, regs + S5P_JPGX_L); } -static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable) +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable) { unsigned long reg; @@ -198,7 +186,7 @@ static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable) writel(reg, regs + S5P_JPGINTSE); } -static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable) +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable) { unsigned long reg; @@ -209,7 +197,7 @@ static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable) writel(reg, regs + S5P_JPGINTSE); } -static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) { unsigned long reg; @@ -220,7 +208,7 @@ static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) writel(reg, regs + S5P_JPGINTSE); } -static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val) +void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val) { unsigned long reg; @@ -231,7 +219,7 @@ static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val) writel(reg, regs + S5P_JPG_TIMER_SE); } -static inline void jpeg_timer_disable(void __iomem *regs) +void s5p_jpeg_timer_disable(void __iomem *regs) { unsigned long reg; @@ -240,13 +228,13 @@ static inline void jpeg_timer_disable(void __iomem *regs) writel(reg, regs + S5P_JPG_TIMER_SE); } -static inline int jpeg_timer_stat(void __iomem *regs) +int s5p_jpeg_timer_stat(void __iomem *regs) { return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK) >> S5P_TIMER_INT_STAT_SHIFT); } -static inline void jpeg_clear_timer_stat(void __iomem *regs) +void s5p_jpeg_clear_timer_stat(void __iomem *regs) { unsigned long reg; @@ -255,7 +243,7 @@ static inline void jpeg_clear_timer_stat(void __iomem *regs) writel(reg, regs + S5P_JPG_TIMER_SE); } -static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size) +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size) { unsigned long reg; @@ -266,13 +254,13 @@ static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size) writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); } -static inline int jpeg_enc_stream_stat(void __iomem *regs) +int s5p_jpeg_enc_stream_stat(void __iomem *regs) { return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) & S5P_ENC_STREAM_INT_STAT_MASK); } -static inline void jpeg_clear_enc_stream_stat(void __iomem *regs) +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs) { unsigned long reg; @@ -281,7 +269,7 @@ static inline void jpeg_clear_enc_stream_stat(void __iomem *regs) writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); } -static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format) +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format) { unsigned long reg, f; @@ -296,17 +284,17 @@ static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format) writel(reg, regs + S5P_JPG_OUTFORM); } -static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr) +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr) { writel(addr, regs + S5P_JPG_JPGADR); } -static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr) +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr) { writel(addr, regs + S5P_JPG_IMGADR); } -static inline void jpeg_coef(void __iomem *regs, unsigned int i, +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, unsigned int j, unsigned int coef) { unsigned long reg; @@ -317,24 +305,24 @@ static inline void jpeg_coef(void __iomem *regs, unsigned int i, writel(reg, regs + S5P_JPG_COEF(i)); } -static inline void jpeg_start(void __iomem *regs) +void s5p_jpeg_start(void __iomem *regs) { writel(1, regs + S5P_JSTART); } -static inline int jpeg_result_stat_ok(void __iomem *regs) +int s5p_jpeg_result_stat_ok(void __iomem *regs) { return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK) >> S5P_RESULT_STAT_SHIFT); } -static inline int jpeg_stream_stat_ok(void __iomem *regs) +int s5p_jpeg_stream_stat_ok(void __iomem *regs) { return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK) >> S5P_STREAM_STAT_SHIFT); } -static inline void jpeg_clear_int(void __iomem *regs) +void s5p_jpeg_clear_int(void __iomem *regs) { unsigned long reg; @@ -343,7 +331,7 @@ static inline void jpeg_clear_int(void __iomem *regs) reg = readl(regs + S5P_JPGOPR); } -static inline unsigned int jpeg_compressed_size(void __iomem *regs) +unsigned int s5p_jpeg_compressed_size(void __iomem *regs) { unsigned long jpeg_size = 0; @@ -353,5 +341,3 @@ static inline unsigned int jpeg_compressed_size(void __iomem *regs) return (unsigned int)jpeg_size; } - -#endif /* JPEG_HW_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h new file mode 100644 index 000000000000..c11ebe86b9c9 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h @@ -0,0 +1,63 @@ +/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@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 JPEG_HW_S5P_H_ +#define JPEG_HW_S5P_H_ + +#include <linux/io.h> +#include <linux/videodev2.h> + +#include "jpeg-regs.h" + +#define S5P_JPEG_MIN_WIDTH 32 +#define S5P_JPEG_MIN_HEIGHT 32 +#define S5P_JPEG_MAX_WIDTH 8192 +#define S5P_JPEG_MAX_HEIGHT 8192 +#define S5P_JPEG_RAW_IN_565 0 +#define S5P_JPEG_RAW_IN_422 1 +#define S5P_JPEG_RAW_OUT_422 0 +#define S5P_JPEG_RAW_OUT_420 1 + +void s5p_jpeg_reset(void __iomem *regs); +void s5p_jpeg_poweron(void __iomem *regs); +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16); +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs); +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri); +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t); +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t); +void s5p_jpeg_y(void __iomem *regs, unsigned int y); +void s5p_jpeg_x(void __iomem *regs, unsigned int x); +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl); +void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val); +void s5p_jpeg_timer_disable(void __iomem *regs); +int s5p_jpeg_timer_stat(void __iomem *regs); +void s5p_jpeg_clear_timer_stat(void __iomem *regs); +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size); +int s5p_jpeg_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format); +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, + unsigned int j, unsigned int coef); +void s5p_jpeg_start(void __iomem *regs); +int s5p_jpeg_result_stat_ok(void __iomem *regs); +int s5p_jpeg_stream_stat_ok(void __iomem *regs); +void s5p_jpeg_clear_int(void __iomem *regs); +unsigned int s5p_jpeg_compressed_size(void __iomem *regs); + +#endif /* JPEG_HW_S5P_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 38e50815668c..33f2c7374cfd 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -2,10 +2,11 @@ * * Register definition file for Samsung JPEG codec driver * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Author: Jacek Anaszewski <j.anaszewski@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 @@ -15,6 +16,8 @@ #ifndef JPEG_REGS_H_ #define JPEG_REGS_H_ +/* Register and bit definitions for S5PC210 */ + /* JPEG mode register */ #define S5P_JPGMOD 0x00 #define S5P_PROC_MODE_MASK (0x1 << 3) @@ -166,5 +169,209 @@ /* JPEG AC Huffman table register */ #define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400) + +/* Register and bit definitions for Exynos 4x12 */ + +/* JPEG Codec Control Registers */ +#define EXYNOS4_JPEG_CNTL_REG 0x00 +#define EXYNOS4_INT_EN_REG 0x04 +#define EXYNOS4_INT_TIMER_COUNT_REG 0x08 +#define EXYNOS4_INT_STATUS_REG 0x0c +#define EXYNOS4_OUT_MEM_BASE_REG 0x10 +#define EXYNOS4_JPEG_IMG_SIZE_REG 0x14 +#define EXYNOS4_IMG_BA_PLANE_1_REG 0x18 +#define EXYNOS4_IMG_SO_PLANE_1_REG 0x1c +#define EXYNOS4_IMG_PO_PLANE_1_REG 0x20 +#define EXYNOS4_IMG_BA_PLANE_2_REG 0x24 +#define EXYNOS4_IMG_SO_PLANE_2_REG 0x28 +#define EXYNOS4_IMG_PO_PLANE_2_REG 0x2c +#define EXYNOS4_IMG_BA_PLANE_3_REG 0x30 +#define EXYNOS4_IMG_SO_PLANE_3_REG 0x34 +#define EXYNOS4_IMG_PO_PLANE_3_REG 0x38 + +#define EXYNOS4_TBL_SEL_REG 0x3c + +#define EXYNOS4_IMG_FMT_REG 0x40 + +#define EXYNOS4_BITSTREAM_SIZE_REG 0x44 +#define EXYNOS4_PADDING_REG 0x48 +#define EXYNOS4_HUFF_CNT_REG 0x4c +#define EXYNOS4_FIFO_STATUS_REG 0x50 +#define EXYNOS4_DECODE_XY_SIZE_REG 0x54 +#define EXYNOS4_DECODE_IMG_FMT_REG 0x58 + +#define EXYNOS4_QUAN_TBL_ENTRY_REG 0x100 +#define EXYNOS4_HUFF_TBL_ENTRY_REG 0x200 + + +/****************************************************************/ +/* Bit definition part */ +/****************************************************************/ + +/* JPEG CNTL Register bit */ +#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0) +#define EXYNOS4_DEC_MODE (1 << 0) +#define EXYNOS4_ENC_MODE (1 << 1) +#define EXYNOS4_AUTO_RST_MARKER (1 << 2) +#define EXYNOS4_RST_INTERVAL_SHIFT 3 +#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \ + << EXYNOS4_RST_INTERVAL_SHIFT) +#define EXYNOS4_HUF_TBL_EN (1 << 19) +#define EXYNOS4_HOR_SCALING_SHIFT 20 +#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \ + << EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING_SHIFT 22 +#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \ + << EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_PADDING (1 << 27) +#define EXYNOS4_SYS_INT_EN (1 << 28) +#define EXYNOS4_SOFT_RESET_HI (1 << 29) + +/* JPEG INT Register bit */ +#define EXYNOS4_INT_EN_MASK (0x1f << 0) +#define EXYNOS4_PROT_ERR_INT_EN (1 << 0) +#define EXYNOS4_IMG_COMPLETION_INT_EN (1 << 1) +#define EXYNOS4_DEC_INVALID_FORMAT_EN (1 << 2) +#define EXYNOS4_MULTI_SCAN_ERROR_EN (1 << 3) +#define EXYNOS4_FRAME_ERR_EN (1 << 4) +#define EXYNOS4_INT_EN_ALL (0x1f << 0) + +#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) +#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) + +#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_420 (2 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_GRAY (3 << 0) + + +/* JPEG IMAGE SIZE Register bit */ +#define EXYNOS4_X_SIZE_SHIFT 0 +#define EXYNOS4_X_SIZE_MASK (0xffff << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_X_SIZE(x) (((x) & 0xffff) << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE_SHIFT 16 +#define EXYNOS4_Y_SIZE_MASK (0xffff << EXYNOS4_Y_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE(x) (((x) & 0xffff) << EXYNOS4_Y_SIZE_SHIFT) + +/* JPEG IMAGE FORMAT Register bit */ +#define EXYNOS4_ENC_IN_FMT_MASK 0xffff0000 +#define EXYNOS4_ENC_GRAY_IMG (0 << 0) +#define EXYNOS4_ENC_RGB_IMG (1 << 0) +#define EXYNOS4_ENC_YUV_444_IMG (2 << 0) +#define EXYNOS4_ENC_YUV_422_IMG (3 << 0) +#define EXYNOS4_ENC_YUV_440_IMG (4 << 0) + +#define EXYNOS4_DEC_GRAY_IMG (0 << 0) +#define EXYNOS4_DEC_RGB_IMG (1 << 0) +#define EXYNOS4_DEC_YUV_444_IMG (2 << 0) +#define EXYNOS4_DEC_YUV_422_IMG (3 << 0) +#define EXYNOS4_DEC_YUV_420_IMG (4 << 0) + +#define EXYNOS4_GRAY_IMG_IP_SHIFT 3 +#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT) +#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT) + +#define EXYNOS4_RGB_IP_SHIFT 6 +#define EXYNOS4_RGB_IP_MASK (7 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT) + +#define EXYNOS4_YUV_444_IP_SHIFT 9 +#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT) + +#define EXYNOS4_YUV_422_IP_SHIFT 12 +#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT) + +#define EXYNOS4_YUV_420_IP_SHIFT 15 +#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT) + +#define EXYNOS4_ENC_FMT_SHIFT 24 +#define EXYNOS4_ENC_FMT_MASK (3 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_GRAY (0 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_444 (1 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_422 (2 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_420 (3 << EXYNOS4_ENC_FMT_SHIFT) + +#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03 + +#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) +#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) + +/* JPEG HUFF count Register bit */ +#define EXYNOS4_HUFF_COUNT_MASK 0xffff + +/* JPEG Decoded_img_x_y_size Register bit */ +#define EXYNOS4_DECODED_SIZE_MASK 0x0000ffff + +/* JPEG Decoded image format Register bit */ +#define EXYNOS4_DECODED_IMG_FMT_MASK 0x3 + +/* JPEG TBL SEL Register bit */ +#define EXYNOS4_Q_TBL_COMP1_0 (0 << 0) +#define EXYNOS4_Q_TBL_COMP1_1 (1 << 0) +#define EXYNOS4_Q_TBL_COMP1_2 (2 << 0) +#define EXYNOS4_Q_TBL_COMP1_3 (3 << 0) + +#define EXYNOS4_Q_TBL_COMP2_0 (0 << 2) +#define EXYNOS4_Q_TBL_COMP2_1 (1 << 2) +#define EXYNOS4_Q_TBL_COMP2_2 (2 << 2) +#define EXYNOS4_Q_TBL_COMP2_3 (3 << 2) + +#define EXYNOS4_Q_TBL_COMP3_0 (0 << 4) +#define EXYNOS4_Q_TBL_COMP3_1 (1 << 4) +#define EXYNOS4_Q_TBL_COMP3_2 (2 << 4) +#define EXYNOS4_Q_TBL_COMP3_3 (3 << 4) + +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 (0 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 (1 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 (2 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 (3 << 6) + +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 (0 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 (1 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 (2 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 (3 << 8) + +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 (0 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 (1 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 (2 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 (3 << 10) + +/* JPEG quantizer table register */ +#define EXYNOS4_QTBL_CONTENT(n) (0x100 + (n) * 0x40) + +/* JPEG DC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLL 0x200 + +/* JPEG DC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLV 0x210 + +/* JPEG DC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCL 0x220 + +/* JPEG DC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCV 0x230 + +/* JPEG AC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLL 0x240 + +/* JPEG AC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLV 0x250 + +/* JPEG AC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCL 0x300 + +/* JPEG AC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCV 0x310 + #endif /* JPEG_REGS_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e46067a57853..e2aac592d29f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -177,21 +177,6 @@ unlock: mutex_unlock(&dev->mfc_mutex); } -static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - - if (!vdev) { - mfc_err("failed to get video_device"); - return MFCNODE_INVALID; - } - if (vdev->index == 0) - return MFCNODE_DECODER; - else if (vdev->index == 1) - return MFCNODE_ENCODER; - return MFCNODE_INVALID; -} - static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev) { mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT); @@ -705,6 +690,7 @@ irq_cleanup_hw: /* Open an MFC node */ static int s5p_mfc_open(struct file *file) { + struct video_device *vdev = video_devdata(file); struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_ctx *ctx = NULL; struct vb2_queue *q; @@ -742,7 +728,7 @@ static int s5p_mfc_open(struct file *file) /* Mark context as idle */ clear_work_bit_irqsave(ctx); dev->ctx[ctx->num] = ctx; - if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { + if (vdev == dev->vfd_dec) { ctx->type = MFCINST_DECODER; ctx->c_ops = get_dec_codec_ops(); s5p_mfc_dec_init(ctx); @@ -752,7 +738,7 @@ static int s5p_mfc_open(struct file *file) mfc_err("Failed to setup mfc controls\n"); goto err_ctrls_setup; } - } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { + } else if (vdev == dev->vfd_enc) { ctx->type = MFCINST_ENCODER; ctx->c_ops = get_enc_codec_ops(); /* only for encoder */ @@ -797,10 +783,10 @@ static int s5p_mfc_open(struct file *file) q = &ctx->vq_dst; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->drv_priv = &ctx->fh; - if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { + if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); - } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { + } else if (vdev == dev->vfd_enc) { q->io_modes = VB2_MMAP | VB2_USERPTR; q->ops = get_enc_queue_ops(); } else { @@ -819,10 +805,10 @@ static int s5p_mfc_open(struct file *file) q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; q->io_modes = VB2_MMAP; q->drv_priv = &ctx->fh; - if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { + if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); - } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { + } else if (vdev == dev->vfd_enc) { q->io_modes = VB2_MMAP | VB2_USERPTR; q->ops = get_enc_queue_ops(); } else { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 6920b546181a..f723f1f2f578 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -115,15 +115,6 @@ enum s5p_mfc_fmt_type { }; /** - * enum s5p_mfc_node_type - The type of an MFC device node. - */ -enum s5p_mfc_node_type { - MFCNODE_INVALID = -1, - MFCNODE_DECODER = 0, - MFCNODE_ENCODER = 1, -}; - -/** * enum s5p_mfc_inst_type - The type of an MFC instance. */ enum s5p_mfc_inst_type { @@ -422,6 +413,11 @@ struct s5p_mfc_vp8_enc_params { enum v4l2_vp8_golden_frame_sel golden_frame_sel; u8 hier_layer; u8 hier_layer_qp[3]; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_frame_qp; + u8 rc_p_frame_qp; + u8 profile; }; /** diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 4ff3b6cd6842..91b6e020ddf3 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -618,6 +618,46 @@ static struct mfc_control controls[] = { .default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV, .menu_skip_mask = 0, }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 127, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 11, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 10, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 10, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 0, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -1557,6 +1597,21 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: p->codec.vp8.golden_frame_sel = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: + p->codec.vp8.rc_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: + p->codec.vp8.rc_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: + p->codec.vp8.rc_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: + p->codec.vp8.rc_p_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + p->codec.vp8.profile = ctrl->val; + break; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); @@ -1863,7 +1918,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) if (ctx->src_bufs_cnt < ctx->pb_count) { mfc_err("Need minimum %d OUTPUT buffers\n", ctx->pb_count); - return -EINVAL; + return -ENOBUFS; } } 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 461358c4a790..f6ff2dbf3a1d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1197,10 +1197,8 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) reg |= ((p->num_b_frame & 0x3) << 16); WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); - /* profile & level */ - reg = 0; - /** profile */ - reg |= (0x1 << 4); + /* profile - 0 ~ 3 */ + reg = p_vp8->profile & 0x3; WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); /* rate control config. */ @@ -1218,6 +1216,26 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); } + /* frame QP */ + reg &= ~(0x7F); + reg |= p_vp8->rc_frame_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + + /* other QPs */ + WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + if (!p->rc_frame && !p->rc_mb) { + reg = 0; + reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8); + reg |= p_vp8->rc_frame_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + } + + /* max QP */ + reg = ((p_vp8->rc_max_qp & 0x7F) << 8); + /* min QP */ + reg |= p_vp8->rc_min_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index 51805a5e2beb..bc08b5f28e44 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -347,19 +347,41 @@ static int mxr_runtime_resume(struct device *dev) { struct mxr_device *mdev = to_mdev(dev); struct mxr_resources *res = &mdev->res; + int ret; mxr_dbg(mdev, "resume - start\n"); mutex_lock(&mdev->mutex); /* turn clocks on */ - clk_enable(res->mixer); - clk_enable(res->vp); - clk_enable(res->sclk_mixer); + ret = clk_prepare_enable(res->mixer); + if (ret < 0) { + dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n"); + goto fail; + } + ret = clk_prepare_enable(res->vp); + if (ret < 0) { + dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n"); + goto fail_mixer; + } + ret = clk_prepare_enable(res->sclk_mixer); + if (ret < 0) { + dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n"); + goto fail_vp; + } /* apply default configuration */ mxr_reg_reset(mdev); mxr_dbg(mdev, "resume - finished\n"); mutex_unlock(&mdev->mutex); return 0; + +fail_vp: + clk_disable_unprepare(res->vp); +fail_mixer: + clk_disable_unprepare(res->mixer); +fail: + mutex_unlock(&mdev->mutex); + dev_err(mdev->dev, "resume failed\n"); + return ret; } static int mxr_runtime_suspend(struct device *dev) @@ -369,9 +391,9 @@ static int mxr_runtime_suspend(struct device *dev) mxr_dbg(mdev, "suspend - start\n"); mutex_lock(&mdev->mutex); /* turn clocks off */ - clk_disable(res->sclk_mixer); - clk_disable(res->vp); - clk_disable(res->mixer); + clk_disable_unprepare(res->sclk_mixer); + clk_disable_unprepare(res->vp); + clk_disable_unprepare(res->mixer); mutex_unlock(&mdev->mutex); mxr_dbg(mdev, "suspend - finished\n"); return 0; diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 81b97db111d8..c5059ba0d733 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -948,7 +948,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) if (count == 0) { mxr_dbg(mdev, "no output buffers queued\n"); - return -EINVAL; + return -ENOBUFS; } /* block any changes in output configuration */ diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 0afa90f0f6ab..5a7c3796f22e 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -55,6 +55,8 @@ struct sdo_device { struct clk *dacphy; /** clock for control of VPLL */ struct clk *fout_vpll; + /** vpll rate before sdo stream was on */ + unsigned long vpll_rate; /** regulator for SDO IP power */ struct regulator *vdac; /** regulator for SDO plug detection */ @@ -193,17 +195,33 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on) static int sdo_streamon(struct sdo_device *sdev) { + int ret; + /* set proper clock for Timing Generator */ - clk_set_rate(sdev->fout_vpll, 54000000); + sdev->vpll_rate = clk_get_rate(sdev->fout_vpll); + ret = clk_set_rate(sdev->fout_vpll, 54000000); + if (ret < 0) { + dev_err(sdev->dev, "Failed to set vpll rate\n"); + return ret; + } dev_info(sdev->dev, "fout_vpll.rate = %lu\n", clk_get_rate(sdev->fout_vpll)); /* enable clock in SDO */ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON); - clk_enable(sdev->dacphy); + ret = clk_prepare_enable(sdev->dacphy); + if (ret < 0) { + dev_err(sdev->dev, "clk_prepare_enable(dacphy) failed\n"); + goto fail; + } /* enable DAC */ sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC); sdo_reg_debug(sdev); return 0; + +fail: + sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); + clk_set_rate(sdev->fout_vpll, sdev->vpll_rate); + return ret; } static int sdo_streamoff(struct sdo_device *sdev) @@ -211,7 +229,7 @@ static int sdo_streamoff(struct sdo_device *sdev) int tries; sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC); - clk_disable(sdev->dacphy); + clk_disable_unprepare(sdev->dacphy); sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); for (tries = 100; tries; --tries) { if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY) @@ -220,6 +238,7 @@ static int sdo_streamoff(struct sdo_device *sdev) } if (tries == 0) dev_err(sdev->dev, "failed to stop streaming\n"); + clk_set_rate(sdev->fout_vpll, sdev->vpll_rate); return tries ? 0 : -EIO; } @@ -254,7 +273,7 @@ static int sdo_runtime_suspend(struct device *dev) dev_info(dev, "suspend\n"); regulator_disable(sdev->vdet); regulator_disable(sdev->vdac); - clk_disable(sdev->sclk_dac); + clk_disable_unprepare(sdev->sclk_dac); return 0; } @@ -266,7 +285,7 @@ static int sdo_runtime_resume(struct device *dev) dev_info(dev, "resume\n"); - ret = clk_enable(sdev->sclk_dac); + ret = clk_prepare_enable(sdev->sclk_dac); if (ret < 0) return ret; @@ -299,7 +318,7 @@ static int sdo_runtime_resume(struct device *dev) vdac_r_dis: regulator_disable(sdev->vdac); dac_clk_dis: - clk_disable(sdev->sclk_dac); + clk_disable_unprepare(sdev->sclk_dac); return ret; } @@ -405,7 +424,11 @@ static int sdo_probe(struct platform_device *pdev) } /* enable gate for dac clock, because mixer uses it */ - clk_enable(sdev->dac); + ret = clk_prepare_enable(sdev->dac); + if (ret < 0) { + dev_err(dev, "clk_prepare_enable(dac) failed\n"); + goto fail_fout_vpll; + } /* configure power management */ pm_runtime_enable(dev); @@ -444,7 +467,7 @@ static int sdo_remove(struct platform_device *pdev) struct sdo_device *sdev = sd_to_sdev(sd); pm_runtime_disable(&pdev->dev); - clk_disable(sdev->dac); + clk_disable_unprepare(sdev->dac); clk_put(sdev->fout_vpll); clk_put(sdev->dacphy); clk_put(sdev->dac); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 4f30341dc2ab..e5f1d4c14f2c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -286,7 +286,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, vb->size = vb->height * bytes_per_line; if (vb->baddr && vb->bsize < vb->size) { /* User buffer too small */ - dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n", + dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n", vb->bsize, vb->baddr); return -EINVAL; } @@ -302,9 +302,10 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, } dev_dbg(vou_dev->v4l2_dev.dev, - "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", + "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n", __func__, vou_dev->pix_idx, bytes_per_line, - videobuf_to_dma_contig(vb), vb->memory, vb->state); + ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }), + vb->memory, vb->state); return 0; } @@ -442,7 +443,7 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev, int pix_idx, int w_idx, int h_idx) { struct sh_vou_fmt *fmt = vou_fmt + pix_idx; - unsigned int black_left, black_top, width_max, height_max, + unsigned int black_left, black_top, width_max, frame_in_height, frame_out_height, frame_out_top; struct v4l2_rect *rect = &vou_dev->rect; struct v4l2_pix_format *pix = &vou_dev->pix; @@ -450,10 +451,10 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev, if (vou_dev->std & V4L2_STD_525_60) { width_max = 858; - height_max = 262; + /* height_max = 262; */ } else { width_max = 864; - height_max = 312; + /* height_max = 312; */ } frame_in_height = pix->height / 2; @@ -1052,7 +1053,6 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) static unsigned long j; struct videobuf_buffer *vb; static int cnt; - static int side; u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked; u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); @@ -1080,7 +1080,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) irq_status, masked, vou_status, cnt); cnt++; - side = vou_status & 0x10000; + /* side = vou_status & 0x10000; */ /* Clear only set interrupts */ sh_vou_reg_a_write(vou_dev, VOUIR, masked); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 104485632501..4835173d7f80 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -34,13 +34,6 @@ #define MIN_FRAME_RATE 15 #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) -/* ISI states */ -enum { - ISI_STATE_IDLE = 0, - ISI_STATE_READY, - ISI_STATE_WAIT_SOF, -}; - /* Frame buffer descriptor */ struct fbd { /* Physical address of the frame buffer */ @@ -75,11 +68,6 @@ struct atmel_isi { void __iomem *regs; int sequence; - /* State of the ISI module in capturing mode */ - int state; - - /* Wait queue for waiting for SOF */ - wait_queue_head_t vsync_wq; struct vb2_alloc_ctx *alloc_ctx; @@ -124,16 +112,16 @@ static int configure_geometry(struct atmel_isi *isi, u32 width, case V4L2_MBUS_FMT_Y8_1X8: cr = ISI_CFG2_GRAYSCALE; break; - case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_VYUY8_2X8: cr = ISI_CFG2_YCC_SWAP_MODE_3; break; - case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: cr = ISI_CFG2_YCC_SWAP_MODE_2; break; - case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: cr = ISI_CFG2_YCC_SWAP_MODE_1; break; - case V4L2_MBUS_FMT_YVYU8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: cr = ISI_CFG2_YCC_SWAP_DEFAULT; break; /* RGB, TODO */ @@ -144,6 +132,8 @@ static int configure_geometry(struct atmel_isi *isi, u32 width, isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); cfg2 = isi_readl(isi, ISI_CFG2); + /* Set YCC swap mode */ + cfg2 &= ~ISI_CFG2_YCC_SWAP_MODE_MASK; cfg2 |= cr; /* Set width */ cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK); @@ -207,12 +197,6 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id) isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS); ret = IRQ_HANDLED; } else { - if ((pending & ISI_SR_VSYNC) && - (isi->state == ISI_STATE_IDLE)) { - isi->state = ISI_STATE_READY; - wake_up_interruptible(&isi->vsync_wq); - ret = IRQ_HANDLED; - } if (likely(pending & ISI_SR_CXFR_DONE)) ret = atmel_isi_handle_streaming(isi); } @@ -259,16 +243,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; - int ret; - - /* Reset ISI */ - ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); - if (ret < 0) { - dev_err(icd->parent, "Reset ISI timed out\n"); - return ret; - } - /* Disable all interrupts */ - isi_writel(isi, ISI_INTDIS, ~0UL); size = icd->sizeimage; @@ -374,6 +348,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); + cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; /* Enable linked list */ cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR; @@ -407,43 +382,27 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - u32 sr = 0; int ret; - spin_lock_irq(&isi->lock); - isi->state = ISI_STATE_IDLE; - /* Clear any pending SOF interrupt */ - sr = isi_readl(isi, ISI_STATUS); - /* Enable VSYNC interrupt for SOF */ - isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC); - isi_writel(isi, ISI_CTRL, ISI_CTRL_EN); - spin_unlock_irq(&isi->lock); - - dev_dbg(icd->parent, "Waiting for SOF\n"); - ret = wait_event_interruptible(isi->vsync_wq, - isi->state != ISI_STATE_IDLE); - if (ret) - goto err; - - if (isi->state != ISI_STATE_READY) { - ret = -EIO; - goto err; + /* Reset ISI */ + ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); + if (ret < 0) { + dev_err(icd->parent, "Reset ISI timed out\n"); + return ret; } + /* Disable all interrupts */ + isi_writel(isi, ISI_INTDIS, ~0UL); spin_lock_irq(&isi->lock); - isi->state = ISI_STATE_WAIT_SOF; - isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC); + /* Clear any pending interrupt */ + sr = isi_readl(isi, ISI_STATUS); + if (count) start_dma(isi, isi->active); spin_unlock_irq(&isi->lock); return 0; -err: - isi->active = NULL; - isi->sequence = 0; - INIT_LIST_HEAD(&isi->video_buffer_list); - return ret; } /* abort streaming and wait for last buffer */ @@ -765,14 +724,16 @@ static int isi_camera_clock_start(struct soc_camera_host *ici) struct atmel_isi *isi = ici->priv; int ret; - ret = clk_enable(isi->pclk); + ret = clk_prepare_enable(isi->pclk); if (ret) return ret; - ret = clk_enable(isi->mck); - if (ret) { - clk_disable(isi->pclk); - return ret; + if (!IS_ERR(isi->mck)) { + ret = clk_prepare_enable(isi->mck); + if (ret) { + clk_disable_unprepare(isi->pclk); + return ret; + } } return 0; @@ -783,8 +744,9 @@ static void isi_camera_clock_stop(struct soc_camera_host *ici) { struct atmel_isi *isi = ici->priv; - clk_disable(isi->mck); - clk_disable(isi->pclk); + if (!IS_ERR(isi->mck)) + clk_disable_unprepare(isi->mck); + clk_disable_unprepare(isi->pclk); } static unsigned int isi_camera_poll(struct file *file, poll_table *pt) @@ -906,7 +868,6 @@ static int atmel_isi_remove(struct platform_device *pdev) struct atmel_isi *isi = container_of(soc_host, struct atmel_isi, soc_host); - free_irq(isi->irq, isi); soc_camera_host_unregister(soc_host); vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); dma_free_coherent(&pdev->dev, @@ -914,13 +875,6 @@ static int atmel_isi_remove(struct platform_device *pdev) isi->p_fb_descriptors, isi->fb_descriptors_phys); - iounmap(isi->regs); - clk_unprepare(isi->mck); - clk_put(isi->mck); - clk_unprepare(isi->pclk); - clk_put(isi->pclk); - kfree(isi); - return 0; } @@ -928,7 +882,6 @@ static int atmel_isi_probe(struct platform_device *pdev) { unsigned int irq; struct atmel_isi *isi; - struct clk *pclk; struct resource *regs; int ret, i; struct device *dev = &pdev->dev; @@ -936,64 +889,50 @@ static int atmel_isi_probe(struct platform_device *pdev) struct isi_platform_data *pdata; pdata = dev->platform_data; - if (!pdata || !pdata->data_width_flags || !pdata->mck_hz) { + if (!pdata || !pdata->data_width_flags) { dev_err(&pdev->dev, "No config available for Atmel ISI\n"); return -EINVAL; } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) - return -ENXIO; - - pclk = clk_get(&pdev->dev, "isi_clk"); - if (IS_ERR(pclk)) - return PTR_ERR(pclk); - - ret = clk_prepare(pclk); - if (ret) - goto err_clk_prepare_pclk; - - isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL); + isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); if (!isi) { - ret = -ENOMEM; dev_err(&pdev->dev, "Can't allocate interface!\n"); - goto err_alloc_isi; + return -ENOMEM; } - isi->pclk = pclk; + isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); + if (IS_ERR(isi->pclk)) + return PTR_ERR(isi->pclk); + isi->pdata = pdata; isi->active = NULL; spin_lock_init(&isi->lock); - init_waitqueue_head(&isi->vsync_wq); INIT_LIST_HEAD(&isi->video_buffer_list); INIT_LIST_HEAD(&isi->dma_desc_head); - /* Get ISI_MCK, provided by programmable clock or external clock */ - isi->mck = clk_get(dev, "isi_mck"); - if (IS_ERR(isi->mck)) { - dev_err(dev, "Failed to get isi_mck\n"); - ret = PTR_ERR(isi->mck); - goto err_clk_get; + /* ISI_MCK is the sensor master clock. It should be handled by the + * sensor driver directly, as the ISI has no use for that clock. Make + * the clock optional here while platforms transition to the correct + * model. + */ + isi->mck = devm_clk_get(dev, "isi_mck"); + if (!IS_ERR(isi->mck)) { + /* Set ISI_MCK's frequency, it should be faster than pixel + * clock. + */ + ret = clk_set_rate(isi->mck, pdata->mck_hz); + if (ret < 0) + return ret; } - ret = clk_prepare(isi->mck); - if (ret) - goto err_clk_prepare_mck; - - /* Set ISI_MCK's frequency, it should be faster than pixel clock */ - ret = clk_set_rate(isi->mck, pdata->mck_hz); - if (ret < 0) - goto err_set_mck_rate; - isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, &isi->fb_descriptors_phys, GFP_KERNEL); if (!isi->p_fb_descriptors) { - ret = -ENOMEM; dev_err(&pdev->dev, "Can't allocate descriptors!\n"); - goto err_alloc_descriptors; + return -ENOMEM; } for (i = 0; i < MAX_BUFFER_NUM; i++) { @@ -1009,9 +948,10 @@ static int atmel_isi_probe(struct platform_device *pdev) goto err_alloc_ctx; } - isi->regs = ioremap(regs->start, resource_size(regs)); - if (!isi->regs) { - ret = -ENOMEM; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + isi->regs = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(isi->regs)) { + ret = PTR_ERR(isi->regs); goto err_ioremap; } @@ -1028,7 +968,7 @@ static int atmel_isi_probe(struct platform_device *pdev) goto err_req_irq; } - ret = request_irq(irq, isi_interrupt, 0, "isi", isi); + ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi); if (ret) { dev_err(&pdev->dev, "Unable to request irq %d\n", irq); goto err_req_irq; @@ -1050,9 +990,7 @@ static int atmel_isi_probe(struct platform_device *pdev) return 0; err_register_soc_camera_host: - free_irq(isi->irq, isi); err_req_irq: - iounmap(isi->regs); err_ioremap: vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); err_alloc_ctx: @@ -1060,17 +998,6 @@ err_alloc_ctx: sizeof(struct fbd) * MAX_BUFFER_NUM, isi->p_fb_descriptors, isi->fb_descriptors_phys); -err_alloc_descriptors: -err_set_mck_rate: - clk_unprepare(isi->mck); -err_clk_prepare_mck: - clk_put(isi->mck); -err_clk_get: - kfree(isi); -err_alloc_isi: - clk_unprepare(pclk); -err_clk_prepare_pclk: - clk_put(pclk); return ret; } diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 45a0276be4e5..d73abca9c6ee 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -659,7 +659,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) unsigned long flags; if (count < 2) - return -EINVAL; + return -ENOBUFS; spin_lock_irqsave(&pcdev->lock, flags); diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 6866bb4fbebc..3b1c05a72d00 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -106,7 +106,7 @@ #define VIN_MAX_HEIGHT 2048 enum chip_id { - RCAR_H2, + RCAR_GEN2, RCAR_H1, RCAR_M1, RCAR_E1, @@ -302,7 +302,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) dmr = 0; break; case V4L2_PIX_FMT_RGB32: - if (priv->chip == RCAR_H2 || priv->chip == RCAR_H1 || + if (priv->chip == RCAR_GEN2 || priv->chip == RCAR_H1 || priv->chip == RCAR_E1) { dmr = VNDMR_EXRGB; break; @@ -1384,7 +1384,8 @@ static struct soc_camera_host_ops rcar_vin_host_ops = { }; static struct platform_device_id rcar_vin_id_table[] = { - { "r8a7790-vin", RCAR_H2 }, + { "r8a7791-vin", RCAR_GEN2 }, + { "r8a7790-vin", RCAR_GEN2 }, { "r8a7779-vin", RCAR_H1 }, { "r8a7778-vin", RCAR_M1 }, { "uPD35004-vin", RCAR_E1 }, diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index cbd3a34f4f3f..8e74fb7f2a07 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -141,8 +141,8 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * Popular special case - some cameras can only handle fixed sizes like * QVGA, VGA,... Take care to avoid infinite loop. */ - width = max(cam_rect->width, 2); - height = max(cam_rect->height, 2); + width = max_t(unsigned int, cam_rect->width, 2); + height = max_t(unsigned int, cam_rect->height, 2); /* * Loop as long as sensor is not covering the requested rectangle and diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index cbf0a806ba1d..be680f839e77 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o -ti-vpe-y := vpe.o vpdma.o +ti-vpe-y := vpe.o sc.o csc.o vpdma.o ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c new file mode 100644 index 000000000000..acfea500710e --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.c @@ -0,0 +1,196 @@ +/* + * Color space converter library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.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/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include "csc.h" + +/* + * 16 coefficients in the order: + * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 + * (we may need to pass non-default values from user space later on, we might + * need to make the coefficient struct more easy to populate) + */ +struct colorspace_coeffs { + u16 sd[12]; + u16 hd[12]; +}; + +/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */ +#define CSC_COEFFS_VIDEO_RANGE_Y2R 0 +#define CSC_COEFFS_GRAPHICS_RANGE_Y2R 1 +#define CSC_COEFFS_VIDEO_RANGE_R2Y 2 +#define CSC_COEFFS_GRAPHICS_RANGE_R2Y 3 + +/* default colorspace coefficients */ +static struct colorspace_coeffs colorspace_coeffs[4] = { + [CSC_COEFFS_VIDEO_RANGE_Y2R] = { + { + /* SDTV */ + 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, + 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, + }, + { + /* HDTV */ + 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, + 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, + }, + }, + [CSC_COEFFS_GRAPHICS_RANGE_Y2R] = { + { + /* SDTV */ + 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, + 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, + }, + { + /* HDTV */ + 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, + 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, + }, + }, + [CSC_COEFFS_VIDEO_RANGE_R2Y] = { + { + /* SDTV */ + 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, + 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, + }, + { + /* HDTV */ + 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, + 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, + }, + }, + [CSC_COEFFS_GRAPHICS_RANGE_R2Y] = { + { + /* SDTV */ + 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, + 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, + }, + { + /* HDTV */ + 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, + 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, + }, + }, +}; + +void csc_dump_regs(struct csc_data *csc) +{ + struct device *dev = &csc->pdev->dev; + + u32 read_reg(struct csc_data *csc, int offset) + { + return ioread32(csc->base + offset); + } + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(csc, CSC_##r)) + + DUMPREG(CSC00); + DUMPREG(CSC01); + DUMPREG(CSC02); + DUMPREG(CSC03); + DUMPREG(CSC04); + DUMPREG(CSC05); + +#undef DUMPREG +} + +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5) +{ + *csc_reg5 |= CSC_BYPASS; +} + +/* + * set the color space converter coefficient shadow register values + */ +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, + enum v4l2_colorspace src_colorspace, + enum v4l2_colorspace dst_colorspace) +{ + u32 *csc_reg5 = csc_reg0 + 5; + u32 *shadow_csc = csc_reg0; + struct colorspace_coeffs *sd_hd_coeffs; + u16 *coeff, *end_coeff; + enum v4l2_colorspace yuv_colorspace; + int sel = 0; + + /* + * support only graphics data range(full range) for now, a control ioctl + * would be nice here + */ + /* Y2R */ + if (dst_colorspace == V4L2_COLORSPACE_SRGB && + (src_colorspace == V4L2_COLORSPACE_SMPTE170M || + src_colorspace == V4L2_COLORSPACE_REC709)) { + /* Y2R */ + sel = 1; + yuv_colorspace = src_colorspace; + } else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M || + dst_colorspace == V4L2_COLORSPACE_REC709) && + src_colorspace == V4L2_COLORSPACE_SRGB) { + /* R2Y */ + sel = 3; + yuv_colorspace = dst_colorspace; + } else { + *csc_reg5 |= CSC_BYPASS; + return; + } + + sd_hd_coeffs = &colorspace_coeffs[sel]; + + /* select between SD or HD coefficients */ + if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M) + coeff = sd_hd_coeffs->sd; + else + coeff = sd_hd_coeffs->hd; + + end_coeff = coeff + 12; + + for (; coeff < end_coeff; coeff += 2) + *shadow_csc++ = (*(coeff + 1) << 16) | *coeff; +} + +struct csc_data *csc_create(struct platform_device *pdev) +{ + struct csc_data *csc; + + dev_dbg(&pdev->dev, "csc_create\n"); + + csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL); + if (!csc) { + dev_err(&pdev->dev, "couldn't alloc csc_data\n"); + return ERR_PTR(-ENOMEM); + } + + csc->pdev = pdev; + + csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "vpe_csc"); + if (csc->res == NULL) { + dev_err(&pdev->dev, "missing platform resources data\n"); + return ERR_PTR(-ENODEV); + } + + csc->base = devm_ioremap_resource(&pdev->dev, csc->res); + if (!csc->base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return ERR_PTR(-ENOMEM); + } + + return csc; +} diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h new file mode 100644 index 000000000000..1ad2b6dad561 --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.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 TI_CSC_H +#define TI_CSC_H + +/* VPE color space converter regs */ +#define CSC_CSC00 0x00 +#define CSC_A0_MASK 0x1fff +#define CSC_A0_SHIFT 0 +#define CSC_B0_MASK 0x1fff +#define CSC_B0_SHIFT 16 + +#define CSC_CSC01 0x04 +#define CSC_C0_MASK 0x1fff +#define CSC_C0_SHIFT 0 +#define CSC_A1_MASK 0x1fff +#define CSC_A1_SHIFT 16 + +#define CSC_CSC02 0x08 +#define CSC_B1_MASK 0x1fff +#define CSC_B1_SHIFT 0 +#define CSC_C1_MASK 0x1fff +#define CSC_C1_SHIFT 16 + +#define CSC_CSC03 0x0c +#define CSC_A2_MASK 0x1fff +#define CSC_A2_SHIFT 0 +#define CSC_B2_MASK 0x1fff +#define CSC_B2_SHIFT 16 + +#define CSC_CSC04 0x10 +#define CSC_C2_MASK 0x1fff +#define CSC_C2_SHIFT 0 +#define CSC_D0_MASK 0x0fff +#define CSC_D0_SHIFT 16 + +#define CSC_CSC05 0x14 +#define CSC_D1_MASK 0x0fff +#define CSC_D1_SHIFT 0 +#define CSC_D2_MASK 0x0fff +#define CSC_D2_SHIFT 16 + +#define CSC_BYPASS (1 << 28) + +struct csc_data { + void __iomem *base; + struct resource *res; + + struct platform_device *pdev; +}; + +void csc_dump_regs(struct csc_data *csc); +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5); +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, + enum v4l2_colorspace src_colorspace, + enum v4l2_colorspace dst_colorspace); +struct csc_data *csc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c new file mode 100644 index 000000000000..93f0af546b76 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.c @@ -0,0 +1,311 @@ +/* + * Scaler library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.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/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "sc.h" +#include "sc_coeff.h" + +void sc_dump_regs(struct sc_data *sc) +{ + struct device *dev = &sc->pdev->dev; + + u32 read_reg(struct sc_data *sc, int offset) + { + return ioread32(sc->base + offset); + } + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(sc, CFG_##r)) + + DUMPREG(SC0); + DUMPREG(SC1); + DUMPREG(SC2); + DUMPREG(SC3); + DUMPREG(SC4); + DUMPREG(SC5); + DUMPREG(SC6); + DUMPREG(SC8); + DUMPREG(SC9); + DUMPREG(SC10); + DUMPREG(SC11); + DUMPREG(SC12); + DUMPREG(SC13); + DUMPREG(SC17); + DUMPREG(SC18); + DUMPREG(SC19); + DUMPREG(SC20); + DUMPREG(SC21); + DUMPREG(SC22); + DUMPREG(SC23); + DUMPREG(SC24); + DUMPREG(SC25); + +#undef DUMPREG +} + +/* + * set the horizontal scaler coefficients according to the ratio of output to + * input widths, after accounting for up to two levels of decimation + */ +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, + unsigned int dst_w) +{ + int sixteenths; + int idx; + int i, j; + u16 *coeff_h = addr; + const u16 *cp; + + if (dst_w > src_w) { + idx = HS_UP_SCALE; + } else { + if ((dst_w << 1) < src_w) + dst_w <<= 1; /* first level decimation */ + if ((dst_w << 1) < src_w) + dst_w <<= 1; /* second level decimation */ + + if (dst_w == src_w) { + idx = HS_LE_16_16_SCALE; + } else { + sixteenths = (dst_w << 4) / src_w; + if (sixteenths < 8) + sixteenths = 8; + idx = HS_LT_9_16_SCALE + sixteenths - 8; + } + } + + if (idx == sc->hs_index) + return; + + cp = scaler_hs_coeffs[idx]; + + for (i = 0; i < SC_NUM_PHASES * 2; i++) { + for (j = 0; j < SC_H_NUM_TAPS; j++) + *coeff_h++ = *cp++; + /* + * for each phase, the scaler expects space for 8 coefficients + * in it's memory. For the horizontal scaler, we copy the first + * 7 coefficients and skip the last slot to move to the next + * row to hold coefficients for the next phase + */ + coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS; + } + + sc->hs_index = idx; + + sc->load_coeff_h = true; +} + +/* + * set the vertical scaler coefficients according to the ratio of output to + * input heights + */ +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, + unsigned int dst_h) +{ + int sixteenths; + int idx; + int i, j; + u16 *coeff_v = addr; + const u16 *cp; + + if (dst_h > src_h) { + idx = VS_UP_SCALE; + } else if (dst_h == src_h) { + idx = VS_1_TO_1_SCALE; + } else { + sixteenths = (dst_h << 4) / src_h; + if (sixteenths < 8) + sixteenths = 8; + idx = VS_LT_9_16_SCALE + sixteenths - 8; + } + + if (idx == sc->vs_index) + return; + + cp = scaler_vs_coeffs[idx]; + + for (i = 0; i < SC_NUM_PHASES * 2; i++) { + for (j = 0; j < SC_V_NUM_TAPS; j++) + *coeff_v++ = *cp++; + /* + * for the vertical scaler, we copy the first 5 coefficients and + * skip the last 3 slots to move to the next row to hold + * coefficients for the next phase + */ + coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS; + } + + sc->vs_index = idx; + sc->load_coeff_v = true; +} + +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, + u32 *sc_reg17, unsigned int src_w, unsigned int src_h, + unsigned int dst_w, unsigned int dst_h) +{ + struct device *dev = &sc->pdev->dev; + u32 val; + int dcm_x, dcm_shift; + bool use_rav; + unsigned long lltmp; + u32 lin_acc_inc, lin_acc_inc_u; + u32 col_acc_offset; + u16 factor = 0; + int row_acc_init_rav = 0, row_acc_init_rav_b = 0; + u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0; + /* + * location of SC register in payload memory with respect to the first + * register in the mmr address data block + */ + u32 *sc_reg9 = sc_reg8 + 1; + u32 *sc_reg12 = sc_reg8 + 4; + u32 *sc_reg13 = sc_reg8 + 5; + u32 *sc_reg24 = sc_reg17 + 7; + + val = sc_reg0[0]; + + /* clear all the features(they may get enabled elsewhere later) */ + val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP | + CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS | + CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS | + CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR); + + if (src_w == dst_w && src_h == dst_h) { + val |= CFG_SC_BYPASS; + sc_reg0[0] = val; + return; + } + + /* we only support linear scaling for now */ + val |= CFG_LINEAR; + + /* configure horizontal scaler */ + + /* enable 2X or 4X decimation */ + dcm_x = src_w / dst_w; + if (dcm_x > 4) { + val |= CFG_DCM_4X; + dcm_shift = 2; + } else if (dcm_x > 2) { + val |= CFG_DCM_2X; + dcm_shift = 1; + } else { + dcm_shift = 0; + } + + lltmp = dst_w - 1; + lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp); + lin_acc_inc_u = 0; + col_acc_offset = 0; + + dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n", + src_w, dst_w, dcm_shift == 2 ? "4x" : + (dcm_shift == 1 ? "2x" : "none"), lin_acc_inc); + + /* configure vertical scaler */ + + /* use RAV for vertical scaler if vertical downscaling is > 4x */ + if (dst_h < (src_h >> 2)) { + use_rav = true; + val |= CFG_USE_RAV; + } else { + use_rav = false; + } + + if (use_rav) { + /* use RAV */ + factor = (u16) ((dst_h << 10) / src_h); + + row_acc_init_rav = factor + ((1 + factor) >> 1); + if (row_acc_init_rav >= 1024) + row_acc_init_rav -= 1024; + + row_acc_init_rav_b = row_acc_init_rav + + (1 + (row_acc_init_rav >> 1)) - + (1024 >> 1); + + if (row_acc_init_rav_b < 0) { + row_acc_init_rav_b += row_acc_init_rav; + row_acc_init_rav *= 2; + } + + dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n", + src_h, dst_h, factor, row_acc_init_rav, + row_acc_init_rav_b); + } else { + /* use polyphase */ + row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1); + row_acc_offset = 0; + row_acc_offset_b = 0; + + dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n", + src_h, dst_h, row_acc_inc); + } + + + sc_reg0[0] = val; + sc_reg0[1] = row_acc_inc; + sc_reg0[2] = row_acc_offset; + sc_reg0[3] = row_acc_offset_b; + + sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) << + CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) | + (dst_h << CFG_TAR_H_SHIFT); + + sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT); + + sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) | + (row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT); + + *sc_reg9 = lin_acc_inc; + + *sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT; + + *sc_reg13 = factor; + + *sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT); +} + +struct sc_data *sc_create(struct platform_device *pdev) +{ + struct sc_data *sc; + + dev_dbg(&pdev->dev, "sc_create\n"); + + sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); + if (!sc) { + dev_err(&pdev->dev, "couldn't alloc sc_data\n"); + return ERR_PTR(-ENOMEM); + } + + sc->pdev = pdev; + + sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sc"); + if (!sc->res) { + dev_err(&pdev->dev, "missing platform resources data\n"); + return ERR_PTR(-ENODEV); + } + + sc->base = devm_ioremap_resource(&pdev->dev, sc->res); + if (!sc->base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return ERR_PTR(-ENOMEM); + } + + return sc; +} diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h new file mode 100644 index 000000000000..60e411e05c30 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.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 TI_SC_H +#define TI_SC_H + +/* Scaler regs */ +#define CFG_SC0 0x0 +#define CFG_INTERLACE_O (1 << 0) +#define CFG_LINEAR (1 << 1) +#define CFG_SC_BYPASS (1 << 2) +#define CFG_INVT_FID (1 << 3) +#define CFG_USE_RAV (1 << 4) +#define CFG_ENABLE_EV (1 << 5) +#define CFG_AUTO_HS (1 << 6) +#define CFG_DCM_2X (1 << 7) +#define CFG_DCM_4X (1 << 8) +#define CFG_HP_BYPASS (1 << 9) +#define CFG_INTERLACE_I (1 << 10) +#define CFG_ENABLE_SIN2_VER_INTP (1 << 11) +#define CFG_Y_PK_EN (1 << 14) +#define CFG_TRIM (1 << 15) +#define CFG_SELFGEN_FID (1 << 16) + +#define CFG_SC1 0x4 +#define CFG_ROW_ACC_INC_MASK 0x07ffffff +#define CFG_ROW_ACC_INC_SHIFT 0 + +#define CFG_SC2 0x08 +#define CFG_ROW_ACC_OFFSET_MASK 0x0fffffff +#define CFG_ROW_ACC_OFFSET_SHIFT 0 + +#define CFG_SC3 0x0c +#define CFG_ROW_ACC_OFFSET_B_MASK 0x0fffffff +#define CFG_ROW_ACC_OFFSET_B_SHIFT 0 + +#define CFG_SC4 0x10 +#define CFG_TAR_H_MASK 0x07ff +#define CFG_TAR_H_SHIFT 0 +#define CFG_TAR_W_MASK 0x07ff +#define CFG_TAR_W_SHIFT 12 +#define CFG_LIN_ACC_INC_U_MASK 0x07 +#define CFG_LIN_ACC_INC_U_SHIFT 24 +#define CFG_NLIN_ACC_INIT_U_MASK 0x07 +#define CFG_NLIN_ACC_INIT_U_SHIFT 28 + +#define CFG_SC5 0x14 +#define CFG_SRC_H_MASK 0x07ff +#define CFG_SRC_H_SHIFT 0 +#define CFG_SRC_W_MASK 0x07ff +#define CFG_SRC_W_SHIFT 12 +#define CFG_NLIN_ACC_INC_U_MASK 0x07 +#define CFG_NLIN_ACC_INC_U_SHIFT 24 + +#define CFG_SC6 0x18 +#define CFG_ROW_ACC_INIT_RAV_MASK 0x03ff +#define CFG_ROW_ACC_INIT_RAV_SHIFT 0 +#define CFG_ROW_ACC_INIT_RAV_B_MASK 0x03ff +#define CFG_ROW_ACC_INIT_RAV_B_SHIFT 10 + +#define CFG_SC8 0x20 +#define CFG_NLIN_LEFT_MASK 0x07ff +#define CFG_NLIN_LEFT_SHIFT 0 +#define CFG_NLIN_RIGHT_MASK 0x07ff +#define CFG_NLIN_RIGHT_SHIFT 12 + +#define CFG_SC9 0x24 +#define CFG_LIN_ACC_INC CFG_SC9 + +#define CFG_SC10 0x28 +#define CFG_NLIN_ACC_INIT CFG_SC10 + +#define CFG_SC11 0x2c +#define CFG_NLIN_ACC_INC CFG_SC11 + +#define CFG_SC12 0x30 +#define CFG_COL_ACC_OFFSET_MASK 0x01ffffff +#define CFG_COL_ACC_OFFSET_SHIFT 0 + +#define CFG_SC13 0x34 +#define CFG_SC_FACTOR_RAV_MASK 0xff +#define CFG_SC_FACTOR_RAV_SHIFT 0 +#define CFG_CHROMA_INTP_THR_MASK 0x03ff +#define CFG_CHROMA_INTP_THR_SHIFT 12 +#define CFG_DELTA_CHROMA_THR_MASK 0x0f +#define CFG_DELTA_CHROMA_THR_SHIFT 24 + +#define CFG_SC17 0x44 +#define CFG_EV_THR_MASK 0x03ff +#define CFG_EV_THR_SHIFT 12 +#define CFG_DELTA_LUMA_THR_MASK 0x0f +#define CFG_DELTA_LUMA_THR_SHIFT 24 +#define CFG_DELTA_EV_THR_MASK 0x0f +#define CFG_DELTA_EV_THR_SHIFT 28 + +#define CFG_SC18 0x48 +#define CFG_HS_FACTOR_MASK 0x03ff +#define CFG_HS_FACTOR_SHIFT 0 +#define CFG_CONF_DEFAULT_MASK 0x01ff +#define CFG_CONF_DEFAULT_SHIFT 16 + +#define CFG_SC19 0x4c +#define CFG_HPF_COEFF0_MASK 0xff +#define CFG_HPF_COEFF0_SHIFT 0 +#define CFG_HPF_COEFF1_MASK 0xff +#define CFG_HPF_COEFF1_SHIFT 8 +#define CFG_HPF_COEFF2_MASK 0xff +#define CFG_HPF_COEFF2_SHIFT 16 +#define CFG_HPF_COEFF3_MASK 0xff +#define CFG_HPF_COEFF3_SHIFT 23 + +#define CFG_SC20 0x50 +#define CFG_HPF_COEFF4_MASK 0xff +#define CFG_HPF_COEFF4_SHIFT 0 +#define CFG_HPF_COEFF5_MASK 0xff +#define CFG_HPF_COEFF5_SHIFT 8 +#define CFG_HPF_NORM_SHIFT_MASK 0x07 +#define CFG_HPF_NORM_SHIFT_SHIFT 16 +#define CFG_NL_LIMIT_MASK 0x1ff +#define CFG_NL_LIMIT_SHIFT 20 + +#define CFG_SC21 0x54 +#define CFG_NL_LO_THR_MASK 0x01ff +#define CFG_NL_LO_THR_SHIFT 0 +#define CFG_NL_LO_SLOPE_MASK 0xff +#define CFG_NL_LO_SLOPE_SHIFT 16 + +#define CFG_SC22 0x58 +#define CFG_NL_HI_THR_MASK 0x01ff +#define CFG_NL_HI_THR_SHIFT 0 +#define CFG_NL_HI_SLOPE_SH_MASK 0x07 +#define CFG_NL_HI_SLOPE_SH_SHIFT 16 + +#define CFG_SC23 0x5c +#define CFG_GRADIENT_THR_MASK 0x07ff +#define CFG_GRADIENT_THR_SHIFT 0 +#define CFG_GRADIENT_THR_RANGE_MASK 0x0f +#define CFG_GRADIENT_THR_RANGE_SHIFT 12 +#define CFG_MIN_GY_THR_MASK 0xff +#define CFG_MIN_GY_THR_SHIFT 16 +#define CFG_MIN_GY_THR_RANGE_MASK 0x0f +#define CFG_MIN_GY_THR_RANGE_SHIFT 28 + +#define CFG_SC24 0x60 +#define CFG_ORG_H_MASK 0x07ff +#define CFG_ORG_H_SHIFT 0 +#define CFG_ORG_W_MASK 0x07ff +#define CFG_ORG_W_SHIFT 16 + +#define CFG_SC25 0x64 +#define CFG_OFF_H_MASK 0x07ff +#define CFG_OFF_H_SHIFT 0 +#define CFG_OFF_W_MASK 0x07ff +#define CFG_OFF_W_SHIFT 16 + +/* number of phases supported by the polyphase scalers */ +#define SC_NUM_PHASES 32 + +/* number of taps used by horizontal polyphase scaler */ +#define SC_H_NUM_TAPS 7 + +/* number of taps used by vertical polyphase scaler */ +#define SC_V_NUM_TAPS 5 + +/* number of taps expected by the scaler in it's coefficient memory */ +#define SC_NUM_TAPS_MEM_ALIGN 8 + +/* + * coefficient memory size in bytes: + * num phases x num sets(luma and chroma) x num taps(aligned) x coeff size + */ +#define SC_COEF_SRAM_SIZE (SC_NUM_PHASES * 2 * SC_NUM_TAPS_MEM_ALIGN * 2) + +struct sc_data { + void __iomem *base; + struct resource *res; + + dma_addr_t loaded_coeff_h; /* loaded h coeffs in SC */ + dma_addr_t loaded_coeff_v; /* loaded v coeffs in SC */ + + bool load_coeff_h; /* have new h SC coeffs */ + bool load_coeff_v; /* have new v SC coeffs */ + + unsigned int hs_index; /* h SC coeffs selector */ + unsigned int vs_index; /* v SC coeffs selector */ + + struct platform_device *pdev; +}; + +void sc_dump_regs(struct sc_data *sc); +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, + unsigned int dst_w); +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, + unsigned int dst_h); +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, + u32 *sc_reg17, unsigned int src_w, unsigned int src_h, + unsigned int dst_w, unsigned int dst_h); +struct sc_data *sc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti-vpe/sc_coeff.h new file mode 100644 index 000000000000..5bfa5c03aec6 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc_coeff.h @@ -0,0 +1,1342 @@ +/* + * VPE SC coefs + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.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 __TI_SC_COEFF_H +#define __TI_SC_COEFF_H + +/* horizontal scaler coefficients */ +enum { + HS_UP_SCALE = 0, + HS_LT_9_16_SCALE, + HS_LT_10_16_SCALE, + HS_LT_11_16_SCALE, + HS_LT_12_16_SCALE, + HS_LT_13_16_SCALE, + HS_LT_14_16_SCALE, + HS_LT_15_16_SCALE, + HS_LE_16_16_SCALE, +}; + +static const u16 scaler_hs_coeffs[13][SC_NUM_PHASES * 2 * SC_H_NUM_TAPS] = { + [HS_UP_SCALE] = { + /* Luma */ + 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, + 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, + 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, + 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, + 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, + 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, + 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, + 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, + 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, + 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, + 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, + 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, + 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, + 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, + 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, + 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, + 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, + 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, + 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, + 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, + 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, + 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, + 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, + 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, + 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, + 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, + 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, + 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, + 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, + 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, + 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, + 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, + /* Chroma */ + 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, + 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, + 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, + 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, + 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, + 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, + 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, + 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, + 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, + 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, + 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, + 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, + 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, + 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, + 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, + 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, + 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, + 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, + 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, + 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, + 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, + 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, + 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, + 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, + 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, + 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, + 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, + 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, + 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, + 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, + 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, + 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, + }, + [HS_LT_9_16_SCALE] = { + /* Luma */ + 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, + 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, + 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, + 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, + 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, + 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, + 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, + 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, + 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, + 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, + 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, + 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, + 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, + 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, + 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, + 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, + 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, + 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, + 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, + 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, + 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, + 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, + 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, + 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, + 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, + 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, + 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, + 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, + 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, + 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, + 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, + 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, + /* Chroma */ + 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, + 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, + 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, + 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, + 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, + 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, + 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, + 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, + 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, + 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, + 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, + 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, + 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, + 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, + 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, + 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, + 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, + 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, + 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, + 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, + 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, + 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, + 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, + 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, + 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, + 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, + 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, + 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, + 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, + 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, + 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, + 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, + }, + [HS_LT_10_16_SCALE] = { + /* Luma */ + 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, + 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, + 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, + 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, + 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, + 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, + 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, + 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, + 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, + 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, + 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, + 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, + 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, + 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, + 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, + 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, + 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, + 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, + 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, + 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, + 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, + 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, + 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, + 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, + 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, + 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, + 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, + 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, + 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, + 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, + 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, + 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, + /* Chroma */ + 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, + 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, + 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, + 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, + 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, + 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, + 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, + 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, + 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, + 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, + 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, + 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, + 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, + 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, + 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, + 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, + 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, + 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, + 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, + 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, + 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, + 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, + 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, + 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, + 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, + 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, + 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, + 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, + 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, + 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, + 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, + 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, + }, + [HS_LT_11_16_SCALE] = { + /* Luma */ + 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, + 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, + 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, + 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, + 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, + 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, + 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, + 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, + 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, + 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, + 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, + 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, + 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, + 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, + 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, + 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, + 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, + 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, + 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, + 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, + 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, + 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, + 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, + 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, + 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, + 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, + 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, + 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, + 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, + 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, + 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, + 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, + /* Chroma */ + 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, + 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, + 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, + 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, + 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, + 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, + 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, + 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, + 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, + 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, + 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, + 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, + 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, + 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, + 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, + 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, + 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, + 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, + 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, + 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, + 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, + 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, + 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, + 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, + 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, + 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, + 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, + 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, + 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, + 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, + 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, + 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, + }, + [HS_LT_12_16_SCALE] = { + /* Luma */ + 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, + 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, + 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, + 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, + 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, + 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, + 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, + 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, + 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, + 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, + 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, + 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, + 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, + 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, + 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, + 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, + 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, + 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, + 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, + 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, + 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, + 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, + 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, + 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, + 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, + 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, + 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, + 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, + 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, + 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, + 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, + 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, + /* Chroma */ + 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, + 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, + 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, + 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, + 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, + 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, + 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, + 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, + 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, + 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, + 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, + 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, + 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, + 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, + 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, + 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, + 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, + 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, + 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, + 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, + 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, + 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, + 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, + 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, + 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, + 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, + 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, + 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, + 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, + 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, + 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, + 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, + }, + [HS_LT_13_16_SCALE] = { + /* Luma */ + 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, + 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, + 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, + 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, + 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, + 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, + 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, + 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, + 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, + 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, + 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, + 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, + 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, + 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, + 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, + 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, + 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, + 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, + 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, + 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, + 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, + 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, + 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, + 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, + 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, + 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, + 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, + 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, + 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, + 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, + 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, + 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, + /* Chroma */ + 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, + 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, + 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, + 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, + 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, + 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, + 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, + 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, + 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, + 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, + 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, + 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, + 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, + 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, + 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, + 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, + 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, + 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, + 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, + 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, + 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, + 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, + 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, + 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, + 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, + 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, + 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, + 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, + 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, + 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, + 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, + 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, + }, + [HS_LT_14_16_SCALE] = { + /* Luma */ + 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, + 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, + 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, + 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, + 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, + 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, + 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, + 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, + 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, + 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, + 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, + 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, + 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, + 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, + 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, + 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, + 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, + 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, + 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, + 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, + 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, + 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, + 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, + 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, + 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, + 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, + 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, + 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, + 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, + 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, + 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, + 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, + /* Chroma */ + 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, + 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, + 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, + 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, + 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, + 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, + 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, + 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, + 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, + 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, + 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, + 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, + 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, + 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, + 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, + 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, + 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, + 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, + 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, + 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, + 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, + 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, + 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, + 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, + 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, + 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, + 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, + 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, + 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, + 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, + 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, + 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, + }, + [HS_LT_15_16_SCALE] = { + /* Luma */ + 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, + 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, + 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, + 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, + 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, + 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, + 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, + 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, + 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, + 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, + 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, + 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, + 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, + 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, + 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, + 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, + 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, + 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, + 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, + 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, + 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, + 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, + 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, + 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, + 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, + 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, + 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, + 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, + 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, + 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, + 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, + 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, + /* Chroma */ + 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, + 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, + 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, + 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, + 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, + 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, + 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, + 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, + 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, + 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, + 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, + 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, + 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, + 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, + 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, + 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, + 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, + 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, + 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, + 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, + 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, + 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, + 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, + 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, + 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, + 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, + 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, + 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, + 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, + 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, + 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, + 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, + }, + [HS_LE_16_16_SCALE] = { + /* Luma */ + 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, + 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, + 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, + 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, + 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, + 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, + 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, + 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, + 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, + 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, + 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, + 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, + 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, + 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, + 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, + 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, + 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, + 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, + 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, + 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, + 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, + 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, + 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, + 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, + 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, + 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, + 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, + 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, + 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, + 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, + 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, + 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, + /* Chroma */ + 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, + 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, + 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, + 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, + 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, + 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, + 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, + 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, + 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, + 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, + 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, + 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, + 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, + 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, + 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, + 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, + 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, + 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, + 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, + 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, + 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, + 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, + 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, + 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, + 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, + 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, + 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, + 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, + 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, + 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, + 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, + 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, + }, +}; + +/* vertical scaler coefficients */ +enum { + VS_UP_SCALE = 0, + VS_LT_9_16_SCALE, + VS_LT_10_16_SCALE, + VS_LT_11_16_SCALE, + VS_LT_12_16_SCALE, + VS_LT_13_16_SCALE, + VS_LT_14_16_SCALE, + VS_LT_15_16_SCALE, + VS_LT_16_16_SCALE, + VS_1_TO_1_SCALE, +}; + +static const u16 scaler_vs_coeffs[15][SC_NUM_PHASES * 2 * SC_V_NUM_TAPS] = { + [VS_UP_SCALE] = { + /* Luma */ + 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + /* Chroma */ + 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + }, + [VS_LT_9_16_SCALE] = { + /* Luma */ + 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, + 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, + 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, + 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, + 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, + 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, + 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, + 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, + 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, + 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, + 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, + 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, + 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, + 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, + 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, + 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, + 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, + 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, + 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, + 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, + 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, + 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, + 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, + 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, + 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, + 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, + 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, + 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, + 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, + 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, + 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, + 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, + /* Chroma */ + 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, + 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, + 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, + 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, + 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, + 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, + 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, + 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, + 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, + 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, + 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, + 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, + 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, + 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, + 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, + 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, + 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, + 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, + 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, + 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, + 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, + 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, + 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, + 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, + 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, + 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, + 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, + 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, + 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, + 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, + 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, + 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, + }, + [VS_LT_10_16_SCALE] = { + /* Luma */ + 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, + 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, + 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, + 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, + 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, + 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, + 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, + 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, + 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, + 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, + 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, + 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, + 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, + 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, + 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, + 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, + 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, + 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, + 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, + 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, + 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, + 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, + 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, + 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, + 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, + 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, + 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, + 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, + 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, + 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, + 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, + 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, + /* Chroma */ + 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, + 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, + 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, + 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, + 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, + 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, + 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, + 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, + 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, + 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, + 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, + 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, + 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, + 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, + 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, + 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, + 0x0094, 0x036C, 0x036C, 0x0094, 0x0000, + 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, + 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, + 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, + 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, + 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, + 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, + 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, + 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, + 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, + 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, + 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, + 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, + 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, + 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, + 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, + }, + [VS_LT_11_16_SCALE] = { + /* Luma */ + 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, + 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, + 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, + 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, + 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, + 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, + 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, + 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, + 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, + 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, + 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, + 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, + 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, + 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, + 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, + 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, + 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, + 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, + 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, + 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, + 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, + 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, + 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, + 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, + 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, + 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, + 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, + 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, + 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, + 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, + 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, + 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, + /* Chroma */ + 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, + 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, + 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, + 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, + 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, + 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, + 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, + 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, + 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, + 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, + 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, + 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, + 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, + 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, + 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, + 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, + 0x0069, 0x0397, 0x0397, 0x0069, 0x0000, + 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, + 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, + 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, + 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, + 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, + 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, + 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, + 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, + 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, + 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, + 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, + 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, + 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, + 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, + 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, + }, + [VS_LT_12_16_SCALE] = { + /* Luma */ + 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, + 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, + 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, + 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, + 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, + 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, + 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, + 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, + 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, + 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, + 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, + 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, + 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, + 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, + 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, + 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, + 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, + 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, + 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, + 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, + 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, + 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, + 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, + 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, + 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, + 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, + 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, + 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, + 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, + 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, + 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, + 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, + /* Chroma */ + 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, + 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, + 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, + 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, + 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, + 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, + 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, + 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, + 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, + 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, + 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, + 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, + 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, + 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, + 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, + 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, + 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, + 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, + 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, + 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, + 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, + 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, + 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, + 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, + 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, + 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, + 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, + 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, + 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, + 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, + 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, + 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, + }, + [VS_LT_13_16_SCALE] = { + /* Luma */ + 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, + 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, + 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, + 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, + 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, + 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, + 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, + 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, + 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, + 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, + 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, + 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, + 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, + 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, + 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, + 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, + 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, + 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, + 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, + 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, + 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, + 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, + 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, + 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, + 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, + 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, + 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, + 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, + 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, + 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, + 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, + 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, + /* Chroma */ + 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, + 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, + 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, + 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, + 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, + 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, + 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, + 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, + 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, + 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, + 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, + 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, + 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, + 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, + 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, + 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, + 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, + 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, + 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, + 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, + 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, + 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, + 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, + 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, + 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, + 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, + 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, + 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, + 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, + 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, + 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, + 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, + }, + [VS_LT_14_16_SCALE] = { + /* Luma */ + 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, + 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, + 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, + 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, + 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, + 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, + 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, + 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, + 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, + 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, + 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, + 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, + 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, + 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, + 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, + 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, + 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, + 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, + 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, + 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, + 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, + 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, + 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, + 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, + 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, + 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, + 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, + 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, + 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, + 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, + 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, + 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, + /* Chroma */ + 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, + 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, + 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, + 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, + 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, + 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, + 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, + 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, + 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, + 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, + 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, + 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, + 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, + 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, + 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, + 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, + 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, + 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, + 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, + 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, + 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, + 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, + 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, + 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, + 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, + 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, + 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, + 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, + 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, + 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, + 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, + 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, + }, + [VS_LT_15_16_SCALE] = { + /* Luma */ + 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, + 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, + 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, + 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, + 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, + 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, + 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, + 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, + 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, + 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, + 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, + 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, + 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, + 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, + 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, + 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, + 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, + 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, + 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, + 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, + 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, + 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, + 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, + 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, + 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, + 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, + 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, + 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, + 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, + 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, + 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, + 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, + /* Chroma */ + 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, + 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, + 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, + 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, + 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, + 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, + 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, + 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, + 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, + 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, + 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, + 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, + 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, + 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, + 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, + 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, + 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, + 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, + 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, + 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, + 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, + 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, + 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, + 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, + 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, + 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, + 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, + 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, + 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, + 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, + 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, + 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, + }, + [VS_LT_16_16_SCALE] = { + /* Luma */ + 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, + 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, + 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, + 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, + 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, + 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, + 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, + 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, + 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, + 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, + 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, + 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, + 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, + 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, + 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, + 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, + 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, + 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, + 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, + 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, + 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, + 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, + 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, + 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, + 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, + 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, + 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, + 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, + 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, + 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, + 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, + 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, + /* Chroma */ + 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, + 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, + 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, + 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, + 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, + 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, + 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, + 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, + 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, + 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, + 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, + 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, + 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, + 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, + 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, + 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, + 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, + 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, + 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, + 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, + 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, + 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, + 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, + 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, + 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, + 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, + 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, + 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, + 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, + 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, + 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, + 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, + }, + [VS_1_TO_1_SCALE] = { + /* Luma */ + 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + /* Chroma */ + 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, + 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, + 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, + 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, + 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, + 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, + 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, + 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, + 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, + 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, + 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, + 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, + 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, + 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, + 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, + 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, + 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, + 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, + 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, + 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, + 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, + 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, + 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, + 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, + 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, + 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, + 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, + 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, + 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, + 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, + 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, + 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, + }, +}; +#endif diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index fcbe48a09cf8..e8175e7938ed 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -30,38 +30,47 @@ const struct vpdma_data_format vpdma_yuv_fmts[] = { [VPDMA_DATA_FMT_Y444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_Y444, .depth = 8, }, [VPDMA_DATA_FMT_Y422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_Y422, .depth = 8, }, [VPDMA_DATA_FMT_Y420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_Y420, .depth = 8, }, [VPDMA_DATA_FMT_C444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_C444, .depth = 8, }, [VPDMA_DATA_FMT_C422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_C422, .depth = 8, }, [VPDMA_DATA_FMT_C420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_C420, .depth = 4, }, [VPDMA_DATA_FMT_YC422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_YC422, .depth = 16, }, [VPDMA_DATA_FMT_YC444] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_YC444, .depth = 24, }, [VPDMA_DATA_FMT_CY422] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_CY422, .depth = 16, }, @@ -69,82 +78,102 @@ const struct vpdma_data_format vpdma_yuv_fmts[] = { const struct vpdma_data_format vpdma_rgb_fmts[] = { [VPDMA_DATA_FMT_RGB565] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGB16_565, .depth = 16, }, [VPDMA_DATA_FMT_ARGB16_1555] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ARGB_1555, .depth = 16, }, [VPDMA_DATA_FMT_ARGB16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ARGB_4444, .depth = 16, }, [VPDMA_DATA_FMT_RGBA16_5551] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGBA_5551, .depth = 16, }, [VPDMA_DATA_FMT_RGBA16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGBA_4444, .depth = 16, }, [VPDMA_DATA_FMT_ARGB24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ARGB24_6666, .depth = 24, }, [VPDMA_DATA_FMT_RGB24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGB24_888, .depth = 24, }, [VPDMA_DATA_FMT_ARGB32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ARGB32_8888, .depth = 32, }, [VPDMA_DATA_FMT_RGBA24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGBA24_6666, .depth = 24, }, [VPDMA_DATA_FMT_RGBA32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_RGBA32_8888, .depth = 32, }, [VPDMA_DATA_FMT_BGR565] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGR16_565, .depth = 16, }, [VPDMA_DATA_FMT_ABGR16_1555] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ABGR_1555, .depth = 16, }, [VPDMA_DATA_FMT_ABGR16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ABGR_4444, .depth = 16, }, [VPDMA_DATA_FMT_BGRA16_5551] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGRA_5551, .depth = 16, }, [VPDMA_DATA_FMT_BGRA16] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGRA_4444, .depth = 16, }, [VPDMA_DATA_FMT_ABGR24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ABGR24_6666, .depth = 24, }, [VPDMA_DATA_FMT_BGR24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGR24_888, .depth = 24, }, [VPDMA_DATA_FMT_ABGR32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_ABGR32_8888, .depth = 32, }, [VPDMA_DATA_FMT_BGRA24] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGRA24_6666, .depth = 24, }, [VPDMA_DATA_FMT_BGRA32] = { + .type = VPDMA_DATA_FMT_TYPE_RGB, .data_type = DATA_TYPE_BGRA32_8888, .depth = 32, }, @@ -152,6 +181,7 @@ const struct vpdma_data_format vpdma_rgb_fmts[] = { const struct vpdma_data_format vpdma_misc_fmts[] = { [VPDMA_DATA_FMT_MV] = { + .type = VPDMA_DATA_FMT_TYPE_MISC, .data_type = DATA_TYPE_MV, .depth = 4, }, @@ -599,10 +629,11 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, channel = next_chan = chan_info[chan].num; - if (fmt->data_type == DATA_TYPE_C420) + if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && + fmt->data_type == DATA_TYPE_C420) depth = 8; - stride = (depth * c_rect->width) >> 3; + stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN); dma_addr += (c_rect->left * depth) >> 3; dtd = list->next; @@ -649,13 +680,14 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width, channel = next_chan = chan_info[chan].num; - if (fmt->data_type == DATA_TYPE_C420) { + if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && + fmt->data_type == DATA_TYPE_C420) { height >>= 1; frame_height >>= 1; depth = 8; } - stride = (depth * c_rect->width) >> 3; + stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN); dma_addr += (c_rect->left * depth) >> 3; dtd = list->next; diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index eaa2a71a5db9..cf40f11b3c8f 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -39,13 +39,23 @@ struct vpdma_data { bool ready; }; +enum vpdma_data_format_type { + VPDMA_DATA_FMT_TYPE_YUV, + VPDMA_DATA_FMT_TYPE_RGB, + VPDMA_DATA_FMT_TYPE_MISC, +}; + struct vpdma_data_format { + enum vpdma_data_format_type type; int data_type; u8 depth; }; #define VPDMA_DESC_ALIGN 16 /* 16-byte descriptor alignment */ - +#define VPDMA_STRIDE_ALIGN 16 /* + * line stride of source and dest + * buffers should be 16 byte aligned + */ #define VPDMA_DTD_DESC_SIZE 32 /* 8 words */ #define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */ diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h index f0e9a8038c1b..c1a6ce1884f3 100644 --- a/drivers/media/platform/ti-vpe/vpdma_priv.h +++ b/drivers/media/platform/ti-vpe/vpdma_priv.h @@ -78,7 +78,7 @@ #define DATA_TYPE_C420 0x6 #define DATA_TYPE_YC422 0x7 #define DATA_TYPE_YC444 0x8 -#define DATA_TYPE_CY422 0x23 +#define DATA_TYPE_CY422 0x27 #define DATA_TYPE_RGB16_565 0x0 #define DATA_TYPE_ARGB_1555 0x1 diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 4e58069e24ff..1296c5386231 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/log2.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> @@ -42,6 +43,8 @@ #include "vpdma.h" #include "vpe_regs.h" +#include "sc.h" +#include "csc.h" #define VPE_MODULE_NAME "vpe" @@ -54,10 +57,6 @@ /* required alignments */ #define S_ALIGN 0 /* multiple of 1 */ #define H_ALIGN 1 /* multiple of 2 */ -#define W_ALIGN 1 /* multiple of 2 */ - -/* multiple of 128 bits, line stride, 16 bytes */ -#define L_ALIGN 4 /* flags that indicate a format can be used for capture/output */ #define VPE_FMT_TYPE_CAPTURE (1 << 0) @@ -268,6 +267,38 @@ static struct vpe_fmt vpe_formats[] = { .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422], }, }, + { + .name = "RGB888 packed", + .fourcc = V4L2_PIX_FMT_RGB24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .name = "ARGB32", + .fourcc = V4L2_PIX_FMT_RGB32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .name = "BGR888 packed", + .fourcc = V4L2_PIX_FMT_BGR24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], + }, + }, + { + .name = "ABGR32", + .fourcc = V4L2_PIX_FMT_BGR32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], + }, + }, }; /* @@ -327,9 +358,12 @@ struct vpe_dev { int irq; void __iomem *base; + struct resource *res; struct vb2_alloc_ctx *alloc_ctx; struct vpdma_data *vpdma; /* vpdma data handle */ + struct sc_data *sc; /* scaler data handle */ + struct csc_data *csc; /* csc data handle */ }; /* @@ -356,6 +390,8 @@ struct vpe_ctx { void *mv_buf[2]; /* virtual addrs of motion vector bufs */ size_t mv_buf_size; /* current motion vector buffer size */ struct vpdma_buf mmr_adb; /* shadow reg addr/data block */ + struct vpdma_buf sc_coeff_h; /* h coeff buffer */ + struct vpdma_buf sc_coeff_v; /* v coeff buffer */ struct vpdma_desc_list desc_list; /* DMA descriptor list */ bool deinterlacing; /* using de-interlacer */ @@ -438,14 +474,23 @@ struct vpe_mmr_adb { u32 us3_regs[8]; struct vpdma_adb_hdr dei_hdr; u32 dei_regs[8]; - struct vpdma_adb_hdr sc_hdr; - u32 sc_regs[1]; - u32 sc_pad[3]; + struct vpdma_adb_hdr sc_hdr0; + u32 sc_regs0[7]; + u32 sc_pad0[1]; + struct vpdma_adb_hdr sc_hdr8; + u32 sc_regs8[6]; + u32 sc_pad8[2]; + struct vpdma_adb_hdr sc_hdr17; + u32 sc_regs17[9]; + u32 sc_pad17[3]; struct vpdma_adb_hdr csc_hdr; u32 csc_regs[6]; u32 csc_pad[2]; }; +#define GET_OFFSET_TOP(ctx, obj, reg) \ + ((obj)->res->start - ctx->dev->res->start + reg) + #define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \ VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) /* @@ -458,8 +503,14 @@ static void init_adb_hdrs(struct vpe_ctx *ctx) VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); - VPE_SET_MMR_ADB_HDR(ctx, sc_hdr, sc_regs, VPE_SC_MP_SC0); - VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); + VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, + GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); }; /* @@ -670,17 +721,20 @@ static void set_src_registers(struct vpe_ctx *ctx) static void set_dst_registers(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace; struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; u32 val = 0; - /* select RGB path when color space conversion is supported in future */ - if (fmt->fourcc == V4L2_PIX_FMT_RGB24) - val |= VPE_RGB_OUT_SELECT | VPE_CSC_SRC_DEI_SCALER; + if (clrspc == V4L2_COLORSPACE_SRGB) + val |= VPE_RGB_OUT_SELECT; else if (fmt->fourcc == V4L2_PIX_FMT_NV16) val |= VPE_COLOR_SEPARATE_422; - /* The source of CHR_DS is always the scaler, whether it's used or not */ - val |= VPE_DS_SRC_DEI_SCALER; + /* + * the source of CHR_DS and CSC is always the scaler, irrespective of + * whether it's used or not + */ + val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; if (fmt->fourcc != V4L2_PIX_FMT_NV12) val |= VPE_DS_BYPASS; @@ -742,28 +796,6 @@ static void set_dei_shadow_registers(struct vpe_ctx *ctx) ctx->load_mmrs = true; } -static void set_csc_coeff_bypass(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *shadow_csc_reg5 = &mmr_adb->csc_regs[5]; - - *shadow_csc_reg5 |= VPE_CSC_BYPASS; - - ctx->load_mmrs = true; -} - -static void set_sc_regs_bypass(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *sc_reg0 = &mmr_adb->sc_regs[0]; - u32 val = 0; - - val |= VPE_SC_BYPASS; - *sc_reg0 = val; - - ctx->load_mmrs = true; -} - /* * Set the shadow registers whose values are modified when either the * source or destination format is changed. @@ -772,6 +804,11 @@ static int set_srcdst_params(struct vpe_ctx *ctx) { struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + unsigned int src_w = s_q_data->c_rect.width; + unsigned int src_h = s_q_data->c_rect.height; + unsigned int dst_w = d_q_data->c_rect.width; + unsigned int dst_h = d_q_data->c_rect.height; size_t mv_buf_size; int ret; @@ -780,12 +817,23 @@ static int set_srcdst_params(struct vpe_ctx *ctx) if ((s_q_data->flags & Q_DATA_INTERLACED) && !(d_q_data->flags & Q_DATA_INTERLACED)) { + int bytes_per_line; const struct vpdma_data_format *mv = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + /* + * we make sure that the source image has a 16 byte aligned + * stride, we need to do the same for the motion vector buffer + * by aligning it's stride to the next 16 byte boundry. this + * extra space will not be used by the de-interlacer, but will + * ensure that vpdma operates correctly + */ + bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3, + VPDMA_STRIDE_ALIGN); + mv_buf_size = bytes_per_line * s_q_data->height; + ctx->deinterlacing = 1; - mv_buf_size = - (s_q_data->width * s_q_data->height * mv->depth) >> 3; + src_h <<= 1; } else { ctx->deinterlacing = 0; mv_buf_size = 0; @@ -799,8 +847,16 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); - set_csc_coeff_bypass(ctx); - set_sc_regs_bypass(ctx); + + csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], + s_q_data->colorspace, d_q_data->colorspace); + + sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); + sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); + + sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], + &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], + src_w, src_h, dst_w, dst_h); return 0; } @@ -916,35 +972,10 @@ static void vpe_dump_regs(struct vpe_dev *dev) DUMPREG(DEI_FMD_STATUS_R0); DUMPREG(DEI_FMD_STATUS_R1); DUMPREG(DEI_FMD_STATUS_R2); - DUMPREG(SC_MP_SC0); - DUMPREG(SC_MP_SC1); - DUMPREG(SC_MP_SC2); - DUMPREG(SC_MP_SC3); - DUMPREG(SC_MP_SC4); - DUMPREG(SC_MP_SC5); - DUMPREG(SC_MP_SC6); - DUMPREG(SC_MP_SC8); - DUMPREG(SC_MP_SC9); - DUMPREG(SC_MP_SC10); - DUMPREG(SC_MP_SC11); - DUMPREG(SC_MP_SC12); - DUMPREG(SC_MP_SC13); - DUMPREG(SC_MP_SC17); - DUMPREG(SC_MP_SC18); - DUMPREG(SC_MP_SC19); - DUMPREG(SC_MP_SC20); - DUMPREG(SC_MP_SC21); - DUMPREG(SC_MP_SC22); - DUMPREG(SC_MP_SC23); - DUMPREG(SC_MP_SC24); - DUMPREG(SC_MP_SC25); - DUMPREG(CSC_CSC00); - DUMPREG(CSC_CSC01); - DUMPREG(CSC_CSC02); - DUMPREG(CSC_CSC03); - DUMPREG(CSC_CSC04); - DUMPREG(CSC_CSC05); #undef DUMPREG + + sc_dump_regs(dev->sc); + csc_dump_regs(dev->csc); } static void add_out_dtd(struct vpe_ctx *ctx, int port) @@ -1053,6 +1084,7 @@ static void disable_irqs(struct vpe_ctx *ctx) static void device_run(void *priv) { struct vpe_ctx *ctx = priv; + struct sc_data *sc = ctx->dev->sc; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { @@ -1075,13 +1107,37 @@ static void device_run(void *priv) ctx->load_mmrs = false; } + if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || + sc->load_coeff_h) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_h, 0); + + sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; + sc->load_coeff_h = false; + } + + if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || + sc->load_coeff_v) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + + sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; + sc->load_coeff_v = false; + } + /* output data descriptors */ if (ctx->deinterlacing) add_out_dtd(ctx, VPE_PORT_MV_OUT); - add_out_dtd(ctx, VPE_PORT_LUMA_OUT); - if (d_q_data->fmt->coplanar) - add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); + if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + add_out_dtd(ctx, VPE_PORT_RGB_OUT); + } else { + add_out_dtd(ctx, VPE_PORT_LUMA_OUT); + if (d_q_data->fmt->coplanar) + add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); + } /* input data descriptors */ if (ctx->deinterlacing) { @@ -1117,9 +1173,16 @@ static void device_run(void *priv) } /* sync on channel control descriptors for output ports */ - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA_OUT); - if (d_q_data->fmt->coplanar) - vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA_OUT); + if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_RGB_OUT); + } else { + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_LUMA_OUT); + if (d_q_data->fmt->coplanar) + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_CHROMA_OUT); + } if (ctx->deinterlacing) vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT); @@ -1198,6 +1261,8 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); vpdma_reset_desc_list(&ctx->desc_list); @@ -1352,7 +1417,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct v4l2_plane_pix_format *plane_fmt; - int i; + unsigned int w_align; + int i, depth, depth_bytes; if (!fmt || !(fmt->types & type)) { vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n", @@ -1363,35 +1429,57 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE) pix->field = V4L2_FIELD_NONE; - v4l_bound_align_image(&pix->width, MIN_W, MAX_W, W_ALIGN, + depth = fmt->vpdma_fmt[VPE_LUMA]->depth; + + /* + * the line stride should 16 byte aligned for VPDMA to work, based on + * the bytes per pixel, figure out how much the width should be aligned + * to make sure line stride is 16 byte aligned + */ + depth_bytes = depth >> 3; + + if (depth_bytes == 3) + /* + * if bpp is 3(as in some RGB formats), the pixel width doesn't + * really help in ensuring line stride is 16 byte aligned + */ + w_align = 4; + else + /* + * for the remainder bpp(4, 2 and 1), the pixel width alignment + * can ensure a line stride alignment of 16 bytes. For example, + * if bpp is 2, then the line stride can be 16 byte aligned if + * the width is 8 byte aligned + */ + w_align = order_base_2(VPDMA_DESC_ALIGN / depth_bytes); + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, &pix->height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); pix->num_planes = fmt->coplanar ? 2 : 1; pix->pixelformat = fmt->fourcc; - if (type == VPE_FMT_TYPE_CAPTURE) { - struct vpe_q_data *s_q_data; - - /* get colorspace from the source queue */ - s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - - pix->colorspace = s_q_data->colorspace; - } else { - if (!pix->colorspace) - pix->colorspace = V4L2_COLORSPACE_SMPTE240M; + if (!pix->colorspace) { + if (fmt->fourcc == V4L2_PIX_FMT_RGB24 || + fmt->fourcc == V4L2_PIX_FMT_BGR24 || + fmt->fourcc == V4L2_PIX_FMT_RGB32 || + fmt->fourcc == V4L2_PIX_FMT_BGR32) { + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + if (pix->height > 1280) /* HD */ + pix->colorspace = V4L2_COLORSPACE_REC709; + else /* SD */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + } } for (i = 0; i < pix->num_planes; i++) { - int depth; - plane_fmt = &pix->plane_fmt[i]; depth = fmt->vpdma_fmt[i]->depth; if (i == VPE_LUMA) - plane_fmt->bytesperline = - round_up((pix->width * depth) >> 3, - 1 << L_ALIGN); + plane_fmt->bytesperline = (pix->width * depth) >> 3; else plane_fmt->bytesperline = pix->width; @@ -1749,6 +1837,14 @@ static int vpe_open(struct file *file) if (ret != 0) goto free_desc_list; + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_mmr_adb; + + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_sc_h; + init_adb_hdrs(ctx); v4l2_fh_init(&ctx->fh, video_devdata(file)); @@ -1770,7 +1866,7 @@ static int vpe_open(struct file *file) s_q_data->height = 1080; s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M; + s_q_data->colorspace = V4L2_COLORSPACE_SMPTE170M; s_q_data->field = V4L2_FIELD_NONE; s_q_data->c_rect.left = 0; s_q_data->c_rect.top = 0; @@ -1817,6 +1913,10 @@ static int vpe_open(struct file *file) exit_fh: v4l2_ctrl_handler_free(hdl); v4l2_fh_exit(&ctx->fh); + vpdma_free_desc_buf(&ctx->sc_coeff_v); +free_sc_h: + vpdma_free_desc_buf(&ctx->sc_coeff_h); +free_mmr_adb: vpdma_free_desc_buf(&ctx->mmr_adb); free_desc_list: vpdma_free_desc_list(&ctx->desc_list); @@ -1938,12 +2038,11 @@ static int vpe_probe(struct platform_device *pdev) { struct vpe_dev *dev; struct video_device *vfd; - struct resource *res; int ret, irq, func; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (IS_ERR(dev)) - return PTR_ERR(dev); + if (!dev) + return -ENOMEM; spin_lock_init(&dev->lock); @@ -1954,16 +2053,17 @@ static int vpe_probe(struct platform_device *pdev) atomic_set(&dev->num_instances, 0); mutex_init(&dev->dev_mutex); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpe_top"); + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "vpe_top"); /* * HACK: we get resource info from device tree in the form of a list of * VPE sub blocks, the driver currently uses only the base of vpe_top * for register access, the driver should be changed later to access * registers based on the sub block base addresses */ - dev->base = devm_ioremap(&pdev->dev, res->start, SZ_32K); - if (IS_ERR(dev->base)) { - ret = PTR_ERR(dev->base); + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); + if (!dev->base) { + ret = -ENOMEM; goto v4l2_dev_unreg; } @@ -2006,9 +2106,23 @@ static int vpe_probe(struct platform_device *pdev) vpe_top_vpdma_reset(dev); + dev->sc = sc_create(pdev); + if (IS_ERR(dev->sc)) { + ret = PTR_ERR(dev->sc); + goto runtime_put; + } + + dev->csc = csc_create(pdev); + if (IS_ERR(dev->csc)) { + ret = PTR_ERR(dev->csc); + goto runtime_put; + } + dev->vpdma = vpdma_create(pdev); - if (IS_ERR(dev->vpdma)) + if (IS_ERR(dev->vpdma)) { + ret = PTR_ERR(dev->vpdma); goto runtime_put; + } vfd = &dev->vfd; *vfd = vpe_videodev; @@ -2081,18 +2195,7 @@ static struct platform_driver vpe_pdrv = { }, }; -static void __exit vpe_exit(void) -{ - platform_driver_unregister(&vpe_pdrv); -} - -static int __init vpe_init(void) -{ - return platform_driver_register(&vpe_pdrv); -} - -module_init(vpe_init); -module_exit(vpe_exit); +module_platform_driver(vpe_pdrv); MODULE_DESCRIPTION("TI VPE driver"); MODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>"); diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h index ed214e828398..74283d79eae1 100644 --- a/drivers/media/platform/ti-vpe/vpe_regs.h +++ b/drivers/media/platform/ti-vpe/vpe_regs.h @@ -306,191 +306,4 @@ #define VPE_FMD_FRAME_DIFF_MASK 0x000fffff #define VPE_FMD_FRAME_DIFF_SHIFT 0 -/* VPE scaler regs */ -#define VPE_SC_MP_SC0 0x0700 -#define VPE_INTERLACE_O (1 << 0) -#define VPE_LINEAR (1 << 1) -#define VPE_SC_BYPASS (1 << 2) -#define VPE_INVT_FID (1 << 3) -#define VPE_USE_RAV (1 << 4) -#define VPE_ENABLE_EV (1 << 5) -#define VPE_AUTO_HS (1 << 6) -#define VPE_DCM_2X (1 << 7) -#define VPE_DCM_4X (1 << 8) -#define VPE_HP_BYPASS (1 << 9) -#define VPE_INTERLACE_I (1 << 10) -#define VPE_ENABLE_SIN2_VER_INTP (1 << 11) -#define VPE_Y_PK_EN (1 << 14) -#define VPE_TRIM (1 << 15) -#define VPE_SELFGEN_FID (1 << 16) - -#define VPE_SC_MP_SC1 0x0704 -#define VPE_ROW_ACC_INC_MASK 0x07ffffff -#define VPE_ROW_ACC_INC_SHIFT 0 - -#define VPE_SC_MP_SC2 0x0708 -#define VPE_ROW_ACC_OFFSET_MASK 0x0fffffff -#define VPE_ROW_ACC_OFFSET_SHIFT 0 - -#define VPE_SC_MP_SC3 0x070c -#define VPE_ROW_ACC_OFFSET_B_MASK 0x0fffffff -#define VPE_ROW_ACC_OFFSET_B_SHIFT 0 - -#define VPE_SC_MP_SC4 0x0710 -#define VPE_TAR_H_MASK 0x07ff -#define VPE_TAR_H_SHIFT 0 -#define VPE_TAR_W_MASK 0x07ff -#define VPE_TAR_W_SHIFT 12 -#define VPE_LIN_ACC_INC_U_MASK 0x07 -#define VPE_LIN_ACC_INC_U_SHIFT 24 -#define VPE_NLIN_ACC_INIT_U_MASK 0x07 -#define VPE_NLIN_ACC_INIT_U_SHIFT 28 - -#define VPE_SC_MP_SC5 0x0714 -#define VPE_SRC_H_MASK 0x07ff -#define VPE_SRC_H_SHIFT 0 -#define VPE_SRC_W_MASK 0x07ff -#define VPE_SRC_W_SHIFT 12 -#define VPE_NLIN_ACC_INC_U_MASK 0x07 -#define VPE_NLIN_ACC_INC_U_SHIFT 24 - -#define VPE_SC_MP_SC6 0x0718 -#define VPE_ROW_ACC_INIT_RAV_MASK 0x03ff -#define VPE_ROW_ACC_INIT_RAV_SHIFT 0 -#define VPE_ROW_ACC_INIT_RAV_B_MASK 0x03ff -#define VPE_ROW_ACC_INIT_RAV_B_SHIFT 10 - -#define VPE_SC_MP_SC8 0x0720 -#define VPE_NLIN_LEFT_MASK 0x07ff -#define VPE_NLIN_LEFT_SHIFT 0 -#define VPE_NLIN_RIGHT_MASK 0x07ff -#define VPE_NLIN_RIGHT_SHIFT 12 - -#define VPE_SC_MP_SC9 0x0724 -#define VPE_LIN_ACC_INC VPE_SC_MP_SC9 - -#define VPE_SC_MP_SC10 0x0728 -#define VPE_NLIN_ACC_INIT VPE_SC_MP_SC10 - -#define VPE_SC_MP_SC11 0x072c -#define VPE_NLIN_ACC_INC VPE_SC_MP_SC11 - -#define VPE_SC_MP_SC12 0x0730 -#define VPE_COL_ACC_OFFSET_MASK 0x01ffffff -#define VPE_COL_ACC_OFFSET_SHIFT 0 - -#define VPE_SC_MP_SC13 0x0734 -#define VPE_SC_FACTOR_RAV_MASK 0x03ff -#define VPE_SC_FACTOR_RAV_SHIFT 0 -#define VPE_CHROMA_INTP_THR_MASK 0x03ff -#define VPE_CHROMA_INTP_THR_SHIFT 12 -#define VPE_DELTA_CHROMA_THR_MASK 0x0f -#define VPE_DELTA_CHROMA_THR_SHIFT 24 - -#define VPE_SC_MP_SC17 0x0744 -#define VPE_EV_THR_MASK 0x03ff -#define VPE_EV_THR_SHIFT 12 -#define VPE_DELTA_LUMA_THR_MASK 0x0f -#define VPE_DELTA_LUMA_THR_SHIFT 24 -#define VPE_DELTA_EV_THR_MASK 0x0f -#define VPE_DELTA_EV_THR_SHIFT 28 - -#define VPE_SC_MP_SC18 0x0748 -#define VPE_HS_FACTOR_MASK 0x03ff -#define VPE_HS_FACTOR_SHIFT 0 -#define VPE_CONF_DEFAULT_MASK 0x01ff -#define VPE_CONF_DEFAULT_SHIFT 16 - -#define VPE_SC_MP_SC19 0x074c -#define VPE_HPF_COEFF0_MASK 0xff -#define VPE_HPF_COEFF0_SHIFT 0 -#define VPE_HPF_COEFF1_MASK 0xff -#define VPE_HPF_COEFF1_SHIFT 8 -#define VPE_HPF_COEFF2_MASK 0xff -#define VPE_HPF_COEFF2_SHIFT 16 -#define VPE_HPF_COEFF3_MASK 0xff -#define VPE_HPF_COEFF3_SHIFT 23 - -#define VPE_SC_MP_SC20 0x0750 -#define VPE_HPF_COEFF4_MASK 0xff -#define VPE_HPF_COEFF4_SHIFT 0 -#define VPE_HPF_COEFF5_MASK 0xff -#define VPE_HPF_COEFF5_SHIFT 8 -#define VPE_HPF_NORM_SHIFT_MASK 0x07 -#define VPE_HPF_NORM_SHIFT_SHIFT 16 -#define VPE_NL_LIMIT_MASK 0x1ff -#define VPE_NL_LIMIT_SHIFT 20 - -#define VPE_SC_MP_SC21 0x0754 -#define VPE_NL_LO_THR_MASK 0x01ff -#define VPE_NL_LO_THR_SHIFT 0 -#define VPE_NL_LO_SLOPE_MASK 0xff -#define VPE_NL_LO_SLOPE_SHIFT 16 - -#define VPE_SC_MP_SC22 0x0758 -#define VPE_NL_HI_THR_MASK 0x01ff -#define VPE_NL_HI_THR_SHIFT 0 -#define VPE_NL_HI_SLOPE_SH_MASK 0x07 -#define VPE_NL_HI_SLOPE_SH_SHIFT 16 - -#define VPE_SC_MP_SC23 0x075c -#define VPE_GRADIENT_THR_MASK 0x07ff -#define VPE_GRADIENT_THR_SHIFT 0 -#define VPE_GRADIENT_THR_RANGE_MASK 0x0f -#define VPE_GRADIENT_THR_RANGE_SHIFT 12 -#define VPE_MIN_GY_THR_MASK 0xff -#define VPE_MIN_GY_THR_SHIFT 16 -#define VPE_MIN_GY_THR_RANGE_MASK 0x0f -#define VPE_MIN_GY_THR_RANGE_SHIFT 28 - -#define VPE_SC_MP_SC24 0x0760 -#define VPE_ORG_H_MASK 0x07ff -#define VPE_ORG_H_SHIFT 0 -#define VPE_ORG_W_MASK 0x07ff -#define VPE_ORG_W_SHIFT 16 - -#define VPE_SC_MP_SC25 0x0764 -#define VPE_OFF_H_MASK 0x07ff -#define VPE_OFF_H_SHIFT 0 -#define VPE_OFF_W_MASK 0x07ff -#define VPE_OFF_W_SHIFT 16 - -/* VPE color space converter regs */ -#define VPE_CSC_CSC00 0x5700 -#define VPE_CSC_A0_MASK 0x1fff -#define VPE_CSC_A0_SHIFT 0 -#define VPE_CSC_B0_MASK 0x1fff -#define VPE_CSC_B0_SHIFT 16 - -#define VPE_CSC_CSC01 0x5704 -#define VPE_CSC_C0_MASK 0x1fff -#define VPE_CSC_C0_SHIFT 0 -#define VPE_CSC_A1_MASK 0x1fff -#define VPE_CSC_A1_SHIFT 16 - -#define VPE_CSC_CSC02 0x5708 -#define VPE_CSC_B1_MASK 0x1fff -#define VPE_CSC_B1_SHIFT 0 -#define VPE_CSC_C1_MASK 0x1fff -#define VPE_CSC_C1_SHIFT 16 - -#define VPE_CSC_CSC03 0x570c -#define VPE_CSC_A2_MASK 0x1fff -#define VPE_CSC_A2_SHIFT 0 -#define VPE_CSC_B2_MASK 0x1fff -#define VPE_CSC_B2_SHIFT 16 - -#define VPE_CSC_CSC04 0x5710 -#define VPE_CSC_C2_MASK 0x1fff -#define VPE_CSC_C2_SHIFT 0 -#define VPE_CSC_D0_MASK 0x0fff -#define VPE_CSC_D0_SHIFT 16 - -#define VPE_CSC_CSC05 0x5714 -#define VPE_CSC_D1_MASK 0x0fff -#define VPE_CSC_D1_SHIFT 0 -#define VPE_CSC_D2_MASK 0x0fff -#define VPE_CSC_D2_SHIFT 16 -#define VPE_CSC_BYPASS (1 << 28) - #endif diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 4da226169e15..151cecd0ea25 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,5 +1,6 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o -vsp1-y += vsp1_lif.o vsp1_uds.o +vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o +vsp1-y += vsp1_sru.o vsp1_uds.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index d6c6ecd039ff..94d1b02680c5 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -28,8 +28,11 @@ struct clk; struct device; struct vsp1_platform_data; +struct vsp1_hsit; struct vsp1_lif; +struct vsp1_lut; struct vsp1_rwpf; +struct vsp1_sru; struct vsp1_uds; #define VPS1_MAX_RPF 5 @@ -47,8 +50,12 @@ struct vsp1_device { struct mutex lock; int ref_count; + struct vsp1_hsit *hsi; + struct vsp1_hsit *hst; struct vsp1_lif *lif; + struct vsp1_lut *lut; struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; + struct vsp1_sru *sru; struct vsp1_uds *uds[VPS1_MAX_UDS]; struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index d16bf0f41e24..0df0a994e575 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -20,8 +20,11 @@ #include <linux/videodev2.h> #include "vsp1.h" +#include "vsp1_hsit.h" #include "vsp1_lif.h" +#include "vsp1_lut.h" #include "vsp1_rwpf.h" +#include "vsp1_sru.h" #include "vsp1_uds.h" /* ----------------------------------------------------------------------------- @@ -152,6 +155,22 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Instantiate all the entities. */ + vsp1->hsi = vsp1_hsit_create(vsp1, true); + if (IS_ERR(vsp1->hsi)) { + ret = PTR_ERR(vsp1->hsi); + goto done; + } + + list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); + + vsp1->hst = vsp1_hsit_create(vsp1, false); + if (IS_ERR(vsp1->hst)) { + ret = PTR_ERR(vsp1->hst); + goto done; + } + + list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); + if (vsp1->pdata->features & VSP1_HAS_LIF) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { @@ -162,6 +181,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); } + if (vsp1->pdata->features & VSP1_HAS_LUT) { + vsp1->lut = vsp1_lut_create(vsp1); + if (IS_ERR(vsp1->lut)) { + ret = PTR_ERR(vsp1->lut); + goto done; + } + + list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); + } + for (i = 0; i < vsp1->pdata->rpf_count; ++i) { struct vsp1_rwpf *rpf; @@ -175,6 +204,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&rpf->entity.list_dev, &vsp1->entities); } + if (vsp1->pdata->features & VSP1_HAS_SRU) { + vsp1->sru = vsp1_sru_create(vsp1); + if (IS_ERR(vsp1->sru)) { + ret = PTR_ERR(vsp1->sru); + goto done; + } + + list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); + } + for (i = 0; i < vsp1->pdata->uds_count; ++i) { struct vsp1_uds *uds; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 9028f9d524f4..0226e47df6d9 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -15,6 +15,7 @@ #include <linux/gfp.h> #include <media/media-entity.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> #include "vsp1.h" @@ -122,12 +123,16 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, unsigned int id; unsigned int reg; } routes[] = { + { VI6_DPR_NODE_HSI, VI6_DPR_HSI_ROUTE }, + { VI6_DPR_NODE_HST, VI6_DPR_HST_ROUTE }, { VI6_DPR_NODE_LIF, 0 }, + { VI6_DPR_NODE_LUT, VI6_DPR_LUT_ROUTE }, { VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) }, { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, { VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) }, { VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) }, { VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) }, + { VI6_DPR_NODE_SRU, VI6_DPR_SRU_ROUTE }, { VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) }, { VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) }, { VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) }, @@ -177,5 +182,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { + if (entity->subdev.ctrl_handler) + v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index c4feab2cbb81..e152798d7f38 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -20,8 +20,12 @@ struct vsp1_device; enum vsp1_entity_type { + VSP1_ENTITY_HSI, + VSP1_ENTITY_HST, VSP1_ENTITY_LIF, + VSP1_ENTITY_LUT, VSP1_ENTITY_RPF, + VSP1_ENTITY_SRU, VSP1_ENTITY_UDS, VSP1_ENTITY_WPF, }; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c new file mode 100644 index 000000000000..285485350d82 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -0,0 +1,222 @@ +/* + * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_hsit.h" + +#define HSIT_MIN_SIZE 4U +#define HSIT_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg) +{ + return vsp1_read(hsit->entity.vsp1, reg); +} + +static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) +{ + vsp1_write(hsit->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_hsit *hsit = to_hsit(subdev); + + if (!enable) + return 0; + + if (hsit->inverse) + vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); + else + vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vsp1_hsit *hsit = to_hsit(subdev); + + if (code->index > 0) + return -EINVAL; + + if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) | + (code->pad == HSIT_PAD_SOURCE && hsit->inverse)) + code->code = V4L2_MBUS_FMT_ARGB8888_1X32; + else + code->code = V4L2_MBUS_FMT_AHSV8888_1X32; + + return 0; +} + +static int hsit_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == HSIT_PAD_SINK) { + fse->min_width = HSIT_MIN_SIZE; + fse->max_width = HSIT_MAX_SIZE; + fse->min_height = HSIT_MIN_SIZE; + fse->max_height = HSIT_MAX_SIZE; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int hsit_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_hsit *hsit = to_hsit(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static int hsit_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_hsit *hsit = to_hsit(subdev); + struct v4l2_mbus_framefmt *format; + + format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == HSIT_PAD_SOURCE) { + /* The HST and HSI output format code and resolution can't be + * modified. + */ + fmt->format = *format; + return 0; + } + + format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32 + : V4L2_MBUS_FMT_ARGB8888_1X32; + format->width = clamp_t(unsigned int, fmt->format.width, + HSIT_MIN_SIZE, HSIT_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + HSIT_MIN_SIZE, HSIT_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE, + fmt->which); + *format = fmt->format; + format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32 + : V4L2_MBUS_FMT_AHSV8888_1X32; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops hsit_video_ops = { + .s_stream = hsit_s_stream, +}; + +static struct v4l2_subdev_pad_ops hsit_pad_ops = { + .enum_mbus_code = hsit_enum_mbus_code, + .enum_frame_size = hsit_enum_frame_size, + .get_fmt = hsit_get_format, + .set_fmt = hsit_set_format, +}; + +static struct v4l2_subdev_ops hsit_ops = { + .video = &hsit_video_ops, + .pad = &hsit_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) +{ + struct v4l2_subdev *subdev; + struct vsp1_hsit *hsit; + int ret; + + hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL); + if (hsit == NULL) + return ERR_PTR(-ENOMEM); + + hsit->inverse = inverse; + + if (inverse) { + hsit->entity.type = VSP1_ENTITY_HSI; + hsit->entity.id = VI6_DPR_NODE_HSI; + } else { + hsit->entity.type = VSP1_ENTITY_HST; + hsit->entity.id = VI6_DPR_NODE_HST; + } + + ret = vsp1_entity_init(vsp1, &hsit->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &hsit->entity.subdev; + v4l2_subdev_init(subdev, &hsit_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s %s", + dev_name(vsp1->dev), inverse ? "hsi" : "hst"); + v4l2_set_subdevdata(subdev, hsit); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return hsit; +} diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h new file mode 100644 index 000000000000..82f1c8426900 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_hsit.h @@ -0,0 +1,38 @@ +/* + * vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ +#ifndef __VSP1_HSIT_H__ +#define __VSP1_HSIT_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define HSIT_PAD_SINK 0 +#define HSIT_PAD_SOURCE 1 + +struct vsp1_hsit { + struct vsp1_entity entity; + bool inverse; +}; + +static inline struct vsp1_hsit *to_hsit(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_hsit, entity.subdev); +} + +struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse); + +#endif /* __VSP1_HSIT_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c new file mode 100644 index 000000000000..4e9dc7c86ef8 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -0,0 +1,252 @@ +/* + * vsp1_lut.c -- R-Car VSP1 Look-Up Table + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/vsp1.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_lut.h" + +#define LUT_MIN_SIZE 4U +#define LUT_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg) +{ + return vsp1_read(lut->entity.vsp1, reg); +} + +static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) +{ + vsp1_write(lut->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config) +{ + memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut, + sizeof(config->lut)); +} + +static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) +{ + struct vsp1_lut *lut = to_lut(subdev); + + switch (cmd) { + case VIDIOC_VSP1_LUT_CONFIG: + lut_configure(lut, arg); + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int lut_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_lut *lut = to_lut(subdev); + + if (!enable) + return 0; + + vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int lut_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AHSV8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + struct v4l2_mbus_framefmt *format; + + if (code->pad == LUT_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + /* The LUT can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int lut_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == LUT_PAD_SINK) { + fse->min_width = LUT_MIN_SIZE; + fse->max_width = LUT_MAX_SIZE; + fse->min_height = LUT_MIN_SIZE; + fse->max_height = LUT_MAX_SIZE; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lut *lut = to_lut(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lut *lut = to_lut(subdev); + struct v4l2_mbus_framefmt *format; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AHSV8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == LUT_PAD_SOURCE) { + /* The LUT output format can't be modified. */ + fmt->format = *format; + return 0; + } + + format->width = clamp_t(unsigned int, fmt->format.width, + LUT_MIN_SIZE, LUT_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + LUT_MIN_SIZE, LUT_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE, + fmt->which); + *format = fmt->format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_core_ops lut_core_ops = { + .ioctl = lut_ioctl, +}; + +static struct v4l2_subdev_video_ops lut_video_ops = { + .s_stream = lut_s_stream, +}; + +static struct v4l2_subdev_pad_ops lut_pad_ops = { + .enum_mbus_code = lut_enum_mbus_code, + .enum_frame_size = lut_enum_frame_size, + .get_fmt = lut_get_format, + .set_fmt = lut_set_format, +}; + +static struct v4l2_subdev_ops lut_ops = { + .core = &lut_core_ops, + .video = &lut_video_ops, + .pad = &lut_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) +{ + struct v4l2_subdev *subdev; + struct vsp1_lut *lut; + int ret; + + lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL); + if (lut == NULL) + return ERR_PTR(-ENOMEM); + + lut->entity.type = VSP1_ENTITY_LUT; + lut->entity.id = VI6_DPR_NODE_LUT; + + ret = vsp1_entity_init(vsp1, &lut->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &lut->entity.subdev; + v4l2_subdev_init(subdev, &lut_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s lut", + dev_name(vsp1->dev)); + v4l2_set_subdevdata(subdev, lut); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return lut; +} diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h new file mode 100644 index 000000000000..f92ffb867350 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -0,0 +1,38 @@ +/* + * vsp1_lut.h -- R-Car VSP1 Look-Up Table + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ +#ifndef __VSP1_LUT_H__ +#define __VSP1_LUT_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define LUT_PAD_SINK 0 +#define LUT_PAD_SOURCE 1 + +struct vsp1_lut { + struct vsp1_entity entity; + u32 lut[256]; +}; + +static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_lut, entity.subdev); +} + +struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_LUT_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 1d3304f1365b..28650806c20f 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -336,8 +336,21 @@ */ #define VI6_SRU_CTRL0 0x2200 +#define VI6_SRU_CTRL0_PARAM0_SHIFT 16 +#define VI6_SRU_CTRL0_PARAM1_SHIFT 8 +#define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4) +#define VI6_SRU_CTRL0_PARAM2 (1 << 3) +#define VI6_SRU_CTRL0_PARAM3 (1 << 2) +#define VI6_SRU_CTRL0_PARAM4 (1 << 1) +#define VI6_SRU_CTRL0_EN (1 << 0) + #define VI6_SRU_CTRL1 0x2204 +#define VI6_SRU_CTRL1_PARAM5 0x7ff + #define VI6_SRU_CTRL2 0x2208 +#define VI6_SRU_CTRL2_PARAM6_SHIFT 16 +#define VI6_SRU_CTRL2_PARAM7_SHIFT 8 +#define VI6_SRU_CTRL2_PARAM8_SHIFT 0 /* ----------------------------------------------------------------------------- * UDS Control Registers @@ -412,6 +425,7 @@ */ #define VI6_LUT_CTRL 0x2800 +#define VI6_LUT_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * CLU Control Registers @@ -424,12 +438,14 @@ */ #define VI6_HST_CTRL 0x2a00 +#define VI6_HST_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * HSI Control Registers */ #define VI6_HSI_CTRL 0x2b00 +#define VI6_HSI_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * BRU Control Registers diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 254871d3423e..bce2be5466b9 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -47,25 +47,36 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) struct vsp1_rwpf *rpf = to_rwpf(subdev); const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->video.format; + const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; if (!enable) return 0; - /* Source size and stride. Cropping isn't supported yet. */ + /* Source size, stride and crop offsets. + * + * The crop offsets correspond to the location of the crop rectangle top + * left corner in the plane buffer. Only two offsets are needed, as + * planes 2 and 3 always have identical strides. + */ vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, - (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | - (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, - (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | - (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline + + crop->left * fmtinfo->bpp[0] / 8; pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; - if (format->num_planes > 1) + if (format->num_planes > 1) { + rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline + + crop->left * fmtinfo->bpp[1] / 8; pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + } vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -113,6 +124,8 @@ static struct v4l2_subdev_pad_ops rpf_pad_ops = { .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_rwpf_get_format, .set_fmt = vsp1_rwpf_set_format, + .get_selection = vsp1_rwpf_get_selection, + .set_selection = vsp1_rwpf_set_selection, }; static struct v4l2_subdev_ops rpf_ops = { @@ -129,11 +142,14 @@ static void rpf_vdev_queue(struct vsp1_video *video, { struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, + buf->addr[0] + rpf->offsets[0]); if (buf->buf.num_planes > 1) - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + buf->addr[1] + rpf->offsets[1]); if (buf->buf.num_planes > 2) - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + buf->addr[2] + rpf->offsets[1]); } static const struct vsp1_video_operations rpf_vdev_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 9752d5516ceb..782f770daee5 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -71,6 +71,19 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, return 0; } +static struct v4l2_rect * +vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &rwpf->crop; + default: + return NULL; + } +} + int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { @@ -87,6 +100,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && @@ -115,6 +129,13 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; + /* Update the sink crop rectangle. */ + crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + /* Propagate the format to the source pad. */ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, fmt->which); @@ -122,3 +143,78 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, return 0; } + +int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + + /* Cropping is implemented on the sink pad. */ + if (sel->pad != RWPF_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which); + break; + + case V4L2_SEL_TGT_CROP_BOUNDS: + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, + RWPF_PAD_SINK, sel->which); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + break; + + default: + return -EINVAL; + } + + return 0; +} + +int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Cropping is implemented on the sink pad. */ + if (sel->pad != RWPF_PAD_SINK) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* Make sure the crop rectangle is entirely contained in the image. The + * WPF top and left offsets are limited to 255. + */ + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK, + sel->which); + sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); + sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); + if (rwpf->entity.type == VSP1_ENTITY_WPF) { + sel->r.left = min_t(unsigned int, sel->r.left, 255); + sel->r.top = min_t(unsigned int, sel->r.top, 255); + } + sel->r.width = min_t(unsigned int, sel->r.width, + format->width - sel->r.left); + sel->r.height = min_t(unsigned int, sel->r.height, + format->height - sel->r.top); + + crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which); + *crop = sel->r; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + sel->which); + format->width = crop->width; + format->height = crop->height; + + return 0; +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index c182d85f36b3..6cbdb547470b 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -29,6 +29,10 @@ struct vsp1_rwpf { unsigned int max_width; unsigned int max_height; + + struct v4l2_rect crop; + + unsigned int offsets[2]; }; static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) @@ -49,5 +53,11 @@ int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt); int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt); +int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); +int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c new file mode 100644 index 000000000000..7ab1a0b2d656 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -0,0 +1,356 @@ +/* + * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_sru.h" + +#define SRU_MIN_SIZE 4U +#define SRU_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg) +{ + return vsp1_read(sru->entity.vsp1, reg); +} + +static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) +{ + vsp1_write(sru->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1) + +static int sru_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_sru *sru = + container_of(ctrl->handler, struct vsp1_sru, ctrls); + + switch (ctrl->id) { + case V4L2_CID_VSP1_SRU_INTENSITY: + sru->intensity = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops sru_ctrl_ops = { + .s_ctrl = sru_s_ctrl, +}; + +static const struct v4l2_ctrl_config sru_intensity_control = { + .ops = &sru_ctrl_ops, + .id = V4L2_CID_VSP1_SRU_INTENSITY, + .name = "Intensity", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 6, + .step = 1, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +struct vsp1_sru_param { + u32 ctrl0; + u32 ctrl2; +}; + +#define VI6_SRU_CTRL0_PARAMS(p0, p1) \ + (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) | \ + ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT)) + +#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8) \ + (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) | \ + ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) | \ + ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT)) + +static const struct vsp1_sru_param vsp1_sru_params[] = { + { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255), + }, { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255), + }, { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255), + }, { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255), + }, { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255), + }, { + .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, + .ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255), + }, +}; + +static int sru_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_sru *sru = to_sru(subdev); + const struct vsp1_sru_param *param; + struct v4l2_mbus_framefmt *input; + struct v4l2_mbus_framefmt *output; + bool upscale; + u32 ctrl0; + + if (!enable) + return 0; + + input = &sru->entity.formats[SRU_PAD_SINK]; + output = &sru->entity.formats[SRU_PAD_SOURCE]; + upscale = input->width != output->width; + param = &vsp1_sru_params[sru->intensity]; + + if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32) + ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 + | VI6_SRU_CTRL0_PARAM4; + else + ctrl0 = VI6_SRU_CTRL0_PARAM3; + + vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 | + (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0)); + vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int sru_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + struct v4l2_mbus_framefmt *format; + + if (code->pad == SRU_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + /* The SRU can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int sru_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == SRU_PAD_SINK) { + fse->min_width = SRU_MIN_SIZE; + fse->max_width = SRU_MAX_SIZE; + fse->min_height = SRU_MIN_SIZE; + fse->max_height = SRU_MAX_SIZE; + } else { + fse->min_width = format->width; + fse->min_height = format->height; + if (format->width <= SRU_MAX_SIZE / 2 && + format->height <= SRU_MAX_SIZE / 2) { + fse->max_width = format->width * 2; + fse->max_height = format->height * 2; + } else { + fse->max_width = format->width; + fse->max_height = format->height; + } + } + + return 0; +} + +static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_sru *sru = to_sru(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + unsigned int input_area; + unsigned int output_area; + + switch (pad) { + case SRU_PAD_SINK: + /* Default to YUV if the requested format is not supported. */ + if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; + + fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE); + fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE); + break; + + case SRU_PAD_SOURCE: + /* The SRU can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&sru->entity, fh, + SRU_PAD_SINK, which); + fmt->code = format->code; + + /* We can upscale by 2 in both direction, but not independently. + * Compare the input and output rectangles areas (avoiding + * integer overflows on the output): if the requested output + * area is larger than 1.5^2 the input area upscale by two, + * otherwise don't scale. + */ + input_area = format->width * format->height; + output_area = min(fmt->width, SRU_MAX_SIZE) + * min(fmt->height, SRU_MAX_SIZE); + + if (fmt->width <= SRU_MAX_SIZE / 2 && + fmt->height <= SRU_MAX_SIZE / 2 && + output_area > input_area * 9 / 4) { + fmt->width = format->width * 2; + fmt->height = format->height * 2; + } else { + fmt->width = format->width; + fmt->height = format->height; + } + break; + } + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_sru *sru = to_sru(subdev); + struct v4l2_mbus_framefmt *format; + + sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which); + + format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + fmt->which); + *format = fmt->format; + + if (fmt->pad == SRU_PAD_SINK) { + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&sru->entity, fh, + SRU_PAD_SOURCE, fmt->which); + *format = fmt->format; + + sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops sru_video_ops = { + .s_stream = sru_s_stream, +}; + +static struct v4l2_subdev_pad_ops sru_pad_ops = { + .enum_mbus_code = sru_enum_mbus_code, + .enum_frame_size = sru_enum_frame_size, + .get_fmt = sru_get_format, + .set_fmt = sru_set_format, +}; + +static struct v4l2_subdev_ops sru_ops = { + .video = &sru_video_ops, + .pad = &sru_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) +{ + struct v4l2_subdev *subdev; + struct vsp1_sru *sru; + int ret; + + sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL); + if (sru == NULL) + return ERR_PTR(-ENOMEM); + + sru->entity.type = VSP1_ENTITY_SRU; + sru->entity.id = VI6_DPR_NODE_SRU; + + ret = vsp1_entity_init(vsp1, &sru->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &sru->entity.subdev; + v4l2_subdev_init(subdev, &sru_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s sru", + dev_name(vsp1->dev)); + v4l2_set_subdevdata(subdev, sru); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&sru->ctrls, 1); + v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); + v4l2_ctrl_handler_setup(&sru->ctrls); + sru->entity.subdev.ctrl_handler = &sru->ctrls; + + return sru; +} diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h new file mode 100644 index 000000000000..381870b74780 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -0,0 +1,41 @@ +/* + * vsp1_sru.h -- R-Car VSP1 Super Resolution Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ +#ifndef __VSP1_SRU_H__ +#define __VSP1_SRU_H__ + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define SRU_PAD_SINK 0 +#define SRU_PAD_SOURCE 1 + +struct vsp1_sru { + struct vsp1_entity entity; + + struct v4l2_ctrl_handler ctrls; + unsigned int intensity; +}; + +static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_sru, entity.subdev); +} + +struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_SRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 4b0ac07af662..b4687a834f85 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -488,11 +488,17 @@ static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) * This function completes the current buffer by filling its sequence number, * time stamp and payload size, and hands it back to the videobuf core. * + * When operating in DU output mode (deep pipeline to the DU through the LIF), + * the VSP1 needs to constantly supply frames to the display. In that case, if + * no other buffer is queued, reuse the one that has just been processed instead + * of handing it back to the videobuf core. + * * Return the next queued buffer or NULL if the queue is empty. */ static struct vsp1_video_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); struct vsp1_video_buffer *next = NULL; struct vsp1_video_buffer *done; unsigned long flags; @@ -507,6 +513,13 @@ vsp1_video_complete_buffer(struct vsp1_video *video) done = list_first_entry(&video->irqqueue, struct vsp1_video_buffer, queue); + + /* In DU output mode reuse the buffer if the list is singular. */ + if (pipe->lif && list_is_singular(&video->irqqueue)) { + spin_unlock_irqrestore(&video->irqlock, flags); + return done; + } + list_del(&done->queue); if (!list_empty(&video->irqqueue)) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index db4b85ee05fc..7baed81ff005 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -48,8 +48,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) struct vsp1_pipeline *pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); struct vsp1_device *vsp1 = wpf->entity.vsp1; - const struct v4l2_mbus_framefmt *format = - &wpf->entity.formats[RWPF_PAD_SOURCE]; + const struct v4l2_rect *crop = &wpf->crop; unsigned int i; u32 srcrpf = 0; u32 outfmt = 0; @@ -68,7 +67,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); - /* Destination stride. Cropping isn't supported yet. */ + /* Destination stride. */ if (!pipe->lif) { struct v4l2_pix_format_mplane *format = &wpf->video.format; @@ -79,10 +78,12 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) format->plane_fmt[1].bytesperline); } - vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, - format->width << VI6_WPF_SZCLIP_SIZE_SHIFT); - vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, - format->height << VI6_WPF_SZCLIP_SIZE_SHIFT); + vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | + (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | + (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); /* Format */ if (!pipe->lif) { @@ -130,6 +131,8 @@ static struct v4l2_subdev_pad_ops wpf_pad_ops = { .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_rwpf_get_format, .set_fmt = vsp1_rwpf_set_format, + .get_selection = vsp1_rwpf_get_selection, + .set_selection = vsp1_rwpf_set_selection, }; static struct v4l2_subdev_ops wpf_ops = { diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 6ecdc39bb366..192f36f2f4aa 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -21,6 +21,12 @@ config RADIO_SI470X source "drivers/media/radio/si470x/Kconfig" +config RADIO_SI4713 + tristate "Silicon Labs Si4713 FM Radio with RDS Transmitter support" + depends on VIDEO_V4L2 + +source "drivers/media/radio/si4713/Kconfig" + config RADIO_SI476X tristate "Silicon Laboratories Si476x I2C FM Radio" depends on I2C && VIDEO_V4L2 @@ -113,29 +119,6 @@ config RADIO_SHARK2 To compile this driver as a module, choose M here: the module will be called radio-shark2. -config I2C_SI4713 - tristate "I2C driver for Silicon Labs Si4713 device" - depends on I2C && VIDEO_V4L2 - ---help--- - Say Y here if you want support to Si4713 I2C device. - This device driver supports only i2c bus. - - To compile this driver as a module, choose M here: the - module will be called si4713. - -config RADIO_SI4713 - tristate "Silicon Labs Si4713 FM Radio Transmitter support" - depends on I2C && VIDEO_V4L2 - select I2C_SI4713 - ---help--- - Say Y here if you want support to Si4713 FM Radio Transmitter. - This device can transmit audio through FM. It can transmit - RDS and RBDS signals as well. This module is the v4l2 radio - interface for the i2c driver of this device. - - To compile this driver as a module, choose M here: the - module will be called radio-si4713. - config USB_KEENE tristate "Keene FM Transmitter USB support" depends on USB && VIDEO_V4L2 @@ -146,6 +129,20 @@ config USB_KEENE To compile this driver as a module, choose M here: the module will be called radio-keene. +config USB_RAREMONO + tristate "Thanko's Raremono AM/FM/SW radio support" + depends on USB && VIDEO_V4L2 + ---help--- + The 'Thanko's Raremono' device contains the Si4734 chip from Silicon Labs Inc. + It is one of the very few or perhaps the only consumer USB radio device + to receive the AM/FM/SW bands. + + Say Y here if you want to connect this type of AM/FM/SW receiver + to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called radio-raremono. + config USB_MA901 tristate "Masterkit MA901 USB FM radio support" depends on USB && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 3b645601800d..120e791199b2 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -17,12 +17,11 @@ obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o 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/ +obj-$(CONFIG_RADIO_SI4713) += si4713/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_USB_KEENE) += radio-keene.o obj-$(CONFIG_USB_MA901) += radio-ma901.o @@ -33,6 +32,7 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ obj-$(CONFIG_RADIO_TEA575X) += tea575x.o +obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o shark2-objs := radio-shark2.o radio-tea5777.o diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c new file mode 100644 index 000000000000..7b3bdbb1be73 --- /dev/null +++ b/drivers/media/radio/radio-raremono.c @@ -0,0 +1,387 @@ +/* + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/hid.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <asm/unaligned.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> + +/* + * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver: + * + * http://www.raremono.jp/product/484.html/ + * + * The USB protocol has been reversed engineered using wireshark, initially + * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil + * <hverkuil@xs4all.nl>. + * + * Sadly the firmware used in this product hides lots of goodies since the + * si4734 has more features than are supported by the firmware. Oh well... + */ + +/* driver and module definitions */ +MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); +MODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver"); +MODULE_LICENSE("GPL v2"); + +/* + * The Device announces itself as Cygnal Integrated Products, Inc. + * + * The vendor and product IDs (and in fact all other lsusb information as + * well) are identical to the si470x Silicon Labs USB FM Radio Reference + * Design board, even though this card has a si4734 device. Clearly the + * designer of this product never bothered to change the USB IDs. + */ + +/* USB Device ID List */ +static struct usb_device_id usb_raremono_device_table[] = { + {USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_raremono_device_table); + +#define BUFFER_LENGTH 64 + +/* Timeout is set to a high value, could probably be reduced. Need more tests */ +#define USB_TIMEOUT 10000 + +/* Frequency limits in KHz */ +#define FM_FREQ_RANGE_LOW 64000 +#define FM_FREQ_RANGE_HIGH 108000 + +#define AM_FREQ_RANGE_LOW 520 +#define AM_FREQ_RANGE_HIGH 1710 + +#define SW_FREQ_RANGE_LOW 2300 +#define SW_FREQ_RANGE_HIGH 26100 + +enum { BAND_FM, BAND_AM, BAND_SW }; + +static const struct v4l2_frequency_band bands[] = { + /* Band FM */ + { + .type = V4L2_TUNER_RADIO, + .index = 0, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = FM_FREQ_RANGE_LOW * 16, + .rangehigh = FM_FREQ_RANGE_HIGH * 16, + .modulation = V4L2_BAND_MODULATION_FM, + }, + /* Band AM */ + { + .type = V4L2_TUNER_RADIO, + .index = 1, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = AM_FREQ_RANGE_LOW * 16, + .rangehigh = AM_FREQ_RANGE_HIGH * 16, + .modulation = V4L2_BAND_MODULATION_AM, + }, + /* Band SW */ + { + .type = V4L2_TUNER_RADIO, + .index = 2, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = SW_FREQ_RANGE_LOW * 16, + .rangehigh = SW_FREQ_RANGE_HIGH * 16, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +struct raremono_device { + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct mutex lock; + + u8 *buffer; + u32 band; + unsigned curfreq; +}; + +static inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct raremono_device, v4l2_dev); +} + +/* Set frequency. */ +static int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq) +{ + unsigned band_offset; + int ret; + + switch (band) { + case BAND_FM: + band_offset = 1; + freq /= 10; + break; + case BAND_AM: + band_offset = 0; + break; + default: + band_offset = 2; + break; + } + radio->buffer[0] = 0x04 + band_offset; + radio->buffer[1] = freq >> 8; + radio->buffer[2] = freq & 0xff; + + ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + HID_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x0300 + radio->buffer[0], 2, + radio->buffer, 3, USB_TIMEOUT); + + if (ret < 0) { + dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret); + return ret; + } + radio->curfreq = (band == BAND_FM) ? freq * 10 : freq; + return 0; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_raremono_device_release. + */ +static void usb_raremono_disconnect(struct usb_interface *intf) +{ + struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf)); + + dev_info(&intf->dev, "Thanko's Raremono disconnected\n"); + + mutex_lock(&radio->lock); + usb_set_intfdata(intf, NULL); + video_unregister_device(&radio->vdev); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* + * Linux Video interface + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct raremono_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-raremono", sizeof(v->driver)); + strlcpy(v->card, "Thanko's Raremono", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + if (band->tuner != 0) + return -EINVAL; + + if (band->index >= ARRAY_SIZE(bands)) + return -EINVAL; + + *band = bands[band->index]; + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct raremono_device *radio = video_drvdata(file); + int ret; + + if (v->index > 0) + return -EINVAL; + + strlcpy(v->name, "AM/FM/SW", sizeof(v->name)); + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = AM_FREQ_RANGE_LOW * 16; + v->rangehigh = FM_FREQ_RANGE_HIGH * 16; + v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ? + V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; + memset(radio->buffer, 1, BUFFER_LENGTH); + ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + 1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (ret < 0) { + dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret); + return ret; + } + v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + return v->index ? -EINVAL : 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct raremono_device *radio = video_drvdata(file); + u32 freq = f->frequency; + unsigned band; + + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8) + band = BAND_FM; + else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8) + band = BAND_AM; + else + band = BAND_SW; + + freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh); + return raremono_cmd_main(radio, band, freq / 16); +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct raremono_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = radio->curfreq * 16; + return 0; +} + +/* File system interface */ +static const struct v4l2_file_operations usb_raremono_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usb_raremono_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_enum_freq_bands = vidioc_enum_freq_bands, +}; + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_raremono_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct raremono_device *radio; + int retval = 0; + + radio = devm_kzalloc(&intf->dev, sizeof(struct raremono_device), GFP_KERNEL); + if (radio) + radio->buffer = devm_kmalloc(&intf->dev, BUFFER_LENGTH, GFP_KERNEL); + + if (!radio || !radio->buffer) + return -ENOMEM; + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + + /* + * This device uses the same USB IDs as the si470x SiLabs reference + * design. So do an additional check: attempt to read the device ID + * from the si470x: the lower 12 bits are 0x0242 for the si470x. The + * Raremono always returns 0x0800 (the meaning of that is unknown, but + * at least it works). + * + * We use this check to determine which device we are dealing with. + */ + msleep(20); + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + HID_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 1, 2, + radio->buffer, 3, 500); + if (retval != 3 || + (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) { + dev_info(&intf->dev, "this is not Thanko's Raremono.\n"); + return -ENODEV; + } + + dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n", + id->idVendor, id->idProduct); + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + return retval; + } + + mutex_init(&radio->lock); + + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_raremono_fops; + radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops; + radio->vdev.lock = &radio->lock; + radio->vdev.release = video_device_release_empty; + + usb_set_intfdata(intf, &radio->v4l2_dev); + + video_set_drvdata(&radio->vdev, radio); + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + raremono_cmd_main(radio, BAND_FM, 95160); + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1); + if (retval == 0) { + dev_info(&intf->dev, "V4L2 device registered as %s\n", + video_device_node_name(&radio->vdev)); + return 0; + } + dev_err(&intf->dev, "could not register video device\n"); + v4l2_device_unregister(&radio->v4l2_dev); + return retval; +} + +/* USB subsystem interface */ +static struct usb_driver usb_raremono_driver = { + .name = "radio-raremono", + .probe = usb_raremono_probe, + .disconnect = usb_raremono_disconnect, + .id_table = usb_raremono_device_table, +}; + +module_usb_driver(usb_raremono_driver); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index d6d4d60261d5..07ef40595efd 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -137,6 +137,8 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); /* interrupt out endpoint 2 every 1 millisecond */ #define UNUSED_REPORT 23 +#define MAX_REPORT_SIZE 64 + /************************************************************************** @@ -208,7 +210,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); */ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) { - unsigned char *report = (unsigned char *) buf; + unsigned char *report = buf; int retval; retval = usb_control_msg(radio->usbdev, @@ -231,7 +233,7 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) */ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) { - unsigned char *report = (unsigned char *) buf; + unsigned char *report = buf; int retval; retval = usb_control_msg(radio->usbdev, @@ -254,15 +256,14 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) */ int si470x_get_register(struct si470x_device *radio, int regnr) { - unsigned char buf[REGISTER_REPORT_SIZE]; int retval; - buf[0] = REGISTER_REPORT(regnr); + radio->usb_buf[0] = REGISTER_REPORT(regnr); - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + retval = si470x_get_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); if (retval >= 0) - radio->registers[regnr] = get_unaligned_be16(&buf[1]); + radio->registers[regnr] = get_unaligned_be16(&radio->usb_buf[1]); return (retval < 0) ? -EINVAL : 0; } @@ -273,13 +274,12 @@ int si470x_get_register(struct si470x_device *radio, int regnr) */ int si470x_set_register(struct si470x_device *radio, int regnr) { - unsigned char buf[REGISTER_REPORT_SIZE]; int retval; - buf[0] = REGISTER_REPORT(regnr); - put_unaligned_be16(radio->registers[regnr], &buf[1]); + radio->usb_buf[0] = REGISTER_REPORT(regnr); + put_unaligned_be16(radio->registers[regnr], &radio->usb_buf[1]); - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + retval = si470x_set_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); return (retval < 0) ? -EINVAL : 0; } @@ -295,18 +295,17 @@ int si470x_set_register(struct si470x_device *radio, int regnr) */ static int si470x_get_all_registers(struct si470x_device *radio) { - unsigned char buf[ENTIRE_REPORT_SIZE]; int retval; unsigned char regnr; - buf[0] = ENTIRE_REPORT; + radio->usb_buf[0] = ENTIRE_REPORT; - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + retval = si470x_get_report(radio, radio->usb_buf, ENTIRE_REPORT_SIZE); if (retval >= 0) for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) radio->registers[regnr] = get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); + &radio->usb_buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -323,14 +322,13 @@ static int si470x_get_all_registers(struct si470x_device *radio) static int si470x_set_led_state(struct si470x_device *radio, unsigned char led_state) { - unsigned char buf[LED_REPORT_SIZE]; int retval; - buf[0] = LED_REPORT; - buf[1] = LED_COMMAND; - buf[2] = led_state; + radio->usb_buf[0] = LED_REPORT; + radio->usb_buf[1] = LED_COMMAND; + radio->usb_buf[2] = led_state; - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + retval = si470x_set_report(radio, radio->usb_buf, LED_REPORT_SIZE); return (retval < 0) ? -EINVAL : 0; } @@ -346,19 +344,18 @@ static int si470x_set_led_state(struct si470x_device *radio, */ static int si470x_get_scratch_page_versions(struct si470x_device *radio) { - unsigned char buf[SCRATCH_REPORT_SIZE]; int retval; - buf[0] = SCRATCH_REPORT; + radio->usb_buf[0] = SCRATCH_REPORT; - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + retval = si470x_get_report(radio, radio->usb_buf, SCRATCH_REPORT_SIZE); if (retval < 0) dev_warn(&radio->intf->dev, "si470x_get_scratch: " "si470x_get_report returned %d\n", retval); else { - radio->software_version = buf[1]; - radio->hardware_version = buf[2]; + radio->software_version = radio->usb_buf[1]; + radio->hardware_version = radio->usb_buf[2]; } return (retval < 0) ? -EINVAL : 0; @@ -509,6 +506,7 @@ static void si470x_usb_release(struct v4l2_device *v4l2_dev) v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->int_in_buffer); kfree(radio->buffer); + kfree(radio->usb_buf); kfree(radio); } @@ -593,6 +591,11 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = -ENOMEM; goto err_initial; } + radio->usb_buf = kmalloc(MAX_REPORT_SIZE, GFP_KERNEL); + if (radio->usb_buf == NULL) { + retval = -ENOMEM; + goto err_radio; + } radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; radio->band = 1; /* Default to 76 - 108 MHz */ @@ -612,7 +615,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, if (!radio->int_in_endpoint) { dev_info(&intf->dev, "could not find interrupt in endpoint\n"); retval = -EIO; - goto err_radio; + goto err_usbbuf; } int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); @@ -621,7 +624,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, if (!radio->int_in_buffer) { dev_info(&intf->dev, "could not allocate int_in_buffer"); retval = -ENOMEM; - goto err_radio; + goto err_usbbuf; } radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -632,6 +635,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, } radio->v4l2_dev.release = si470x_usb_release; + + /* + * The si470x SiLabs reference design uses the same USB IDs as + * 'Thanko's Raremono' si4734 based receiver. So check here which we + * have: attempt to read the device ID from the si470x: the lower 12 + * bits should be 0x0242 for the si470x. + * + * We use this check to determine which device we are dealing with. + */ + if (id->idVendor == 0x10c4 && id->idProduct == 0x818a) { + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + HID_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 1, 2, + radio->usb_buf, 3, 500); + if (retval != 3 || + (get_unaligned_be16(&radio->usb_buf[1]) & 0xfff) != 0x0242) { + dev_info(&intf->dev, "this is not a si470x device.\n"); + retval = -ENODEV; + goto err_urb; + } + } + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&intf->dev, "couldn't register v4l2_device\n"); @@ -743,6 +770,8 @@ err_urb: usb_free_urb(radio->int_in_urb); err_intbuffer: kfree(radio->int_in_buffer); +err_usbbuf: + kfree(radio->usb_buf); err_radio: kfree(radio); err_initial: diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 467e95575488..4b7660470e2f 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -167,6 +167,7 @@ struct si470x_device { /* reference to USB and video device */ struct usb_device *usbdev; struct usb_interface *intf; + char *usb_buf; /* Interrupt endpoint handling */ char *int_in_buffer; diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig new file mode 100644 index 000000000000..a7c3ba85d12b --- /dev/null +++ b/drivers/media/radio/si4713/Kconfig @@ -0,0 +1,40 @@ +config USB_SI4713 + tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB" + depends on USB && RADIO_SI4713 + select SI4713 + ---help--- + This is a driver for USB devices with the Silicon Labs SI4713 + chip. Currently these devices are known to work. + - 10c4:8244: Silicon Labs FM Transmitter USB device. + + Say Y here if you want to connect this type of radio to your + computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called radio-usb-si4713. + +config PLATFORM_SI4713 + tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C" + depends on I2C && RADIO_SI4713 + select SI4713 + ---help--- + This is a driver for I2C devices with the Silicon Labs SI4713 + chip. + + Say Y here if you want to connect this type of radio to your + computer's I2C port. + + To compile this driver as a module, choose M here: the + module will be called radio-platform-si4713. + +config I2C_SI4713 + tristate "Silicon Labs Si4713 FM Radio Transmitter support" + depends on I2C && RADIO_SI4713 + ---help--- + Say Y here if you want support to Si4713 FM Radio Transmitter. + This device can transmit audio through FM. It can transmit + RDS and RBDS signals as well. This module is the v4l2 radio + interface for the i2c driver of this device. + + To compile this driver as a module, choose M here: the + module will be called si4713. diff --git a/drivers/media/radio/si4713/Makefile b/drivers/media/radio/si4713/Makefile new file mode 100644 index 000000000000..ddaaf925e883 --- /dev/null +++ b/drivers/media/radio/si4713/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for radios with Silicon Labs Si4713 FM Radio Transmitters +# + +obj-$(CONFIG_I2C_SI4713) += si4713.o +obj-$(CONFIG_USB_SI4713) += radio-usb-si4713.o +obj-$(CONFIG_PLATFORM_SI4713) += radio-platform-si4713.o diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c index ba4cfc946868..ba4cfc946868 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/si4713/radio-platform-si4713.c diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c new file mode 100644 index 000000000000..779855b74bcd --- /dev/null +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -0,0 +1,540 @@ +/* + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* kernel includes */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/i2c.h> +/* V4l includes */ +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/si4713.h> + +#include "si4713.h" + +/* driver and module definitions */ +MODULE_AUTHOR("Dinesh Ram <dinesh.ram@cern.ch>"); +MODULE_DESCRIPTION("Si4713 FM Transmitter USB driver"); +MODULE_LICENSE("GPL v2"); + +/* The Device announces itself as Cygnal Integrated Products, Inc. */ +#define USB_SI4713_VENDOR 0x10c4 +#define USB_SI4713_PRODUCT 0x8244 + +#define BUFFER_LENGTH 64 +#define USB_TIMEOUT 1000 +#define USB_RESP_TIMEOUT 50000 + +/* USB Device ID List */ +static struct usb_device_id usb_si4713_usb_device_table[] = { + {USB_DEVICE_AND_INTERFACE_INFO(USB_SI4713_VENDOR, USB_SI4713_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_si4713_usb_device_table); + +struct si4713_usb_device { + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_subdev *v4l2_subdev; + struct mutex lock; + struct i2c_adapter i2c_adapter; + + u8 *buffer; +}; + +static inline struct si4713_usb_device *to_si4713_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct si4713_usb_device, v4l2_dev); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct si4713_usb_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-usb-si4713", sizeof(v->driver)); + strlcpy(v->card, "Si4713 FM Transmitter", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int vidioc_g_modulator(struct file *file, void *priv, + struct v4l2_modulator *vm) +{ + struct si4713_usb_device *radio = video_drvdata(file); + + return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_modulator, vm); +} + +static int vidioc_s_modulator(struct file *file, void *priv, + const struct v4l2_modulator *vm) +{ + struct si4713_usb_device *radio = video_drvdata(file); + + return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_modulator, vm); +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *vf) +{ + struct si4713_usb_device *radio = video_drvdata(file); + + return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_frequency, vf); +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *vf) +{ + struct si4713_usb_device *radio = video_drvdata(file); + + return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_frequency, vf); +} + +static const struct v4l2_ioctl_ops usb_si4713_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_modulator = vidioc_g_modulator, + .vidioc_s_modulator = vidioc_s_modulator, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* File system interface */ +static const struct v4l2_file_operations usb_si4713_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static void usb_si4713_video_device_release(struct v4l2_device *v4l2_dev) +{ + struct si4713_usb_device *radio = to_si4713_dev(v4l2_dev); + struct i2c_adapter *adapter = &radio->i2c_adapter; + + i2c_del_adapter(adapter); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->buffer); + kfree(radio); +} + +/* + * This command sequence emulates the behaviour of the Windows driver. + * The structure of these commands was determined by sniffing the + * usb traffic of the device during startup. + * Most likely, these commands make some queries to the device. + * Commands are sent to enquire parameters like the bus mode, + * component revision, boot mode, the device serial number etc. + * + * These commands are necessary to be sent in this order during startup. + * The device fails to powerup if these commands are not sent. + * + * The complete list of startup commands is given in the start_seq table below. + */ +static int si4713_send_startup_command(struct si4713_usb_device *radio) +{ + unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1; + u8 *buffer = radio->buffer; + int retval; + + /* send the command */ + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 0x09, 0x21, 0x033f, 0, radio->buffer, + BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + for (;;) { + /* receive the response */ + retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + 0x01, 0xa1, 0x033f, 0, radio->buffer, + BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + if (!radio->buffer[1]) { + /* USB traffic sniffing showed that some commands require + * additional checks. */ + switch (buffer[1]) { + case 0x32: + if (radio->buffer[2] == 0) + return 0; + break; + case 0x14: + case 0x12: + if (radio->buffer[2] & SI4713_CTS) + return 0; + break; + case 0x06: + if ((radio->buffer[2] & SI4713_CTS) && radio->buffer[9] == 0x08) + return 0; + break; + default: + return 0; + } + } + if (time_is_before_jiffies(until_jiffies)) + return -EIO; + msleep(3); + } + + return retval; +} + +struct si4713_start_seq_table { + int len; + u8 payload[8]; +}; + +/* + * Some of the startup commands that could be recognized are : + * (0x03): Get serial number of the board (Response : CB000-00-00) + * (0x06, 0x03, 0x03, 0x08, 0x01, 0x0f) : Get Component revision + */ +static struct si4713_start_seq_table start_seq[] = { + + { 1, { 0x03 } }, + { 2, { 0x32, 0x7f } }, + { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } }, + { 2, { 0x14, 0x02 } }, + { 2, { 0x09, 0x90 } }, + { 3, { 0x08, 0x90, 0xfa } }, + { 2, { 0x36, 0x01 } }, + { 2, { 0x05, 0x03 } }, + { 7, { 0x06, 0x00, 0x06, 0x0e, 0x01, 0x0f, 0x05 } }, + { 1, { 0x12 } }, + /* Commands that are sent after pressing the 'Initialize' + button in the windows application */ + { 1, { 0x03 } }, + { 1, { 0x01 } }, + { 2, { 0x09, 0x90 } }, + { 3, { 0x08, 0x90, 0xfa } }, + { 1, { 0x34 } }, + { 2, { 0x35, 0x01 } }, + { 2, { 0x36, 0x01 } }, + { 2, { 0x30, 0x09 } }, + { 4, { 0x30, 0x06, 0x00, 0xe2 } }, + { 3, { 0x31, 0x01, 0x30 } }, + { 3, { 0x31, 0x04, 0x09 } }, + { 2, { 0x05, 0x02 } }, + { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } }, +}; + +static int si4713_start_seq(struct si4713_usb_device *radio) +{ + int retval = 0; + int i; + + radio->buffer[0] = 0x3f; + + for (i = 0; i < ARRAY_SIZE(start_seq); i++) { + int len = start_seq[i].len; + u8 *payload = start_seq[i].payload; + + memcpy(radio->buffer + 1, payload, len); + memset(radio->buffer + len + 1, 0, BUFFER_LENGTH - 1 - len); + retval = si4713_send_startup_command(radio); + } + + return retval; +} + +static struct i2c_board_info si4713_board_info = { + I2C_BOARD_INFO("si4713", SI4713_I2C_ADDR_BUSEN_HIGH), +}; + +struct si4713_command_table { + int command_id; + u8 payload[8]; +}; + +/* + * Structure of a command : + * Byte 1 : 0x3f (always) + * Byte 2 : 0x06 (send a command) + * Byte 3 : Unknown + * Byte 4 : Number of arguments + 1 (for the command byte) + * Byte 5 : Number of response bytes + */ +static struct si4713_command_table command_table[] = { + + { SI4713_CMD_POWER_UP, { 0x00, SI4713_PWUP_NARGS + 1, SI4713_PWUP_NRESP} }, + { SI4713_CMD_GET_REV, { 0x03, 0x01, SI4713_GETREV_NRESP } }, + { SI4713_CMD_POWER_DOWN, { 0x00, 0x01, SI4713_PWDN_NRESP} }, + { SI4713_CMD_SET_PROPERTY, { 0x00, SI4713_SET_PROP_NARGS + 1, SI4713_SET_PROP_NRESP } }, + { SI4713_CMD_GET_PROPERTY, { 0x00, SI4713_GET_PROP_NARGS + 1, SI4713_GET_PROP_NRESP } }, + { SI4713_CMD_TX_TUNE_FREQ, { 0x03, SI4713_TXFREQ_NARGS + 1, SI4713_TXFREQ_NRESP } }, + { SI4713_CMD_TX_TUNE_POWER, { 0x03, SI4713_TXPWR_NARGS + 1, SI4713_TXPWR_NRESP } }, + { SI4713_CMD_TX_TUNE_MEASURE, { 0x03, SI4713_TXMEA_NARGS + 1, SI4713_TXMEA_NRESP } }, + { SI4713_CMD_TX_TUNE_STATUS, { 0x00, SI4713_TXSTATUS_NARGS + 1, SI4713_TXSTATUS_NRESP } }, + { SI4713_CMD_TX_ASQ_STATUS, { 0x03, SI4713_ASQSTATUS_NARGS + 1, SI4713_ASQSTATUS_NRESP } }, + { SI4713_CMD_GET_INT_STATUS, { 0x03, 0x01, SI4713_GET_STATUS_NRESP } }, + { SI4713_CMD_TX_RDS_BUFF, { 0x03, SI4713_RDSBUFF_NARGS + 1, SI4713_RDSBUFF_NRESP } }, + { SI4713_CMD_TX_RDS_PS, { 0x00, SI4713_RDSPS_NARGS + 1, SI4713_RDSPS_NRESP } }, +}; + +static int send_command(struct si4713_usb_device *radio, u8 *payload, char *data, int len) +{ + int retval; + + radio->buffer[0] = 0x3f; + radio->buffer[1] = 0x06; + + memcpy(radio->buffer + 2, payload, 3); + memcpy(radio->buffer + 5, data, len); + memset(radio->buffer + 5 + len, 0, BUFFER_LENGTH - 5 - len); + + /* send the command */ + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 0x09, 0x21, 0x033f, 0, radio->buffer, + BUFFER_LENGTH, USB_TIMEOUT); + + return retval < 0 ? retval : 0; +} + +static int si4713_i2c_read(struct si4713_usb_device *radio, char *data, int len) +{ + unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1; + int retval; + + /* receive the response */ + for (;;) { + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + 0x01, 0xa1, 0x033f, 0, radio->buffer, + BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + /* + * Check that we get a valid reply back (buffer[1] == 0) and + * that CTS is set before returning, otherwise we wait and try + * again. The i2c driver also does the CTS check, but the timeouts + * used there are much too small for this USB driver, so we wait + * for it here. + */ + if (radio->buffer[1] == 0 && (radio->buffer[2] & SI4713_CTS)) { + memcpy(data, radio->buffer + 2, len); + return 0; + } + if (time_is_before_jiffies(until_jiffies)) { + /* Zero the status value, ensuring CTS isn't set */ + data[0] = 0; + return 0; + } + msleep(3); + } +} + +static int si4713_i2c_write(struct si4713_usb_device *radio, char *data, int len) +{ + int retval = -EINVAL; + int i; + + if (len > BUFFER_LENGTH - 5) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(command_table); i++) { + if (data[0] == command_table[i].command_id) + retval = send_command(radio, command_table[i].payload, + data, len); + } + + return retval < 0 ? retval : 0; +} + +static int si4713_transfer(struct i2c_adapter *i2c_adapter, + struct i2c_msg *msgs, int num) +{ + struct si4713_usb_device *radio = i2c_get_adapdata(i2c_adapter); + int retval = -EINVAL; + int i; + + if (num <= 0) + return 0; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) + retval = si4713_i2c_read(radio, msgs[i].buf, msgs[i].len); + else + retval = si4713_i2c_write(radio, msgs[i].buf, msgs[i].len); + if (retval) + break; + } + + return retval ? retval : num; +} + +static u32 si4713_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm si4713_algo = { + .master_xfer = si4713_transfer, + .functionality = si4713_functionality, +}; + +/* This name value shows up in the sysfs filename associated + with this I2C adapter */ +static struct i2c_adapter si4713_i2c_adapter_template = { + .name = "si4713-i2c", + .owner = THIS_MODULE, + .algo = &si4713_algo, +}; + +static int si4713_register_i2c_adapter(struct si4713_usb_device *radio) +{ + radio->i2c_adapter = si4713_i2c_adapter_template; + /* set up sysfs linkage to our parent device */ + radio->i2c_adapter.dev.parent = &radio->usbdev->dev; + i2c_set_adapdata(&radio->i2c_adapter, radio); + + return i2c_add_adapter(&radio->i2c_adapter); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_si4713_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct si4713_usb_device *radio; + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; + int retval = -ENOMEM; + + dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", + id->idVendor, id->idProduct); + + /* Initialize local device structure */ + radio = kzalloc(sizeof(struct si4713_usb_device), GFP_KERNEL); + if (radio) + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + + if (!radio || !radio->buffer) { + dev_err(&intf->dev, "kmalloc for si4713_usb_device failed\n"); + kfree(radio); + return -ENOMEM; + } + + mutex_init(&radio->lock); + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + + retval = si4713_start_seq(radio); + if (retval < 0) + goto err_v4l2; + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + retval = si4713_register_i2c_adapter(radio); + if (retval < 0) { + dev_err(&intf->dev, "could not register i2c device\n"); + goto err_i2cdev; + } + + adapter = &radio->i2c_adapter; + sd = v4l2_i2c_new_subdev_board(&radio->v4l2_dev, adapter, + &si4713_board_info, NULL); + radio->v4l2_subdev = sd; + if (!sd) { + dev_err(&intf->dev, "cannot get v4l2 subdevice\n"); + retval = -ENODEV; + goto del_adapter; + } + + radio->vdev.ctrl_handler = sd->ctrl_handler; + radio->v4l2_dev.release = usb_si4713_video_device_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_si4713_fops; + radio->vdev.ioctl_ops = &usb_si4713_ioctl_ops; + radio->vdev.lock = &radio->lock; + radio->vdev.release = video_device_release_empty; + radio->vdev.vfl_dir = VFL_DIR_TX; + + video_set_drvdata(&radio->vdev, radio); + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto del_adapter; + } + + dev_info(&intf->dev, "V4L2 device registered as %s\n", + video_device_node_name(&radio->vdev)); + + return 0; + +del_adapter: + i2c_del_adapter(adapter); +err_i2cdev: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); + kfree(radio); + return retval; +} + +static void usb_si4713_disconnect(struct usb_interface *intf) +{ + struct si4713_usb_device *radio = to_si4713_dev(usb_get_intfdata(intf)); + + dev_info(&intf->dev, "Si4713 development board now disconnected\n"); + + mutex_lock(&radio->lock); + usb_set_intfdata(intf, NULL); + video_unregister_device(&radio->vdev); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* USB subsystem interface */ +static struct usb_driver usb_si4713_driver = { + .name = "radio-usb-si4713", + .probe = usb_si4713_probe, + .disconnect = usb_si4713_disconnect, + .id_table = usb_si4713_usb_device_table, +}; + +module_usb_driver(usb_si4713_driver); diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713/si4713.c index 9ec48ccbcf0b..07d5153811e8 100644 --- a/drivers/media/radio/si4713-i2c.c +++ b/drivers/media/radio/si4713/si4713.c @@ -27,13 +27,12 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/gpio.h> -#include <linux/regulator/consumer.h> #include <linux/module.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-common.h> -#include "si4713-i2c.h" +#include "si4713.h" /* module parameters */ static int debug; @@ -45,23 +44,18 @@ MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>"); MODULE_DESCRIPTION("I2C driver for Si4713 FM Radio Transmitter"); MODULE_VERSION("0.0.1"); -static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = { - "vio", - "vdd", -}; - #define DEFAULT_RDS_PI 0x00 #define DEFAULT_RDS_PTY 0x00 #define DEFAULT_RDS_DEVIATION 0x00C8 #define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 #define DEFAULT_LIMITER_RTIME 0x1392 #define DEFAULT_LIMITER_DEV 0x102CA -#define DEFAULT_PILOT_FREQUENCY 0x4A38 +#define DEFAULT_PILOT_FREQUENCY 0x4A38 #define DEFAULT_PILOT_DEVIATION 0x1A5E #define DEFAULT_ACOMP_ATIME 0x0000 #define DEFAULT_ACOMP_RTIME 0xF4240L #define DEFAULT_ACOMP_GAIN 0x0F -#define DEFAULT_ACOMP_THRESHOLD (-0x28) +#define DEFAULT_ACOMP_THRESHOLD (-0x28) #define DEFAULT_MUTE 0x01 #define DEFAULT_POWER_LEVEL 88 #define DEFAULT_FREQUENCY 8800 @@ -213,6 +207,7 @@ static int si4713_send_command(struct si4713_device *sdev, const u8 command, u8 response[], const int respn, const int usecs) { struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); + unsigned long until_jiffies; u8 data1[MAX_ARGS + 1]; int err; @@ -228,30 +223,42 @@ static int si4713_send_command(struct si4713_device *sdev, const u8 command, if (err != argn + 1) { v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", command); - return (err > 0) ? -EIO : err; + return err < 0 ? err : -EIO; } + until_jiffies = jiffies + usecs_to_jiffies(usecs) + 1; + /* Wait response from interrupt */ - if (!wait_for_completion_timeout(&sdev->work, + if (client->irq) { + if (!wait_for_completion_timeout(&sdev->work, usecs_to_jiffies(usecs) + 1)) - v4l2_warn(&sdev->sd, + v4l2_warn(&sdev->sd, "(%s) Device took too much time to answer.\n", __func__); - - /* Then get the response */ - err = i2c_master_recv(client, response, respn); - if (err != respn) { - v4l2_err(&sdev->sd, - "Error while reading response for command 0x%02x\n", - command); - return (err > 0) ? -EIO : err; } - DBG_BUFFER(&sdev->sd, "Response", response, respn); - if (check_command_failed(response[0])) - return -EBUSY; + do { + err = i2c_master_recv(client, response, respn); + if (err != respn) { + v4l2_err(&sdev->sd, + "Error %d while reading response for command 0x%02x\n", + err, command); + return err < 0 ? err : -EIO; + } - return 0; + DBG_BUFFER(&sdev->sd, "Response", response, respn); + if (!check_command_failed(response[0])) + return 0; + + if (client->irq) + return -EBUSY; + if (usecs <= 1000) + usleep_range(usecs, 1000); + else + usleep_range(1000, 2000); + } while (time_is_after_jiffies(until_jiffies)); + + return -EBUSY; } /* @@ -265,9 +272,9 @@ static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv) int err; u8 val[SI4713_GET_PROP_NRESP]; /* - * .First byte = 0 - * .Second byte = property's MSB - * .Third byte = property's LSB + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB */ const u8 args[SI4713_GET_PROP_NARGS] = { 0x00, @@ -302,11 +309,11 @@ static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val) int rval; u8 resp[SI4713_SET_PROP_NRESP]; /* - * .First byte = 0 - * .Second byte = property's MSB - * .Third byte = property's LSB - * .Fourth byte = value's MSB - * .Fifth byte = value's LSB + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB */ const u8 args[SI4713_SET_PROP_NARGS] = { 0x00, @@ -344,31 +351,36 @@ static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val) */ static int si4713_powerup(struct si4713_device *sdev) { + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); int err; u8 resp[SI4713_PWUP_NRESP]; /* - * .First byte = Enabled interrupts and boot function - * .Second byte = Input operation mode + * .First byte = Enabled interrupts and boot function + * .Second byte = Input operation mode */ - const u8 args[SI4713_PWUP_NARGS] = { - SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, + u8 args[SI4713_PWUP_NARGS] = { + SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, SI4713_PWUP_OPMOD_ANALOG, }; if (sdev->power_state) return 0; - err = regulator_bulk_enable(ARRAY_SIZE(sdev->supplies), - sdev->supplies); - if (err) { - v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err); - return err; + if (sdev->supplies) { + err = regulator_bulk_enable(sdev->supplies, sdev->supply_data); + if (err) { + v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err); + return err; + } } if (gpio_is_valid(sdev->gpio_reset)) { udelay(50); gpio_set_value(sdev->gpio_reset, 1); } + if (client->irq) + args[0] |= SI4713_PWUP_CTSIEN; + err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, args, ARRAY_SIZE(args), resp, ARRAY_SIZE(resp), @@ -380,13 +392,15 @@ static int si4713_powerup(struct si4713_device *sdev) v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); sdev->power_state = POWER_ON; - err = si4713_write_property(sdev, SI4713_GPO_IEN, + if (client->irq) + err = si4713_write_property(sdev, SI4713_GPO_IEN, SI4713_STC_INT | SI4713_CTS); - } else { - if (gpio_is_valid(sdev->gpio_reset)) - gpio_set_value(sdev->gpio_reset, 0); - err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), - sdev->supplies); + return err; + } + if (gpio_is_valid(sdev->gpio_reset)) + gpio_set_value(sdev->gpio_reset, 0); + if (sdev->supplies) { + err = regulator_bulk_disable(sdev->supplies, sdev->supply_data); if (err) v4l2_err(&sdev->sd, "Failed to disable supplies: %d\n", err); @@ -418,11 +432,13 @@ static int si4713_powerdown(struct si4713_device *sdev) v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); if (gpio_is_valid(sdev->gpio_reset)) gpio_set_value(sdev->gpio_reset, 0); - err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), - sdev->supplies); - if (err) - v4l2_err(&sdev->sd, - "Failed to disable supplies: %d\n", err); + if (sdev->supplies) { + err = regulator_bulk_disable(sdev->supplies, + sdev->supply_data); + if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + } sdev->power_state = POWER_OFF; } @@ -451,7 +467,7 @@ static int si4713_checkrev(struct si4713_device *sdev) v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); } else { - v4l2_err(&sdev->sd, "Invalid product number\n"); + v4l2_err(&sdev->sd, "Invalid product number 0x%X\n", resp[1]); rval = -EINVAL; } return rval; @@ -465,39 +481,45 @@ static int si4713_checkrev(struct si4713_device *sdev) */ static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) { - int err; + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); u8 resp[SI4713_GET_STATUS_NRESP]; + unsigned long start_jiffies = jiffies; + int err; - /* Wait response from STC interrupt */ - if (!wait_for_completion_timeout(&sdev->work, - usecs_to_jiffies(usecs) + 1)) + if (client->irq && + !wait_for_completion_timeout(&sdev->work, usecs_to_jiffies(usecs) + 1)) v4l2_warn(&sdev->sd, - "%s: device took too much time to answer (%d usec).\n", - __func__, usecs); - - /* Clear status bits */ - err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, - NULL, 0, - resp, ARRAY_SIZE(resp), - DEFAULT_TIMEOUT); - - if (err < 0) - goto exit; - - v4l2_dbg(1, debug, &sdev->sd, - "%s: status bits: 0x%02x\n", __func__, resp[0]); - - if (!(resp[0] & SI4713_STC_INT)) - err = -EIO; - -exit: - return err; + "(%s) Device took too much time to answer.\n", __func__); + + for (;;) { + /* Clear status bits */ + err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + /* The USB device returns errors when it waits for the + * STC bit to be set. Hence polling */ + if (err >= 0) { + v4l2_dbg(1, debug, &sdev->sd, + "%s: status bits: 0x%02x\n", __func__, resp[0]); + + if (resp[0] & SI4713_STC_INT) + return 0; + } + if (jiffies_to_usecs(jiffies - start_jiffies) > usecs) + return err < 0 ? err : -EIO; + /* We sleep here for 3-4 ms in order to avoid flooding the device + * with USB requests. The si4713 USB driver was developed + * by reverse engineering the Windows USB driver. The windows + * driver also has a ~2.5 ms delay between responses. */ + usleep_range(3000, 4000); + } } /* * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning - * frequency between 76 and 108 MHz in 10 kHz units and - * steps of 50 kHz. + * frequency between 76 and 108 MHz in 10 kHz units and + * steps of 50 kHz. * @sdev: si4713_device structure for the device we are communicating * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) */ @@ -506,9 +528,9 @@ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) int err; u8 val[SI4713_TXFREQ_NRESP]; /* - * .First byte = 0 - * .Second byte = frequency's MSB - * .Third byte = frequency's LSB + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB */ const u8 args[SI4713_TXFREQ_NARGS] = { 0x00, @@ -535,14 +557,14 @@ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) } /* - * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in - * 1 dB units. A value of 0x00 indicates off. The command - * also sets the antenna tuning capacitance. A value of 0 - * indicates autotuning, and a value of 1 - 191 indicates - * a manual override, which results in a tuning - * capacitance of 0.25 pF x @antcap. + * si4713_tx_tune_power - Sets the RF voltage level between 88 and 120 dBuV in + * 1 dB units. A value of 0x00 indicates off. The command + * also sets the antenna tuning capacitance. A value of 0 + * indicates autotuning, and a value of 1 - 191 indicates + * a manual override, which results in a tuning + * capacitance of 0.25 pF x @antcap. * @sdev: si4713_device structure for the device we are communicating - * @power: tuning power (88 - 115 dBuV, unit/step 1 dB) + * @power: tuning power (88 - 120 dBuV, unit/step 1 dB) * @antcap: value of antenna tuning capacitor (0 - 191) */ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, @@ -551,21 +573,21 @@ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, int err; u8 val[SI4713_TXPWR_NRESP]; /* - * .First byte = 0 - * .Second byte = 0 - * .Third byte = power - * .Fourth byte = antcap + * .First byte = 0 + * .Second byte = 0 + * .Third byte = power + * .Fourth byte = antcap */ - const u8 args[SI4713_TXPWR_NARGS] = { + u8 args[SI4713_TXPWR_NARGS] = { 0x00, 0x00, power, antcap, }; - if (((power > 0) && (power < SI4713_MIN_POWER)) || - power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP) - return -EDOM; + /* Map power values 1-87 to MIN_POWER (88) */ + if (power > 0 && power < SI4713_MIN_POWER) + args[2] = power = SI4713_MIN_POWER; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, args, ARRAY_SIZE(args), val, @@ -583,12 +605,12 @@ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, /* * si4713_tx_tune_measure - Enters receive mode and measures the received noise - * level in units of dBuV on the selected frequency. - * The Frequency must be between 76 and 108 MHz in 10 kHz - * units and steps of 50 kHz. The command also sets the - * antenna tuning capacitance. A value of 0 means - * autotuning, and a value of 1 to 191 indicates manual - * override. + * level in units of dBuV on the selected frequency. + * The Frequency must be between 76 and 108 MHz in 10 kHz + * units and steps of 50 kHz. The command also sets the + * antenna tuning capacitance. A value of 0 means + * autotuning, and a value of 1 to 191 indicates manual + * override. * @sdev: si4713_device structure for the device we are communicating * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) * @antcap: value of antenna tuning capacitor (0 - 191) @@ -599,10 +621,10 @@ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, int err; u8 val[SI4713_TXMEA_NRESP]; /* - * .First byte = 0 - * .Second byte = frequency's MSB - * .Third byte = frequency's LSB - * .Fourth byte = antcap + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + * .Fourth byte = antcap */ const u8 args[SI4713_TXMEA_NARGS] = { 0x00, @@ -632,11 +654,11 @@ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, /* * si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or - * tx_tune_power commands. This command return the current - * frequency, output voltage in dBuV, the antenna tunning - * capacitance value and the received noise level. The - * command also clears the stcint interrupt bit when the - * first bit of its arguments is high. + * tx_tune_power commands. This command return the current + * frequency, output voltage in dBuV, the antenna tunning + * capacitance value and the received noise level. The + * command also clears the stcint interrupt bit when the + * first bit of its arguments is high. * @sdev: si4713_device structure for the device we are communicating * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. * @frequency: returned frequency @@ -651,7 +673,7 @@ static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, int err; u8 val[SI4713_TXSTATUS_NRESP]; /* - * .First byte = intack bit + * .First byte = intack bit */ const u8 args[SI4713_TXSTATUS_NARGS] = { intack & SI4713_INTACK_MASK, @@ -812,8 +834,9 @@ static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) return rval; } -static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) +static int si4713_set_rds_radio_text(struct si4713_device *sdev, const char *rt) { + static const char cr[RDS_RADIOTEXT_BLK_SIZE] = { RDS_CARRIAGE_RETURN, 0 }; int rval = 0, i; u16 t_index = 0; u8 b_index = 0, cr_inserted = 0; @@ -837,7 +860,7 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) { if (!rt[t_index + i] || rt[t_index + i] == RDS_CARRIAGE_RETURN) { - rt[t_index + i] = RDS_CARRIAGE_RETURN; + rt = cr; cr_inserted = 1; break; } @@ -1024,7 +1047,6 @@ static int si4713_initialize(struct si4713_device *sdev) if (rval < 0) return rval; - sdev->frequency = DEFAULT_FREQUENCY; sdev->stereo = 1; sdev->tune_rnl = DEFAULT_TUNE_RNL; @@ -1345,7 +1367,7 @@ static int si4713_probe(struct i2c_client *client, struct v4l2_ctrl_handler *hdl; int rval, i; - sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); if (!sdev) { dev_err(&client->dev, "Failed to alloc video device.\n"); rval = -ENOMEM; @@ -1362,13 +1384,14 @@ static int si4713_probe(struct i2c_client *client, } sdev->gpio_reset = pdata->gpio_reset; gpio_direction_output(sdev->gpio_reset, 0); + sdev->supplies = pdata->supplies; } - for (i = 0; i < ARRAY_SIZE(sdev->supplies); i++) - sdev->supplies[i].supply = si4713_supply_names[i]; + for (i = 0; i < sdev->supplies; i++) + sdev->supply_data[i].supply = pdata->supply_names[i]; - rval = regulator_bulk_get(&client->dev, ARRAY_SIZE(sdev->supplies), - sdev->supplies); + rval = regulator_bulk_get(&client->dev, sdev->supplies, + sdev->supply_data); if (rval) { dev_err(&client->dev, "Cannot get regulators: %d\n", rval); goto free_gpio; @@ -1420,8 +1443,8 @@ static int si4713_probe(struct i2c_client *client, 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, + 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, @@ -1443,9 +1466,11 @@ static int si4713_probe(struct i2c_client *client, 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); + V4L2_CID_TUNE_POWER_LEVEL, 0, SI4713_MAX_POWER, + 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); + V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, SI4713_MAX_ANTCAP, + 1, 0); if (hdl->error) { rval = hdl->error; @@ -1481,7 +1506,7 @@ free_irq: free_ctrls: v4l2_ctrl_handler_free(hdl); put_reg: - regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); + regulator_bulk_free(sdev->supplies, sdev->supply_data); free_gpio: if (gpio_is_valid(sdev->gpio_reset)) gpio_free(sdev->gpio_reset); @@ -1505,7 +1530,7 @@ static int si4713_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); - regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); + regulator_bulk_free(sdev->supplies, sdev->supply_data); if (gpio_is_valid(sdev->gpio_reset)) gpio_free(sdev->gpio_reset); kfree(sdev); diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713/si4713.h index 25cdea26343b..4837cf6e0e1b 100644 --- a/drivers/media/radio/si4713-i2c.h +++ b/drivers/media/radio/si4713/si4713.h @@ -15,6 +15,7 @@ #ifndef SI4713_I2C_H #define SI4713_I2C_H +#include <linux/regulator/consumer.h> #include <media/v4l2-subdev.h> #include <media/v4l2-ctrls.h> #include <media/si4713.h> @@ -226,7 +227,8 @@ struct si4713_device { struct v4l2_ctrl *tune_ant_cap; }; struct completion work; - struct regulator_bulk_data supplies[SI4713_NUM_SUPPLIES]; + unsigned supplies; + struct regulator_bulk_data supply_data[SI4713_NUM_SUPPLIES]; int gpio_reset; u32 power_state; u32 rds_enabled; diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c index cef06981b7c9..7c14060a40b8 100644 --- a/drivers/media/radio/tea575x.c +++ b/drivers/media/radio/tea575x.c @@ -20,12 +20,12 @@ * */ -#include <asm/io.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/sched.h> +#include <asm/io.h> #include <media/v4l2-device.h> #include <media/v4l2-dev.h> #include <media/v4l2-fh.h> diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index f329485c6629..822b9f47ca72 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1909,10 +1909,8 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx) int ret, i; idev = input_allocate_device(); - if (!idev) { - dev_err(ictx->dev, "input dev allocation failed\n"); + if (!idev) goto out; - } snprintf(ictx->name_idev, sizeof(ictx->name_idev), "iMON Panel, Knob and Mouse(%04x:%04x)", @@ -1960,10 +1958,8 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx) int ret; touch = input_allocate_device(); - if (!touch) { - dev_err(ictx->dev, "touchscreen input dev allocation failed\n"); + if (!touch) goto touch_alloc_failed; - } snprintf(ictx->name_touch, sizeof(ictx->name_touch), "iMON USB Touchscreen (%04x:%04x)", diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index b1cde8c0422b..0b8c54919010 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -98,4 +98,5 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ rc-winfast.o \ - rc-winfast-usbii-deluxe.o + rc-winfast-usbii-deluxe.o \ + rc-su3000.o diff --git a/drivers/media/rc/keymaps/rc-su3000.c b/drivers/media/rc/keymaps/rc-su3000.c new file mode 100644 index 000000000000..8dbd3e9bc951 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-su3000.c @@ -0,0 +1,75 @@ +/* rc-su3000.h - Keytable for Geniatech HDStar Remote Controller + * + * Copyright (c) 2013 by Evgeny Plehov <Evgeny Plehov@ukr.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <media/rc-map.h> +#include <linux/module.h> + +static struct rc_map_table su3000[] = { + { 0x25, KEY_POWER }, /* right-bottom Red */ + { 0x0a, KEY_MUTE }, /* -/-- */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x00, KEY_0 }, + { 0x20, KEY_UP }, /* CH+ */ + { 0x21, KEY_DOWN }, /* CH+ */ + { 0x12, KEY_VOLUMEUP }, /* Brightness Up */ + { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */ + { 0x1f, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x16, KEY_PAUSE }, + { 0x0b, KEY_STOP }, + { 0x27, KEY_FASTFORWARD },/* >> */ + { 0x26, KEY_REWIND }, /* << */ + { 0x0d, KEY_OK }, /* Mute */ + { 0x11, KEY_LEFT }, /* VOL- */ + { 0x10, KEY_RIGHT }, /* VOL+ */ + { 0x29, KEY_BACK }, /* button under 9 */ + { 0x2c, KEY_MENU }, /* TTX */ + { 0x2b, KEY_EPG }, /* EPG */ + { 0x1e, KEY_RED }, /* OSD */ + { 0x0e, KEY_GREEN }, /* Window */ + { 0x2d, KEY_YELLOW }, /* button under << */ + { 0x0f, KEY_BLUE }, /* bottom yellow button */ + { 0x14, KEY_AUDIO }, /* Snapshot */ + { 0x38, KEY_TV }, /* TV/Radio */ + { 0x0c, KEY_ESC } /* upper Red button */ +}; + +static struct rc_map_list su3000_map = { + .map = { + .scan = su3000, + .size = ARRAY_SIZE(su3000), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_SU3000, + } +}; + +static int __init init_rc_map_su3000(void) +{ + return rc_map_register(&su3000_map); +} + +static void __exit exit_rc_map_su3000(void) +{ + rc_map_unregister(&su3000_map); +} + +module_init(init_rc_map_su3000) +module_exit(exit_rc_map_su3000) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeny Plehov <Evgeny Plehov@ukr.net>"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 3c761014d3ce..a25bb1581e46 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -199,6 +199,7 @@ static bool debug; #define VENDOR_TIVO 0x105a #define VENDOR_CONEXANT 0x0572 #define VENDOR_TWISTEDMELON 0x2596 +#define VENDOR_HAUPPAUGE 0x2040 enum mceusb_model_type { MCE_GEN2 = 0, /* Most boards */ @@ -210,6 +211,7 @@ enum mceusb_model_type { MULTIFUNCTION, TIVO_KIT, MCE_GEN2_NO_TX, + HAUPPAUGE_CX_HYBRID_TV, }; struct mceusb_model { @@ -258,6 +260,11 @@ static const struct mceusb_model mceusb_model[] = { .no_tx = 1, /* tx isn't wired up at all */ .name = "Conexant Hybrid TV (cx231xx) MCE IR", }, + [HAUPPAUGE_CX_HYBRID_TV] = { + .rc_map = RC_MAP_HAUPPAUGE, + .no_tx = 1, /* eeprom says it has no tx */ + .name = "Conexant Hybrid TV (cx231xx) MCE IR no TX", + }, [MULTIFUNCTION] = { .mce_gen2 = 1, .ir_intfnum = 2, @@ -399,6 +406,9 @@ static struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) }, /* Twisted Melon Inc. - Manta Transceiver */ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) }, + /* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */ + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, /* Terminating entry */ { } }; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 46da365c9c84..02e2f38c9c85 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -22,6 +22,10 @@ #include <linux/module.h> #include "rc-core-priv.h" +/* Bitmap to store allocated device numbers from 0 to IRRCV_NUM_DEVICES - 1 */ +#define IRRCV_NUM_DEVICES 256 +DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES); + /* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */ #define IR_TAB_MIN_SIZE 256 #define IR_TAB_MAX_SIZE 8192 @@ -1065,10 +1069,9 @@ EXPORT_SYMBOL_GPL(rc_free_device); int rc_register_device(struct rc_dev *dev) { static bool raw_init = false; /* raw decoders loaded? */ - static atomic_t devno = ATOMIC_INIT(0); struct rc_map *rc_map; const char *path; - int rc; + int rc, devno; if (!dev || !dev->map_name) return -EINVAL; @@ -1096,7 +1099,15 @@ int rc_register_device(struct rc_dev *dev) */ mutex_lock(&dev->lock); - dev->devno = (unsigned long)(atomic_inc_return(&devno) - 1); + do { + devno = find_first_zero_bit(ir_core_dev_number, + IRRCV_NUM_DEVICES); + /* No free device slots */ + if (devno >= IRRCV_NUM_DEVICES) + return -ENOMEM; + } while (test_and_set_bit(devno, ir_core_dev_number)); + + dev->devno = devno; dev_set_name(&dev->dev, "rc%ld", dev->devno); dev_set_drvdata(&dev->dev, dev); rc = device_add(&dev->dev); @@ -1186,6 +1197,7 @@ out_dev: device_del(&dev->dev); out_unlock: mutex_unlock(&dev->lock); + clear_bit(dev->devno, ir_core_dev_number); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); @@ -1197,6 +1209,8 @@ void rc_unregister_device(struct rc_dev *dev) del_timer_sync(&dev->timer_keyup); + clear_bit(dev->devno, ir_core_dev_number); + if (dev->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(dev); diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 65120c2d47ad..8f0cddb9e8f2 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/reset.h> #include <media/rc-core.h> #include <linux/pinctrl/consumer.h> @@ -28,6 +29,7 @@ struct st_rc_device { int sample_mult; int sample_div; bool rxuhfmode; + struct reset_control *rstc; }; /* Registers */ @@ -161,6 +163,10 @@ static void st_rc_hardware_init(struct st_rc_device *dev) unsigned int rx_max_symbol_per = MAX_SYMB_TIME; unsigned int rx_sampling_freq_div; + /* Enable the IP */ + if (dev->rstc) + reset_control_deassert(dev->rstc); + clk_prepare_enable(dev->sys_clock); baseclock = clk_get_rate(dev->sys_clock); @@ -271,6 +277,11 @@ static int st_rc_probe(struct platform_device *pdev) else rc_dev->rx_base = rc_dev->base; + + rc_dev->rstc = reset_control_get(dev, NULL); + if (IS_ERR(rc_dev->rstc)) + rc_dev->rstc = NULL; + rc_dev->dev = dev; platform_set_drvdata(pdev, rc_dev); st_rc_hardware_init(rc_dev); @@ -338,6 +349,8 @@ static int st_rc_suspend(struct device *dev) writel(0x00, rc_dev->rx_base + IRB_RX_EN); writel(0x00, rc_dev->rx_base + IRB_RX_INT_EN); clk_disable_unprepare(rc_dev->sys_clock); + if (rc_dev->rstc) + reset_control_assert(rc_dev->rstc); } return 0; diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 15665debc572..ba2e365296cf 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -215,6 +215,13 @@ config MEDIA_TUNER_FC2580 help FCI FC2580 silicon tuner driver. +config MEDIA_TUNER_M88TS2022 + tristate "Montage M88TS2022 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Montage M88TS2022 silicon tuner driver. + config MEDIA_TUNER_TUA9001 tristate "Infineon TUA 9001 silicon tuner" depends on MEDIA_SUPPORT && I2C diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 308f108eadba..efe82a904b12 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 72971a8d3c37..40c1da707d15 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -243,8 +243,10 @@ static int e4000_set_params(struct dvb_frontend *fe) break; } - if (i == ARRAY_SIZE(e4000_pll_lut)) + if (i == ARRAY_SIZE(e4000_pll_lut)) { + ret = -EINVAL; goto err; + } /* * Note: Currently f_vco overflows when c->frequency is 1 073 741 824 Hz @@ -271,8 +273,10 @@ static int e4000_set_params(struct dvb_frontend *fe) break; } - if (i == ARRAY_SIZE(e400_lna_filter_lut)) + if (i == ARRAY_SIZE(e400_lna_filter_lut)) { + ret = -EINVAL; goto err; + } ret = e4000_wr_reg(priv, 0x10, e400_lna_filter_lut[i].val); if (ret < 0) @@ -284,8 +288,10 @@ static int e4000_set_params(struct dvb_frontend *fe) break; } - if (i == ARRAY_SIZE(e4000_if_filter_lut)) + if (i == ARRAY_SIZE(e4000_if_filter_lut)) { + ret = -EINVAL; goto err; + } buf[0] = e4000_if_filter_lut[i].reg11_val; buf[1] = e4000_if_filter_lut[i].reg12_val; @@ -300,8 +306,10 @@ static int e4000_set_params(struct dvb_frontend *fe) break; } - if (i == ARRAY_SIZE(e4000_band_lut)) + if (i == ARRAY_SIZE(e4000_band_lut)) { + ret = -EINVAL; goto err; + } ret = e4000_wr_reg(priv, 0x07, e4000_band_lut[i].reg07_val); if (ret < 0) diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c new file mode 100644 index 000000000000..40c42dec721b --- /dev/null +++ b/drivers/media/tuners/m88ts2022.c @@ -0,0 +1,674 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * 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. + * + * Some calculations are taken from existing TS2020 driver. + */ + +#include "m88ts2022_priv.h" + +/* write multiple registers */ +static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, + u8 reg, const u8 *val, int len) +{ +#define MAX_WR_LEN 3 +#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) + int ret; + u8 buf[MAX_WR_XFER_LEN]; + struct i2c_msg msg[1] = { + { + .addr = priv->client->addr, + .flags = 0, + .len = 1 + len, + .buf = buf, + } + }; + + if (WARN_ON(len > MAX_WR_LEN)) + return -EINVAL; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(priv->client->adapter, msg, 1); + if (ret == 1) { + ret = 0; + } else { + dev_warn(&priv->client->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, + u8 *val, int len) +{ +#define MAX_RD_LEN 1 +#define MAX_RD_XFER_LEN (MAX_RD_LEN) + int ret; + u8 buf[MAX_RD_XFER_LEN]; + struct i2c_msg msg[2] = { + { + .addr = priv->client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (WARN_ON(len > MAX_RD_LEN)) + return -EINVAL; + + ret = i2c_transfer(priv->client->adapter, msg, 2); + if (ret == 2) { + memcpy(val, buf, len); + ret = 0; + } else { + dev_warn(&priv->client->dev, + "%s: i2c rd failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* write single register */ +static int m88ts2022_wr_reg(struct m88ts2022_priv *priv, u8 reg, u8 val) +{ + return m88ts2022_wr_regs(priv, reg, &val, 1); +} + +/* read single register */ +static int m88ts2022_rd_reg(struct m88ts2022_priv *priv, u8 reg, u8 *val) +{ + return m88ts2022_rd_regs(priv, reg, val, 1); +} + +/* write single register with mask */ +static int m88ts2022_wr_reg_mask(struct m88ts2022_priv *priv, + u8 reg, u8 val, u8 mask) +{ + int ret; + u8 u8tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = m88ts2022_rd_regs(priv, reg, &u8tmp, 1); + if (ret) + return ret; + + val &= mask; + u8tmp &= ~mask; + val |= u8tmp; + } + + return m88ts2022_wr_regs(priv, reg, &val, 1); +} + +static int m88ts2022_cmd(struct dvb_frontend *fe, + int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + int ret, i; + u8 u8tmp; + struct m88ts2022_reg_val reg_vals[] = { + {0x51, 0x1f - op}, + {0x51, 0x1f}, + {0x50, 0x00 + op}, + {0x50, 0x00}, + }; + + for (i = 0; i < 2; i++) { + dev_dbg(&priv->client->dev, + "%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n", + __func__, i, op, reg, mask, val); + + for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { + ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, + reg_vals[i].val); + if (ret) + goto err; + } + + usleep_range(sleep * 1000, sleep * 10000); + + ret = m88ts2022_rd_reg(priv, reg, &u8tmp); + if (ret) + goto err; + + if ((u8tmp & mask) != val) + break; + } + + if (reg_val) + *reg_val = u8tmp; +err: + return ret; +} + +static int m88ts2022_set_params(struct dvb_frontend *fe) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int frequency_khz, frequency_offset_khz, f_3db_hz; + unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28; + u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min; + u16 u16tmp; + dev_dbg(&priv->client->dev, + "%s: frequency=%d symbol_rate=%d rolloff=%d\n", + __func__, c->frequency, c->symbol_rate, c->rolloff); + /* + * Integer-N PLL synthesizer + * kHz is used for all calculations to keep calculations within 32-bit + */ + f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000); + div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); + + if (c->symbol_rate < 5000000) + frequency_offset_khz = 3000; /* 3 MHz */ + else + frequency_offset_khz = 0; + + frequency_khz = c->frequency + frequency_offset_khz; + + if (frequency_khz < 1103000) { + div_out = 4; + u8tmp = 0x1b; + } else { + div_out = 2; + u8tmp = 0x0b; + } + + buf[0] = u8tmp; + buf[1] = 0x40; + ret = m88ts2022_wr_regs(priv, 0x10, buf, 2); + if (ret) + goto err; + + f_vco_khz = frequency_khz * div_out; + pll_n = f_vco_khz * div_ref / f_ref_khz; + pll_n += pll_n % 2; + priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; + + if (pll_n < 4095) + u16tmp = pll_n - 1024; + else if (pll_n < 6143) + u16tmp = pll_n + 1024; + else + u16tmp = pll_n + 3072; + + buf[0] = (u16tmp >> 8) & 0x3f; + buf[1] = (u16tmp >> 0) & 0xff; + buf[2] = div_ref - 8; + ret = m88ts2022_wr_regs(priv, 0x01, buf, 3); + if (ret) + goto err; + + dev_dbg(&priv->client->dev, + "%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", + __func__, priv->frequency_khz, + priv->frequency_khz - c->frequency, f_vco_khz, pll_n, + div_ref, div_out); + + ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); + if (ret) + goto err; + + ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); + if (ret) + goto err; + + u8tmp &= 0x7f; + if (u8tmp < 64) { + ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x80, 0x80); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x11, 0x6f); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); + if (ret) + goto err; + } + + ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); + if (ret) + goto err; + + u8tmp &= 0x1f; + if (u8tmp > 19) { + ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x00, 0x02); + if (ret) + goto err; + } + + ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x25, 0x00); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x27, 0x70); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x41, 0x09); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x08, 0x0b); + if (ret) + goto err; + + /* filters */ + gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U); + + ret = m88ts2022_wr_reg(priv, 0x04, gdiv28); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + if (ret) + goto err; + + cap_code = u8tmp & 0x3f; + + ret = m88ts2022_wr_reg(priv, 0x41, 0x0d); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + if (ret) + goto err; + + u8tmp &= 0x3f; + cap_code = (cap_code + u8tmp) / 2; + gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151); + div_max = gdiv28 * 135 / 100; + div_min = gdiv28 * 78 / 100; + div_max = clamp_val(div_max, 0U, 63U); + + f_3db_hz = c->symbol_rate * 135UL / 200UL; + f_3db_hz += 2000000U + (frequency_offset_khz * 1000U); + f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U); + +#define LPF_COEFF 3200U + lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz); + lpf_gm = clamp_val(lpf_gm, 1U, 23U); + + lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); + if (lpf_mxdiv < div_min) + lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); + lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max); + + ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x06, lpf_gm); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + if (ret) + goto err; + + cap_code = u8tmp & 0x3f; + + ret = m88ts2022_wr_reg(priv, 0x41, 0x09); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); + if (ret) + goto err; + + u8tmp &= 0x3f; + cap_code = (cap_code + u8tmp) / 2; + + u8tmp = cap_code | 0x80; + ret = m88ts2022_wr_reg(priv, 0x25, u8tmp); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x27, 0x30); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x08, 0x09); + if (ret) + goto err; + + ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL); + if (ret) + goto err; +err: + if (ret) + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int m88ts2022_init(struct dvb_frontend *fe) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + int ret, i; + u8 u8tmp; + static const struct m88ts2022_reg_val reg_vals[] = { + {0x7d, 0x9d}, + {0x7c, 0x9a}, + {0x7a, 0x76}, + {0x3b, 0x01}, + {0x63, 0x88}, + {0x61, 0x85}, + {0x22, 0x30}, + {0x30, 0x40}, + {0x20, 0x23}, + {0x24, 0x02}, + {0x12, 0xa0}, + }; + dev_dbg(&priv->client->dev, "%s:\n", __func__); + + ret = m88ts2022_wr_reg(priv, 0x00, 0x01); + if (ret) + goto err; + + ret = m88ts2022_wr_reg(priv, 0x00, 0x03); + if (ret) + goto err; + + switch (priv->cfg.clock_out) { + case M88TS2022_CLOCK_OUT_DISABLED: + u8tmp = 0x60; + break; + case M88TS2022_CLOCK_OUT_ENABLED: + u8tmp = 0x70; + ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); + if (ret) + goto err; + break; + case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: + u8tmp = 0x6c; + break; + default: + goto err; + } + + ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); + if (ret) + goto err; + + if (priv->cfg.loop_through) + u8tmp = 0xec; + else + u8tmp = 0x6c; + + ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); + if (ret) + goto err; + + for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { + ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, reg_vals[i].val); + if (ret) + goto err; + } +err: + if (ret) + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ts2022_sleep(struct dvb_frontend *fe) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + int ret; + dev_dbg(&priv->client->dev, "%s:\n", __func__); + + ret = m88ts2022_wr_reg(priv, 0x00, 0x00); + if (ret) + goto err; +err: + if (ret) + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + dev_dbg(&priv->client->dev, "%s:\n", __func__); + + *frequency = priv->frequency_khz; + return 0; +} + +static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + dev_dbg(&priv->client->dev, "%s:\n", __func__); + + *frequency = 0; /* Zero-IF */ + return 0; +} + +static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + int ret; + u8 u8tmp; + u16 gain, u16tmp; + unsigned int gain1, gain2, gain3; + + ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp); + if (ret) + goto err; + + gain1 = (u8tmp >> 0) & 0x1f; + gain1 = clamp(gain1, 0U, 15U); + + ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp); + if (ret) + goto err; + + gain2 = (u8tmp >> 0) & 0x1f; + gain2 = clamp(gain2, 2U, 16U); + + ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp); + if (ret) + goto err; + + gain3 = (u8tmp >> 3) & 0x07; + gain3 = clamp(gain3, 0U, 6U); + + gain = gain1 * 265 + gain2 * 338 + gain3 * 285; + + /* scale value to 0x0000-0xffff */ + u16tmp = (0xffff - gain); + u16tmp = clamp_val(u16tmp, 59000U, 61500U); + + *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); +err: + if (ret) + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops m88ts2022_tuner_ops = { + .info = { + .name = "Montage M88TS2022", + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .init = m88ts2022_init, + .sleep = m88ts2022_sleep, + .set_params = m88ts2022_set_params, + + .get_frequency = m88ts2022_get_frequency, + .get_if_frequency = m88ts2022_get_if_frequency, + .get_rf_strength = m88ts2022_get_rf_strength, +}; + +static int m88ts2022_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct m88ts2022_config *cfg = client->dev.platform_data; + struct dvb_frontend *fe = cfg->fe; + struct m88ts2022_priv *priv; + int ret; + u8 chip_id, u8tmp; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } + + memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config)); + priv->client = client; + + /* check if the tuner is there */ + ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp); + if (ret) + goto err; + + if ((u8tmp & 0x03) == 0x00) { + ret = m88ts2022_wr_reg(priv, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(2000, 50000); + } + + ret = m88ts2022_wr_reg(priv, 0x00, 0x03); + if (ret) + goto err; + + usleep_range(2000, 50000); + + ret = m88ts2022_rd_reg(priv, 0x00, &chip_id); + if (ret) + goto err; + + dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id); + + switch (chip_id) { + case 0xc3: + case 0x83: + break; + default: + goto err; + } + + switch (priv->cfg.clock_out) { + case M88TS2022_CLOCK_OUT_DISABLED: + u8tmp = 0x60; + break; + case M88TS2022_CLOCK_OUT_ENABLED: + u8tmp = 0x70; + ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); + if (ret) + goto err; + break; + case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: + u8tmp = 0x6c; + break; + default: + goto err; + } + + ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); + if (ret) + goto err; + + if (priv->cfg.loop_through) + u8tmp = 0xec; + else + u8tmp = 0x6c; + + ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); + if (ret) + goto err; + + /* sleep */ + ret = m88ts2022_wr_reg(priv, 0x00, 0x00); + if (ret) + goto err; + + dev_info(&priv->client->dev, + "%s: Montage M88TS2022 successfully identified\n", + KBUILD_MODNAME); + + fe->tuner_priv = priv; + memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + i2c_set_clientdata(client, priv); + return 0; +err: + dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + kfree(priv); + return ret; +} + +static int m88ts2022_remove(struct i2c_client *client) +{ + struct m88ts2022_priv *priv = i2c_get_clientdata(client); + struct dvb_frontend *fe = priv->cfg.fe; + dev_dbg(&client->dev, "%s:\n", __func__); + + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = NULL; + kfree(priv); + + return 0; +} + +static const struct i2c_device_id m88ts2022_id[] = { + {"m88ts2022", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, m88ts2022_id); + +static struct i2c_driver m88ts2022_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "m88ts2022", + }, + .probe = m88ts2022_probe, + .remove = m88ts2022_remove, + .id_table = m88ts2022_id, +}; + +module_i2c_driver(m88ts2022_driver); + +MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h new file mode 100644 index 000000000000..659fa1b1633a --- /dev/null +++ b/drivers/media/tuners/m88ts2022.h @@ -0,0 +1,54 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * 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. + */ + +#ifndef M88TS2022_H +#define M88TS2022_H + +#include "dvb_frontend.h" + +struct m88ts2022_config { + /* + * clock + * 16000000 - 32000000 + */ + u32 clock; + + /* + * RF loop-through + */ + u8 loop_through:1; + + /* + * clock output + */ +#define M88TS2022_CLOCK_OUT_DISABLED 0 +#define M88TS2022_CLOCK_OUT_ENABLED 1 +#define M88TS2022_CLOCK_OUT_ENABLED_XTALOUT 2 + u8 clock_out:2; + + /* + * clock output divider + * 1 - 31 + */ + u8 clock_out_div:5; + + /* + * pointer to DVB frontend + */ + struct dvb_frontend *fe; +}; + +#endif diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h new file mode 100644 index 000000000000..0363dd866a2d --- /dev/null +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -0,0 +1,34 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * 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. + */ + +#ifndef M88TS2022_PRIV_H +#define M88TS2022_PRIV_H + +#include "m88ts2022.h" + +struct m88ts2022_priv { + struct m88ts2022_config cfg; + struct i2c_client *client; + struct dvb_frontend *fe; + u32 frequency_khz; +}; + +struct m88ts2022_reg_val { + u8 reg; + u8 val; +}; + +#endif diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 4be5cf808a40..cca508d4aafb 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -134,15 +134,6 @@ struct xc2028_data { _rc; \ }) -#define i2c_rcv(priv, buf, size) ({ \ - int _rc; \ - _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \ - if (size != _rc) \ - tuner_err("i2c input error: rc = %d (should be %d)\n", \ - _rc, (int)size); \ - _rc; \ -}) - #define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \ int _rc; \ _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \ @@ -276,6 +267,7 @@ static int check_device_status(struct xc2028_data *priv) case XC2028_WAITING_FIRMWARE: return -EAGAIN; case XC2028_ACTIVE: + return 1; case XC2028_SLEEP: return 0; case XC2028_NODEV: @@ -718,6 +710,8 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type, return 0; } +static int xc2028_sleep(struct dvb_frontend *fe); + static int check_firmware(struct dvb_frontend *fe, unsigned int type, v4l2_std_id std, __u16 int_freq) { @@ -890,7 +884,7 @@ read_not_reliable: return 0; fail: - priv->state = XC2028_SLEEP; + priv->state = XC2028_NO_FIRMWARE; memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); if (retry_count < 8) { @@ -900,6 +894,9 @@ fail: goto retry; } + /* Firmware didn't load. Put the device to sleep */ + xc2028_sleep(fe); + if (rc == -ENOENT) rc = -EINVAL; return rc; @@ -917,6 +914,12 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) if (rc < 0) return rc; + /* If the device is sleeping, no channel is tuned */ + if (!rc) { + *strength = 0; + return 0; + } + mutex_lock(&priv->lock); /* Sync Lock Indicator */ @@ -964,6 +967,12 @@ static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc) if (rc < 0) return rc; + /* If the device is sleeping, no channel is tuned */ + if (!rc) { + *afc = 0; + return 0; + } + mutex_lock(&priv->lock); /* Sync Lock Indicator */ @@ -1281,6 +1290,10 @@ static int xc2028_sleep(struct dvb_frontend *fe) if (rc < 0) return rc; + /* Device is already in sleep mode */ + if (!rc) + return 0; + /* Avoid firmware reload on slow devices or if PM disabled */ if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; @@ -1298,7 +1311,8 @@ static int xc2028_sleep(struct dvb_frontend *fe) else rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); - priv->state = XC2028_SLEEP; + if (rc >= 0) + priv->state = XC2028_SLEEP; mutex_unlock(&priv->lock); @@ -1366,7 +1380,7 @@ static void load_firmware_cb(const struct firmware *fw, if (rc < 0) return; - priv->state = XC2028_SLEEP; + priv->state = XC2028_ACTIVE; } static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index cfe8056b91aa..39d824e2bb69 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -17,7 +17,6 @@ source "drivers/media/usb/cpia2/Kconfig" source "drivers/media/usb/zr364xx/Kconfig" source "drivers/media/usb/stkwebcam/Kconfig" source "drivers/media/usb/s2255/Kconfig" -source "drivers/media/usb/sn9c102/Kconfig" source "drivers/media/usb/usbtv/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 0935f47497a6..7ac4b143dce8 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ -obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index bd9d19a73efd..ab45a6f9dcc9 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -173,9 +173,8 @@ static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { int ifnum; -#ifdef CONFIG_VIDEO_AU0828_V4L2 - int retval; -#endif + int retval = 0; + struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -257,7 +256,11 @@ static int au0828_usb_probe(struct usb_interface *interface, #endif /* Digital TV */ - au0828_dvb_register(dev); + retval = au0828_dvb_register(dev); + if (retval) + pr_err("%s() au0282_dev_register failed\n", + __func__); + /* Store the pointer to the au0828_dev so it can be accessed in au0828_usb_disconnect */ @@ -268,7 +271,7 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_unlock(&dev->lock); - return 0; + return retval; } static struct usb_driver au0828_usb_driver = { diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 9a6f15613a38..4ae8b1074649 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -33,6 +33,10 @@ #include "mxl5007t.h" #include "tda18271.h" +static int preallocate_big_buffers; +module_param_named(preallocate_big_buffers, preallocate_big_buffers, int, 0644); +MODULE_PARM_DESC(preallocate_big_buffers, "Preallocate the larger transfer buffers at module load time"); + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define _AU0828_BULKPIPE 0x83 @@ -153,9 +157,13 @@ static int stop_urb_transfer(struct au0828_dev *dev) dev->urb_streaming = 0; for (i = 0; i < URB_COUNT; i++) { - usb_kill_urb(dev->urbs[i]); - kfree(dev->urbs[i]->transfer_buffer); - usb_free_urb(dev->urbs[i]); + if (dev->urbs[i]) { + usb_kill_urb(dev->urbs[i]); + if (!preallocate_big_buffers) + kfree(dev->urbs[i]->transfer_buffer); + + usb_free_urb(dev->urbs[i]); + } } return 0; @@ -181,10 +189,18 @@ static int start_urb_transfer(struct au0828_dev *dev) purb = dev->urbs[i]; - purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (preallocate_big_buffers) + purb->transfer_buffer = dev->dig_transfer_buffer[i]; + else + purb->transfer_buffer = kzalloc(URB_BUFSIZE, + GFP_KERNEL); + if (!purb->transfer_buffer) { usb_free_urb(purb); dev->urbs[i] = NULL; + printk(KERN_ERR + "%s: failed big buffer allocation, err = %d\n", + __func__, ret); goto err; } @@ -217,6 +233,27 @@ err: return ret; } +static void au0828_start_transport(struct au0828_dev *dev) +{ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + +} + +static void au0828_stop_transport(struct au0828_dev *dev, int full_stop) +{ + if (full_stop) { + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + } + au0828_write(dev, 0x60b, 0x00); +} + + + static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; @@ -231,13 +268,17 @@ static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) if (dvb) { mutex_lock(&dvb->lock); + dvb->start_count++; + dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, + dvb->start_count, dvb->stop_count); if (dvb->feeding++ == 0) { /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); + au0828_start_transport(dev); ret = start_urb_transfer(dev); + if (ret < 0) { + au0828_stop_transport(dev, 0); + dvb->feeding--; /* We ran out of memory... */ + } } mutex_unlock(&dvb->lock); } @@ -256,10 +297,16 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) if (dvb) { mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = stop_urb_transfer(dev); - au0828_write(dev, 0x60b, 0x00); + dvb->stop_count++; + dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, + dvb->start_count, dvb->stop_count); + if (dvb->feeding > 0) { + dvb->feeding--; + if (dvb->feeding == 0) { + /* Stop transport */ + ret = stop_urb_transfer(dev); + au0828_stop_transport(dev, 0); + } } mutex_unlock(&dvb->lock); } @@ -282,16 +329,10 @@ static void au0828_restart_dvb_streaming(struct work_struct *work) /* Stop transport */ stop_urb_transfer(dev); - au0828_write(dev, 0x608, 0x00); - au0828_write(dev, 0x609, 0x00); - au0828_write(dev, 0x60a, 0x00); - au0828_write(dev, 0x60b, 0x00); + au0828_stop_transport(dev, 1); /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); + au0828_start_transport(dev); start_urb_transfer(dev); mutex_unlock(&dvb->lock); @@ -304,6 +345,23 @@ static int dvb_register(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) { + dev->dig_transfer_buffer[i] = kzalloc(URB_BUFSIZE, + GFP_KERNEL); + + if (!dev->dig_transfer_buffer[i]) { + result = -ENOMEM; + + printk(KERN_ERR + "%s: failed buffer allocation (errno = %d)\n", + DRIVER_NAME, result); + goto fail_adapter; + } + } + } + INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming); /* register adapter */ @@ -375,6 +433,9 @@ static int dvb_register(struct au0828_dev *dev) /* register network adapter */ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + + dvb->start_count = 0; + dvb->stop_count = 0; return 0; fail_fe_conn: @@ -391,6 +452,13 @@ fail_frontend: dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); fail_adapter: + + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) + kfree(dev->dig_transfer_buffer[i]); + } + return result; } @@ -411,6 +479,14 @@ void au0828_dvb_unregister(struct au0828_dev *dev) dvb_unregister_frontend(dvb->frontend); dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); + + if (preallocate_big_buffers) { + int i; + for (i = 0; i < URB_COUNT; i++) + kfree(dev->dig_transfer_buffer[i]); + } + + } /* All the DVB attach calls go here, this function get's modified diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index ef1f57f22be7..5439772c1551 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -102,6 +102,8 @@ struct au0828_dvb { struct dmx_frontend fe_mem; struct dvb_net net; int feeding; + int start_count; + int stop_count; }; enum au0828_stream_state { @@ -260,6 +262,10 @@ struct au0828_dev { /* USB / URB Related */ int urb_streaming; struct urb *urbs[URB_COUNT]; + + /* Preallocated transfer digital transfer buffers */ + + char *dig_transfer_buffer[URB_COUNT]; }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 86feeeaf61c2..f14c5e89a567 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -45,6 +45,8 @@ config VIDEO_CX231XX_DVB select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 528cce958a82..2ee03e4ddd86 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -709,6 +709,8 @@ const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); /* table of devices that work with this driver */ struct usb_device_id cx231xx_id_table[] = { + {USB_DEVICE(0x1D19, 0x6109), + .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, {USB_DEVICE(0x0572, 0x5A3C), .driver_info = CX231XX_BOARD_UNKNOWN}, {USB_DEVICE(0x0572, 0x58A2), diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 96a5a0965399..7c0f797f1057 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -371,9 +371,9 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, mutex_lock(&dev->i2c_lock); for (i = 0; i < num; i++) { - addr = msgs[i].addr >> 1; + addr = msgs[i].addr; - dprintk2(2, "%s %s addr=%x len=%d:", + dprintk2(2, "%s %s addr=0x%x len=%d:", (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); if (!msgs[i].len) { @@ -390,32 +390,41 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]); if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); + printk(KERN_CONT " %02x", msgs[i].buf[byte]); } } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && msgs[i].addr == msgs[i + 1].addr && (msgs[i].len <= 2) && (bus->nr < 3)) { + /* write bytes */ + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + printk(KERN_CONT "\n"); + } /* read bytes */ + dprintk2(2, "plus %s %s addr=0x%x len=%d:", + (msgs[i+1].flags & I2C_M_RD) ? "read" : "write", + i+1 == num - 1 ? "stop" : "nonstop", addr, msgs[i+1].len); rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap, &msgs[i], &msgs[i + 1]); if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); + for (byte = 0; byte < msgs[i+1].len; byte++) + printk(KERN_CONT " %02x", msgs[i+1].buf[byte]); } i++; } else { /* write bytes */ if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); + printk(KERN_CONT " %02x", msgs[i].buf[byte]); } rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]); } if (rc < 0) goto err; if (i2c_debug >= 2) - printk("\n"); + printk(KERN_CONT "\n"); } mutex_unlock(&dev->i2c_lock); return num; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 8f9b2cea88f0..8ede8ea762e6 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1539,6 +1539,8 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a05, &af9035_props, "Leadtek WinFast DTV Dongle Dual", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xf900, + &af9035_props, "Hauppauge WinTV-MiniStick 2", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 90cfa35ef6e6..eeab79bdd2aa 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -442,6 +442,7 @@ static struct cxd2820r_config anysee_cxd2820r_config = { * IOD[0] ZL10353 1=enabled * IOE[0] tuner 0=enabled * tuner is behind ZL10353 I2C-gate + * tuner is behind TDA10023 I2C-gate * * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)" * PCB: 508TC (rev0.6) @@ -956,7 +957,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) if (fe && adap->fe[1]) { /* attach tuner for 2nd FE */ - fe = dvb_attach(dvb_pll_attach, adap->fe[0], + fe = dvb_attach(dvb_pll_attach, adap->fe[1], (0xc0 >> 1), &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); } diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 44c64ef361bf..c1051c347744 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -68,6 +68,19 @@ static struct drxk_config terratec_h7_drxk = { .microcode_name = "dvb-usb-terratec-h7-drxk.fw", }; +static struct drxk_config cablestar_hdci_drxk = { + .adr = 0x29, + .parallel_ts = true, + .dynamic_clk = true, + .single_master = true, + .enable_merr_cfg = true, + .no_i2c_bridge = false, + .chunk_size = 64, + .mpeg_out_clk_strength = 0x02, + .qam_demod_parameter_count = 2, + .microcode_name = "dvb-usb-technisat-cablestar-hdci-drxk.fw", +}; + static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { struct az6007_device_state *st = fe_to_priv(fe); @@ -630,6 +643,27 @@ static int az6007_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int az6007_cablestar_hdci_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct az6007_device_state *st = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + + pr_debug("attaching demod drxk\n"); + + adap->fe[0] = dvb_attach(drxk_attach, &cablestar_hdci_drxk, + &d->i2c_adap); + if (!adap->fe[0]) + return -EINVAL; + + adap->fe[0]->sec_priv = adap; + st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl; + adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; + + az6007_ci_init(adap); + + return 0; +} + static int az6007_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap_to_d(adap); @@ -868,6 +902,29 @@ static struct dvb_usb_device_properties az6007_props = { } }; +static struct dvb_usb_device_properties az6007_cablestar_hdci_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .firmware = AZ6007_FIRMWARE, + + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct az6007_device_state), + .i2c_algo = &az6007_i2c_algo, + .tuner_attach = az6007_tuner_attach, + .frontend_attach = az6007_cablestar_hdci_frontend_attach, + .streaming_ctrl = az6007_streaming_ctrl, +/* ditch get_rc_config as it can't work (TS35 remote, I believe it's rc5) */ + .get_rc_config = NULL, + .read_mac_address = az6007_read_mac_addr, + .download_firmware = az6007_download_firmware, + .identify_state = az6007_identify_state, + .power_ctrl = az6007_power_ctrl, + .num_adapters = 1, + .adapter = { + { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), } + } +}; + static struct usb_device_id az6007_usb_table[] = { {DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007, &az6007_props, "Azurewave 6007", RC_MAP_EMPTY)}, @@ -875,6 +932,8 @@ static struct usb_device_id az6007_usb_table[] = { &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2, &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, + {DVB_USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI, + &az6007_cablestar_hdci_props, "Technisat CableStar Combo HD CI", RC_MAP_EMPTY)}, {0}, }; diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index 5c68f3918bc8..0c2b377704ff 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -170,7 +170,7 @@ static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], error: mutex_unlock(&d->i2c_mutex); - return i; + return ret; } static u32 ec168_i2c_func(struct i2c_adapter *adapter) diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 1cb6899cf797..fe95a586dd5d 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -799,6 +799,9 @@ static const struct usb_device_id it913x_id_table[] = { { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2, &it913x_properties, "Digital Dual TV Receiver CTVDIGDUAL_V2", RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335, + &it913x_properties, "Avermedia H335", + RC_MAP_IT913X_V2) }, {} /* Terminating entry */ }; diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index d83df4bb72d3..0a98d04c53e4 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -1,7 +1,7 @@ /* * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 @@ -601,7 +601,7 @@ struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, EXPORT_SYMBOL_GPL(mxl111sf_demod_attach); MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); -MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h index 3f3f8bfd190b..2d4530f5be54 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h @@ -1,7 +1,7 @@ /* * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c index e4121cb8f5ef..a619410adde4 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c @@ -1,7 +1,7 @@ /* * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h index 0220f54299a5..b85a5772d771 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h @@ -1,7 +1,7 @@ /* * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c index 34434557ef65..a101d06eb143 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c @@ -1,7 +1,7 @@ /* * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h index a57a45ffb9e4..465762145ad2 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h @@ -1,7 +1,7 @@ /* * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c index b741b3a7a325..f6b348024bec 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c @@ -1,7 +1,7 @@ /* * mxl111sf-phy.c - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h index f0756071d347..0643738de7de 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h @@ -1,7 +1,7 @@ /* * mxl111sf-phy.h - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h index 17831b0fb9db..89bf115e927e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h @@ -1,7 +1,7 @@ /* * mxl111sf-reg.h - driver for the MaxLinear MXL111SF * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index 879c529640f7..a8d2c7053674 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -1,7 +1,7 @@ /* * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 @@ -512,7 +512,7 @@ struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); -MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h index 90f583e5d6a6..2046db22519e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h @@ -1,7 +1,7 @@ /* * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner * - * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> * * 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 @@ -68,7 +68,7 @@ struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, #else static inline struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, - struct mxl111sf_state *mxl_state + struct mxl111sf_state *mxl_state, struct mxl111sf_tuner_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 08240e498451..c7304fa8ab73 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com) + * Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org) * * 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 @@ -105,7 +105,7 @@ int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) ret = -EINVAL; } - pr_debug("R: (0x%02x, 0x%02x)\n", addr, *data); + pr_debug("R: (0x%02x, 0x%02x)\n", addr, buf[1]); fail: return ret; } @@ -1421,7 +1421,7 @@ static struct usb_driver mxl111sf_usb_driver = { module_usb_driver(mxl111sf_usb_driver); -MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index 9816de86e48c..8516c011b7cc 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com) + * Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org) * * 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 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index ecca03667f98..fda5c64ba0e8 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1407,6 +1407,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680, &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_MINID, + &rtl2832u_props, "Leadtek Winfast DTV Dongle Mini D", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3, &rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1102, diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 20e345d9fe8f..a1c641e18362 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -149,6 +149,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; int i; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) @@ -173,7 +174,8 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (1 + msg[i].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[i].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = 0; obuf[1] = msg[i].len; @@ -193,12 +195,14 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (3 + msg[i].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[i].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } if (1 + msg[i + 1].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[i + 1].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[i].len; obuf[1] = msg[i+1].len; @@ -223,7 +227,8 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (2 + msg[i].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[i].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[i].addr; obuf[1] = msg[i].len; @@ -237,8 +242,14 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], } } + if (i == num) + ret = num; + else + ret = -EREMOTEIO; + +unlock: mutex_unlock(&d->i2c_mutex); - return i == num ? num : -EREMOTEIO; + return ret; } static u32 cxusb_i2c_func(struct i2c_adapter *adapter) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index c1a63b2a6baa..ae0f56a32e4d 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -2,7 +2,7 @@ * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, * TeVii S600, S630, S650, S660, S480, S421, S632 * Prof 1100, 7500, - * Geniatech SU3000 Cards + * Geniatech SU3000, T220 Cards * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "m88rs2000.h" +#include "tda18271.h" +#include "cxd2820r.h" /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 64 @@ -110,11 +112,6 @@ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." -struct rc_map_dvb_usb_table_table { - struct rc_map_table *rc_keys; - int rc_keys_size; -}; - struct su3000_state { u8 initialized; }; @@ -129,12 +126,6 @@ module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))." DVB_USB_DEBUG_STATUS); -/* keymaps */ -static int ir_keymap; -module_param_named(keymap, ir_keymap, int, 0644); -MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..." - " 256=none"); - /* demod probe */ static int demod_probe = 1; module_param_named(demod, demod_probe, int, 0644); @@ -301,6 +292,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; if (!d) return -ENODEV; @@ -316,7 +308,8 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms if (2 + msg[1].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[1].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[0].addr << 1; @@ -340,7 +333,8 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms if (2 + msg[0].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[1].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[0].addr << 1; @@ -357,7 +351,8 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms if (2 + msg[0].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[1].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[0].addr << 1; @@ -386,15 +381,17 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms break; } + ret = num; +unlock: mutex_unlock(&d->i2c_mutex); - return num; + return ret; } static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int len, i, j; + int len, i, j, ret; if (!d) return -ENODEV; @@ -430,7 +427,8 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i if (2 + msg[j].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[j].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } dw210x_op_rw(d->udev, 0xc3, @@ -466,7 +464,8 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i if (2 + msg[j].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[j].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[j].addr << 1; @@ -481,15 +480,18 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i } } + ret = num; +unlock: mutex_unlock(&d->i2c_mutex); - return num; + return ret; } static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; int i; if (!d) @@ -506,7 +508,8 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (2 + msg[1].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[1].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; @@ -530,7 +533,8 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (2 + msg[0].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[0].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; @@ -556,9 +560,11 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], msg[i].flags == 0 ? ">>>" : "<<<"); debug_dump(msg[i].buf, msg[i].len, deb_xfer); } + ret = num; +unlock: mutex_unlock(&d->i2c_mutex); - return num; + return ret; } static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], @@ -566,7 +572,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct usb_device *udev; - int len, i, j; + int len, i, j, ret; if (!d) return -ENODEV; @@ -618,7 +624,8 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (msg[j].len > sizeof(ibuf)) { warn("i2c rd: len=%d is too big!\n", msg[j].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } dw210x_op_rw(d->udev, 0x91, 0, 0, @@ -652,7 +659,8 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (2 + msg[j].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[j].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[j + 1].len; @@ -671,7 +679,8 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (2 + msg[j].len > sizeof(obuf)) { warn("i2c wr: len=%d is too big!\n", msg[j].len); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto unlock; } obuf[0] = msg[j].len + 1; obuf[1] = (msg[j].addr << 1); @@ -685,9 +694,11 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } } } + ret = num; +unlock: mutex_unlock(&d->i2c_mutex); - return num; + return ret; } static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], @@ -1095,6 +1106,16 @@ static struct ds3000_config su3000_ds3000_config = { .set_lock_led = dw210x_led_ctrl, }; +static struct cxd2820r_config cxd2820r_config = { + .i2c_address = 0x6c, /* (0xd8 >> 1) */ + .ts_mode = 0x38, +}; + +static struct tda18271_config tda18271_config = { + .output_opt = TDA18271_OUTPUT_LT_OFF, + .gate = TDA18271_GATE_DIGITAL, +}; + static u8 m88rs2000_inittab[] = { DEMOD_WRITE, 0x9a, 0x30, DEMOD_WRITE, 0x00, 0x01, @@ -1364,6 +1385,49 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) return -EIO; } +static int t220_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[3] = { 0xe, 0x80, 0 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x83; + obuf[2] = 0; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + msleep(100); + + obuf[0] = 0xe; + obuf[1] = 0x80; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0x51; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe_adap[0].fe = dvb_attach(cxd2820r_attach, &cxd2820r_config, + &d->dev->i2c_adap, NULL); + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(tda18271_attach, d->fe_adap[0].fe, 0x60, + &d->dev->i2c_adap, &tda18271_config)) { + info("Attached TDA18271HD/CXD2820R!\n"); + return 0; + } + } + + info("Failed to attach TDA18271HD/CXD2820R!\n"); + return -EIO; +} + static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) { u8 obuf[] = { 0x51 }; @@ -1404,174 +1468,29 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct rc_map_table rc_map_dw210x_table[] = { - { 0xf80a, KEY_POWER2 }, /*power*/ - { 0xf80c, KEY_MUTE }, /*mute*/ - { 0xf811, KEY_1 }, - { 0xf812, KEY_2 }, - { 0xf813, KEY_3 }, - { 0xf814, KEY_4 }, - { 0xf815, KEY_5 }, - { 0xf816, KEY_6 }, - { 0xf817, KEY_7 }, - { 0xf818, KEY_8 }, - { 0xf819, KEY_9 }, - { 0xf810, KEY_0 }, - { 0xf81c, KEY_CHANNELUP }, /*ch+*/ - { 0xf80f, KEY_CHANNELDOWN }, /*ch-*/ - { 0xf81a, KEY_VOLUMEUP }, /*vol+*/ - { 0xf80e, KEY_VOLUMEDOWN }, /*vol-*/ - { 0xf804, KEY_RECORD }, /*rec*/ - { 0xf809, KEY_FAVORITES }, /*fav*/ - { 0xf808, KEY_REWIND }, /*rewind*/ - { 0xf807, KEY_FASTFORWARD }, /*fast*/ - { 0xf80b, KEY_PAUSE }, /*pause*/ - { 0xf802, KEY_ESC }, /*cancel*/ - { 0xf803, KEY_TAB }, /*tab*/ - { 0xf800, KEY_UP }, /*up*/ - { 0xf81f, KEY_OK }, /*ok*/ - { 0xf801, KEY_DOWN }, /*down*/ - { 0xf805, KEY_CAMERA }, /*cap*/ - { 0xf806, KEY_STOP }, /*stop*/ - { 0xf840, KEY_ZOOM }, /*full*/ - { 0xf81e, KEY_TV }, /*tvmode*/ - { 0xf81b, KEY_LAST }, /*recall*/ -}; - -static struct rc_map_table rc_map_tevii_table[] = { - { 0xf80a, KEY_POWER }, - { 0xf80c, KEY_MUTE }, - { 0xf811, KEY_1 }, - { 0xf812, KEY_2 }, - { 0xf813, KEY_3 }, - { 0xf814, KEY_4 }, - { 0xf815, KEY_5 }, - { 0xf816, KEY_6 }, - { 0xf817, KEY_7 }, - { 0xf818, KEY_8 }, - { 0xf819, KEY_9 }, - { 0xf810, KEY_0 }, - { 0xf81c, KEY_MENU }, - { 0xf80f, KEY_VOLUMEDOWN }, - { 0xf81a, KEY_LAST }, - { 0xf80e, KEY_OPEN }, - { 0xf804, KEY_RECORD }, - { 0xf809, KEY_VOLUMEUP }, - { 0xf808, KEY_CHANNELUP }, - { 0xf807, KEY_PVR }, - { 0xf80b, KEY_TIME }, - { 0xf802, KEY_RIGHT }, - { 0xf803, KEY_LEFT }, - { 0xf800, KEY_UP }, - { 0xf81f, KEY_OK }, - { 0xf801, KEY_DOWN }, - { 0xf805, KEY_TUNER }, - { 0xf806, KEY_CHANNELDOWN }, - { 0xf840, KEY_PLAYPAUSE }, - { 0xf81e, KEY_REWIND }, - { 0xf81b, KEY_FAVORITES }, - { 0xf81d, KEY_BACK }, - { 0xf84d, KEY_FASTFORWARD }, - { 0xf844, KEY_EPG }, - { 0xf84c, KEY_INFO }, - { 0xf841, KEY_AB }, - { 0xf843, KEY_AUDIO }, - { 0xf845, KEY_SUBTITLE }, - { 0xf84a, KEY_LIST }, - { 0xf846, KEY_F1 }, - { 0xf847, KEY_F2 }, - { 0xf85e, KEY_F3 }, - { 0xf85c, KEY_F4 }, - { 0xf852, KEY_F5 }, - { 0xf85a, KEY_F6 }, - { 0xf856, KEY_MODE }, - { 0xf858, KEY_SWITCHVIDEOMODE }, -}; - -static struct rc_map_table rc_map_tbs_table[] = { - { 0xf884, KEY_POWER }, - { 0xf894, KEY_MUTE }, - { 0xf887, KEY_1 }, - { 0xf886, KEY_2 }, - { 0xf885, KEY_3 }, - { 0xf88b, KEY_4 }, - { 0xf88a, KEY_5 }, - { 0xf889, KEY_6 }, - { 0xf88f, KEY_7 }, - { 0xf88e, KEY_8 }, - { 0xf88d, KEY_9 }, - { 0xf892, KEY_0 }, - { 0xf896, KEY_CHANNELUP }, - { 0xf891, KEY_CHANNELDOWN }, - { 0xf893, KEY_VOLUMEUP }, - { 0xf88c, KEY_VOLUMEDOWN }, - { 0xf883, KEY_RECORD }, - { 0xf898, KEY_PAUSE }, - { 0xf899, KEY_OK }, - { 0xf89a, KEY_SHUFFLE }, - { 0xf881, KEY_UP }, - { 0xf890, KEY_LEFT }, - { 0xf882, KEY_RIGHT }, - { 0xf888, KEY_DOWN }, - { 0xf895, KEY_FAVORITES }, - { 0xf897, KEY_SUBTITLE }, - { 0xf89d, KEY_ZOOM }, - { 0xf89f, KEY_EXIT }, - { 0xf89e, KEY_MENU }, - { 0xf89c, KEY_EPG }, - { 0xf880, KEY_PREVIOUS }, - { 0xf89b, KEY_MODE } -}; +static int dw2102_rc_query(struct dvb_usb_device *d) +{ + u8 key[2]; + struct i2c_msg msg = { + .addr = DW2102_RC_QUERY, + .flags = I2C_M_RD, + .buf = key, + .len = 2 + }; -static struct rc_map_table rc_map_su3000_table[] = { - { 0x25, KEY_POWER }, /* right-bottom Red */ - { 0x0a, KEY_MUTE }, /* -/-- */ - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x00, KEY_0 }, - { 0x20, KEY_UP }, /* CH+ */ - { 0x21, KEY_DOWN }, /* CH+ */ - { 0x12, KEY_VOLUMEUP }, /* Brightness Up */ - { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */ - { 0x1f, KEY_RECORD }, - { 0x17, KEY_PLAY }, - { 0x16, KEY_PAUSE }, - { 0x0b, KEY_STOP }, - { 0x27, KEY_FASTFORWARD },/* >> */ - { 0x26, KEY_REWIND }, /* << */ - { 0x0d, KEY_OK }, /* Mute */ - { 0x11, KEY_LEFT }, /* VOL- */ - { 0x10, KEY_RIGHT }, /* VOL+ */ - { 0x29, KEY_BACK }, /* button under 9 */ - { 0x2c, KEY_MENU }, /* TTX */ - { 0x2b, KEY_EPG }, /* EPG */ - { 0x1e, KEY_RED }, /* OSD */ - { 0x0e, KEY_GREEN }, /* Window */ - { 0x2d, KEY_YELLOW }, /* button under << */ - { 0x0f, KEY_BLUE }, /* bottom yellow button */ - { 0x14, KEY_AUDIO }, /* Snapshot */ - { 0x38, KEY_TV }, /* TV/Radio */ - { 0x0c, KEY_ESC } /* upper Red button */ -}; + if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { + if (msg.buf[0] != 0xff) { + deb_rc("%s: rc code: %x, %x\n", + __func__, key[0], key[1]); + rc_keydown(d->rc_dev, key[0], 1); + } + } -static struct rc_map_dvb_usb_table_table keys_tables[] = { - { rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) }, - { rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) }, - { rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) }, - { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) }, -}; + return 0; +} -static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +static int prof_rc_query(struct dvb_usb_device *d) { - struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; - int keymap_size = d->props.rc.legacy.rc_map_size; u8 key[2]; struct i2c_msg msg = { .addr = DW2102_RC_QUERY, @@ -1579,32 +1498,34 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) .buf = key, .len = 2 }; - int i; - /* override keymap */ - if ((ir_keymap > 0) && (ir_keymap <= ARRAY_SIZE(keys_tables))) { - keymap = keys_tables[ir_keymap - 1].rc_keys ; - keymap_size = keys_tables[ir_keymap - 1].rc_keys_size; - } else if (ir_keymap > ARRAY_SIZE(keys_tables)) - return 0; /* none */ - - *state = REMOTE_NO_KEY_PRESSED; - if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { - for (i = 0; i < keymap_size ; i++) { - if (rc5_data(&keymap[i]) == msg.buf[0]) { - *state = REMOTE_KEY_PRESSED; - *event = keymap[i].keycode; - break; - } + if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { + if (msg.buf[0] != 0xff) { + deb_rc("%s: rc code: %x, %x\n", + __func__, key[0], key[1]); + rc_keydown(d->rc_dev, key[0]^0xff, 1); } + } - if ((*state) == REMOTE_KEY_PRESSED) - deb_rc("%s: found rc key: %x, %x, event: %x\n", - __func__, key[0], key[1], (*event)); - else if (key[0] != 0xff) - deb_rc("%s: unknown rc key: %x, %x\n", - __func__, key[0], key[1]); + return 0; +} +static int su3000_rc_query(struct dvb_usb_device *d) +{ + u8 key[2]; + struct i2c_msg msg = { + .addr = DW2102_RC_QUERY, + .flags = I2C_M_RD, + .buf = key, + .len = 2 + }; + + if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { + if (msg.buf[0] != 0xff) { + deb_rc("%s: rc code: %x, %x\n", + __func__, key[0], key[1]); + rc_keydown(d->rc_dev, key[1] << 8 | key[0], 1); + } } return 0; @@ -1630,6 +1551,7 @@ enum dw2102_table_entry { TEVII_S632, TERRATEC_CINERGY_S2_R2, GOTVIEW_SAT_HD, + GENIATECH_T220, }; static struct usb_device_id dw2102_table[] = { @@ -1652,6 +1574,7 @@ static struct usb_device_id dw2102_table[] = { [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)}, + [GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)}, { } }; @@ -1711,9 +1634,7 @@ static int dw2102_load_firmware(struct usb_device *dev, /* init registers */ switch (dev->descriptor.idProduct) { case USB_PID_TEVII_S650: - dw2104_properties.rc.legacy.rc_map_table = rc_map_tevii_table; - dw2104_properties.rc.legacy.rc_map_size = - ARRAY_SIZE(rc_map_tevii_table); + dw2104_properties.rc.core.rc_codes = RC_MAP_TEVII_NEC; case USB_PID_DW2104: reset = 1; dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, @@ -1777,10 +1698,11 @@ static struct dvb_usb_device_properties dw2102_properties = { .i2c_algo = &dw2102_serit_i2c_algo, - .rc.legacy = { - .rc_map_table = rc_map_dw210x_table, - .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc.core = { .rc_interval = 150, + .rc_codes = RC_MAP_DM1105_NEC, + .module_name = "dw2102", + .allowed_protos = RC_BIT_NEC, .rc_query = dw2102_rc_query, }, @@ -1831,10 +1753,11 @@ static struct dvb_usb_device_properties dw2104_properties = { .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, - .rc.legacy = { - .rc_map_table = rc_map_dw210x_table, - .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc.core = { .rc_interval = 150, + .rc_codes = RC_MAP_DM1105_NEC, + .module_name = "dw2102", + .allowed_protos = RC_BIT_NEC, .rc_query = dw2102_rc_query, }, @@ -1881,10 +1804,11 @@ static struct dvb_usb_device_properties dw3101_properties = { .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, - .rc.legacy = { - .rc_map_table = rc_map_dw210x_table, - .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc.core = { .rc_interval = 150, + .rc_codes = RC_MAP_DM1105_NEC, + .module_name = "dw2102", + .allowed_protos = RC_BIT_NEC, .rc_query = dw2102_rc_query, }, @@ -1929,10 +1853,11 @@ static struct dvb_usb_device_properties s6x0_properties = { .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, - .rc.legacy = { - .rc_map_table = rc_map_tevii_table, - .rc_map_size = ARRAY_SIZE(rc_map_tevii_table), + .rc.core = { .rc_interval = 150, + .rc_codes = RC_MAP_TEVII_NEC, + .module_name = "dw2102", + .allowed_protos = RC_BIT_NEC, .rc_query = dw2102_rc_query, }, @@ -2022,11 +1947,12 @@ static struct dvb_usb_device_properties su3000_properties = { .identify_state = su3000_identify_state, .i2c_algo = &su3000_i2c_algo, - .rc.legacy = { - .rc_map_table = rc_map_su3000_table, - .rc_map_size = ARRAY_SIZE(rc_map_su3000_table), + .rc.core = { .rc_interval = 150, - .rc_query = dw2102_rc_query, + .rc_codes = RC_MAP_SU3000, + .module_name = "dw2102", + .allowed_protos = RC_BIT_RC5, + .rc_query = su3000_rc_query, }, .read_mac_address = su3000_read_mac_address, @@ -2077,6 +2003,55 @@ static struct dvb_usb_device_properties su3000_properties = { } }; +static struct dvb_usb_device_properties t220_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = sizeof(struct su3000_state), + .power_ctrl = su3000_power_ctrl, + .num_adapters = 1, + .identify_state = su3000_identify_state, + .i2c_algo = &su3000_i2c_algo, + + .rc.core = { + .rc_interval = 150, + .rc_codes = RC_MAP_SU3000, + .module_name = "dw2102", + .allowed_protos = RC_BIT_RC5, + .rc_query = su3000_rc_query, + }, + + .read_mac_address = su3000_read_mac_address, + + .generic_bulk_ctrl_endpoint = 0x01, + + .adapter = { + { + .num_frontends = 1, + .fe = { { + .streaming_ctrl = su3000_streaming_ctrl, + .frontend_attach = t220_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + } + } }, + } + }, + .num_device_descs = 1, + .devices = { + { "Geniatech T220 DVB-T/T2 USB2.0", + { &dw2102_table[GENIATECH_T220], NULL }, + { NULL }, + }, + } +}; + static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -2088,8 +2063,8 @@ static int dw2102_probe(struct usb_interface *intf, /* fill only different fields */ p1100->firmware = P1100_FIRMWARE; p1100->devices[0] = d1100; - p1100->rc.legacy.rc_map_table = rc_map_tbs_table; - p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); + p1100->rc.core.rc_query = prof_rc_query; + p1100->rc.core.rc_codes = RC_MAP_TBS_NEC; p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach; s660 = kmemdup(&s6x0_properties, @@ -2114,8 +2089,8 @@ static int dw2102_probe(struct usb_interface *intf, } p7500->firmware = P7500_FIRMWARE; p7500->devices[0] = d7500; - p7500->rc.legacy.rc_map_table = rc_map_tbs_table; - p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); + p7500->rc.core.rc_query = prof_rc_query; + p7500->rc.core.rc_codes = RC_MAP_TBS_NEC; p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; @@ -2149,7 +2124,9 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, s421, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &su3000_properties, - THIS_MODULE, NULL, adapter_nr)) + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &t220_properties, + THIS_MODULE, NULL, adapter_nr)) return 0; return -ENODEV; @@ -2169,7 +2146,7 @@ MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," " TeVii S600, S630, S650, S660, S480, S421, S632" " Prof 1100, 7500 USB2.0," - " Geniatech SU3000 devices"); + " Geniatech SU3000, T220 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DW2101_FIRMWARE); diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index ca5ee6aceb62..a1fccf3096de 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -1,8 +1,12 @@ config VIDEO_EM28XX - tristate "Empia EM28xx USB video capture support" + tristate "Empia EM28xx USB devices support" depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM + +config VIDEO_EM28XX_V4L2 + tristate "Empia EM28xx analog TV, video capture and/or webcam support" + depends on VIDEO_EM28XX select VIDEOBUF2_VMALLOC select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT @@ -49,6 +53,8 @@ config VIDEO_EM28XX_DVB select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index ad6d48557940..3f850d5063d0 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -1,10 +1,11 @@ -em28xx-y += em28xx-video.o em28xx-i2c.o em28xx-cards.o -em28xx-y += em28xx-core.o em28xx-vbi.o em28xx-camera.o +em28xx-y += em28xx-core.o em28xx-i2c.o em28xx-cards.o em28xx-camera.o +em28xx-v4l-objs := em28xx-video.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o em28xx-rc-objs := em28xx-input.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o +obj-$(CONFIG_VIDEO_EM28XX_V4L2) += em28xx-v4l.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 2fdb66ee44ab..05e9bd11a3ff 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> * - * Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com> + * Copyright (C) 2007-2014 Mauro Carvalho Chehab * - Port to work with the in-kernel driver * - Cleanups, fixes, alsa-controls, etc. * @@ -50,6 +50,9 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "activates debug info"); +#define EM28XX_MAX_AUDIO_BUFS 5 +#define EM28XX_MIN_AUDIO_PACKETS 64 + #define dprintk(fmt, arg...) do { \ if (debug) \ printk(KERN_INFO "em28xx-audio %s: " fmt, \ @@ -63,17 +66,13 @@ static int em28xx_deinit_isoc_audio(struct em28xx *dev) int i; dprintk("Stopping isoc\n"); - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + for (i = 0; i < dev->adev.num_urb; i++) { + struct urb *urb = dev->adev.urb[i]; + if (!irqs_disabled()) - usb_kill_urb(dev->adev.urb[i]); + usb_kill_urb(urb); else - usb_unlink_urb(dev->adev.urb[i]); - - usb_free_urb(dev->adev.urb[i]); - dev->adev.urb[i] = NULL; - - kfree(dev->adev.transfer_buffer[i]); - dev->adev.transfer_buffer[i] = NULL; + usb_unlink_urb(urb); } return 0; @@ -91,6 +90,12 @@ static void em28xx_audio_isocirq(struct urb *urb) struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; + if (dev->disconnected) { + dprintk("device disconnected while streaming. URB status=%d.\n", urb->status); + atomic_set(&dev->stream_started, 0); + return; + } + switch (urb->status) { case 0: /* success */ case -ETIMEDOUT: /* NAK */ @@ -158,63 +163,27 @@ static void em28xx_audio_isocirq(struct urb *urb) urb->status = 0; status = usb_submit_urb(urb, GFP_ATOMIC); - if (status < 0) { + if (status < 0) em28xx_errdev("resubmit of audio urb failed (error=%i)\n", status); - } return; } static int em28xx_init_audio_isoc(struct em28xx *dev) { int i, errCode; - const int sb_size = EM28XX_NUM_AUDIO_PACKETS * - EM28XX_AUDIO_MAX_PACKET_SIZE; dprintk("Starting isoc transfers\n"); - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - - dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev.transfer_buffer[i]) - return -ENOMEM; - - memset(dev->adev.transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) { - em28xx_errdev("usb_alloc_urb failed!\n"); - for (j = 0; j < i; j++) { - usb_free_urb(dev->adev.urb[j]); - kfree(dev->adev.transfer_buffer[j]); - } - return -ENOMEM; - } - - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->adev.transfer_buffer[i]; - urb->interval = 1; - urb->complete = em28xx_audio_isocirq; - urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - - for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; - j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - EM28XX_AUDIO_MAX_PACKET_SIZE; - } - dev->adev.urb[i] = urb; - } + /* Start streaming */ + for (i = 0; i < dev->adev.num_urb; i++) { + memset(dev->adev.transfer_buffer[i], 0x80, + dev->adev.urb[i]->transfer_buffer_length); - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); if (errCode) { - em28xx_errdev("submit of audio urb failed\n"); + em28xx_errdev("submit of audio urb failed (error=%i)\n", + errCode); em28xx_deinit_isoc_audio(dev); atomic_set(&dev->stream_started, 0); return errCode; @@ -255,15 +224,26 @@ static struct snd_pcm_hardware snd_em28xx_hw_capture = { .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, /* 12544/2, */ - .period_bytes_max = 12544, + + + /* + * The period is 12.288 bytes. Allow a 10% of variation along its + * value, in order to avoid overruns/underruns due to some clock + * drift. + * + * FIXME: This period assumes 64 packets, and a 48000 PCM rate. + * Calculate it dynamically. + */ + .period_bytes_min = 11059, + .period_bytes_max = 13516, + .periods_min = 2, .periods_max = 98, /* 12544, */ }; @@ -274,28 +254,48 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; int ret = 0; - dprintk("opening device and trying to acquire exclusive lock\n"); - if (!dev) { em28xx_err("BUG: em28xx can't find device struct." " Can't proceed with open\n"); return -ENODEV; } + if (dev->disconnected) + return -ENODEV; + + dprintk("opening device and trying to acquire exclusive lock\n"); + runtime->hw = snd_em28xx_hw_capture; - if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) { - if (dev->audio_ifnum) + if ((dev->alt == 0 || dev->is_audio_only) && dev->adev.users == 0) { + int nonblock = !!(substream->f_flags & O_NONBLOCK); + + if (nonblock) { + if (!mutex_trylock(&dev->lock)) + return -EAGAIN; + } else + mutex_lock(&dev->lock); + if (dev->is_audio_only) + /* vendor audio is on a separate interface */ dev->alt = 1; else + /* vendor audio is on the same interface as video */ dev->alt = 7; + /* + * FIXME: The intention seems to be to select the alt + * setting with the largest wMaxPacketSize for the video + * endpoint. + * At least dev->alt should be used instead, but we + * should probably not touch it at all if it is + * already >0, because wMaxPacketSize of the audio + * endpoints seems to be the same for all. + */ dprintk("changing alternate number on interface %d to %d\n", - dev->audio_ifnum, dev->alt); - usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt); + dev->ifnum, dev->alt); + usb_set_interface(dev->udev, dev->ifnum, dev->alt); /* Sets volume, mute, etc */ dev->mute = 0; - mutex_lock(&dev->lock); ret = em28xx_audio_analog_set(dev); if (ret < 0) goto err; @@ -304,7 +304,12 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) mutex_unlock(&dev->lock); } + /* Dynamically adjust the period size */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + dev->adev.period * 95 / 100, + dev->adev.period * 105 / 100); + dev->adev.capture_pcm_substream = substream; return 0; @@ -344,6 +349,10 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { int ret; + struct em28xx *dev = snd_pcm_substream_chip(substream); + + if (dev->disconnected) + return -ENODEV; dprintk("Setting capture parameters\n"); @@ -383,6 +392,9 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream) { struct em28xx *dev = snd_pcm_substream_chip(substream); + if (dev->disconnected) + return -ENODEV; + dev->adev.hwptr_done_capture = 0; dev->adev.capture_transfer_done = 0; @@ -408,6 +420,9 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, struct em28xx *dev = snd_pcm_substream_chip(substream); int retval = 0; + if (dev->disconnected) + return -ENODEV; + switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ @@ -434,6 +449,9 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream snd_pcm_uframes_t hwptr_done; dev = snd_pcm_substream_chip(substream); + if (dev->disconnected) + return SNDRV_PCM_POS_XRUN; + spin_lock_irqsave(&dev->adev.slock, flags); hwptr_done = dev->adev.hwptr_done_capture; spin_unlock_irqrestore(&dev->adev.slock, flags); @@ -455,6 +473,11 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, static int em28xx_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info) { + struct em28xx *dev = snd_kcontrol_chip(kcontrol); + + if (dev->disconnected) + return -ENODEV; + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = 2; info->value.integer.min = 0; @@ -467,11 +490,22 @@ static int em28xx_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) { struct em28xx *dev = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream; u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) | (0x1f - (value->value.integer.value[1] & 0x1f)) << 8; + int nonblock = 0; int rc; - mutex_lock(&dev->lock); + if (dev->disconnected) + return -ENODEV; + + if (substream) + nonblock = !!(substream->f_flags & O_NONBLOCK); + if (nonblock) { + if (!mutex_trylock(&dev->lock)) + return -EAGAIN; + } else + mutex_lock(&dev->lock); rc = em28xx_read_ac97(dev, kcontrol->private_value); if (rc < 0) goto err; @@ -496,9 +530,20 @@ static int em28xx_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) { struct em28xx *dev = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream; + int nonblock = 0; int val; - mutex_lock(&dev->lock); + if (dev->disconnected) + return -ENODEV; + + if (substream) + nonblock = !!(substream->f_flags & O_NONBLOCK); + if (nonblock) { + if (!mutex_trylock(&dev->lock)) + return -EAGAIN; + } else + mutex_lock(&dev->lock); val = em28xx_read_ac97(dev, kcontrol->private_value); mutex_unlock(&dev->lock); if (val < 0) @@ -520,9 +565,20 @@ static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol, { struct em28xx *dev = snd_kcontrol_chip(kcontrol); u16 val = value->value.integer.value[0]; + struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream; + int nonblock = 0; int rc; - mutex_lock(&dev->lock); + if (dev->disconnected) + return -ENODEV; + + if (substream) + nonblock = !!(substream->f_flags & O_NONBLOCK); + if (nonblock) { + if (!mutex_trylock(&dev->lock)) + return -EAGAIN; + } else + mutex_lock(&dev->lock); rc = em28xx_read_ac97(dev, kcontrol->private_value); if (rc < 0) goto err; @@ -550,9 +606,20 @@ static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) { struct em28xx *dev = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream; + int nonblock = 0; int val; - mutex_lock(&dev->lock); + if (dev->disconnected) + return -ENODEV; + + if (substream) + nonblock = !!(substream->f_flags & O_NONBLOCK); + if (nonblock) { + if (!mutex_trylock(&dev->lock)) + return -EAGAIN; + } else + mutex_lock(&dev->lock); val = em28xx_read_ac97(dev, kcontrol->private_value); mutex_unlock(&dev->lock); if (val < 0) @@ -634,25 +701,204 @@ static struct snd_pcm_ops snd_em28xx_pcm_capture = { .page = snd_pcm_get_vmalloc_page, }; +static void em28xx_audio_free_urb(struct em28xx *dev) +{ + int i; + + for (i = 0; i < dev->adev.num_urb; i++) { + struct urb *urb = dev->adev.urb[i]; + + if (!urb) + continue; + + usb_free_coherent(dev->udev, urb->transfer_buffer_length, + dev->adev.transfer_buffer[i], + urb->transfer_dma); + + usb_free_urb(urb); + } + kfree(dev->adev.urb); + kfree(dev->adev.transfer_buffer); + dev->adev.num_urb = 0; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +static int em28xx_audio_ep_packet_size(struct usb_device *udev, + struct usb_endpoint_descriptor *e) +{ + int size = le16_to_cpu(e->wMaxPacketSize); + + if (udev->speed == USB_SPEED_HIGH) + return (size & 0x7ff) * (1 + (((size) >> 11) & 0x03)); + + return size & 0x7ff; +} + +static int em28xx_audio_urb_init(struct em28xx *dev) +{ + struct usb_interface *intf; + struct usb_endpoint_descriptor *e, *ep = NULL; + int i, ep_size, interval, num_urb, npackets; + int urb_size, bytes_per_transfer; + u8 alt; + + if (dev->ifnum) + alt = 1; + else + alt = 7; + + intf = usb_ifnum_to_if(dev->udev, dev->ifnum); + + if (intf->num_altsetting <= alt) { + em28xx_errdev("alt %d doesn't exist on interface %d\n", + dev->ifnum, alt); + return -ENODEV; + } + + for (i = 0; i < intf->altsetting[alt].desc.bNumEndpoints; i++) { + e = &intf->altsetting[alt].endpoint[i].desc; + if (!usb_endpoint_dir_in(e)) + continue; + if (e->bEndpointAddress == EM28XX_EP_AUDIO) { + ep = e; + break; + } + } + + if (!ep) { + em28xx_errdev("Couldn't find an audio endpoint"); + return -ENODEV; + } + + ep_size = em28xx_audio_ep_packet_size(dev->udev, ep); + interval = 1 << (ep->bInterval - 1); + + em28xx_info("Endpoint 0x%02x %s on intf %d alt %d interval = %d, size %d\n", + EM28XX_EP_AUDIO, usb_speed_string(dev->udev->speed), + dev->ifnum, alt, + interval, + ep_size); + + /* Calculate the number and size of URBs to better fit the audio samples */ + + /* + * Estimate the number of bytes per DMA transfer. + * + * This is given by the bit rate (for now, only 48000 Hz) multiplied + * by 2 channels and 2 bytes/sample divided by the number of microframe + * intervals and by the microframe rate (125 us) + */ + bytes_per_transfer = DIV_ROUND_UP(48000 * 2 * 2, 125 * interval); + + /* + * Estimate the number of transfer URBs. Don't let it go past the + * maximum number of URBs that is known to be supported by the device. + */ + num_urb = DIV_ROUND_UP(bytes_per_transfer, ep_size); + if (num_urb > EM28XX_MAX_AUDIO_BUFS) + num_urb = EM28XX_MAX_AUDIO_BUFS; + + /* + * Now that we know the number of bytes per transfer and the number of + * URBs, estimate the typical size of an URB, in order to adjust the + * minimal number of packets. + */ + urb_size = bytes_per_transfer / num_urb; + + /* + * Now, calculate the amount of audio packets to be filled on each + * URB. In order to preserve the old behaviour, use a minimal + * threshold for this value. + */ + npackets = EM28XX_MIN_AUDIO_PACKETS; + if (urb_size > ep_size * npackets) + npackets = DIV_ROUND_UP(urb_size, ep_size); + + em28xx_info("Number of URBs: %d, with %d packets and %d size", + num_urb, npackets, urb_size); + + /* Estimate the bytes per period */ + dev->adev.period = urb_size * npackets; + + /* Allocate space to store the number of URBs to be used */ + + dev->adev.transfer_buffer = kcalloc(num_urb, + sizeof(*dev->adev.transfer_buffer), + GFP_ATOMIC); + if (!dev->adev.transfer_buffer) { + return -ENOMEM; + } + + dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_ATOMIC); + if (!dev->adev.urb) { + kfree(dev->adev.transfer_buffer); + return -ENOMEM; + } + + /* Alloc memory for each URB and for each transfer buffer */ + dev->adev.num_urb = num_urb; + for (i = 0; i < num_urb; i++) { + struct urb *urb; + int j, k; + void *buf; + + urb = usb_alloc_urb(npackets, GFP_ATOMIC); + if (!urb) { + em28xx_errdev("usb_alloc_urb failed!\n"); + em28xx_audio_free_urb(dev); + return -ENOMEM; + } + dev->adev.urb[i] = urb; + + buf = usb_alloc_coherent(dev->udev, npackets * ep_size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + em28xx_errdev("usb_alloc_coherent failed!\n"); + em28xx_audio_free_urb(dev); + return -ENOMEM; + } + dev->adev.transfer_buffer[i] = buf; + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer = buf; + urb->interval = interval; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = npackets; + urb->transfer_buffer_length = ep_size * npackets; + + for (j = k = 0; j < npackets; j++, k += ep_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = ep_size; + } + } + + return 0; +} + static int em28xx_audio_init(struct em28xx *dev) { struct em28xx_audio *adev = &dev->adev; struct snd_pcm *pcm; struct snd_card *card; static int devnr; - int err; + int err; - if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { + if (!dev->has_alsa_audio) { /* This device does not support the extension (in this case the device is expecting the snd-usb-audio module or doesn't have analog audio support at all) */ return 0; } - printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n"); + em28xx_info("Binding audio extension\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " "Rechberger\n"); - printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n"); + printk(KERN_INFO + "em28xx-audio.c: Copyright (C) 2007-2014 Mauro Carvalho Chehab\n"); err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, &card); @@ -660,11 +906,12 @@ static int em28xx_audio_init(struct em28xx *dev) return err; spin_lock_init(&adev->slock); + adev->sndcard = card; + adev->udev = dev->udev; + err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto card_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); pcm->info_flags = 0; @@ -694,15 +941,25 @@ static int em28xx_audio_init(struct em28xx *dev) em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); } + err = em28xx_audio_urb_init(dev); + if (err) + goto card_free; + err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - adev->sndcard = card; - adev->udev = dev->udev; + if (err < 0) + goto urb_free; + em28xx_info("Audio extension successfully initialized\n"); return 0; + +urb_free: + em28xx_audio_free_urb(dev); + +card_free: + snd_card_free(card); + adev->sndcard = NULL; + + return err; } static int em28xx_audio_fini(struct em28xx *dev) @@ -717,7 +974,14 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } + em28xx_info("Closing audio extension"); + if (dev->adev.sndcard) { + snd_card_disconnect(dev->adev.sndcard); + flush_work(&dev->wq_trigger); + + em28xx_audio_free_urb(dev); + snd_card_free(dev->adev.sndcard); dev->adev.sndcard = NULL; } @@ -745,7 +1009,8 @@ static void __exit em28xx_alsa_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); -MODULE_DESCRIPTION("Em28xx Audio driver"); +MODULE_DESCRIPTION(DRIVER_DESC " - audio interface"); +MODULE_VERSION(EM28XX_VERSION); module_init(em28xx_alsa_register); module_exit(em28xx_alsa_unregister); diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index d666741797d4..c29f5c4e7b40 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -454,3 +454,4 @@ int em28xx_init_camera(struct em28xx *dev) return ret; } +EXPORT_SYMBOL_GPL(em28xx_init_camera); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a5196697627f..4d97a76cc3b0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -36,7 +36,6 @@ #include <media/tvaudio.h> #include <media/i2c-addr.h> #include <media/tveeprom.h> -#include <media/v4l2-clk.h> #include <media/v4l2-common.h> #include "em28xx.h" @@ -67,7 +66,7 @@ MODULE_PARM_DESC(usb_xfer_mode, /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ -static unsigned long em28xx_devused; +DECLARE_BITMAP(em28xx_devused, EM28XX_MAXBOARDS); struct em28xx_hash_table { unsigned long hash; @@ -356,6 +355,28 @@ static struct em28xx_reg_seq c3tech_digital_duo_digital[] = { { -1, -1, -1, -1}, }; +/* + * 2013:0258 PCTV DVB-S2 Stick (461e) + * GPIO 0 = POWER_ON + * GPIO 1 = BOOST + * GPIO 2 = VUV_LNB (red LED) + * GPIO 3 = #EXT_12V + * GPIO 4 = INT_DEM + * GPIO 5 = INT_LNB + * GPIO 6 = #RESET_DEM + * GPIO 7 = P07_LED (green LED) + */ +static struct em28xx_reg_seq pctv_461e[] = { + {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0}, + {0x0d, 0xff, 0xff, 0}, + {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */ + {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 200}, /* reset demod */ + {0x0d, 0x42, 0xff, 0}, + {EM2874_R80_GPIO_P0_CTRL, 0xeb, 0xff, 0}, + {EM2874_R5F_TS_ENABLE, 0x84, 0x84, 0}, /* parallel? | null discard */ + { -1, -1, -1, -1}, +}; + #if 0 static struct em28xx_reg_seq hauppauge_930c_gpio[] = { {EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10}, @@ -412,6 +433,70 @@ static struct em28xx_reg_seq pctv_520e[] = { { -1, -1, -1, -1}, }; +/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam + * reg 0x80/0x84: + * GPIO_0: capturing LED, 0=on, 1=off + * GPIO_2: AV mute button, 0=pressed, 1=unpressed + * GPIO 3: illumination button, 0=pressed, 1=unpressed + * GPIO_6: illumination/flash LED, 0=on, 1=off + * reg 0x81/0x85: + * GPIO_7: snapshot button, 0=pressed, 1=unpressed + */ +static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = { + {EM2820_R08_GPIO_CTRL, 0xf7, 0xff, 10}, + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xb2, 10}, + { -1, -1, -1, -1}, +}; + +/* + * Button definitions + */ +static struct em28xx_button std_snapshot_button[] = { + { + .role = EM28XX_BUTTON_SNAPSHOT, + .reg_r = EM28XX_R0C_USBSUSP, + .reg_clearing = EM28XX_R0C_USBSUSP, + .mask = EM28XX_R0C_USBSUSP_SNAPSHOT, + .inverted = 0, + }, + {-1, 0, 0, 0, 0}, +}; + +static struct em28xx_button speedlink_vad_laplace_buttons[] = { + { + .role = EM28XX_BUTTON_SNAPSHOT, + .reg_r = EM2874_R85_GPIO_P1_STATE, + .mask = 0x80, + .inverted = 1, + }, + { + .role = EM28XX_BUTTON_ILLUMINATION, + .reg_r = EM2874_R84_GPIO_P0_STATE, + .mask = 0x08, + .inverted = 1, + }, + {-1, 0, 0, 0, 0}, +}; + +/* + * LED definitions + */ +static struct em28xx_led speedlink_vad_laplace_leds[] = { + { + .role = EM28XX_LED_ANALOG_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = 0x01, + .inverted = 1, + }, + { + .role = EM28XX_LED_ILLUMINATION, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = 0x40, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -1391,7 +1476,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0", - .has_snapshot_button = 1, + .buttons = std_snapshot_button, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, .decoder = EM28XX_SAA711X, @@ -1413,7 +1498,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = { .name = "EM2860/SAA711X Reference Design", - .has_snapshot_button = 1, + .buttons = std_snapshot_button, .tuner_type = TUNER_ABSENT, .decoder = EM28XX_SAA711X, .input = { { @@ -2020,7 +2105,7 @@ struct em28xx_board em28xx_boards[] = { }, /* 1b80:e1cc Delock 61959 * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 - * mostly the same as MaxMedia UB-425-TC but different remote */ + * mostly the same as MaxMedia UB-425-TC but different remote */ [EM2874_BOARD_DELOCK_61959] = { .name = "Delock 61959", .tuner_type = TUNER_ABSENT, @@ -2043,7 +2128,38 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = default_tuner_gpio, .def_i2c_bus = 1, }, + /* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam + * Empia EM2765 + OmniVision OV2640 */ + [EM2765_BOARD_SPEEDLINK_VAD_LAPLACE] = { + .name = "SpeedLink Vicious And Devine Laplace webcam", + .xclk = EM28XX_XCLK_FREQUENCY_24MHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ, + .def_i2c_bus = 1, + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .amux = EM28XX_AMUX_VIDEO, + .gpio = speedlink_vad_laplace_reg_seq, + } }, + .buttons = speedlink_vad_laplace_buttons, + .leds = speedlink_vad_laplace_leds, + }, + /* 2013:0258 PCTV DVB-S2 Stick (461e) + * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293 */ + [EM28178_BOARD_PCTV_461E] = { + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .name = "PCTV DVB-S2 Stick (461e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_461e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, }; +EXPORT_SYMBOL_GPL(em28xx_boards); + const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); /* table of devices that work with this driver */ @@ -2208,6 +2324,12 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2884_BOARD_PCTV_520E }, { USB_DEVICE(0x1b80, 0xe1cc), .driver_info = EM2874_BOARD_DELOCK_61959 }, + { USB_DEVICE(0x1ae7, 0x9003), + .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE }, + { USB_DEVICE(0x1ae7, 0x9004), + .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE }, + { USB_DEVICE(0x2013, 0x0258), + .driver_info = EM28178_BOARD_PCTV_461E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -2239,24 +2361,6 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { }; /* 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[] = { - 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ - 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ - I2C_CLIENT_END }; - -static unsigned short tvp5150_addrs[] = { - 0xb8 >> 1, - 0xba >> 1, - I2C_CLIENT_END -}; - -static unsigned short msp3400_addrs[] = { - 0x80 >> 1, - 0x88 >> 1, - I2C_CLIENT_END -}; - int em28xx_tuner_callback(void *ptr, int component, int command, int arg) { struct em28xx_i2c_bus *i2c_bus = ptr; @@ -2408,113 +2512,6 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_SUSPEND); } -static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) -{ - memset(ctl, 0, sizeof(*ctl)); - - ctl->fname = XC2028_DEFAULT_FIRMWARE; - ctl->max_len = 64; - ctl->mts = em28xx_boards[dev->model].mts_firmware; - - switch (dev->model) { - case EM2880_BOARD_EMPIRE_DUAL_TV: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2882_BOARD_TERRATEC_HYBRID_XS: - ctl->demod = XC3028_FE_ZARLINK456; - break; - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: - case EM2881_BOARD_PINNACLE_HYBRID_PRO: - ctl->demod = XC3028_FE_ZARLINK456; - break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: - ctl->demod = XC3028_FE_DEFAULT; - break; - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - ctl->demod = XC3028_FE_DEFAULT; - ctl->fname = XC3028L_DEFAULT_FIRMWARE; - break; - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - /* FIXME: Better to specify the needed IF */ - ctl->demod = XC3028_FE_DEFAULT; - break; - case EM2883_BOARD_KWORLD_HYBRID_330U: - case EM2882_BOARD_DIKOM_DK300: - case EM2882_BOARD_KWORLD_VS_DVBT: - ctl->demod = XC3028_FE_CHINA; - ctl->fname = XC2028_DEFAULT_FIRMWARE; - break; - case EM2882_BOARD_EVGA_INDTUBE: - ctl->demod = XC3028_FE_CHINA; - ctl->fname = XC3028L_DEFAULT_FIRMWARE; - break; - default: - ctl->demod = XC3028_FE_OREN538; - } -} - -static void em28xx_tuner_setup(struct em28xx *dev) -{ - struct tuner_setup tun_setup; - struct v4l2_frequency f; - - if (dev->tuner_type == TUNER_ABSENT) - return; - - memset(&tun_setup, 0, sizeof(tun_setup)); - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.tuner_callback = em28xx_tuner_callback; - - if (dev->board.radio.type) { - tun_setup.type = dev->board.radio.type; - tun_setup.addr = dev->board.radio_addr; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - } - - if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - } - - if (dev->tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); - } - - if (dev->tuner_type == TUNER_XC2028) { - struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; - - memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); - memset(&ctl, 0, sizeof(ctl)); - - em28xx_setup_xc3028(dev, &ctl); - - xc2028_cfg.tuner = TUNER_XC2028; - xc2028_cfg.priv = &ctl; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); - } - - /* configure tuner */ - f.tuner = 0; - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = 9076; /* just a magic number */ - dev->ctl_freq = f.frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); -} - static int em28xx_hint_board(struct em28xx *dev) { int i; @@ -2768,57 +2765,56 @@ static void em28xx_card_setup(struct em28xx *dev) /* 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[dev->def_i2c_bus], - "msp3400", 0, msp3400_addrs); - - if (dev->board.decoder == EM28XX_SAA711X) - 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[dev->def_i2c_bus], - "tvp5150", 0, tvp5150_addrs); - - if (dev->board.adecoder == EM28XX_TVAUDIO) - 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[dev->def_i2c_bus], - "tuner", dev->board.radio_addr, NULL); - - if (has_demod) - v4l2_i2c_new_subdev(&dev->v4l2_dev, - &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 = - has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - struct v4l2_subdev *sd; - - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &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[dev->def_i2c_bus], - "tuner", dev->tuner_addr, NULL); - } - } +void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) +{ + memset(ctl, 0, sizeof(*ctl)); - em28xx_tuner_setup(dev); + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + ctl->mts = em28xx_boards[dev->model].mts_firmware; - em28xx_init_camera(dev); + switch (dev->model) { + case EM2880_BOARD_EMPIRE_DUAL_TV: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: + ctl->demod = XC3028_FE_ZARLINK456; + break; + case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: + ctl->demod = XC3028_FE_ZARLINK456; + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: + ctl->demod = XC3028_FE_DEFAULT; + break; + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + ctl->demod = XC3028_FE_DEFAULT; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + /* FIXME: Better to specify the needed IF */ + ctl->demod = XC3028_FE_DEFAULT; + break; + case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC2028_DEFAULT_FIRMWARE; + break; + case EM2882_BOARD_EVGA_INDTUBE: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; + default: + ctl->demod = XC3028_FE_OREN538; + } } - +EXPORT_SYMBOL_GPL(em28xx_setup_xc3028); static void request_module_async(struct work_struct *work) { @@ -2831,17 +2827,30 @@ static void request_module_async(struct work_struct *work) * can be initialised right now. Otherwise, the module init * code will do it. */ + + /* + * Devicdes with an audio-only interface also have a V4L/DVB/RC + * interface. Don't register extensions twice on those devices. + */ + if (dev->is_audio_only) { +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("em28xx-alsa"); +#endif + return; + } + em28xx_init_extension(dev); #if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->has_video) + request_module("em28xx-v4l"); if (dev->has_audio_class) request_module("snd-usb-audio"); else if (dev->has_alsa_audio) request_module("em28xx-alsa"); - if (dev->board.has_dvb) request_module("em28xx-dvb"); - if (dev->board.has_snapshot_button || + if (dev->board.buttons || ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir)) request_module("em28xx-rc"); #endif /* CONFIG_MODULES */ @@ -2867,23 +2876,20 @@ void em28xx_release_resources(struct em28xx *dev) { /*FIXME: I2C IR should be disconnected */ - em28xx_release_analog_resources(dev); + mutex_lock(&dev->lock); if (dev->def_i2c_bus) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); - if (dev->clk) - v4l2_clk_unregister_fixed(dev->clk); - - v4l2_ctrl_handler_free(&dev->ctrl_handler); - - v4l2_device_unregister(&dev->v4l2_dev); usb_put_dev(dev->udev); /* Mark device as unused */ - clear_bit(dev->devno, &em28xx_devused); + clear_bit(dev->devno, em28xx_devused); + + mutex_unlock(&dev->lock); }; +EXPORT_SYMBOL_GPL(em28xx_release_resources); /* * em28xx_init_dev() @@ -2893,7 +2899,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, struct usb_interface *interface, int minor) { - struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; int retval; static const char *default_chip_name = "em28xx"; const char *chip_name = default_chip_name; @@ -2968,6 +2973,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->wait_after_write = 0; dev->eeprom_addrwidth_16bit = 1; break; + case CHIP_ID_EM28178: + chip_name = "em28178"; + dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2883: chip_name = "em2882/3"; dev->wait_after_write = 0; @@ -2983,6 +2993,16 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, } } + if (dev->chip_id == CHIP_ID_EM2870 || + dev->chip_id == CHIP_ID_EM2874 || + dev->chip_id == CHIP_ID_EM28174 || + dev->chip_id == CHIP_ID_EM28178) { + /* Digital only device - don't load any alsa module */ + dev->audio_mode.has_audio = false; + dev->has_audio_class = false; + dev->has_alsa_audio = false; + } + if (chip_name != default_chip_name) printk(KERN_INFO DRIVER_NAME ": chip ID is %s\n", chip_name); @@ -3015,15 +3035,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, } } - retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (retval < 0) { - em28xx_errdev("Call to v4l2_device_register() failed!\n"); - return retval; - } - - v4l2_ctrl_handler_init(hdl, 8); - dev->v4l2_dev.ctrl_handler = hdl; - rt_mutex_init(&dev->i2c_bus_lock); /* register i2c bus 0 */ @@ -3034,7 +3045,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, if (retval < 0) { em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n", __func__, retval); - goto unregister_dev; + return retval; } /* register i2c bus 1 */ @@ -3048,88 +3059,17 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, 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 - */ - dev->vinmode = 0x10; - dev->vinctl = EM28XX_VINCTRL_INTERLACED | - EM28XX_VINCTRL_CCIR656_ENABLE; - /* Do board specific init and eeprom reading */ - em28xx_card_setup(dev); + em28xx_i2c_unregister(dev, 0); - /* Configure audio */ - retval = em28xx_audio_setup(dev); - if (retval < 0) { - em28xx_errdev("%s: Error while setting audio - error [%d]!\n", - __func__, retval); - goto fail; - } - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); - v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); - } else { - /* install the em28xx notify callback */ - v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), - em28xx_ctrl_notify, dev); - v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), - em28xx_ctrl_notify, dev); - } - - /* wake i2c devices */ - em28xx_wake_i2c(dev); - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vbiq.active); - - if (dev->board.has_msp34xx) { - /* Send a reset to other chips via gpio */ - retval = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_reg - " - "msp34xx(1) failed! error [%d]\n", - __func__, retval); - goto fail; - } - msleep(3); - - retval = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_reg - " - "msp34xx(2) failed! error [%d]\n", - __func__, retval); - goto fail; + return retval; } - msleep(3); - } - - retval = em28xx_register_analog_devices(dev); - if (retval < 0) { - goto fail; } - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + /* Do board specific init and eeprom reading */ + em28xx_card_setup(dev); return 0; - -fail: - if (dev->def_i2c_bus) - em28xx_i2c_unregister(dev, 1); - em28xx_i2c_unregister(dev, 0); - v4l2_ctrl_handler_free(&dev->ctrl_handler); - -unregister_dev: - v4l2_device_unregister(&dev->v4l2_dev); - - return retval; } /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ @@ -3154,7 +3094,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* Check to see next free device and mark as used */ do { - nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS); if (nr >= EM28XX_MAXBOARDS) { /* No free device slots */ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", @@ -3162,7 +3102,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, retval = -ENOMEM; goto err_no_slot; } - } while (test_and_set_bit(nr, &em28xx_devused)); + } while (test_and_set_bit(nr, em28xx_devused)); /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { @@ -3332,7 +3272,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->alt = -1; dev->is_audio_only = has_audio && !(has_video || has_dvb); dev->has_alsa_audio = has_audio; - dev->audio_ifnum = ifnum; + dev->audio_mode.has_audio = has_audio; + dev->has_video = has_video; + dev->ifnum = ifnum; /* Checks if audio is provided by some interface */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { @@ -3369,15 +3311,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - /* initialize videobuf2 stuff */ - em28xx_vb2_setup(dev); - /* allocate device struct */ mutex_init(&dev->lock); - mutex_lock(&dev->lock); retval = em28xx_init_dev(dev, udev, interface, nr); if (retval) { - goto unlock_and_free; + goto err_free; } if (usb_xfer_mode < 0) { @@ -3402,26 +3340,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_info("dvb set to %s mode.\n", dev->dvb_xfer_bulk ? "bulk" : "isoc"); - - /* pre-allocate DVB usb transfer buffers */ - if (dev->dvb_xfer_bulk) { - retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, - dev->dvb_xfer_bulk, - EM28XX_DVB_NUM_BUFS, - 512, - EM28XX_DVB_BULK_PACKET_MULTIPLIER); - } else { - retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, - dev->dvb_xfer_bulk, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size_isoc, - EM28XX_DVB_NUM_ISOC_PACKETS); - } - if (retval) { - printk(DRIVER_NAME - ": Failed to pre-allocate USB transfer buffers for DVB.\n"); - goto unlock_and_free; - } } request_modules(dev); @@ -3429,19 +3347,15 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* Should be the last thing to do, to avoid newer udev's to open the device before fully initializing it */ - mutex_unlock(&dev->lock); return 0; -unlock_and_free: - mutex_unlock(&dev->lock); - err_free: kfree(dev->alt_max_pkt_size_isoc); kfree(dev); err: - clear_bit(nr, &em28xx_devused); + clear_bit(nr, em28xx_devused); err_no_slot: usb_put_dev(udev); @@ -3465,36 +3379,13 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->disconnected = 1; - if (dev->is_audio_only) { - mutex_lock(&dev->lock); - em28xx_close_extension(dev); - mutex_unlock(&dev->lock); - return; - } - - em28xx_info("disconnecting %s\n", dev->vdev->name); + em28xx_info("Disconnecting %s\n", dev->name); flush_request_modules(dev); - mutex_lock(&dev->lock); - - v4l2_device_disconnect(&dev->v4l2_dev); - - if (dev->users) { - em28xx_warn("device %s is open! Deregistration and memory deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); - - em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); - em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); - } - em28xx_close_extension(dev); - /* NOTE: must be called BEFORE the resources are released */ - - if (!dev->users) - em28xx_release_resources(dev); - mutex_unlock(&dev->lock); + em28xx_release_resources(dev); if (!dev->users) { kfree(dev->alt_max_pkt_size_isoc); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index fc157af5234a..898fb9bd88a2 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -23,6 +23,7 @@ */ #include <linux/init.h> +#include <linux/jiffies.h> #include <linux/list.h> #include <linux/module.h> #include <linux/slab.h> @@ -33,6 +34,16 @@ #include "em28xx.h" +#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \ + "Markus Rechberger <mrechberger@gmail.com>, " \ + "Mauro Carvalho Chehab <mchehab@infradead.org>, " \ + "Sascha Sommer <saschasommer@freenet.de>" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EM28XX_VERSION); + /* #define ENABLE_DEBUG_ISOC_FRAMES */ static unsigned int core_debug; @@ -53,14 +64,6 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static int alt; -module_param(alt, int, 0644); -MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); - -static unsigned int disable_vbi; -module_param(disable_vbi, int, 0644); -MODULE_PARM_DESC(disable_vbi, "disable vbi support"); - /* FIXME */ #define em28xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ @@ -226,21 +229,42 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); /* + * em28xx_toggle_reg_bits() + * toggles/inverts the bits (specified by bitmask) of a register + */ +int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask) +{ + int oldval; + u8 newval; + + oldval = em28xx_read_reg(dev, reg); + if (oldval < 0) + return oldval; + + newval = (~oldval & bitmask) | (oldval & ~bitmask); + + return em28xx_write_reg(dev, reg, newval); +} +EXPORT_SYMBOL_GPL(em28xx_toggle_reg_bits); + +/* * em28xx_is_ac97_ready() * Checks if ac97 is ready */ static int em28xx_is_ac97_ready(struct em28xx *dev) { - int ret, i; + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_AC97_XFER_TIMEOUT); + int ret; /* Wait up to 50 ms for AC97 command to complete */ - for (i = 0; i < 10; i++, msleep(5)) { + while (time_is_after_jiffies(timeout)) { ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); if (ret < 0) return ret; if (!(ret & 0x01)) return 0; + msleep(5); } em28xx_warn("AC97 command still being executed: not handled properly!\n"); @@ -482,16 +506,8 @@ int em28xx_audio_setup(struct em28xx *dev) int vid1, vid2, feat, cfg; u32 vid; - if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 - || dev->chip_id == CHIP_ID_EM28174) { - /* Digital only device - don't load any alsa module */ - dev->audio_mode.has_audio = false; - dev->has_audio_class = false; - dev->has_alsa_audio = false; + if (!dev->audio_mode.has_audio) return 0; - } - - dev->audio_mode.has_audio = true; /* See how this device is configured */ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); @@ -504,17 +520,19 @@ int em28xx_audio_setup(struct em28xx *dev) dev->has_alsa_audio = false; dev->audio_mode.has_audio = false; return 0; - } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_3_SAMPRATES) { - em28xx_info("I2S Audio (3 sample rates)\n"); - dev->audio_mode.i2s_3rates = 1; - } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_5_SAMPRATES) { - em28xx_info("I2S Audio (5 sample rates)\n"); - dev->audio_mode.i2s_5rates = 1; - } - - if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { + if (dev->chip_id < CHIP_ID_EM2860 && + (cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM2820_CHIPCFG_I2S_1_SAMPRATE) + dev->audio_mode.i2s_samplerates = 1; + else if (dev->chip_id >= CHIP_ID_EM2860 && + (cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM2860_CHIPCFG_I2S_5_SAMPRATES) + dev->audio_mode.i2s_samplerates = 5; + else + dev->audio_mode.i2s_samplerates = 3; + em28xx_info("I2S Audio (%d sample rate(s))\n", + dev->audio_mode.i2s_samplerates); /* Skip the code that does AC97 vendor detection */ dev->audio_mode.ac97 = EM28XX_NO_AC97; goto init_audio; @@ -582,23 +600,21 @@ init_audio: } EXPORT_SYMBOL_GPL(em28xx_audio_setup); -int em28xx_colorlevels_set_default(struct em28xx *dev) +const struct em28xx_led *em28xx_find_led(struct em28xx *dev, + enum em28xx_led_role role) { - 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); - em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); - em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); - em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); - return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); + if (dev->board.leds) { + u8 k = 0; + while (dev->board.leds[k].role >= 0 && + dev->board.leds[k].role < EM28XX_NUM_LED_ROLES) { + if (dev->board.leds[k].role == role) + return &dev->board.leds[k]; + k++; + } + } + return NULL; } +EXPORT_SYMBOL_GPL(em28xx_find_led); int em28xx_capture_start(struct em28xx *dev, int start) { @@ -606,271 +622,57 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM2884 || - dev->chip_id == CHIP_ID_EM28174) { + dev->chip_id == CHIP_ID_EM28174 || + dev->chip_id == CHIP_ID_EM28178) { /* The Transport Stream Enable Register moved in em2874 */ - if (!start) { - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - 0x00, - EM2874_TS1_CAPTURE_ENABLE); - return rc; - } - - /* Enable Transport Stream */ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - EM2874_TS1_CAPTURE_ENABLE, + start ? + EM2874_TS1_CAPTURE_ENABLE : 0x00, EM2874_TS1_CAPTURE_ENABLE); - return rc; - } - - - /* FIXME: which is the best order? */ - /* video registers are sampled by VREF */ - rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, - start ? 0x10 : 0x00, 0x10); - if (rc < 0) - return rc; - - if (!start) { - /* disable video capture */ - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); - return rc; - } - - if (dev->board.is_webcam) - rc = em28xx_write_reg(dev, 0x13, 0x0c); - - /* enable video capture */ - rc = em28xx_write_reg(dev, 0x48, 0x00); - - if (dev->mode == EM28XX_ANALOG_MODE) - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); - - msleep(6); - - return rc; -} - -int em28xx_vbi_supported(struct em28xx *dev) -{ - /* Modprobe option to manually disable */ - 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; - - /* Version of em28xx that does not support VBI */ - return 0; -} - -int em28xx_set_outfmt(struct em28xx *dev) -{ - int ret; - 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; - - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); - if (ret < 0) - return ret; - - vinctrl = dev->vinctl; - if (em28xx_vbi_supported(dev) == 1) { - vinctrl |= EM28XX_VINCTRL_VBI_RAW; - em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); - em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); - em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); - } - } - - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); -} - -static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, - u8 ymin, u8 ymax) -{ - em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", - xmin, ymin, xmax, ymax); - - em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); - em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); - em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); - return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); -} - -static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, - u16 width, u16 height) -{ - u8 cwidth = width >> 2; - u8 cheight = height >> 2; - u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); - /* NOTE: size limit: 2047x1023 = 2MPix */ - - em28xx_coredbg("capture area set to (%d,%d): %dx%d\n", - hstart, vstart, - ((overflow & 2) << 9 | cwidth << 2), - ((overflow & 1) << 10 | cheight << 2)); - - em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); - em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); - 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) -{ - u8 mode; - /* the em2800 scaler only supports scaling down to 50% */ - - if (dev->board.is_em2800) { - mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); } else { - u8 buf[2]; - - buf[0] = h; - buf[1] = h >> 8; - em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); - - buf[0] = v; - buf[1] = v >> 8; - em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); - /* it seems that both H and V scalers must be active - to work correctly */ - mode = (h || v) ? 0x30 : 0x00; - } - return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); -} - -/* FIXME: this only function read values from dev */ -int em28xx_resolution_set(struct em28xx *dev) -{ - int width, height; - width = norm_maxw(dev); - height = norm_maxh(dev); - - /* Properly setup VBI */ - dev->vbi_width = 720; - if (dev->norm & V4L2_STD_525_60) - dev->vbi_height = 12; - else - dev->vbi_height = 18; - - em28xx_set_outfmt(dev); + /* FIXME: which is the best order? */ + /* video registers are sampled by VREF */ + rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, + start ? 0x10 : 0x00, 0x10); + if (rc < 0) + return rc; - em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); + if (start) { + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); - /* If we don't set the start position to 2 in VBI mode, we end up - with line 20/21 being YUYV encoded instead of being in 8-bit - greyscale. The core of the issue is that line 21 (and line 23 for - PAL WSS) are inside of active video region, and as a result they - get the pixelformatting associated with that area. So by cropping - it out, we end up with the same format as the rest of the VBI - region */ - if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 2, width, height); - else - em28xx_capture_area_set(dev, 0, 0, width, height); + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); - return em28xx_scaler_set(dev, dev->hscale, dev->vscale); -} + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_write_reg(dev, + EM28XX_R12_VINENABLE, 0x67); + else + rc = em28xx_write_reg(dev, + EM28XX_R12_VINENABLE, 0x37); -/* Set USB alternate setting for analog video */ -int em28xx_set_alternate(struct em28xx *dev) -{ - int errCode; - int i; - unsigned int min_pkt_size = dev->width * 2 + 4; - - /* NOTE: for isoc transfers, only alt settings > 0 are allowed - bulk transfers seem to work only with alt=0 ! */ - dev->alt = 0; - if ((alt > 0) && (alt < dev->num_alt)) { - em28xx_coredbg("alternate forced to %d\n", dev->alt); - dev->alt = alt; - goto set_alt; + msleep(6); + } else { + /* disable video capture */ + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + } } - if (dev->analog_xfer_bulk) - goto set_alt; - /* When image size is bigger than a certain value, - the frame size should be increased, otherwise, only - green screen will be received. - */ - if (dev->width * 2 * dev->height > 720 * 240 * 2) - min_pkt_size *= 2; + if (rc < 0) + return rc; - for (i = 0; i < dev->num_alt; i++) { - /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { - dev->alt = i; - break; - /* otherwise make sure that we end up with the maximum bandwidth - because the min_pkt_size equation might be wrong... - */ - } else if (dev->alt_max_pkt_size_isoc[i] > - dev->alt_max_pkt_size_isoc[dev->alt]) - dev->alt = i; + /* Switch (explicitly controlled) analog capturing LED on/off */ + if (dev->mode == EM28XX_ANALOG_MODE) { + const struct em28xx_led *led; + led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING); + if (led) + em28xx_write_reg_bits(dev, led->gpio_reg, + (!start ^ led->inverted) ? + ~led->gpio_mask : led->gpio_mask, + led->gpio_mask); } -set_alt: - /* NOTE: for bulk transfers, we need to call usb_set_interface() - * even if the previous settings were the same. Otherwise streaming - * fails with all urbs having status = -EOVERFLOW ! */ - if (dev->analog_xfer_bulk) { - dev->max_pkt_size = 512; /* USB 2.0 spec */ - dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; - } else { /* isoc */ - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = - dev->alt_max_pkt_size_isoc[dev->alt]; - dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; - } - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); - if (errCode < 0) { - em28xx_errdev("cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; - } - return 0; + return rc; } int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) @@ -1238,18 +1040,6 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer); /* - * em28xx_wake_i2c() - * configure i2c attached devices - */ -void em28xx_wake_i2c(struct em28xx *dev) -{ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, - INPUT(dev->ctl_input)->vmux, 0, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); -} - -/* * Device control list */ @@ -1272,7 +1062,7 @@ int em28xx_register_extension(struct em28xx_ops *ops) ops->init(dev); } mutex_unlock(&em28xx_devlist_mutex); - printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + printk(KERN_INFO "em28xx: Registered (%s) extension\n", ops->name); return 0; } EXPORT_SYMBOL(em28xx_register_extension); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 344042bb845c..a0a669e81362 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -51,10 +51,14 @@ #include "a8293.h" #include "qt1010.h" #include "mb86a20s.h" +#include "m88ds3103.h" +#include "m88ts2022.h" -MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface"); +MODULE_VERSION(EM28XX_VERSION); + static unsigned int debug; module_param(debug, int, 0644); @@ -87,6 +91,7 @@ struct em28xx_dvb { struct semaphore pll_mutex; bool dont_attach_fe1; int lna_gpio; + struct i2c_client *i2c_client_tuner; }; @@ -198,7 +203,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(dev->udev, 0, dvb_alt); + usb_set_interface(dev->udev, dev->ifnum, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; @@ -271,7 +276,7 @@ static int em28xx_stop_feed(struct dvb_demux_feed *feed) static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) { struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; - struct em28xx *dev = i2c_bus->dev; + struct em28xx *dev = i2c_bus->dev; if (acquire) return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); @@ -370,7 +375,6 @@ static struct drxk_config terratec_h5_drxk = { .no_i2c_bridge = 1, .microcode_name = "dvb-usb-terratec-h5-drxk.fw", .qam_demod_parameter_count = 2, - .load_firmware_sync = true, }; static struct drxk_config hauppauge_930c_drxk = { @@ -380,7 +384,6 @@ static struct drxk_config hauppauge_930c_drxk = { .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", .chunk_size = 56, .qam_demod_parameter_count = 2, - .load_firmware_sync = true, }; static struct drxk_config terratec_htc_stick_drxk = { @@ -394,7 +397,6 @@ static struct drxk_config terratec_htc_stick_drxk = { .antenna_dvbt = true, /* The windows driver uses the same. This will disable LNA. */ .antenna_gpio = 0x6, - .load_firmware_sync = true, }; static struct drxk_config maxmedia_ub425_tc_drxk = { @@ -403,7 +405,6 @@ static struct drxk_config maxmedia_ub425_tc_drxk = { .no_i2c_bridge = 1, .microcode_name = "dvb-demod-drxk-01.fw", .chunk_size = 62, - .load_firmware_sync = true, .qam_demod_parameter_count = 2, }; @@ -415,7 +416,6 @@ static struct drxk_config pctv_520e_drxk = { .chunk_size = 58, .antenna_dvbt = true, /* disable LNA */ .antenna_gpio = (1 << 2), /* disable LNA */ - .load_firmware_sync = true, }; static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) @@ -808,6 +808,14 @@ static struct tda18271_config c3tech_duo_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; +static const struct m88ds3103_config pctv_461e_m88ds3103_config = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_PARALLEL_16, + .agc = 0x99, +}; /* ------------------------------------------------------------------ */ @@ -815,11 +823,16 @@ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) { struct dvb_frontend *fe; struct xc2028_config cfg; + struct xc2028_ctrl ctl; memset(&cfg, 0, sizeof(cfg)); cfg.i2c_adap = &dev->i2c_adap[dev->def_i2c_bus]; cfg.i2c_addr = addr; + memset(&ctl, 0, sizeof(ctl)); + em28xx_setup_xc3028(dev, &ctl); + cfg.ctrl = &ctl; + if (!dev->dvb->fe[0]) { em28xx_errdev("/2: dvb frontend not attached. " "Can't attach xc3028\n"); @@ -979,12 +992,18 @@ static int em28xx_dvb_init(struct em28xx *dev) int result = 0, mfe_shared = 0; struct em28xx_dvb *dvb; + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + if (!dev->board.has_dvb) { /* This device does not support the extension */ - printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n"); return 0; } + em28xx_info("Binding DVB extension\n"); + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { @@ -994,6 +1013,27 @@ static int em28xx_dvb_init(struct em28xx *dev) dev->dvb = dvb; dvb->fe[0] = dvb->fe[1] = NULL; + /* pre-allocate DVB usb transfer buffers */ + if (dev->dvb_xfer_bulk) { + result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + 512, + EM28XX_DVB_BULK_PACKET_MULTIPLIER); + } else { + result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size_isoc, + EM28XX_DVB_NUM_ISOC_PACKETS); + } + if (result) { + em28xx_errdev("em28xx_dvb: failed to pre-allocate USB transfer buffers for DVB.\n"); + kfree(dvb); + dev->dvb = NULL; + return result; + } + mutex_lock(&dev->lock); em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ @@ -1330,6 +1370,48 @@ static int em28xx_dvb_init(struct em28xx *dev) goto out_free; } break; + case EM28178_BOARD_PCTV_461E: + { + /* demod I2C adapter */ + struct i2c_adapter *i2c_adapter; + struct i2c_board_info info; + struct m88ts2022_config m88ts2022_config = { + .clock = 27000000, + }; + memset(&info, 0, sizeof(struct i2c_board_info)); + + /* attach demod */ + dvb->fe[0] = dvb_attach(m88ds3103_attach, + &pctv_461e_m88ds3103_config, + &dev->i2c_adap[dev->def_i2c_bus], + &i2c_adapter); + if (dvb->fe[0] == NULL) { + result = -ENODEV; + goto out_free; + } + + /* attach tuner */ + m88ts2022_config.fe = dvb->fe[0]; + strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &m88ts2022_config; + request_module("m88ts2022"); + dvb->i2c_client_tuner = i2c_new_device(i2c_adapter, &info); + + /* delegate signal strength measurement to tuner */ + dvb->fe[0]->ops.read_signal_strength = + dvb->fe[0]->ops.tuner_ops.get_rf_strength; + + /* attach SEC */ + if (!dvb_attach(a8293_attach, dvb->fe[0], + &dev->i2c_adap[dev->def_i2c_bus], + &em28xx_a8293_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -ENODEV; + goto out_free; + } + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); @@ -1354,7 +1436,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* MFE lock */ dvb->adapter.mfe_shared = mfe_shared; - em28xx_info("Successfully loaded em28xx-dvb\n"); + em28xx_info("DVB extension successfully initialized\n"); ret: em28xx_set_mode(dev, EM28XX_SUSPEND); mutex_unlock(&dev->lock); @@ -1375,14 +1457,23 @@ static inline void prevent_sleep(struct dvb_frontend_ops *ops) static int em28xx_dvb_fini(struct em28xx *dev) { + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + if (!dev->board.has_dvb) { /* This device does not support the extension */ return 0; } + em28xx_info("Closing DVB extension"); + if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); + if (dev->disconnected) { /* We cannot tell the device to sleep * once it has been unplugged. */ @@ -1392,6 +1483,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) prevent_sleep(&dvb->fe[1]->ops); } + i2c_release_client(dvb->i2c_client_tuner); em28xx_unregister_dvb(dvb); kfree(dvb); dev->dvb = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index c4ff9739a7ae..7e1724076ac4 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/usb.h> #include <linux/i2c.h> +#include <linux/jiffies.h> #include "em28xx.h" #include "tuner-xc2028.h" @@ -40,7 +41,7 @@ MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); +MODULE_PARM_DESC(i2c_debug, "i2c debug message level (1: normal debug, 2: show I2C transfers)"); /* * em2800_i2c_send_bytes() @@ -48,8 +49,8 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); */ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); int ret; - int write_timeout; u8 b2[6]; if (len < 1 || len > 4) @@ -74,22 +75,26 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) return (ret < 0) ? ret : -EIO; } /* wait for completion */ - for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { + while (time_is_after_jiffies(timeout)) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x80 + len - 1) { + if (ret == 0x80 + len - 1) return len; - } else if (ret == 0x94 + len - 1) { - return -ENODEV; - } else if (ret < 0) { + if (ret == 0x94 + len - 1) { + if (i2c_debug == 1) + em28xx_warn("R05 returned 0x%02x: I2C timeout", + ret); + return -ENXIO; + } + if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; } msleep(5); } - em28xx_warn("write to i2c device at 0x%x timed out\n", addr); - return -EIO; + if (i2c_debug) + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); + return -ETIMEDOUT; } /* @@ -98,9 +103,9 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) */ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); u8 buf2[4]; int ret; - int read_timeout; int i; if (len < 1 || len > 4) @@ -117,22 +122,28 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } /* wait for completion */ - for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0; - read_timeout -= 5) { + while (time_is_after_jiffies(timeout)) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x84 + len - 1) { + if (ret == 0x84 + len - 1) break; - } else if (ret == 0x94 + len - 1) { - return -ENODEV; - } else if (ret < 0) { + if (ret == 0x94 + len - 1) { + if (i2c_debug == 1) + em28xx_warn("R05 returned 0x%02x: I2C timeout", + ret); + return -ENXIO; + } + if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; } msleep(5); } - if (ret != 0x84 + len - 1) - em28xx_warn("read from i2c device at 0x%x timed out\n", addr); + if (ret != 0x84 + len - 1) { + if (i2c_debug) + em28xx_warn("read from i2c device at 0x%x timed out\n", + addr); + } /* get the received message */ ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); @@ -168,7 +179,8 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len, int stop) { - int write_timeout, ret; + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); + int ret; if (len < 1 || len > 64) return -EOPNOTSUPP; @@ -191,16 +203,19 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, } } - /* Check success of the i2c operation */ - for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { + /* wait for completion */ + while (time_is_after_jiffies(timeout)) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0) { /* success */ + if (ret == 0) /* success */ return len; - } else if (ret == 0x10) { - return -ENODEV; - } else if (ret < 0) { - em28xx_warn("failed to read i2c transfer status from bridge (error=%i)\n", + if (ret == 0x10) { + if (i2c_debug == 1) + em28xx_warn("I2C transfer timeout on writing to addr 0x%02x", + addr); + return -ENXIO; + } + if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; } @@ -211,8 +226,10 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, * (even with high payload) ... */ } - em28xx_warn("write to i2c device at 0x%x timed out\n", addr); - return -EIO; + if (i2c_debug) + em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n", + addr, ret); + return -ETIMEDOUT; } /* @@ -242,26 +259,28 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) * 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 + * Anyway, the next check will fail with -ENXIO 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) /* success */ + return len; if (ret < 0) { - em28xx_warn("failed to read i2c transfer status from bridge (error=%i)\n", + em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; } - if (ret > 0) { - if (ret == 0x10) { - return -ENODEV; - } else { - em28xx_warn("unknown i2c error (status=%i)\n", ret); - return -EIO; - } + if (ret == 0x10) { + if (i2c_debug == 1) + em28xx_warn("I2C transfer timeout on writing to addr 0x%02x", + addr); + return -ENXIO; } - return len; + + em28xx_warn("unknown i2c error (status=%i)\n", ret); + return -ETIMEDOUT; } /* @@ -316,8 +335,12 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, */ if (!ret) return len; - else if (ret > 0) - return -ENODEV; + else if (ret > 0) { + if (i2c_debug == 1) + em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout", + ret); + return -ENXIO; + } return ret; /* @@ -355,7 +378,7 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, * 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 + * Anyway, the next check will fail with -ENXIO in this case, so avoid * spamming the system log on device probing and do nothing here. */ @@ -367,8 +390,12 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, */ if (!ret) return len; - else if (ret > 0) - return -ENODEV; + else if (ret > 0) { + if (i2c_debug == 1) + em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout", + ret); + return -ENXIO; + } return ret; /* @@ -409,10 +436,6 @@ static inline int i2c_check_for_device(struct em28xx_i2c_bus *i2c_bus, u16 addr) 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; } @@ -421,7 +444,7 @@ static inline int i2c_recv_bytes(struct em28xx_i2c_bus *i2c_bus, { struct em28xx *dev = i2c_bus->dev; u16 addr = msg.addr << 1; - int byte, rc = -EOPNOTSUPP; + int rc = -EOPNOTSUPP; if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) rc = em28xx_i2c_recv_bytes(dev, addr, msg.buf, msg.len); @@ -429,10 +452,6 @@ static inline int i2c_recv_bytes(struct em28xx_i2c_bus *i2c_bus, 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; } @@ -441,12 +460,8 @@ static inline int i2c_send_bytes(struct em28xx_i2c_bus *i2c_bus, { struct em28xx *dev = i2c_bus->dev; u16 addr = msg.addr << 1; - int byte, rc = -EOPNOTSUPP; + int 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) @@ -491,33 +506,53 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; - if (i2c_debug) + if (i2c_debug > 1) 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 (!msgs[i].len) { + /* + * no len: check only for device presence + * This code is only called during device probe. + */ rc = i2c_check_for_device(i2c_bus, addr); - if (rc == -ENODEV) { + if (rc < 0) { + if (rc == -ENXIO) { + if (i2c_debug > 1) + printk(KERN_CONT " no device\n"); + rc = -ENODEV; + } else { + if (i2c_debug > 1) + printk(KERN_CONT " ERROR: %i\n", rc); + } rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ rc = i2c_recv_bytes(i2c_bus, msgs[i]); + + if (i2c_debug > 1 && rc >= 0) + printk(KERN_CONT " %*ph", + msgs[i].len, msgs[i].buf); } else { + if (i2c_debug > 1) + printk(KERN_CONT " %*ph", + msgs[i].len, msgs[i].buf); + /* write bytes */ rc = i2c_send_bytes(i2c_bus, msgs[i], i == num - 1); } if (rc < 0) { - if (i2c_debug) - printk(" ERROR: %i\n", rc); + if (i2c_debug > 1) + printk(KERN_CONT " ERROR: %i\n", rc); rt_mutex_unlock(&dev->i2c_bus_lock); return rc; } - if (i2c_debug) - printk("\n"); + if (i2c_debug > 1) + printk(KERN_CONT "\n"); } rt_mutex_unlock(&dev->i2c_bus_lock); @@ -600,7 +635,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, * 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; + int err; struct em28xx_eeprom *dev_config; u8 buf, *data; @@ -631,20 +666,14 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, goto error; } - /* Display eeprom content */ - for (i = 0; i < len; 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 (i2c_debug) { + /* Display eeprom content */ + print_hex_dump(KERN_INFO, "eeprom ", DUMP_PREFIX_OFFSET, + 16, 1, data, len, true); + + if (dev->eeprom_addrwidth_16bit) + em28xx_info("eeprom %06x: ... (skipped)\n", 256); } - if (dev->eeprom_addrwidth_16bit) - em28xx_info("i2c eeprom %04x: ... (skipped)\n", i); if (dev->eeprom_addrwidth_16bit && data[0] == 0x26 && data[3] == 0x00) { @@ -736,10 +765,16 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, em28xx_info("\tAC97 audio (5 sample rates)\n"); break; case 2: - em28xx_info("\tI2S audio, sample rate=32k\n"); + if (dev->chip_id < CHIP_ID_EM2860) + em28xx_info("\tI2S audio, sample rate=32k\n"); + else + em28xx_info("\tI2S audio, 3 sample rates\n"); break; case 3: - em28xx_info("\tI2S audio, 3 sample rates\n"); + if (dev->chip_id < CHIP_ID_EM2860) + em28xx_info("\tI2S audio, 3 sample rates\n"); + else + em28xx_info("\tI2S audio, 5 sample rates\n"); break; } diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ea181e4b68c5..18f65d89d4bc 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -30,8 +30,9 @@ #include "em28xx.h" -#define EM28XX_SNAPSHOT_KEY KEY_CAMERA -#define EM28XX_SBUTTON_QUERY_INTERVAL 500 +#define EM28XX_SNAPSHOT_KEY KEY_CAMERA +#define EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL 500 /* [ms] */ +#define EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL 100 /* [ms] */ static unsigned int ir_debug; module_param(ir_debug, int, 0644); @@ -442,6 +443,7 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + case CHIP_ID_EM28178: return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", @@ -470,54 +472,98 @@ static int em28xx_probe_i2c_ir(struct em28xx *dev) } /********************************************************** - Handle Webcam snapshot button + Handle buttons **********************************************************/ -static void em28xx_query_sbutton(struct work_struct *work) +static void em28xx_query_buttons(struct work_struct *work) { - /* Poll the register and see if the button is depressed */ struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); + container_of(work, struct em28xx, buttons_query_work.work); + u8 i, j; + int regval; + bool is_pressed, was_pressed; + const struct em28xx_led *led; + + /* Poll and evaluate all addresses */ + for (i = 0; i < dev->num_button_polling_addresses; i++) { + /* Read value from register */ + regval = em28xx_read_reg(dev, dev->button_polling_addresses[i]); + if (regval < 0) + continue; + /* Check states of the buttons and act */ + j = 0; + while (dev->board.buttons[j].role >= 0 && + dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) { + struct em28xx_button *button = &dev->board.buttons[j]; + /* Check if button uses the current address */ + if (button->reg_r != dev->button_polling_addresses[i]) { + j++; + continue; + } + /* Determine if button is and was pressed last time */ + is_pressed = regval & button->mask; + was_pressed = dev->button_polling_last_values[i] + & button->mask; + if (button->inverted) { + is_pressed = !is_pressed; + was_pressed = !was_pressed; + } + /* Clear button state (if needed) */ + if (is_pressed && button->reg_clearing) + em28xx_write_reg(dev, button->reg_clearing, + (~regval & button->mask) + | (regval & ~button->mask)); + /* Handle button state */ + if (!is_pressed || was_pressed) { + j++; + continue; + } + switch (button->role) { + case EM28XX_BUTTON_SNAPSHOT: + /* Emulate the keypress */ + input_report_key(dev->sbutton_input_dev, + EM28XX_SNAPSHOT_KEY, 1); + /* Unpress the key */ + input_report_key(dev->sbutton_input_dev, + EM28XX_SNAPSHOT_KEY, 0); + break; + case EM28XX_BUTTON_ILLUMINATION: + led = em28xx_find_led(dev, + EM28XX_LED_ILLUMINATION); + /* Switch illumination LED on/off */ + if (led) + em28xx_toggle_reg_bits(dev, + led->gpio_reg, + led->gpio_mask); + break; + default: + WARN_ONCE(1, "BUG: unhandled button role."); + } + /* Next button */ + j++; + } + /* Save current value for comparison during the next polling */ + dev->button_polling_last_values[i] = regval; } - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + schedule_delayed_work(&dev->buttons_query_work, + msecs_to_jiffies(dev->button_polling_interval)); } -static void em28xx_register_snapshot_button(struct em28xx *dev) +static int em28xx_register_snapshot_button(struct em28xx *dev) { struct input_dev *input_dev; int err; em28xx_info("Registering snapshot button...\n"); input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } + if (!input_dev) + return -ENOMEM; usb_make_path(dev->udev, dev->snapshot_button_path, sizeof(dev->snapshot_button_path)); strlcat(dev->snapshot_button_path, "/sbutton", sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); input_dev->name = "em28xx snapshot button"; input_dev->phys = dev->snapshot_button_path; @@ -535,25 +581,86 @@ static void em28xx_register_snapshot_button(struct em28xx *dev) if (err) { em28xx_errdev("input_register_device failed\n"); input_free_device(input_dev); - return; + return err; } dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; + return 0; +} +static void em28xx_init_buttons(struct em28xx *dev) +{ + u8 i = 0, j = 0; + bool addr_new = 0; + + dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL; + while (dev->board.buttons[i].role >= 0 && + dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) { + struct em28xx_button *button = &dev->board.buttons[i]; + /* Check if polling address is already on the list */ + addr_new = 1; + for (j = 0; j < dev->num_button_polling_addresses; j++) { + if (button->reg_r == dev->button_polling_addresses[j]) { + addr_new = 0; + break; + } + } + /* Check if max. number of polling addresses is exceeded */ + if (addr_new && dev->num_button_polling_addresses + >= EM28XX_NUM_BUTTON_ADDRESSES_MAX) { + WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded."); + goto next_button; + } + /* Button role specific checks and actions */ + if (button->role == EM28XX_BUTTON_SNAPSHOT) { + /* Register input device */ + if (em28xx_register_snapshot_button(dev) < 0) + goto next_button; + } else if (button->role == EM28XX_BUTTON_ILLUMINATION) { + /* Check sanity */ + if (!em28xx_find_led(dev, EM28XX_LED_ILLUMINATION)) { + em28xx_errdev("BUG: illumination button defined, but no illumination LED.\n"); + goto next_button; + } + } + /* Add read address to list of polling addresses */ + if (addr_new) { + unsigned int index = dev->num_button_polling_addresses; + dev->button_polling_addresses[index] = button->reg_r; + dev->num_button_polling_addresses++; + } + /* Reduce polling interval if necessary */ + if (!button->reg_clearing) + dev->button_polling_interval = + EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL; +next_button: + /* Next button */ + i++; + } + + /* Start polling */ + if (dev->num_button_polling_addresses) { + memset(dev->button_polling_last_values, 0, + EM28XX_NUM_BUTTON_ADDRESSES_MAX); + INIT_DELAYED_WORK(&dev->buttons_query_work, + em28xx_query_buttons); + schedule_delayed_work(&dev->buttons_query_work, + msecs_to_jiffies(dev->button_polling_interval)); + } } -static void em28xx_deregister_snapshot_button(struct em28xx *dev) +static void em28xx_shutdown_buttons(struct em28xx *dev) { + /* Cancel polling */ + cancel_delayed_work_sync(&dev->buttons_query_work); + /* Clear polling addresses list */ + dev->num_button_polling_addresses = 0; + /* Deregister input devices */ if (dev->sbutton_input_dev != NULL) { em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); input_unregister_device(dev->sbutton_input_dev); dev->sbutton_input_dev = NULL; } - return; } static int em28xx_ir_init(struct em28xx *dev) @@ -564,8 +671,13 @@ static int em28xx_ir_init(struct em28xx *dev) u64 rc_type; u16 i2c_rc_dev_addr = 0; - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + if (dev->board.buttons) + em28xx_init_buttons(dev); if (dev->board.has_ir_i2c) { i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev); @@ -583,6 +695,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; } + em28xx_info("Registering input extension\n"); + ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) @@ -633,6 +747,7 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + case CHIP_ID_EM28178: ir->get_key = em2874_polling_getkey; rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; @@ -675,6 +790,8 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; + em28xx_info("Input extension successfully initalized\n"); + return 0; error: @@ -688,7 +805,14 @@ static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; - em28xx_deregister_snapshot_button(dev); + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + em28xx_info("Closing input extension"); + + em28xx_shutdown_buttons(dev); /* skip detach on non attached boards */ if (!ir) @@ -722,7 +846,8 @@ static void __exit em28xx_rc_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); -MODULE_DESCRIPTION("Em28xx Input driver"); +MODULE_DESCRIPTION(DRIVER_DESC " - input interface"); +MODULE_VERSION(EM28XX_VERSION); module_init(em28xx_rc_register); module_exit(em28xx_rc_unregister); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 0e0477847965..311fb349dafa 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -25,10 +25,12 @@ #define EM28XX_R00_CHIPCFG 0x00 /* em28xx Chip Configuration 0x00 */ -#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80 -#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 -#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30 -#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20 +#define EM2860_CHIPCFG_VENDOR_AUDIO 0x80 +#define EM2860_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 +#define EM2820_CHIPCFG_I2S_3_SAMPRATES 0x30 +#define EM2860_CHIPCFG_I2S_5_SAMPRATES 0x30 +#define EM2820_CHIPCFG_I2S_1_SAMPRATE 0x20 +#define EM2860_CHIPCFG_I2S_3_SAMPRATES 0x20 #define EM28XX_CHIPCFG_AC97 0x10 #define EM28XX_CHIPCFG_AUDIOMASK 0x30 @@ -245,6 +247,7 @@ enum em28xx_chip_id { CHIP_ID_EM2874 = 65, CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, + CHIP_ID_EM28178 = 114, }; /* diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h new file mode 100644 index 000000000000..bce438691e0e --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -0,0 +1,20 @@ +/* + em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices + + Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation 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. + */ + + +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); +int em28xx_stop_vbi_streaming(struct vb2_queue *vq); +extern struct vb2_ops em28xx_vbi_qops; diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 39f39c527c13..db3d655600df 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -27,6 +27,7 @@ #include <linux/init.h> #include "em28xx.h" +#include "em28xx-v4l.h" static unsigned int vbibufs = 5; module_param(vbibufs, int, 0644); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index dd19c9ff76e0..c3c928937dcd 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -38,9 +38,11 @@ #include <linux/slab.h> #include "em28xx.h" +#include "em28xx-v4l.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> +#include <media/v4l2-clk.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -49,19 +51,23 @@ "Mauro Carvalho Chehab <mchehab@infradead.org>, " \ "Sascha Sommer <saschasommer@freenet.de>" -#define DRIVER_DESC "Empia em28xx based USB video device driver" +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); -#define EM28XX_VERSION "0.2.0" +static int alt; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static unsigned int isoc_debug; -module_param(isoc_debug, int, 0644); -MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); - #define em28xx_isocdbg(fmt, arg...) \ do {\ if (isoc_debug) { \ @@ -71,7 +77,7 @@ do {\ } while (0) MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface"); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); @@ -135,6 +141,257 @@ static struct em28xx_fmt format[] = { }, }; +static int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + 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; + + /* Version of em28xx that does not support VBI */ + return 0; +} + +/* + * em28xx_wake_i2c() + * configure i2c attached devices + */ +static void em28xx_wake_i2c(struct em28xx *dev) +{ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + INPUT(dev->ctl_input)->vmux, 0, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); +} + +static int em28xx_colorlevels_set_default(struct em28xx *dev) +{ + 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); + em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); + return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); +} + +static int em28xx_set_outfmt(struct em28xx *dev) +{ + int ret; + 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; + + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); + if (ret < 0) + return ret; + + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); + } + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); +} + +static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, + u8 ymin, u8 ymax) +{ + em28xx_videodbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); + + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); +} + +static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, + u16 width, u16 height) +{ + u8 cwidth = width >> 2; + u8 cheight = height >> 2; + u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); + /* NOTE: size limit: 2047x1023 = 2MPix */ + + em28xx_videodbg("capture area set to (%d,%d): %dx%d\n", + hstart, vstart, + ((overflow & 2) << 9 | cwidth << 2), + ((overflow & 1) << 10 | cheight << 2)); + + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + 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) +{ + u8 mode; + /* the em2800 scaler only supports scaling down to 50% */ + + if (dev->board.is_em2800) { + mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); + } else { + u8 buf[2]; + + buf[0] = h; + buf[1] = h >> 8; + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); + + buf[0] = v; + buf[1] = v >> 8; + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ + mode = (h || v) ? 0x30 : 0x00; + } + return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); +} + +/* FIXME: this only function read values from dev */ +static int em28xx_resolution_set(struct em28xx *dev) +{ + int width, height; + width = norm_maxw(dev); + height = norm_maxh(dev); + + /* Properly setup VBI */ + dev->vbi_width = 720; + if (dev->norm & V4L2_STD_525_60) + dev->vbi_height = 12; + else + dev->vbi_height = 18; + + em28xx_set_outfmt(dev); + + em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); + + /* If we don't set the start position to 2 in VBI mode, we end up + with line 20/21 being YUYV encoded instead of being in 8-bit + greyscale. The core of the issue is that line 21 (and line 23 for + PAL WSS) are inside of active video region, and as a result they + get the pixelformatting associated with that area. So by cropping + it out, we end up with the same format as the rest of the VBI + region */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 2, width, height); + else + em28xx_capture_area_set(dev, 0, 0, width, height); + + return em28xx_scaler_set(dev, dev->hscale, dev->vscale); +} + +/* Set USB alternate setting for analog video */ +static int em28xx_set_alternate(struct em28xx *dev) +{ + int errCode; + int i; + unsigned int min_pkt_size = dev->width * 2 + 4; + + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + bulk transfers seem to work only with alt=0 ! */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { + em28xx_videodbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + if (dev->analog_xfer_bulk) + goto set_alt; + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { + dev->alt = i; + break; + /* otherwise make sure that we end up with the maximum bandwidth + because the min_pkt_size equation might be wrong... + */ + } else if (dev->alt_max_pkt_size_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) + dev->alt = i; + } + +set_alt: + /* NOTE: for bulk transfers, we need to call usb_set_interface() + * even if the previous settings were the same. Otherwise streaming + * fails with all urbs having status = -EOVERFLOW ! */ + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } + em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, dev->ifnum, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; + } + return 0; +} + /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ @@ -763,7 +1020,7 @@ static struct vb2_ops em28xx_video_qops = { .wait_finish = vb2_ops_wait_finish, }; -int em28xx_vb2_setup(struct em28xx *dev) +static int em28xx_vb2_setup(struct em28xx *dev) { int rc; struct vb2_queue *q; @@ -831,7 +1088,7 @@ static void video_mux(struct em28xx *dev, int index) em28xx_audio_analog_set(dev); } -void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) +static void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { struct em28xx *dev = priv; @@ -890,7 +1147,7 @@ static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) return (ret < 0) ? ret : 0; } -const struct v4l2_ctrl_ops em28xx_ctrl_ops = { +static const struct v4l2_ctrl_ops em28xx_ctrl_ops = { .s_ctrl = em28xx_s_ctrl, }; @@ -1368,7 +1625,7 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; } else { __le16 val = 0; - ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, + ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); if (ret < 0) return ret; @@ -1570,6 +1827,10 @@ static int em28xx_v4l2_open(struct file *filp) case VFL_TYPE_VBI: fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; break; + case VFL_TYPE_RADIO: + break; + default: + return -EINVAL; } em28xx_videodbg("open dev=%s type=%s users=%d\n", @@ -1590,15 +1851,17 @@ static int em28xx_v4l2_open(struct file *filp) fh->type = fh_type; filp->private_data = fh; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + if (dev->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_resolution_set(dev); - /* Needed, since GPIO might have disabled power of - some i2c device + if (vdev->vfl_type != VFL_TYPE_RADIO) + em28xx_resolution_set(dev); + + /* + * Needed, since GPIO might have disabled power + * of some i2c devices */ em28xx_wake_i2c(dev); - } if (vdev->vfl_type == VFL_TYPE_RADIO) { @@ -1615,40 +1878,59 @@ static int em28xx_v4l2_open(struct file *filp) } /* - * em28xx_realease_resources() + * em28xx_v4l2_fini() * unregisters the v4l2,i2c and usb devices * called when the device gets disconected or at module unload */ -void em28xx_release_analog_resources(struct em28xx *dev) +static int em28xx_v4l2_fini(struct em28xx *dev) { + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + if (!dev->has_video) { + /* This device does not support the v4l2 extension */ + return 0; + } - /*FIXME: I2C IR should be disconnected */ + em28xx_info("Closing video extension"); + + mutex_lock(&dev->lock); + + v4l2_device_disconnect(&dev->v4l2_dev); + + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); if (dev->radio_dev) { - if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); - else - video_device_release(dev->radio_dev); - dev->radio_dev = NULL; + em28xx_info("V4L2 device %s deregistered\n", + video_device_node_name(dev->radio_dev)); + video_unregister_device(dev->radio_dev); } if (dev->vbi_dev) { em28xx_info("V4L2 device %s deregistered\n", video_device_node_name(dev->vbi_dev)); - if (video_is_registered(dev->vbi_dev)) - video_unregister_device(dev->vbi_dev); - else - video_device_release(dev->vbi_dev); - dev->vbi_dev = NULL; + video_unregister_device(dev->vbi_dev); } if (dev->vdev) { em28xx_info("V4L2 device %s deregistered\n", video_device_node_name(dev->vdev)); - if (video_is_registered(dev->vdev)) - video_unregister_device(dev->vdev); - else - video_device_release(dev->vdev); - dev->vdev = NULL; + video_unregister_device(dev->vdev); } + + if (dev->clk) { + v4l2_clk_unregister_fixed(dev->clk); + dev->clk = NULL; + } + + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); + + if (dev->users) + em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n"); + mutex_unlock(&dev->lock); + + return 0; } /* @@ -1668,14 +1950,10 @@ static int em28xx_v4l2_close(struct file *filp) mutex_lock(&dev->lock); if (dev->users == 1) { - /* the device is already disconnect, - free the remaining resources */ + /* free the remaining resources if device is disconnected */ if (dev->disconnected) { - em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size_isoc); - mutex_unlock(&dev->lock); - kfree(dev); - return 0; + goto exit; } /* Save some power by putting tuner to sleep */ @@ -1694,11 +1972,29 @@ static int em28xx_v4l2_close(struct file *filp) } } +exit: dev->users--; mutex_unlock(&dev->lock); return 0; } +/* + * em28xx_videodevice_release() + * called when the last user of the video device exits and frees the memeory + */ +static void em28xx_videodevice_release(struct video_device *vdev) +{ + struct em28xx *dev = video_get_drvdata(vdev); + + video_device_release(vdev); + if (vdev == dev->vdev) + dev->vdev = NULL; + else if (vdev == dev->vbi_dev) + dev->vbi_dev = NULL; + else if (vdev == dev->radio_dev) + dev->radio_dev = NULL; +} + static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, @@ -1753,11 +2049,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { }; static const struct video_device em28xx_video_template = { - .fops = &em28xx_v4l_fops, - .release = video_device_release_empty, - .ioctl_ops = &video_ioctl_ops, - - .tvnorms = V4L2_STD_ALL, + .fops = &em28xx_v4l_fops, + .ioctl_ops = &video_ioctl_ops, + .release = em28xx_videodevice_release, + .tvnorms = V4L2_STD_ALL, }; static const struct v4l2_file_operations radio_fops = { @@ -1783,14 +2078,30 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { }; static struct video_device em28xx_radio_template = { - .name = "em28xx-radio", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .release = em28xx_videodevice_release, }; -/******************************** usb interface ******************************/ +/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ +static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; +static unsigned short tvp5150_addrs[] = { + 0xb8 >> 1, + 0xba >> 1, + I2C_CLIENT_END +}; +static unsigned short msp3400_addrs[] = { + 0x80 >> 1, + 0x88 >> 1, + I2C_CLIENT_END +}; + +/******************************** usb interface ******************************/ static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, @@ -1817,14 +2128,198 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -int em28xx_register_analog_devices(struct em28xx *dev) +static void em28xx_tuner_setup(struct em28xx *dev) +{ + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + if (dev->tuner_type == TUNER_ABSENT) + return; + + memset(&tun_setup, 0, sizeof(tun_setup)); + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.tuner_callback = em28xx_tuner_callback; + + if (dev->board.radio.type) { + tun_setup.type = dev->board.radio.type; + tun_setup.addr = dev->board.radio_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if (dev->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); + } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + em28xx_setup_xc3028(dev, &ctl); + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); + } + + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + dev->ctl_freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); +} + +static int em28xx_v4l2_init(struct em28xx *dev) { u8 val; int ret; unsigned int maxw; + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + if (!dev->has_video) { + /* This device does not support the v4l2 extension */ + return 0; + } + + em28xx_info("Registering V4L2 extension\n"); + + mutex_lock(&dev->lock); + + ret = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); + if (ret < 0) { + em28xx_errdev("Call to v4l2_device_register() failed!\n"); + goto err; + } + + v4l2_ctrl_handler_init(hdl, 8); + dev->v4l2_dev.ctrl_handler = hdl; + + /* + * Default format, used for tvp5150 or saa711x output formats + */ + dev->vinmode = 0x10; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; + + /* request some modules */ + + if (dev->board.has_msp34xx) + 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[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[dev->def_i2c_bus], + "tvp5150", 0, tvp5150_addrs); + + if (dev->board.adecoder == EM28XX_TVAUDIO) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], + "tvaudio", dev->board.tvaudio_addr, NULL); + + /* Initialize tuner and camera */ + + 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[dev->def_i2c_bus], + "tuner", dev->board.radio_addr, NULL); + + if (has_demod) + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &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 = + has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &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[dev->def_i2c_bus], + "tuner", dev->tuner_addr, NULL); + } + } + + em28xx_tuner_setup(dev); + em28xx_init_camera(dev); + + /* Configure audio */ + ret = em28xx_audio_setup(dev); + if (ret < 0) { + em28xx_errdev("%s: Error while setting audio - error [%d]!\n", + __func__, ret); + goto unregister_dev; + } + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); + } else { + /* install the em28xx notify callback */ + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), + em28xx_ctrl_notify, dev); + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), + em28xx_ctrl_notify, dev); + } + + /* wake i2c devices */ + em28xx_wake_i2c(dev); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vbiq.active); - printk(KERN_INFO "%s: v4l2 driver version %s\n", - dev->name, EM28XX_VERSION); + if (dev->board.has_msp34xx) { + /* Send a reset to other chips via gpio */ + ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); + if (ret < 0) { + em28xx_errdev("%s: em28xx_write_reg - msp34xx(1) failed! error [%d]\n", + __func__, ret); + goto unregister_dev; + } + msleep(3); + + ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); + if (ret < 0) { + em28xx_errdev("%s: em28xx_write_reg - msp34xx(2) failed! error [%d]\n", + __func__, ret); + goto unregister_dev; + } + msleep(3); + } /* set default norm */ dev->norm = V4L2_STD_PAL; @@ -1888,14 +2383,16 @@ int em28xx_register_analog_devices(struct em28xx *dev) /* 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; + ret = dev->ctrl_handler.error; + if (ret) + goto unregister_dev; /* allocate and fill video video_device struct */ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); if (!dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; + ret = -ENODEV; + goto unregister_dev; } dev->vdev->queue = &dev->vb_vidq; dev->vdev->queue->lock = &dev->vb_queue_lock; @@ -1925,7 +2422,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) if (ret) { em28xx_errdev("unable to register video device (error=%i).\n", ret); - return ret; + goto unregister_dev; } /* Allocate and fill vbi video_device struct */ @@ -1954,7 +2451,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) vbi_nr[dev->devno]); if (ret < 0) { em28xx_errdev("unable to register vbi device\n"); - return ret; + goto unregister_dev; } } @@ -1963,13 +2460,14 @@ int em28xx_register_analog_devices(struct em28xx *dev) "radio"); if (!dev->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; + ret = -ENODEV; + goto unregister_dev; } ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { em28xx_errdev("can't register radio device\n"); - return ret; + goto unregister_dev; } em28xx_info("Registered radio device as %s\n", video_device_node_name(dev->radio_dev)); @@ -1982,5 +2480,41 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_info("V4L2 VBI device registered as %s\n", video_device_node_name(dev->vbi_dev)); + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + + /* initialize videobuf2 stuff */ + em28xx_vb2_setup(dev); + + em28xx_info("V4L2 extension successfully initialized\n"); + + mutex_unlock(&dev->lock); return 0; + +unregister_dev: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); +err: + mutex_unlock(&dev->lock); + return ret; +} + +static struct em28xx_ops v4l2_ops = { + .id = EM28XX_V4L2, + .name = "Em28xx v4l2 Extension", + .init = em28xx_v4l2_init, + .fini = em28xx_v4l2_fini, +}; + +static int __init em28xx_video_register(void) +{ + return em28xx_register_extension(&v4l2_ops); +} + +static void __exit em28xx_video_unregister(void) +{ + em28xx_unregister_extension(&v4l2_ops); } + +module_init(em28xx_video_register); +module_exit(em28xx_video_unregister); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f8726ad5d0a8..32d8a4bb7961 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -26,6 +26,9 @@ #ifndef _EM28XX_H #define _EM28XX_H +#define EM28XX_VERSION "0.2.1" +#define DRIVER_DESC "Empia em28xx device driver" + #include <linux/workqueue.h> #include <linux/i2c.h> #include <linux/mutex.h> @@ -132,6 +135,8 @@ #define EM2884_BOARD_C3TECH_DIGITAL_DUO 88 #define EM2874_BOARD_DELOCK_61959 89 #define EM2874_BOARD_KWORLD_UB435Q_V2 90 +#define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE 91 +#define EM28178_BOARD_PCTV_461E 92 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -178,8 +183,27 @@ #define EM28XX_INTERLACED_DEFAULT 1 -/* time in msecs to wait for i2c writes to finish */ -#define EM2800_I2C_XFER_TIMEOUT 20 +/* + * Time in msecs to wait for i2c xfers to finish. + * 35ms is the maximum time a SMBUS device could wait when + * clock stretching is used. As the transfer itself will take + * some time to happen, set it to 35 ms. + * + * Ok, I2C doesn't specify any limit. So, eventually, we may need + * to increase this timeout. + * + * FIXME: this assumes that an I2C message is not longer than 1ms. + * This is actually dependent on the I2C bus speed, although most + * devices use a 100kHz clock. So, this assumtion is true most of + * the time. + */ +#define EM28XX_I2C_XFER_TIMEOUT 36 + +/* time in msecs to wait for AC97 xfers to finish */ +#define EM28XX_AC97_XFER_TIMEOUT 100 + +/* max. number of button state polling addresses */ +#define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5 enum em28xx_mode { EM28XX_SUSPEND, @@ -287,8 +311,7 @@ struct em28xx_audio_mode { unsigned int has_audio:1; - unsigned int i2s_3rates:1; - unsigned int i2s_5rates:1; + u8 i2s_samplerates; }; /* em28xx has two audio inputs: tuner and line in. @@ -374,6 +397,33 @@ enum em28xx_adecoder { EM28XX_TVAUDIO, }; +enum em28xx_led_role { + EM28XX_LED_ANALOG_CAPTURING = 0, + EM28XX_LED_ILLUMINATION, + EM28XX_NUM_LED_ROLES, /* must be the last */ +}; + +struct em28xx_led { + enum em28xx_led_role role; + u8 gpio_reg; + u8 gpio_mask; + bool inverted; +}; + +enum em28xx_button_role { + EM28XX_BUTTON_SNAPSHOT = 0, + EM28XX_BUTTON_ILLUMINATION, + EM28XX_NUM_BUTTON_ROLES, /* must be the last */ +}; + +struct em28xx_button { + enum em28xx_button_role role; + u8 reg_r; + u8 reg_clearing; + u8 mask; + bool inverted; +}; + struct em28xx_board { char *name; int vchannels; @@ -395,7 +445,6 @@ struct em28xx_board { unsigned int mts_firmware:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; - unsigned int has_snapshot_button:1; unsigned int is_webcam:1; unsigned int valid:1; unsigned int has_ir_i2c:1; @@ -410,6 +459,12 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; char *ir_codes; + + /* LEDs that need to be controlled explicitly */ + struct em28xx_led *leds; + + /* Buttons */ + struct em28xx_button *buttons; }; struct em28xx_eeprom { @@ -426,15 +481,13 @@ struct em28xx_eeprom { u8 string_idx_table; }; -#define EM28XX_AUDIO_BUFS 5 -#define EM28XX_NUM_AUDIO_PACKETS 64 -#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ #define EM28XX_CAPTURE_STREAM_EN 1 /* em28xx extensions */ #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 #define EM28XX_RC 0x30 +#define EM28XX_V4L2 0x40 /* em28xx resource types (used for res_get/res_lock etc */ #define EM28XX_RESOURCE_VIDEO 0x01 @@ -442,8 +495,9 @@ struct em28xx_eeprom { struct em28xx_audio { char name[50]; - char *transfer_buffer[EM28XX_AUDIO_BUFS]; - struct urb *urb[EM28XX_AUDIO_BUFS]; + unsigned num_urb; + char **transfer_buffer; + struct urb **urb; struct usb_device *udev; unsigned int capture_transfer_done; struct snd_pcm_substream *capture_pcm_substream; @@ -451,6 +505,8 @@ struct em28xx_audio { unsigned int hwptr_done_capture; struct snd_card *sndcard; + size_t period; + int users; spinlock_t slock; }; @@ -485,11 +541,13 @@ 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 int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */ unsigned char disconnected:1; /* device has been diconnected */ - - int audio_ifnum; + unsigned int has_video:1; + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + unsigned int is_audio_only:1; struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; @@ -507,10 +565,6 @@ struct em28xx { /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; - unsigned int has_audio_class:1; - unsigned int has_alsa_audio:1; - 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 */ @@ -608,6 +662,7 @@ struct em28xx { /* usb transfer */ struct usb_device *udev; /* the usb device */ + u8 ifnum; /* number of the assigned usb interface */ 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 */ @@ -639,10 +694,15 @@ struct em28xx { enum em28xx_mode mode; - /* Snapshot button */ + /* Button state polling */ + struct delayed_work buttons_query_work; + u8 button_polling_addresses[EM28XX_NUM_BUTTON_ADDRESSES_MAX]; + u8 button_polling_last_values[EM28XX_NUM_BUTTON_ADDRESSES_MAX]; + u8 num_button_polling_addresses; + u16 button_polling_interval; /* [ms] */ + /* Snapshot button input device */ char snapshot_button_path[30]; /* path of the input dev */ struct input_dev *sbutton_input_dev; - struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; }; @@ -672,6 +732,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, u8 bitmask); +int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask); int em28xx_read_ac97(struct em28xx *dev, u8 reg); int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); @@ -679,12 +740,9 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); -int em28xx_colorlevels_set_default(struct em28xx *dev); +const struct em28xx_led *em28xx_find_led(struct em28xx *dev, + enum em28xx_led_role role); int em28xx_capture_start(struct em28xx *dev, int start); -int em28xx_vbi_supported(struct em28xx *dev); -int em28xx_set_outfmt(struct em28xx *dev); -int em28xx_resolution_set(struct em28xx *dev); -int em28xx_set_alternate(struct em28xx *dev); int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, int num_bufs, int max_pkt_size, int packet_multiplier); int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, @@ -696,30 +754,18 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(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); int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); void em28xx_init_extension(struct em28xx *dev); void em28xx_close_extension(struct em28xx *dev); -/* Provided by em28xx-video.c */ -int em28xx_vb2_setup(struct em28xx *dev); -int em28xx_register_analog_devices(struct em28xx *dev); -void em28xx_release_analog_resources(struct em28xx *dev); -void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); -int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); -int em28xx_stop_vbi_streaming(struct vb2_queue *vq); -extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; - /* Provided by em28xx-cards.c */ extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; int em28xx_tuner_callback(void *ptr, int component, int command, int arg); +void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl); 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); diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index 2f0c89cbac76..c5638964c3f2 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -198,7 +198,6 @@ 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); @@ -214,6 +213,9 @@ static int device_authorization(struct hdpvr_device *dev) retval = ret != 8; unlock: mutex_unlock(&dev->usbc_mutex); +#ifdef HDPVR_DEBUG + kfree(print_buf); +#endif return retval; } diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 78c9bc8e7f56..abf365ab025d 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1078,7 +1078,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* register webcam snapshot button input device */ pdev->button_dev = input_allocate_device(); if (!pdev->button_dev) { - PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); rc = -ENOMEM; goto err_video_unreg; } diff --git a/drivers/media/usb/sn9c102/Kconfig b/drivers/media/usb/sn9c102/Kconfig deleted file mode 100644 index 6ebaf2940d06..000000000000 --- a/drivers/media/usb/sn9c102/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config USB_SN9C102 - tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - ---help--- - This driver is DEPRECATED please use the gspca sonixb and - sonixj modules instead. - - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. - - See <file:Documentation/video4linux/sn9c102.txt> for more info. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. diff --git a/drivers/media/usb/sn9c102/Makefile b/drivers/media/usb/sn9c102/Makefile deleted file mode 100644 index 7ecd5a90c7c9..000000000000 --- a/drivers/media/usb/sn9c102/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -sn9c102-objs := sn9c102_core.o \ - sn9c102_hv7131d.o \ - sn9c102_hv7131r.o \ - sn9c102_mi0343.o \ - sn9c102_mi0360.o \ - sn9c102_mt9v111.o \ - sn9c102_ov7630.o \ - sn9c102_ov7660.o \ - sn9c102_pas106b.o \ - sn9c102_pas202bcb.o \ - sn9c102_tas5110c1b.o \ - sn9c102_tas5110d.o \ - sn9c102_tas5130d1b.o - -obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/media/usb/sn9c102/sn9c102.h b/drivers/media/usb/sn9c102/sn9c102.h deleted file mode 100644 index 8a917f060503..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102.h +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 _SN9C102_H_ -#define _SN9C102_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <linux/device.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/time.h> -#include <linux/wait.h> -#include <linux/types.h> -#include <linux/param.h> -#include <linux/rwsem.h> -#include <linux/mutex.h> -#include <linux/string.h> -#include <linux/stddef.h> -#include <linux/kref.h> - -#include "sn9c102_config.h" -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -enum sn9c102_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct sn9c102_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum sn9c102_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum sn9c102_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum sn9c102_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum sn9c102_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -typedef char sn9c102_sof_header_t[62]; - -struct sn9c102_sof_t { - sn9c102_sof_header_t header; - u16 bytesread; -}; - -struct sn9c102_sysfs_attr { - u16 reg, i2c_reg; - sn9c102_sof_header_t frame_header; -}; - -struct sn9c102_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(sn9c102_sysfs_lock); -static DECLARE_RWSEM(sn9c102_dev_lock); - -struct sn9c102_device { - struct video_device* v4ldev; - - struct v4l2_device v4l2_dev; - - enum sn9c102_bridge bridge; - struct sn9c102_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[SN9C102_URBS]; - void* transfer_buffer[SN9C102_URBS]; - u8* control_buffer; - - struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum sn9c102_io_method io; - enum sn9c102_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct sn9c102_sysfs_attr sysfs; - struct sn9c102_sof_t sof; - u16 reg[384]; - - struct sn9c102_module_param module_param; - - struct kref kref; - enum sn9c102_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); -} - - -enum sn9c102_bridge -sn9c102_get_bridge(struct sn9c102_device* cam) -{ - return cam->bridge; -} - - -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) -{ - return &cam->sensor; -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef SN9C102_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_printk_ioctl(name, cmd); \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("sn9c102: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -#else -# define DBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ - -#endif /* _SN9C102_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_config.h b/drivers/media/usb/sn9c102/sn9c102_config.h deleted file mode 100644 index 0f4e0378b071..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_config.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 _SN9C102_CONFIG_H_ -#define _SN9C102_CONFIG_H_ - -#include <linux/types.h> -#include <linux/jiffies.h> - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 0 - -/*****************************************************************************/ - -static const u8 SN9C102_Y_QTABLE0[64] = { - 8, 5, 5, 8, 12, 20, 25, 30, - 6, 6, 7, 9, 13, 29, 30, 27, - 7, 6, 8, 12, 20, 28, 34, 28, - 7, 8, 11, 14, 25, 43, 40, 31, - 9, 11, 18, 28, 34, 54, 51, 38, - 12, 17, 27, 32, 40, 52, 56, 46, - 24, 32, 39, 43, 51, 60, 60, 50, - 36, 46, 47, 49, 56, 50, 51, 49 -}; - -static const u8 SN9C102_UV_QTABLE0[64] = { - 8, 9, 12, 23, 49, 49, 49, 49, - 9, 10, 13, 33, 49, 49, 49, 49, - 12, 13, 28, 49, 49, 49, 49, 49, - 23, 33, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 -}; - -static const u8 SN9C102_Y_QTABLE1[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const u8 SN9C102_UV_QTABLE1[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c deleted file mode 100644 index 2cb44de2b92c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ /dev/null @@ -1,3434 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/compiler.h> -#include <linux/ioctl.h> -#include <linux/poll.h> -#include <linux/stat.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/version.h> -#include <linux/page-flags.h> -#include <asm/byteorder.h> -#include <asm/page.h> -#include <asm/uaccess.h> - -#include "sn9c102.h" - -/*****************************************************************************/ - -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_ALIAS "sn9c1xx" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" -#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" -#define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.48" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, sn9c102_id_table); - -MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); -MODULE_DESCRIPTION(SN9C102_MODULE_NAME); -MODULE_ALIAS(SN9C102_MODULE_ALIAS); -MODULE_VERSION(SN9C102_MODULE_VERSION); -MODULE_LICENSE(SN9C102_MODULE_LICENSE); - -static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - " <-1|n[,...]>" - "\nSpecify V4L2 minor mode number." - "\n-1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) - " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - " <0|1[,...]>" - "\nForce the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n0 = do not force memory unmapping" - "\n1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - " <0|n[,...]>" - "\nTimeout for a video frame in seconds before" - "\nreturning an I/O error; 0 for infinity." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." - "\n"); - -#ifdef SN9C102_DEBUG -static unsigned short debug = SN9C102_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - " <n>" - "\nDebugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only." - "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." - "\n"); -#endif - -/* - Add the probe entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - -/*****************************************************************************/ - -static u32 -sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, - enum sn9c102_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > SN9C102_MAX_FRAMES) - count = SN9C102_MAX_FRAMES; - - if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) - imagesize += 589 + 2; /* length of JPEG header + EOI marker */ - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - } - - return cam->nbuffers; -} - - -static void sn9c102_release_buffers(struct sn9c102_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void sn9c102_empty_framequeues(struct sn9c102_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < SN9C102_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) -{ - struct sn9c102_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -/* - Write a sequence of count value/register pairs. Returns -1 after the first - failed write, or 0 for no errors. -*/ -int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], - int count) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int i, res; - - for (i = 0; i < count; i++) { - u8 index = valreg[i][1]; - - /* - index is a u8, so it must be <256 and can't be out of range. - If we put in a check anyway, gcc annoys us with a warning - hat our check is useless. People get all uppity when they - see warnings in the kernel compile. - */ - - *buff = valreg[i][0]; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, - 0x41, index, 0, buff, 1, - SN9C102_CTRL_TIMEOUT); - - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, " - "index 0x%02X, error %d)", *buff, index, res); - return -1; - } - - cam->reg[index] = *buff; - } - - return 0; -} - - -int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - cam->reg[index] = value; - - return 0; -} - - -/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ -int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) -{ - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - return cam->reg[index]; -} - - -static int -sn9c102_i2c_wait(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 5; i++) { - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - return -EIO; - if (r & 0x04) - return 0; - if (sensor->frequency & SN9C102_I2C_400KHZ) - udelay(5*16); - else - udelay(16*16); - } - return -EBUSY; -} - - -static int -sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r , err = 0; - - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - err += r; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if (!(r & 0x08)) - err += -1; - } else { - if (r & 0x08) - err += -1; - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r; - r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; -} - - -int -sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 data0, - u8 data1, u8 n, u8 buffer[]) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int i = 0, err = 0, res; - - /* Write cycle */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; - data[1] = data0; /* I2C slave id */ - data[2] = data1; /* address */ - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* Read cycle - n bytes */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | - (n << 4) | 0x02; - data[1] = data0; - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* The first read byte will be placed in data[4] */ - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_detect_read_error(cam, sensor); - - PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, - data[4]); - - if (err) { - DBG(3, "I2C read failed for %s image sensor", sensor->name); - return -1; - } - - if (buffer) - for (i = 0; i < n && i < 5; i++) - buffer[n-i-1] = data[4-i]; - - return (int)data[4]; -} - - -int -sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, u8 data0, - u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - /* Write cycle. It usually is address + value */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) - | ((n - 1) << 4); - data[1] = data0; - data[2] = data1; - data[3] = data2; - data[4] = data3; - data[5] = data4; - data[6] = data5; - data[7] = 0x17; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - err += sn9c102_i2c_detect_write_error(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", - n, data0, data1, data2, data3, data4, data5); - - return err ? -1 : 0; -} - - -int -sn9c102_i2c_try_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 address) -{ - return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, - address, 1, NULL); -} - - -static int sn9c102_i2c_try_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 address, u8 value) -{ - return sn9c102_i2c_try_raw_write(cam, sensor, 3, - sensor->i2c_slave_id, address, - value, 0, 0, 0); -} - - -int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) -{ - return sn9c102_i2c_try_read(cam, &cam->sensor, address); -} - - -int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) -{ - return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); -} - -/*****************************************************************************/ - -static size_t sn9c102_sof_length(struct sn9c102_device* cam) -{ - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - return 12; - case BRIDGE_SN9C103: - return 18; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - return 62; - } - - return 0; -} - - -static void* -sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - const char *m = mem; - size_t soflen = 0, i, j; - - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < len; i++) { - size_t b; - - /* Read the variable part of the header */ - if (unlikely(cam->sof.bytesread >= sizeof(marker))) { - cam->sof.header[cam->sof.bytesread] = *(m+i); - if (++cam->sof.bytesread == soflen) { - cam->sof.bytesread = 0; - return mem + i; - } - continue; - } - - /* Search for the SOF marker (fixed part) in the header */ - for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { - if (unlikely(i+j == len)) - return NULL; - if (*(m+i+j) == marker[cam->sof.bytesread]) { - cam->sof.header[cam->sof.bytesread] = *(m+i+j); - if (++cam->sof.bytesread == sizeof(marker)) { - PDBGG("Bytes to analyze: %zd. SOF " - "starts at byte #%zd", len, i); - i += j+1; - break; - } - } else { - cam->sof.bytesread = 0; - break; - } - } - } - - return NULL; -} - - -static void* -sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const u8 eof_header[4][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, - }; - size_t i, j; - - /* The EOF header does not exist in compressed data */ - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; - - /* - The EOF header might cross the packet boundary, but this is not a - problem, since the end of a frame is determined by checking its size - in the first place. - */ - for (i = 0; (len >= 4) && (i <= len - 4); i++) - for (j = 0; j < ARRAY_SIZE(eof_header); j++) - if (!memcmp(mem + i, eof_header[j], 4)) - return mem + i; - - return NULL; -} - - -static void -sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) -{ - static const u8 jpeg_header[589] = { - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, - 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, - 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, - 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, - 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, - 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, - 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, - 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, - 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, - 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, - 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, - 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, - 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, - 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 - }; - u8 *pos = f->bufmem; - - memcpy(pos, jpeg_header, sizeof(jpeg_header)); - *(pos + 6) = 0x00; - *(pos + 7 + 64) = 0x01; - if (cam->compression.quality == 0) { - memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); - } else if (cam->compression.quality == 1) { - memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); - } - *(pos + 564) = cam->sensor.pix_format.width & 0xFF; - *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; - *(pos + 562) = cam->sensor.pix_format.height & 0xFF; - *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; - *(pos + 567) = 0x21; - - f->buf.bytesused += sizeof(jpeg_header); -} - - -static void sn9c102_urb_complete(struct urb *urb) -{ - struct sn9c102_device* cam = urb->context; - struct sn9c102_frame_t** f; - size_t imagesize, soflen; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - cam->sof.bytesread = 0; - DBG(3, "Stream interrupted by application"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - imagesize += 589; /* length of jpeg header */ - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int img, len, status; - void *pos, *sof, *eof; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - cam->sof.bytesread = 0; - continue; - } - - PDBGG("Isochrnous frame: length %u, #%u i", len, i); - -redo: - sof = sn9c102_find_sof_header(cam, pos, len); - if (likely(!sof)) { - eof = sn9c102_find_eof_header(cam, pos, len); - if ((*f)->state == F_GRABBING) { -end_of_frame: - img = len; - - if (eof) - img = (eof > pos) ? eof - pos - 1 : 0; - - if ((*f)->buf.bytesused + img > imagesize) { - u32 b; - b = (*f)->buf.bytesused + img - - imagesize; - img = imagesize - (*f)->buf.bytesused; - PDBGG("Expected EOF not found: video " - "frame cut"); - if (eof) - DBG(3, "Exceeded limit: +%u " - "bytes", (unsigned)(b)); - } - - memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, - img); - - if ((*f)->buf.bytesused == 0) - v4l2_get_timestamp( - &(*f)->buf.timestamp); - - (*f)->buf.bytesused += img; - - if ((*f)->buf.bytesused == imagesize || - ((cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) && eof)) { - u32 b; - - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, - &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry( - cam->inqueue.next, - struct sn9c102_frame_t, - frame ); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - - memcpy(cam->sysfs.frame_header, - cam->sof.header, soflen); - - DBG(3, "Video frame captured: %lu " - "bytes", (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - } else if (eof) { - (*f)->state = F_ERROR; - DBG(3, "Not expected EOF after %lu " - "bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - } - - if (sof) /* (1) */ - goto start_of_frame; - - } else if (eof) { - DBG(3, "EOF without SOF"); - continue; - - } else { - PDBGG("Ignoring pointless isochronous frame"); - continue; - } - - } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { -start_of_frame: - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - len -= (sof - pos); - pos = sof; - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) - sn9c102_write_jpegheader(cam, (*f)); - DBG(3, "SOF detected: new video frame"); - if (len) - goto redo; - - } else if ((*f)->state == F_GRABBING) { - eof = sn9c102_find_eof_header(cam, pos, len); - if (eof && eof < sof) - goto end_of_frame; /* (1) */ - else { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) { - if (sof - pos >= soflen) { - eof = sof - soflen; - } else { /* remove header */ - eof = pos; - (*f)->buf.bytesused -= - (soflen - (sof - pos)); - } - goto end_of_frame; - } else { - DBG(3, "SOF before expected EOF after " - "%lu bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - goto start_of_frame; - } - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int sn9c102_start_transfer(struct sn9c102_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - SN9C102_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < SN9C102_URBS; i++) { - urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SN9C102_ISO_PACKETS; - urb->complete = sn9c102_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < SN9C102_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - /* Enable video */ - if (!(cam->reg[0x01] & 0x04)) { - err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - } - - err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - cam->sof.bytesread = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int sn9c102_stop_transfer(struct sn9c102_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = SN9C102_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int sn9c102_stream_interrupt(struct sn9c102_device* cam) -{ - cam->stream = STREAM_INTERRUPT; - wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - SN9C102_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. " - "To use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) -{ - char str[7]; - char* endp; - unsigned long val; - - if (len < 6) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 6); - str[6] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xffff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u16)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t sn9c102_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (index >= ARRAY_SIZE(cam->reg) || !count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_green(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - enum sn9c102_bridge bridge; - ssize_t res = 0; - u16 value; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - bridge = cam->bridge; - - mutex_unlock(&sn9c102_sysfs_lock); - - value = sn9c102_strtou16(buf, len, &count); - if (!count) - return -EINVAL; - - switch (bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - if (value > 0x0f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - case BRIDGE_SN9C103: - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (value > 0x7f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - } - - return res; -} - - -static ssize_t -sn9c102_store_blue(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t -sn9c102_store_red(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t sn9c102_show_frame_header(struct device* cd, - struct device_attribute *attr, - char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) - return -ENODEV; - - count = sizeof(cam->sysfs.frame_header); - memcpy(buf, cam->sysfs.frame_header, count); - - DBG(3, "Frame header, read bytes: %zd", count); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); -static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); - - -static int sn9c102_create_sysfs(struct sn9c102_device* cam) -{ - struct device *dev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(dev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(dev, &dev_attr_val))) - goto err_reg; - if ((err = device_create_file(dev, &dev_attr_frame_header))) - goto err_val; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(dev, &dev_attr_i2c_reg))) - goto err_frame_header; - if ((err = device_create_file(dev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if ((err = device_create_file(dev, &dev_attr_green))) - goto err_i2c_val; - } else { - if ((err = device_create_file(dev, &dev_attr_blue))) - goto err_i2c_val; - if ((err = device_create_file(dev, &dev_attr_red))) - goto err_blue; - } - - return 0; - -err_blue: - device_remove_file(dev, &dev_attr_blue); -err_i2c_val: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_val); -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_reg); -err_frame_header: - device_remove_file(dev, &dev_attr_frame_header); -err_val: - device_remove_file(dev, &dev_attr_val); -err_reg: - device_remove_file(dev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - } - } else { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - } - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_set_compression(struct sn9c102_device* cam, - struct v4l2_jpegcompression* compression) -{ - int i, err = 0; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, - 0x17); - else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, - 0x17); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (compression->quality == 0) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, - 0x18); - } else if (compression->quality == 1) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, - 0x18); - } - break; - } - - return err ? -EIO : 0; -} - - -static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) -{ - u8 r = 0; - int err = 0; - - if (scale == 1) - r = cam->reg[0x18] & 0xcf; - else if (scale == 2) { - r = cam->reg[0x18] & 0xcf; - r |= 0x10; - } else if (scale == 4) - r = cam->reg[0x18] | 0x20; - - err += sn9c102_write_reg(cam, r, 0x18); - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &cam->sensor; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), - v_start = (u8)(rect->top - s->cropcap.bounds.top), - h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16); - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - err += sn9c102_write_reg(cam, h_size, 0x15); - err += sn9c102_write_reg(cam, v_size, 0x16); - if (err) - return -EIO; - - PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " - "%u %u %u %u", h_start, v_start, h_size, v_size); - - return 0; -} - - -static int sn9c102_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); - err += sn9c102_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if (!(cam->state & DEV_INITIALIZED)) - if (cam->bridge == BRIDGE_SN9C101 || - cam->bridge == BRIDGE_SN9C102 || - cam->bridge == BRIDGE_SN9C103) { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; - cam->compression.quality = cam->reg[0x17] & 0x01 ? - 0 : 1; - } else { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; - cam->compression.quality = cam->reg[0x18] & 0x40 ? - 0 : 1; - err += sn9c102_set_compression(cam, &cam->compression); - } - else - err += sn9c102_set_compression(cam, &cam->compression); - err += sn9c102_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void sn9c102_release_resources(struct kref *kref) -{ - struct sn9c102_device *cam; - - mutex_lock(&sn9c102_sysfs_lock); - - cam = container_of(kref, struct sn9c102_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - v4l2_device_unregister(&cam->v4l2_dev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&sn9c102_sysfs_lock); - -} - - -static int sn9c102_open(struct file *filp) -{ - struct sn9c102_device* cam; - int err = 0; - - /* - A read_trylock() in open() is the only safe way to prevent race - conditions with disconnect(), one close() and multiple (not - necessarily simultaneous) attempts to open(). For example, it - prevents from waiting for a second access, while the device - structure is being deallocated, after a possible disconnect() and - during a following close() holding the write lock: given that, after - this deallocation, no access will be possible anymore, using the - non-trylock version would have let open() gain the access to the - device structure improperly. - For this reason the lock must also not be per-device. - */ - if (!down_read_trylock(&sn9c102_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - /* - Make sure to isolate all the simultaneous opens. - */ - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, sn9c102_release_resources); - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - /* - open() must follow the open flags and should block - eventually while the device is in use. - */ - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&sn9c102_dev_lock); - /* - We will not release the "open_mutex" lock, so that only one - process can be in the wait queue below. This way the process - will be sleeping while holding the lock, without losing its - priority after any wake_up(). - */ - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&sn9c102_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = sn9c102_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = sn9c102_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - sn9c102_empty_framequeues(cam); - - DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, sn9c102_release_resources); - - up_read(&sn9c102_dev_lock); - return err; -} - - -static int sn9c102_release(struct file *filp) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_dev_lock); - - cam = video_drvdata(filp); - - sn9c102_stop_transfer(cam); - sn9c102_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); - - return 0; -} - - -static ssize_t -sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose " - "the read method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - sn9c102_empty_framequeues(cam); - sn9c102_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) { - mutex_unlock(&cam->fileop_mutex); - return err; - } - } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (cam->state & DEV_MISCONFIGURED) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - sn9c102_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return count; -} - - -static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - sn9c102_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void sn9c102_vm_open(struct vm_area_struct* vma) -{ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void sn9c102_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct sn9c102_vm_ops = { - .open = sn9c102_vm_open, - .close = sn9c102_vm_close, -}; - - -static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct sn9c102_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &sn9c102_vm_ops; - vma->vm_private_data = &cam->frame[i]; - sn9c102_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "sn9c102", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id && ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return err; -} - - -static int -sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return 0; -} - - -static int -sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_pix_format* pix_format = &(s->pix_format); - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (SN9C102_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err = sn9c102_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - } - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - break; - } - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, - void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - } - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. Unmap the " - "buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err += sn9c102_set_pix_format(cam, pix); - err += sn9c102_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum sn9c102_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - err += sn9c102_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. " - "To use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " - "still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - sn9c102_release_buffers(cam); - if (rb.count) - rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - sn9c102_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - b = cam->frame[b.index].buf; - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct sn9c102_frame_t *f; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) - return err; - } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - b = f->buf; - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) - sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static int -sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - strcpy(audio.name, "Microphone"); - audio.capability = 0; - audio.mode = 0; - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - memset(&audio, 0, sizeof(audio)); - strcpy(audio.name, "Microphone"); - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - return 0; -} - - -static long sn9c102_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return sn9c102_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return sn9c102_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return sn9c102_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return sn9c102_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return sn9c102_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return sn9c102_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return sn9c102_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return sn9c102_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return sn9c102_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return sn9c102_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return sn9c102_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_ENUM_FMT: - return sn9c102_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return sn9c102_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_G_JPEGCOMP: - return sn9c102_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return sn9c102_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return sn9c102_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return sn9c102_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return sn9c102_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return sn9c102_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return sn9c102_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return sn9c102_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return sn9c102_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return sn9c102_vidioc_s_parm(cam, arg); - - case VIDIOC_ENUMAUDIO: - return sn9c102_vidioc_enumaudio(cam, arg); - - case VIDIOC_G_AUDIO: - return sn9c102_vidioc_g_audio(cam, arg); - - case VIDIOC_S_AUDIO: - return sn9c102_vidioc_s_audio(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long sn9c102_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "sn9c102", cmd); - - err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - -/*****************************************************************************/ - -static const struct v4l2_file_operations sn9c102_fops = { - .owner = THIS_MODULE, - .open = sn9c102_open, - .release = sn9c102_release, - .unlocked_ioctl = sn9c102_ioctl, - .read = sn9c102_read, - .poll = sn9c102_poll, - .mmap = sn9c102_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct sn9c102_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0, r; - - if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - /* register v4l2_device early so it can be used for printks */ - if (v4l2_device_register(&intf->dev, &cam->v4l2_dev)) { - dev_err(&intf->dev, "v4l2_device_register failed\n"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kzalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx-based camera " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - err = -ENODEV; - goto fail; - } - - cam->bridge = id->driver_info; - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C103: - DBG(2, "SN9C103 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C105: - DBG(2, "SN9C105 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C120: - DBG(2, "SN9C120 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - } - - for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { - err = sn9c102_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) { - DBG(2, "%s image sensor detected", cam->sensor.name); - DBG(3, "Support for %s maintained by %s", - cam->sensor.name, cam->sensor.maintainer); - } else { - DBG(1, "No supported image sensor detected for this bridge"); - err = -ENODEV; - goto fail; - } - - if (!(cam->bridge & cam->sensor.supported_bridge)) { - DBG(1, "Bridge not supported"); - err = -ENODEV; - goto fail; - } - - if (sn9c102_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); - cam->v4ldev->fops = &sn9c102_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->v4l2_dev = &cam->v4l2_dev; - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - video_set_drvdata(cam->v4ldev, cam); - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = sn9c102_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create optional 'sysfs' interface for " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); - } - return err; -} - - -static void sn9c102_usb_disconnect(struct usb_interface* intf) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - sn9c102_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - v4l2_device_disconnect(&cam->v4l2_dev); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); -} - - -static struct usb_driver sn9c102_usb_driver = { - .name = "sn9c102", - .id_table = sn9c102_id_table, - .probe = sn9c102_usb_probe, - .disconnect = sn9c102_usb_disconnect, -}; - -module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/media/usb/sn9c102/sn9c102_devtable.h b/drivers/media/usb/sn9c102/sn9c102_devtable.h deleted file mode 100644 index b3d2cc729657..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_devtable.h +++ /dev/null @@ -1,147 +0,0 @@ -/*************************************************************************** - * Table of device identifiers of the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 _SN9C102_DEVTABLE_H_ -#define _SN9C102_DEVTABLE_H_ - -#include <linux/usb.h> - -struct sn9c102_device; - -/* - Each SN9C1xx camera has proper PID/VID identifiers. - SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to - handle the video class interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, bridge) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = 0xff, \ - .driver_info = (bridge) - -static const struct usb_device_id sn9c102_id_table[] = { - /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ - { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ - { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ - /* SN9C103 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE -/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ -#endif - /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE - { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ - { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, - /* SN9C120 */ - { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ - { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ - { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, -#endif - { } -}; - -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor(). - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C1XX chip. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); -extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); -extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); -extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); - -#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131d.c b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c deleted file mode 100644 index 2dce5c908c8e..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_hv7131d.c +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x60, 0x17}, - {0x0e, 0x18}, {0xf2, 0x19}); - - err += sn9c102_i2c_write(cam, 0x01, 0x04); - err += sn9c102_i2c_write(cam, 0x02, 0x00); - err += sn9c102_i2c_write(cam, 0x28, 0x00); - - return err; -} - - -static int hv7131d_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x26), - r2 = sn9c102_i2c_read(cam, 0x27); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 8) | (r2 & 0xff); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_RESET_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - ctrl->value &= 0x3f; - return 0; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131d_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); - err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_RESET_LEVEL: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - err += sn9c102_i2c_write(cam, 0x34, ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x42, 0x19); - else - err += sn9c102_write_reg(cam, 0xf2, 0x19); - - return err; -} - - -static const struct sn9c102_sensor hv7131d = { - .name = "HV7131D", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131d_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x0250, - .maximum = 0xffff, - .step = 0x0001, - .default_value = 0x0250, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1e, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_RESET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "reset level", - .minimum = 0x19, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x30, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "pixel bias voltage", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x02, - .flags = 0, - }, - }, - .get_ctrl = &hv7131d_get_ctrl, - .set_ctrl = &hv7131d_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131d_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131d_set_pix_format -}; - - -int sn9c102_probe_hv7131d(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); - r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); - if (err || r0 < 0 || r1 < 0) - return -EIO; - - if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131d); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131r.c b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c deleted file mode 100644 index 4295887ff609..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_hv7131r.c +++ /dev/null @@ -1,363 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131r_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, - {0x20, 0x05}, {0x20, 0x06}, - {0x03, 0x10}, {0x00, 0x14}, - {0x60, 0x17}, {0x0a, 0x18}, - {0xf0, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x44, 0x05}, {0x3e, 0x06}, - {0x1a, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa3, 0x17}, - {0x4b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x20, 0x00); - err += sn9c102_i2c_write(cam, 0x21, 0xd6); - err += sn9c102_i2c_write(cam, 0x25, 0x06); - - return err; -} - - -static int hv7131r_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLACK_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x08) ? 1 : 0; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131r_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, ctrl->value); - break; - case V4L2_CID_BLACK_LEVEL: - { - int r = sn9c102_i2c_read(cam, 0x01); - if (r < 0) - return -EIO; - err += sn9c102_i2c_write(cam, 0x01, - (ctrl->value<<3) | (r&0xf7)); - } - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131r_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131r_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa0, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } else { - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa5, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x24); - } else { - err += sn9c102_write_reg(cam, 0xa3, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor hv7131r = { - .name = "HV7131R", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131r_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x2f, - .flags = 0, - }, - { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto black level compensation", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &hv7131r_get_ctrl, - .set_ctrl = &hv7131r_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131r_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131r_set_pix_format -}; - - -int sn9c102_probe_hv7131r(struct sn9c102_device* cam) -{ - int devid, err; - - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, - {0x34, 0x01}, {0x20, 0x17}, - {0x34, 0x01}, {0x46, 0x01}); - - devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); - if (err || devid < 0) - return -EIO; - - if (devid != 0x02) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131r); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0343.c b/drivers/media/usb/sn9c102/sn9c102_mi0343.c deleted file mode 100644 index 1f5b09bec89c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mi0343.c +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0343_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0343_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - break; - default: - return -EINVAL; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = data[1] | (data[0] << 8); - if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) - ctrl->value -= 0x10; - else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) - ctrl->value -= 0x60; - else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) - ctrl->value -= 0xe0; - } - - return 0; -} - - -static int mi0343_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u16 reg = 0; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (ctrl->value <= (0x3f-0x10)) - reg = 0x10 + ctrl->value; - else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) - reg = 0x60 + (ctrl->value - (0x3f-0x10)); - else - reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); - break; - } - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, reg >> 8, reg & 0xff, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, reg >> 8, reg & 0xff, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, reg >> 8, reg & 0xff, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0343_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0343_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x03, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - } - - return err; -} - - -static const struct sn9c102_sensor mi0343 = { - .name = "MI-0343", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0343_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &mi0343_get_ctrl, - .set_ctrl = &mi0343_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0343_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0343_set_pix_format -}; - - -int sn9c102_probe_mi0343(struct sn9c102_device* cam) -{ - u8 data[2]; - - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[1] != 0x42 || data[0] != 0xe3) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0343); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0360.c b/drivers/media/usb/sn9c102/sn9c102_mi0360.c deleted file mode 100644 index d973fc1973d9..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mi0360.c +++ /dev/null @@ -1,453 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0360_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x50, 0x05}, {0x20, 0x06}, - {0x10, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa2, 0x17}, - {0x47, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0360_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return 0; -} - - -static int mi0360_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, 0x03, ctrl->value, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, 0x03, ctrl->value, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0360_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0360_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0x60, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa6, 0x17); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x02, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mi0360 = { - .name = "MI-0360", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0360_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x0f, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x32, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - }, - .get_ctrl = &mi0360_get_ctrl, - .set_ctrl = &mi0360_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0360_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0360_set_pix_format -}; - - -int sn9c102_probe_mi0360(struct sn9c102_device* cam) -{ - - u8 data[2]; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - default: - break; - } - - if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x43) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0360); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mt9v111.c b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c deleted file mode 100644 index 95986eb492e4..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mt9v111.c +++ /dev/null @@ -1,260 +0,0 @@ -/*************************************************************************** - * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mt9v111_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x1f, 0x05}, {0x20, 0x06}, - {0x1f, 0x07}, {0x81, 0x08}, - {0x5c, 0x09}, {0x00, 0x0a}, - {0x00, 0x0b}, {0x00, 0x0c}, - {0x00, 0x0d}, {0x00, 0x0e}, - {0x00, 0x0f}, {0x03, 0x10}, - {0x00, 0x11}, {0x00, 0x12}, - {0x02, 0x13}, {0x14, 0x14}, - {0x28, 0x15}, {0x1e, 0x16}, - {0xe2, 0x17}, {0x06, 0x18}, - {0x00, 0x19}, {0x00, 0x1a}, - {0x00, 0x1b}, {0x08, 0x20}, - {0x39, 0x21}, {0x51, 0x22}, - {0x63, 0x23}, {0x73, 0x24}, - {0x82, 0x25}, {0x8f, 0x26}, - {0x9b, 0x27}, {0xa7, 0x28}, - {0xb1, 0x29}, {0xbc, 0x2a}, - {0xc6, 0x2b}, {0xcf, 0x2c}, - {0xd8, 0x2d}, {0xe1, 0x2e}, - {0xea, 0x2f}, {0xf2, 0x30}, - {0x13, 0x84}, {0x00, 0x85}, - {0x25, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xee, 0x8a}, {0x0f, 0x8b}, - {0xe5, 0x8c}, {0x0f, 0x8d}, - {0x2e, 0x8e}, {0x00, 0x8f}, - {0x30, 0x90}, {0x00, 0x91}, - {0xd4, 0x92}, {0x0f, 0x93}, - {0xfc, 0x94}, {0x0f, 0x95}, - {0x14, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x60, 0x99}, - {0x07, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x2d, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x04, 0x80, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x00, 0x08, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, - 0x00, 0x16, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe7, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x87, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x40, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x09, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, - 0x30, 0x02, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, - 0x00, 0xb0, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, - 0x00, 0x7c, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - - return err; -} - -static int mt9v111_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, - ctrl->value ? 0x80 : 0x00, - ctrl->value ? 0x80 : 0x00, 0, - 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - -static int mt9v111_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xb4, 0x17); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mt9v111 = { - .name = "MT9V111", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5c, - .init = &mt9v111_init, - .qctrl = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - }, - .get_ctrl = &mt9v111_get_ctrl, - .set_ctrl = &mt9v111_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mt9v111_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mt9v111_set_pix_format -}; - - -int sn9c102_probe_mt9v111(struct sn9c102_device *cam) -{ - u8 data[2]; - int err = 0; - - err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x42, 0x17}, - {0x62, 0x17}, {0x08, 0x01}); - err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, - mt9v111.i2c_slave_id, 0x01, 0x00, - 0x04, 0, 0); - if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, - mt9v111.i2c_slave_id, 0x36, 2, - data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x3a) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mt9v111); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7630.c b/drivers/media/usb/sn9c102/sn9c102_ov7630.c deleted file mode 100644 index 803712c29f02..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_ov7630.c +++ /dev/null @@ -1,626 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7630_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, - {0x0f, 0x18}, {0x50, 0x19}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x35); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x03, 0x10}, {0x0a, 0x14}, - {0x60, 0x17}, {0x0f, 0x18}, - {0x50, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x0a, 0x14}, {0xe2, 0x17}, - {0x0b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x24, 0x21}, - {0x3b, 0x22}, {0x47, 0x23}, - {0x60, 0x24}, {0x71, 0x25}, - {0x80, 0x26}, {0x8f, 0x27}, - {0x9d, 0x28}, {0xaa, 0x29}, - {0xb8, 0x2a}, {0xc4, 0x2b}, - {0xd1, 0x2c}, {0xdd, 0x2d}, - {0xe8, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x17, 0x84}, {0x00, 0x85}, - {0x2e, 0x86}, {0x00, 0x87}, - {0x09, 0x88}, {0x00, 0x89}, - {0xe8, 0x8a}, {0x0f, 0x8b}, - {0xda, 0x8c}, {0x0f, 0x8d}, - {0x40, 0x8e}, {0x00, 0x8f}, - {0x37, 0x90}, {0x00, 0x91}, - {0xcf, 0x92}, {0x0f, 0x93}, - {0xfa, 0x94}, {0x0f, 0x95}, - {0x00, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x00, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x00, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x12, 0x48); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x80); - err += sn9c102_i2c_write(cam, 0x04, 0x10); - err += sn9c102_i2c_write(cam, 0x05, 0x20); - err += sn9c102_i2c_write(cam, 0x06, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x0c, 0x20); - err += sn9c102_i2c_write(cam, 0x0d, 0x20); - err += sn9c102_i2c_write(cam, 0x15, 0x80); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1b); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x05); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x21, 0x1b); - err += sn9c102_i2c_write(cam, 0x22, 0x00); - err += sn9c102_i2c_write(cam, 0x23, 0xde); - err += sn9c102_i2c_write(cam, 0x24, 0x10); - err += sn9c102_i2c_write(cam, 0x25, 0x8a); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0xca); - err += sn9c102_i2c_write(cam, 0x28, 0xa2); - err += sn9c102_i2c_write(cam, 0x29, 0x74); - err += sn9c102_i2c_write(cam, 0x2a, 0x88); - err += sn9c102_i2c_write(cam, 0x2b, 0x34); - err += sn9c102_i2c_write(cam, 0x2c, 0x88); - err += sn9c102_i2c_write(cam, 0x2e, 0x00); - err += sn9c102_i2c_write(cam, 0x2f, 0x00); - err += sn9c102_i2c_write(cam, 0x30, 0x00); - err += sn9c102_i2c_write(cam, 0x32, 0xc2); - err += sn9c102_i2c_write(cam, 0x33, 0x08); - err += sn9c102_i2c_write(cam, 0x4c, 0x40); - err += sn9c102_i2c_write(cam, 0x4d, 0xf3); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x40); - err += sn9c102_i2c_write(cam, 0x62, 0x12); - err += sn9c102_i2c_write(cam, 0x63, 0x57); - err += sn9c102_i2c_write(cam, 0x64, 0x73); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x66, 0x55); - err += sn9c102_i2c_write(cam, 0x67, 0x01); - err += sn9c102_i2c_write(cam, 0x68, 0xac); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x1f); - err += sn9c102_i2c_write(cam, 0x70, 0x01); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x72, 0x10); - err += sn9c102_i2c_write(cam, 0x73, 0x50); - err += sn9c102_i2c_write(cam, 0x74, 0x20); - err += sn9c102_i2c_write(cam, 0x76, 0x01); - err += sn9c102_i2c_write(cam, 0x77, 0xf3); - err += sn9c102_i2c_write(cam, 0x78, 0x90); - err += sn9c102_i2c_write(cam, 0x79, 0x98); - err += sn9c102_i2c_write(cam, 0x7a, 0x98); - err += sn9c102_i2c_write(cam, 0x7b, 0x00); - err += sn9c102_i2c_write(cam, 0x7c, 0x38); - err += sn9c102_i2c_write(cam, 0x7d, 0xff); - break; - default: - break; - } - - return err; -} - - -static int ov7630_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x05); - else - ctrl->value = sn9c102_pread_reg(cam, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x07); - else - ctrl->value = sn9c102_pread_reg(cam, 0x05); - break; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_WHITENESS: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - case V4L2_CID_VFLIP: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x80) ? 1 : 0; - break; - case SN9C102_V4L2_CID_GAMMA: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_WHITENESS: - err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value | - (ctrl->value << 1)); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); - break; - case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7630_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) - err += sn9c102_write_reg(cam, 0x50, 0x19); - else - err += sn9c102_write_reg(cam, 0x20, 0x19); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xe5, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x04); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x02); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor ov7630 = { - .name = "OV7630", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | - BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7630_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x60, - .flags = 0, - }, - { - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: blue", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "rgb gamma", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7630_get_ctrl, - .set_ctrl = &ov7630_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7630_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SN9C10X, - .priv = 8, - }, - .set_pix_format = &ov7630_set_pix_format -}; - - -int sn9c102_probe_ov7630(struct sn9c102_device* cam) -{ - int pid, ver, err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, - {0x28, 0x17}, {0x44, 0x02}); - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - if (err || pid < 0) /* try a different initialization */ - err += sn9c102_write_const_regs(cam, {0x01, 0x01}, - {0x00, 0x01}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x74, 0x02}, - {0x0e, 0x01}, {0x44, 0x01}); - break; - default: - break; - } - - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x31) - return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7660.c b/drivers/media/usb/sn9c102/sn9c102_ov7660.c deleted file mode 100644 index 7977795d342b..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_ov7660.c +++ /dev/null @@ -1,538 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7660_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x08, 0x14}, {0x20, 0x17}, - {0x8b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xec, 0x8a}, {0x0f, 0x8b}, - {0xd8, 0x8c}, {0x0f, 0x8d}, - {0x3d, 0x8e}, {0x00, 0x8f}, - {0x3d, 0x90}, {0x00, 0x91}, - {0xcd, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0c, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9a}, {0x00, 0x9b}, - {0x04, 0x9c}, {0x00, 0x9d}, - {0x08, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3C, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x00, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x09); - err += sn9c102_i2c_write(cam, 0x00, 0x0A); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x00); - err += sn9c102_i2c_write(cam, 0x04, 0x00); - err += sn9c102_i2c_write(cam, 0x05, 0x08); - err += sn9c102_i2c_write(cam, 0x06, 0x0B); - err += sn9c102_i2c_write(cam, 0x07, 0x00); - err += sn9c102_i2c_write(cam, 0x08, 0x1C); - err += sn9c102_i2c_write(cam, 0x09, 0x01); - err += sn9c102_i2c_write(cam, 0x0A, 0x76); - err += sn9c102_i2c_write(cam, 0x0B, 0x60); - err += sn9c102_i2c_write(cam, 0x0C, 0x00); - err += sn9c102_i2c_write(cam, 0x0D, 0x08); - err += sn9c102_i2c_write(cam, 0x0E, 0x04); - err += sn9c102_i2c_write(cam, 0x0F, 0x6F); - err += sn9c102_i2c_write(cam, 0x10, 0x20); - err += sn9c102_i2c_write(cam, 0x11, 0x03); - err += sn9c102_i2c_write(cam, 0x12, 0x05); - err += sn9c102_i2c_write(cam, 0x13, 0xC7); - err += sn9c102_i2c_write(cam, 0x14, 0x2C); - err += sn9c102_i2c_write(cam, 0x15, 0x00); - err += sn9c102_i2c_write(cam, 0x16, 0x02); - err += sn9c102_i2c_write(cam, 0x17, 0x10); - err += sn9c102_i2c_write(cam, 0x18, 0x60); - err += sn9c102_i2c_write(cam, 0x19, 0x02); - err += sn9c102_i2c_write(cam, 0x1A, 0x7B); - err += sn9c102_i2c_write(cam, 0x1B, 0x02); - err += sn9c102_i2c_write(cam, 0x1C, 0x7F); - err += sn9c102_i2c_write(cam, 0x1D, 0xA2); - err += sn9c102_i2c_write(cam, 0x1E, 0x01); - err += sn9c102_i2c_write(cam, 0x1F, 0x0E); - err += sn9c102_i2c_write(cam, 0x20, 0x05); - err += sn9c102_i2c_write(cam, 0x21, 0x05); - err += sn9c102_i2c_write(cam, 0x22, 0x05); - err += sn9c102_i2c_write(cam, 0x23, 0x05); - err += sn9c102_i2c_write(cam, 0x24, 0x68); - err += sn9c102_i2c_write(cam, 0x25, 0x58); - err += sn9c102_i2c_write(cam, 0x26, 0xD4); - err += sn9c102_i2c_write(cam, 0x27, 0x80); - err += sn9c102_i2c_write(cam, 0x28, 0x80); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2A, 0x00); - err += sn9c102_i2c_write(cam, 0x2B, 0x00); - err += sn9c102_i2c_write(cam, 0x2C, 0x80); - err += sn9c102_i2c_write(cam, 0x2D, 0x00); - err += sn9c102_i2c_write(cam, 0x2E, 0x00); - err += sn9c102_i2c_write(cam, 0x2F, 0x0E); - err += sn9c102_i2c_write(cam, 0x30, 0x08); - err += sn9c102_i2c_write(cam, 0x31, 0x30); - err += sn9c102_i2c_write(cam, 0x32, 0xB4); - err += sn9c102_i2c_write(cam, 0x33, 0x00); - err += sn9c102_i2c_write(cam, 0x34, 0x07); - err += sn9c102_i2c_write(cam, 0x35, 0x84); - err += sn9c102_i2c_write(cam, 0x36, 0x00); - err += sn9c102_i2c_write(cam, 0x37, 0x0C); - err += sn9c102_i2c_write(cam, 0x38, 0x02); - err += sn9c102_i2c_write(cam, 0x39, 0x43); - err += sn9c102_i2c_write(cam, 0x3A, 0x00); - err += sn9c102_i2c_write(cam, 0x3B, 0x0A); - err += sn9c102_i2c_write(cam, 0x3C, 0x6C); - err += sn9c102_i2c_write(cam, 0x3D, 0x99); - err += sn9c102_i2c_write(cam, 0x3E, 0x0E); - err += sn9c102_i2c_write(cam, 0x3F, 0x41); - err += sn9c102_i2c_write(cam, 0x40, 0xC1); - err += sn9c102_i2c_write(cam, 0x41, 0x22); - err += sn9c102_i2c_write(cam, 0x42, 0x08); - err += sn9c102_i2c_write(cam, 0x43, 0xF0); - err += sn9c102_i2c_write(cam, 0x44, 0x10); - err += sn9c102_i2c_write(cam, 0x45, 0x78); - err += sn9c102_i2c_write(cam, 0x46, 0xA8); - err += sn9c102_i2c_write(cam, 0x47, 0x60); - err += sn9c102_i2c_write(cam, 0x48, 0x80); - err += sn9c102_i2c_write(cam, 0x49, 0x00); - err += sn9c102_i2c_write(cam, 0x4A, 0x00); - err += sn9c102_i2c_write(cam, 0x4B, 0x00); - err += sn9c102_i2c_write(cam, 0x4C, 0x00); - err += sn9c102_i2c_write(cam, 0x4D, 0x00); - err += sn9c102_i2c_write(cam, 0x4E, 0x00); - err += sn9c102_i2c_write(cam, 0x4F, 0x46); - err += sn9c102_i2c_write(cam, 0x50, 0x36); - err += sn9c102_i2c_write(cam, 0x51, 0x0F); - err += sn9c102_i2c_write(cam, 0x52, 0x17); - err += sn9c102_i2c_write(cam, 0x53, 0x7F); - err += sn9c102_i2c_write(cam, 0x54, 0x96); - err += sn9c102_i2c_write(cam, 0x55, 0x40); - err += sn9c102_i2c_write(cam, 0x56, 0x40); - err += sn9c102_i2c_write(cam, 0x57, 0x40); - err += sn9c102_i2c_write(cam, 0x58, 0x0F); - err += sn9c102_i2c_write(cam, 0x59, 0xBA); - err += sn9c102_i2c_write(cam, 0x5A, 0x9A); - err += sn9c102_i2c_write(cam, 0x5B, 0x22); - err += sn9c102_i2c_write(cam, 0x5C, 0xB9); - err += sn9c102_i2c_write(cam, 0x5D, 0x9B); - err += sn9c102_i2c_write(cam, 0x5E, 0x10); - err += sn9c102_i2c_write(cam, 0x5F, 0xF0); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x60); - err += sn9c102_i2c_write(cam, 0x62, 0x00); - err += sn9c102_i2c_write(cam, 0x63, 0x00); - err += sn9c102_i2c_write(cam, 0x64, 0x50); - err += sn9c102_i2c_write(cam, 0x65, 0x30); - err += sn9c102_i2c_write(cam, 0x66, 0x00); - err += sn9c102_i2c_write(cam, 0x67, 0x80); - err += sn9c102_i2c_write(cam, 0x68, 0x7A); - err += sn9c102_i2c_write(cam, 0x69, 0x90); - err += sn9c102_i2c_write(cam, 0x6A, 0x80); - err += sn9c102_i2c_write(cam, 0x6B, 0x0A); - err += sn9c102_i2c_write(cam, 0x6C, 0x30); - err += sn9c102_i2c_write(cam, 0x6D, 0x48); - err += sn9c102_i2c_write(cam, 0x6E, 0x80); - err += sn9c102_i2c_write(cam, 0x6F, 0x74); - err += sn9c102_i2c_write(cam, 0x70, 0x64); - err += sn9c102_i2c_write(cam, 0x71, 0x60); - err += sn9c102_i2c_write(cam, 0x72, 0x5C); - err += sn9c102_i2c_write(cam, 0x73, 0x58); - err += sn9c102_i2c_write(cam, 0x74, 0x54); - err += sn9c102_i2c_write(cam, 0x75, 0x4C); - err += sn9c102_i2c_write(cam, 0x76, 0x40); - err += sn9c102_i2c_write(cam, 0x77, 0x38); - err += sn9c102_i2c_write(cam, 0x78, 0x34); - err += sn9c102_i2c_write(cam, 0x79, 0x30); - err += sn9c102_i2c_write(cam, 0x7A, 0x2F); - err += sn9c102_i2c_write(cam, 0x7B, 0x2B); - err += sn9c102_i2c_write(cam, 0x7C, 0x03); - err += sn9c102_i2c_write(cam, 0x7D, 0x07); - err += sn9c102_i2c_write(cam, 0x7E, 0x17); - err += sn9c102_i2c_write(cam, 0x7F, 0x34); - err += sn9c102_i2c_write(cam, 0x80, 0x41); - err += sn9c102_i2c_write(cam, 0x81, 0x4D); - err += sn9c102_i2c_write(cam, 0x82, 0x58); - err += sn9c102_i2c_write(cam, 0x83, 0x63); - err += sn9c102_i2c_write(cam, 0x84, 0x6E); - err += sn9c102_i2c_write(cam, 0x85, 0x77); - err += sn9c102_i2c_write(cam, 0x86, 0x87); - err += sn9c102_i2c_write(cam, 0x87, 0x95); - err += sn9c102_i2c_write(cam, 0x88, 0xAF); - err += sn9c102_i2c_write(cam, 0x89, 0xC7); - err += sn9c102_i2c_write(cam, 0x8A, 0xDF); - err += sn9c102_i2c_write(cam, 0x8B, 0x99); - err += sn9c102_i2c_write(cam, 0x8C, 0x99); - err += sn9c102_i2c_write(cam, 0x8D, 0xCF); - err += sn9c102_i2c_write(cam, 0x8E, 0x20); - err += sn9c102_i2c_write(cam, 0x8F, 0x26); - err += sn9c102_i2c_write(cam, 0x90, 0x10); - err += sn9c102_i2c_write(cam, 0x91, 0x0C); - err += sn9c102_i2c_write(cam, 0x92, 0x25); - err += sn9c102_i2c_write(cam, 0x93, 0x00); - err += sn9c102_i2c_write(cam, 0x94, 0x50); - err += sn9c102_i2c_write(cam, 0x95, 0x50); - err += sn9c102_i2c_write(cam, 0x96, 0x00); - err += sn9c102_i2c_write(cam, 0x97, 0x01); - err += sn9c102_i2c_write(cam, 0x98, 0x10); - err += sn9c102_i2c_write(cam, 0x99, 0x40); - err += sn9c102_i2c_write(cam, 0x9A, 0x40); - err += sn9c102_i2c_write(cam, 0x9B, 0x20); - err += sn9c102_i2c_write(cam, 0x9C, 0x00); - err += sn9c102_i2c_write(cam, 0x9D, 0x99); - err += sn9c102_i2c_write(cam, 0x9E, 0x7F); - err += sn9c102_i2c_write(cam, 0x9F, 0x00); - err += sn9c102_i2c_write(cam, 0xA0, 0x00); - err += sn9c102_i2c_write(cam, 0xA1, 0x00); - - return err; -} - - -static int ov7660_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x04) ? 1 : 0; - break; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0) - return -EIO; - ctrl->value &= 0x08; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x1f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, 0xc0 | - (ctrl->value * 0x07)); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7660_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int r0, err = 0; - - r0 = sn9c102_pread_reg(cam, 0x01); - - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - } else { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x0d); - } - - return err; -} - - -static const struct sn9c102_sensor ov7660 = { - .name = "OV7660", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7660_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x09, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x27, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "night mode", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7660_get_ctrl, - .set_ctrl = &ov7660_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7660_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - }, - .set_pix_format = &ov7660_set_pix_format -}; - - -int sn9c102_probe_ov7660(struct sn9c102_device* cam) -{ - int pid, ver, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x60) - return -ENODEV; - - sn9c102_attach_sensor(cam, &ov7660); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas106b.c b/drivers/media/usb/sn9c102/sn9c102_pas106b.c deleted file mode 100644 index 81cd969c1b7b..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_pas106b.c +++ /dev/null @@ -1,302 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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/delay.h> -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas106b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x20, 0x19}, {0x09, 0x18}); - - err += sn9c102_i2c_write(cam, 0x02, 0x0c); - err += sn9c102_i2c_write(cam, 0x05, 0x5a); - err += sn9c102_i2c_write(cam, 0x06, 0x88); - err += sn9c102_i2c_write(cam, 0x07, 0x80); - err += sn9c102_i2c_write(cam, 0x10, 0x06); - err += sn9c102_i2c_write(cam, 0x11, 0x06); - err += sn9c102_i2c_write(cam, 0x12, 0x00); - err += sn9c102_i2c_write(cam, 0x14, 0x02); - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - msleep(400); - - return err; -} - - -static int pas106b_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x03), - r2 = sn9c102_i2c_read(cam, 0x04); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 4) | (r2 & 0x0f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_CONTRAST: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x1f) << 1; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0xf8; - return 0; - default: - return -EINVAL; - } -} - - -static int pas106b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); - err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); - break; - case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); - err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - return err ? -EIO : 0; -} - - -static int pas106b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int pas106b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2c, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static const struct sn9c102_sensor pas106b = { - .name = "PAS106B", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas106b_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x125, - .maximum = 0xfff, - .step = 0x001, - .default_value = 0x140, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x00, /* 0x00~0x03 have same effect */ - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3e, - .step = 0x02, - .default_value = 0x02, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - }, - .get_ctrl = &pas106b_get_ctrl, - .set_ctrl = &pas106b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &pas106b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, /* we use this field as 'bits per pixel' */ - }, - .set_pix_format = &pas106b_set_pix_format -}; - - -int sn9c102_probe_pas106b(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0; - unsigned int pid = 0; - - /* - Minimal initialization to enable the I2C communication - NOTE: do NOT change the values! - */ - if (sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* sensor power down */ - {0x00, 0x01}, /* sensor power on */ - {0x28, 0x17})) /* sensor clock at 24 MHz */ - return -EIO; - - r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); - if (r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 11) | ((r1 & 0xf0) >> 4); - if (pid != 0x007) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas106b); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c deleted file mode 100644 index 2e86fdc86989..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c +++ /dev/null @@ -1,335 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * - * <medaglia@undl.org.br> * - * * - * Support for SN9C103, DAC Magnitude, exposure and green gain controls * - * added by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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/delay.h> -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas202bcb_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x10, 0x21}, - {0x20, 0x22}, {0x30, 0x23}, - {0x40, 0x24}, {0x50, 0x25}, - {0x60, 0x26}, {0x70, 0x27}, - {0x80, 0x28}, {0x90, 0x29}, - {0xa0, 0x2a}, {0xb0, 0x2b}, - {0xc0, 0x2c}, {0xd0, 0x2d}, - {0xe0, 0x2e}, {0xf0, 0x2f}, - {0xff, 0x30}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bcb_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x04), - r2 = sn9c102_i2c_read(cam, 0x05); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x28, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bcb_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bcb_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static const struct sn9c102_sensor pas202bcb = { - .name = "PAS202BCB", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0b, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bcb_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bcb_set_pix_format -}; - - -int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - /* - * Minimal initialization to enable the I2C communication - * NOTE: do NOT change the values! - */ - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* power down */ - {0x40, 0x01}, /* power on */ - {0x28, 0x17});/* clock 24 MHz */ - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, - {0x44, 0x02}, {0x29, 0x17}); - break; - default: - break; - } - - r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - - if (err || r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_sensor.h b/drivers/media/usb/sn9c102/sn9c102_sensor.h deleted file mode 100644 index 3679970dba2c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_sensor.h +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 _SN9C102_SENSOR_H_ -#define _SN9C102_SENSOR_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <linux/device.h> -#include <linux/stddef.h> -#include <linux/errno.h> -#include <asm/types.h> - -struct sn9c102_device; -struct sn9c102_sensor; - -/*****************************************************************************/ - -/* - OVERVIEW. - This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C1XX bridges. The entire API is documented - below. In the most general case, to support a sensor there are three steps - you have to follow: - 1) define the main "sn9c102_sensor" structure by setting the basic fields; - 2) write a probing function to be called by the core module when the USB - camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables in sn9c102_devtable.h; - 3) implement the methods that you want/need (and fill the rest of the main - structure accordingly). - "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do - NOT need to touch the source code of the core module for the things to work - properly, unless you find bugs or flaws in it. Finally, do not forget to - read the V4L2 API for completeness. -*/ - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, - BRIDGE_SN9C105 = 0x08, - BRIDGE_SN9C120 = 0x10, -}; - -/* Return the bridge name */ -enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); - -/* Return a pointer the sensor struct attached to the camera */ -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); - -/* Identify a device */ -extern struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); - -/* Attach a probed sensor to the camera. */ -extern void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor); - -/* - Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C1XX - chip for some of its registers. To work around this problem, a pseudo-read - call is provided instead: it returns the last successfully written value - on the register (0 if it has never been written), the usual -1 on error. -*/ - -/* The "try" I2C I/O versions are used when probing the sensor */ -extern int sn9c102_i2c_try_read(struct sn9c102_device*, - const struct sn9c102_sensor*, u8 address); - -/* - These must be used if and only if the sensor doesn't implement the standard - I2C protocol. There are a number of good reasons why you must use the - single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX - chip. The second one programs the registers 0x09 and 0x10 with data0 and - data1, and places the n bytes read from the sensor register table in the - buffer pointed by 'buffer'. Both the functions return -1 on error; the write - version returns 0 on success, while the read version returns the first read - byte. -*/ -extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, - u8 data0, u8 data1, u8 data2, u8 data3, - u8 data4, u8 data5); -extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 data0, u8 data1, u8 n, u8 buffer[]); - -/* To be used after the sensor struct has been attached to the camera struct */ -extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); -extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); - -/* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); -extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], - int count); -/* - Write multiple registers with constant values. For example: - sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); - Register addresses must be < 256. -*/ -#define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ static const u8 _valreg[][2] = {data}; \ - sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) - -/*****************************************************************************/ - -enum sn9c102_i2c_sysfs_ops { - SN9C102_I2C_READ = 0x01, - SN9C102_I2C_WRITE = 0x02, -}; - -enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ - SN9C102_I2C_100KHZ = 0x01, - SN9C102_I2C_400KHZ = 0x02, -}; - -enum sn9c102_i2c_interface { - SN9C102_I2C_2WIRES, - SN9C102_I2C_3WIRES, -}; - -#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct sn9c102_sensor { - char name[32], /* sensor name */ - maintainer[64]; /* name of the maintainer <email> */ - - enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ - - /* Supported operations through the 'sysfs' interface */ - enum sn9c102_i2c_sysfs_ops sysfs_ops; - - /* - These sensor capabilities must be provided if the SN9C1XX controller - needs to communicate through the sensor serial interface by using - at least one of the i2c functions available. - */ - enum sn9c102_i2c_frequency frequency; - enum sn9c102_i2c_interface interface; - - /* - This identifier must be provided if the image sensor implements - the standard I2C protocol. - */ - u8 i2c_slave_id; /* reg. 0x09 */ - - /* - NOTE: Where not noted,most of the functions below are not mandatory. - Set to null if you do not implement them. If implemented, - they must return 0 on success, the proper error otherwise. - */ - - int (*init)(struct sn9c102_device* cam); - /* - This function will be called after the sensor has been attached. - It should be used to initialize the sensor only, but may also - configure part of the SN9C1XX chip if necessary. You don't need to - setup picture settings like brightness, contrast, etc.. here, if - the corresponding controls are implemented (see below), since - they are adjusted in the core driver by calling the set_ctrl() - method after init(), where the arguments are the default values - specified in the v4l2_queryctrl list of supported controls; - Same suggestions apply for other settings, _if_ the corresponding - methods are present; if not, the initialization must configure the - sensor according to the default configuration structures below. - */ - - struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; - /* - Optional list of default controls, defined as indicated in the - V4L2 API. Menu type controls are not handled by this interface. - */ - - int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); - int (*set_ctrl)(struct sn9c102_device* cam, - const struct v4l2_control* ctrl); - /* - You must implement at least the set_ctrl method if you have defined - the list above. The returned value must follow the V4L2 - specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER - are not supported by this driver, so do not implement them. Also, - you don't have to check whether the passed values are out of bounds, - given that this is done by the core module. - */ - - struct v4l2_cropcap cropcap; - /* - Think the image sensor as a grid of R,G,B monochromatic pixels - disposed according to a particular Bayer pattern, which describes - the complete array of pixels, from (0,0) to (xmax, ymax). We will - use this coordinate system from now on. It is assumed the sensor - chip can be programmed to capture/transmit a subsection of that - array of pixels: we will call this subsection "active window". - It is not always true that the largest achievable active window can - cover the whole array of pixels. The V4L2 API defines another - area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C1XX chip is always programmed to read the - source rectangle. - The bounds of both the active window and the source rectangle are - specified in the cropcap substructures 'bounds' and 'defrect'. - By default, the source rectangle should cover the largest possible - area. Again, it is not always true that the largest source rectangle - can cover the entire active window, although it is a rare case for - the hardware we have. The bounds of the source rectangle _must_ be - multiple of 16 and must use the same coordinate system as indicated - before; their centers shall align initially. - If necessary, the sensor chip must be initialized during init() to - set the bounds of the active sensor window; however, by default, it - usually covers the largest achievable area (maxwidth x maxheight) - of pixels, so no particular initialization is needed, if you have - defined the correct default bounds in the structures. - See the V4L2 API for further details. - NOTE: once you have defined the bounds of the active window - (struct cropcap.bounds) you must not change them.anymore. - Only 'bounds' and 'defrect' fields are mandatory, other fields - will be ignored. - */ - - int (*set_crop)(struct sn9c102_device* cam, - const struct v4l2_rect* rect); - /* - To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C1XX regs (also - scaling), but you may need to override/adjust specific stuff. - 'rect' contains width and height values that are multiple of 16: in - case you override the default function, you always have to program - the chip to match those values; on error return the corresponding - error code without rolling back. - NOTE: in case, you must program the SN9C1XX chip to get rid of - blank pixels or blank lines at the _start_ of each line or - frame after each HSYNC or VSYNC, so that the image starts with - real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, - V_SIZE you don't have to care about blank pixels or blank - lines at the end of each line or frame). - */ - - struct v4l2_pix_format pix_format; - /* - What you have to define here are: 1) initial 'width' and 'height' of - the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) - or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate - the number of bits per pixel for uncompressed video, 8 or 9 (despite - the current value of 'pixelformat'). - NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 - of cropcap.defrect.width and cropcap.defrect.height. I - suggest 1/1. - NOTE 2: The initial compression quality is defined by the first bit - of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C1XX chip to get - rid of any blank pixels, so that the output of the sensor - matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). - */ - - int (*set_pix_format)(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix); - /* - To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to - SN9C10X pixel format or viceversa. On error return the corresponding - error code without rolling back. - */ - - /* - Do NOT write to the data below, it's READ ONLY. It is used by the - core module to store successfully updated values of the above - settings, for rollbacks..etc..in case of errors during atomic I/O - */ - struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -/*****************************************************************************/ - -/* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) -#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) -#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) -#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) -#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) - -#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c deleted file mode 100644 index 04cdfdde8564..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110c1b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, - {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0xc0, 0x80); - - return err; -} - - -static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5110c1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Don't change ! */ - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110c1b = { - .name = "TAS5110C1B", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5110c1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - }, - .set_ctrl = &tas5110c1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110c1b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110c1b_set_pix_format -}; - - -int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110c1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6001), }, - { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x60ab), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5110c1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110c1b); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110d.c b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c deleted file mode 100644 index 9372e6f9fcff..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5110d.c +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0x9a, 0xca); - - return err; -} - - -static int tas5110d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - - return err; -} - - -static int tas5110d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x3b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110d = { - .name = "TAS5110D", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x61, - .init = &tas5110d_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110d_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110d_set_pix_format -}; - - -int sn9c102_probe_tas5110d(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110d_id_table[] = { - { USB_DEVICE(0x0c45, 0x6007), }, - { } - }; - - if (!sn9c102_match_id(cam, tas5110d_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110d); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c deleted file mode 100644 index a30bbc4389f5..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5130d1b_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, - {0x04, 0x01}, {0x01, 0x10}, - {0x00, 0x11}, {0x00, 0x14}, - {0x60, 0x17}, {0x07, 0x18}); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5130d1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Do NOT change! */ - err += sn9c102_write_reg(cam, 0x1f, 0x1a); - err += sn9c102_write_reg(cam, 0x1a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x63, 0x19); - else - err += sn9c102_write_reg(cam, 0xf3, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5130d1b = { - .name = "TAS5130D1B", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &tas5130d1b_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5130d1b_set_pix_format -}; - - -int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6024), }, - { USB_DEVICE(0x0c45, 0x6025), }, - { USB_DEVICE(0x0c45, 0x60aa), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5130d1b); - - return 0; -} diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 8c05565a240e..2189bfb2e828 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -83,14 +83,3 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS - -config VIDEO_V4L2_INT_DEVICE - tristate "V4L2 int device (DEPRECATED)" - depends on VIDEO_V4L2 - ---help--- - An early framework for a hardware-independent interface for - image sensors and bridges etc. Currently used by omap24xxcam and - tcm825x drivers that should be converted to V4L2 subdev. - - Do not use for new developments. - diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 1a85eee581f8..c6ae7bad951e 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -15,7 +15,6 @@ ifeq ($(CONFIG_OF),y) endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o -obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fb46790d0eca..6ff002bd5909 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -745,6 +745,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: return "VPX Deblocking Effect Control"; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: return "VPX Golden Frame Refresh Period"; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return "VPX Golden Frame Indicator"; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: return "VPX Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: return "VPX Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index b5aaaac427ad..0a30dbf3d05c 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -872,8 +872,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); - video_device[vdev->minor] = vdev; vdev->index = get_index(vdev); + video_device[vdev->minor] = vdev; mutex_unlock(&videodev_lock); if (vdev->ioctl_ops) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index ee52b9f4a944..f7902fe8a526 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -515,6 +515,7 @@ bool v4l2_detect_gtf(unsigned frame_height, aspect.denominator = 9; } image_width = ((image_height * aspect.numerator) / aspect.denominator); + image_width = (image_width + GTF_CELL_GRAN/2) & ~(GTF_CELL_GRAN - 1); /* Horizontal */ if (default_gtf) diff --git a/drivers/media/v4l2-core/v4l2-int-device.c b/drivers/media/v4l2-core/v4l2-int-device.c deleted file mode 100644 index f4473494af7a..000000000000 --- a/drivers/media/v4l2-core/v4l2-int-device.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * drivers/media/video/v4l2-int-device.c - * - * V4L2 internal ioctl interface. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/sort.h> -#include <linux/string.h> -#include <linux/module.h> - -#include <media/v4l2-int-device.h> - -static DEFINE_MUTEX(mutex); -static LIST_HEAD(int_list); - -void v4l2_int_device_try_attach_all(void) -{ - struct v4l2_int_device *m, *s; - - list_for_each_entry(m, &int_list, head) { - if (m->type != v4l2_int_type_master) - continue; - - list_for_each_entry(s, &int_list, head) { - if (s->type != v4l2_int_type_slave) - continue; - - /* Slave is connected? */ - if (s->u.slave->master) - continue; - - /* Slave wants to attach to master? */ - if (s->u.slave->attach_to[0] != 0 - && strncmp(m->name, s->u.slave->attach_to, - V4L2NAMESIZE)) - continue; - - if (!try_module_get(m->module)) - continue; - - s->u.slave->master = m; - if (m->u.master->attach(s)) { - s->u.slave->master = NULL; - module_put(m->module); - continue; - } - } - } -} -EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all); - -static int ioctl_sort_cmp(const void *a, const void *b) -{ - const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b; - - if (d1->num > d2->num) - return 1; - - if (d1->num < d2->num) - return -1; - - return 0; -} - -int v4l2_int_device_register(struct v4l2_int_device *d) -{ - if (d->type == v4l2_int_type_slave) - sort(d->u.slave->ioctls, d->u.slave->num_ioctls, - sizeof(struct v4l2_int_ioctl_desc), - &ioctl_sort_cmp, NULL); - mutex_lock(&mutex); - list_add(&d->head, &int_list); - v4l2_int_device_try_attach_all(); - mutex_unlock(&mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_int_device_register); - -void v4l2_int_device_unregister(struct v4l2_int_device *d) -{ - mutex_lock(&mutex); - list_del(&d->head); - if (d->type == v4l2_int_type_slave - && d->u.slave->master != NULL) { - d->u.slave->master->u.master->detach(d); - module_put(d->u.slave->master->module); - d->u.slave->master = NULL; - } - mutex_unlock(&mutex); -} -EXPORT_SYMBOL_GPL(v4l2_int_device_unregister); - -/* Adapted from search_extable in extable.c. */ -static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd, - v4l2_int_ioctl_func *no_such_ioctl) -{ - const struct v4l2_int_ioctl_desc *first = slave->ioctls; - const struct v4l2_int_ioctl_desc *last = - first + slave->num_ioctls - 1; - - while (first <= last) { - const struct v4l2_int_ioctl_desc *mid; - - mid = (last - first) / 2 + first; - - if (mid->num < cmd) - first = mid + 1; - else if (mid->num > cmd) - last = mid - 1; - else - return mid->func; - } - - return no_such_ioctl; -} - -static int no_such_ioctl_0(struct v4l2_int_device *d) -{ - return -ENOIOCTLCMD; -} - -int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd) -{ - return ((v4l2_int_ioctl_func_0 *) - find_ioctl(d->u.slave, cmd, - (v4l2_int_ioctl_func *)no_such_ioctl_0))(d); -} -EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0); - -static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg) -{ - return -ENOIOCTLCMD; -} - -int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg) -{ - return ((v4l2_int_ioctl_func_1 *) - find_ioctl(d->u.slave, cmd, - (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg); -} -EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1); - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 68e6b5e912ff..707aef705a47 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -28,6 +28,9 @@ #include <media/v4l2-device.h> #include <media/videobuf2-core.h> +#define CREATE_TRACE_POINTS +#include <trace/events/v4l2.h> + /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ #define CLEAR_AFTER_FIELD(p, field) \ @@ -2338,6 +2341,12 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) err = -ENOTTY; + if (err == 0) { + if (cmd == VIDIOC_DQBUF) + trace_v4l2_dqbuf(video_devdata(file)->minor, parg); + else if (cmd == VIDIOC_QBUF) + trace_v4l2_qbuf(video_devdata(file)->minor, parg); + } if (has_array_args) { *kernel_ptr = user_ptr; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 73035ee0f4de..178ce96556c6 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -558,6 +558,8 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, if (m2m_ctx->m2m_dev->m2m_ops->unlock) m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv); + else if (m2m_ctx->q_lock) + mutex_unlock(m2m_ctx->q_lock); if (list_empty(&src_q->done_list)) poll_wait(file, &src_q->done_wq, wait); @@ -566,6 +568,8 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, if (m2m_ctx->m2m_dev->m2m_ops->lock) m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); + else if (m2m_ctx->q_lock) + mutex_lock(m2m_ctx->q_lock); spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) @@ -693,6 +697,13 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, if (ret) goto err; + /* + * If both queues use same mutex assign it as the common buffer + * queues lock to the m2m context. This lock is used in the + * v4l2_m2m_ioctl_* helpers. + */ + if (out_q_ctx->q.lock == cap_q_ctx->q.lock) + m2m_ctx->q_lock = out_q_ctx->q.lock; return m2m_ctx; err: @@ -740,3 +751,118 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb) } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); +/* Videobuf2 ioctl helpers */ + +int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_reqbufs(file, fh->m2m_ctx, rb); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_reqbufs); + +int v4l2_m2m_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_create_bufs(file, fh->m2m_ctx, create); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_create_bufs); + +int v4l2_m2m_ioctl_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_querybuf(file, fh->m2m_ctx, buf); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_querybuf); + +int v4l2_m2m_ioctl_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_qbuf); + +int v4l2_m2m_ioctl_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_dqbuf); + +int v4l2_m2m_ioctl_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_expbuf(file, fh->m2m_ctx, eb); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_expbuf); + +int v4l2_m2m_ioctl_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_streamon(file, fh->m2m_ctx, type); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamon); + +int v4l2_m2m_ioctl_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_streamoff(file, fh->m2m_ctx, type); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff); + +/* + * v4l2_file_operations helpers. It is assumed here same lock is used + * for the output and the capture buffer queue. + */ + +int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct v4l2_fh *fh = file->private_data; + struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx; + int ret; + + if (m2m_ctx->q_lock && mutex_lock_interruptible(m2m_ctx->q_lock)) + return -ERESTARTSYS; + + ret = v4l2_m2m_mmap(file, m2m_ctx, vma); + + if (m2m_ctx->q_lock) + mutex_unlock(m2m_ctx->q_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_fop_mmap); + +unsigned int v4l2_m2m_fop_poll(struct file *file, poll_table *wait) +{ + struct v4l2_fh *fh = file->private_data; + struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx; + unsigned int ret; + + if (m2m_ctx->q_lock) + mutex_lock(m2m_ctx->q_lock); + + ret = v4l2_m2m_poll(file, m2m_ctx, wait); + + if (m2m_ctx->q_lock) + mutex_unlock(m2m_ctx->q_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_fop_poll); + diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index a6478dca0cde..42e3e8a5e361 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -121,9 +121,11 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, * 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. + * + * Return: 0. */ -void v4l2_of_parse_endpoint(const struct device_node *node, - struct v4l2_of_endpoint *endpoint) +int v4l2_of_parse_endpoint(const struct device_node *node, + struct v4l2_of_endpoint *endpoint) { struct device_node *port_node = of_get_parent(node); @@ -146,6 +148,8 @@ void v4l2_of_parse_endpoint(const struct device_node *node, v4l2_of_parse_parallel_bus(node, endpoint); of_node_put(port_node); + + return 0; } EXPORT_SYMBOL(v4l2_of_parse_endpoint); @@ -262,6 +266,6 @@ struct device_node *v4l2_of_get_remote_port(const struct device_node *node) np = of_parse_phandle(node, "remote-endpoint", 0); if (!np) return NULL; - return of_get_parent(np); + return of_get_next_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 65411adcd0ea..7e6b209b7002 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -66,14 +66,11 @@ static void __videobuf_dc_free(struct device *dev, static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; - dev_dbg(q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", + dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); map->count++; - videobuf_queue_unlock(q); } static void videobuf_vm_close(struct vm_area_struct *vma) @@ -85,11 +82,12 @@ static void videobuf_vm_close(struct vm_area_struct *vma) dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); - if (!--map->count) { + map->count--; + if (0 == map->count) { struct videobuf_dma_contig_memory *mem; dev_dbg(q->dev, "munmap %p q=%p\n", map, q); + videobuf_queue_lock(q); /* We need first to cancel streams, before unmapping */ if (q->streaming) @@ -128,8 +126,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma) kfree(map); + videobuf_queue_unlock(q); } - videobuf_queue_unlock(q); } static const struct vm_operations_struct videobuf_vm_ops = { diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 9db674ccdc68..828e7c10bd70 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -338,14 +338,11 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free); static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); map->count++; - videobuf_queue_unlock(q); } static void videobuf_vm_close(struct vm_area_struct *vma) @@ -358,9 +355,10 @@ static void videobuf_vm_close(struct vm_area_struct *vma) dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); - if (!--map->count) { + map->count--; + if (0 == map->count) { dprintk(1, "munmap %p q=%p\n", map, q); + videobuf_queue_lock(q); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; @@ -376,9 +374,9 @@ static void videobuf_vm_close(struct vm_area_struct *vma) q->bufs[i]->baddr = 0; q->ops->buf_release(q, q->bufs[i]); } + videobuf_queue_unlock(q); kfree(map); } - videobuf_queue_unlock(q); return; } diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c index 1365c651c177..2ff7fcc77b11 100644 --- a/drivers/media/v4l2-core/videobuf-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf-vmalloc.c @@ -54,14 +54,11 @@ MODULE_LICENSE("GPL"); static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); map->count++; - videobuf_queue_unlock(q); } static void videobuf_vm_close(struct vm_area_struct *vma) @@ -73,11 +70,12 @@ static void videobuf_vm_close(struct vm_area_struct *vma) dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); - videobuf_queue_lock(q); - if (!--map->count) { + map->count--; + if (0 == map->count) { struct videobuf_vmalloc_memory *mem; dprintk(1, "munmap %p q=%p\n", map, q); + videobuf_queue_lock(q); /* We need first to cancel streams, before unmapping */ if (q->streaming) @@ -116,8 +114,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma) kfree(map); + videobuf_queue_unlock(q); } - videobuf_queue_unlock(q); return; } diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 0edc165f418d..a127925c9d61 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -298,10 +298,28 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) * related information, if no buffers are left return the queue to an * uninitialized state. Might be called even if the queue has already been freed. */ -static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) +static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; + /* + * Sanity check: when preparing a buffer the queue lock is released for + * a short while (see __buf_prepare for the details), which would allow + * a race with a reqbufs which can call this function. Removing the + * buffers from underneath __buf_prepare is obviously a bad idea, so we + * check if any of the buffers is in the state PREPARING, and if so we + * just return -EAGAIN. + */ + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { + if (q->bufs[buffer] == NULL) + continue; + if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { + dprintk(1, "reqbufs: preparing buffers, cannot free\n"); + return -EAGAIN; + } + } + /* Call driver-provided cleanup function for each buffer, if provided */ if (q->ops->buf_cleanup) { for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; @@ -326,6 +344,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) if (!q->num_buffers) q->memory = 0; INIT_LIST_HEAD(&q->queued_list); + return 0; } /** @@ -481,6 +500,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) case VB2_BUF_STATE_PREPARED: b->flags |= V4L2_BUF_FLAG_PREPARED; break; + case VB2_BUF_STATE_PREPARING: case VB2_BUF_STATE_DEQUEUED: /* nothing */ break; @@ -657,7 +677,9 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EBUSY; } - __vb2_queue_free(q, q->num_buffers); + ret = __vb2_queue_free(q, q->num_buffers); + if (ret) + return ret; /* * In case of REQBUFS(0) return immediately without calling @@ -1116,7 +1138,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) int ret; int write = !V4L2_TYPE_IS_OUTPUT(q->type); - /* Verify and copy relevant information provided by the userspace */ + /* Copy relevant information provided by the userspace */ __fill_vb2_buffer(vb, b, planes); for (plane = 0; plane < vb->num_planes; ++plane) { @@ -1135,6 +1157,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) if (planes[plane].length < planes[plane].data_offset + q->plane_sizes[plane]) { + dprintk(1, "qbuf: invalid dmabuf length for plane %d\n", + plane); ret = -EINVAL; goto err; } @@ -1226,6 +1250,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; + struct rw_semaphore *mmap_sem; int ret; ret = __verify_length(vb, b); @@ -1235,12 +1260,32 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return ret; } + vb->state = VB2_BUF_STATE_PREPARING; switch (q->memory) { case V4L2_MEMORY_MMAP: ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: + /* + * In case of user pointer buffers vb2 allocators need to get + * direct access to userspace pages. This requires getting + * the mmap semaphore for read access in the current process + * structure. The same semaphore is taken before calling mmap + * operation, while both qbuf/prepare_buf and mmap are called + * by the driver or v4l2 core with the driver's lock held. + * To avoid an AB-BA deadlock (mmap_sem then driver's lock in + * mmap and driver's lock then mmap_sem in qbuf/prepare_buf), + * the videobuf2 core releases the driver's lock, takes + * mmap_sem and then takes the driver's lock again. + */ + mmap_sem = ¤t->mm->mmap_sem; + call_qop(q, wait_prepare, q); + down_read(mmap_sem); + call_qop(q, wait_finish, q); + ret = __qbuf_userptr(vb, b); + + up_read(mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); @@ -1254,105 +1299,36 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = call_qop(q, buf_prepare, vb); if (ret) dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); - else - vb->state = VB2_BUF_STATE_PREPARED; + vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED; return ret; } static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, - const char *opname, - int (*handler)(struct vb2_queue *, - struct v4l2_buffer *, - struct vb2_buffer *)) + const char *opname) { - struct rw_semaphore *mmap_sem = NULL; - struct vb2_buffer *vb; - int ret; - - /* - * In case of user pointer buffers vb2 allocators need to get direct - * access to userspace pages. This requires getting the mmap semaphore - * for read access in the current process structure. The same semaphore - * is taken before calling mmap operation, while both qbuf/prepare_buf - * and mmap are called by the driver or v4l2 core with the driver's lock - * held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap - * and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2 - * core releases the driver's lock, takes mmap_sem and then takes the - * driver's lock again. - * - * To avoid racing with other vb2 calls, which might be called after - * releasing the driver's lock, this operation is performed at the - * beginning of qbuf/prepare_buf processing. This way the queue status - * is consistent after getting the driver's lock back. - */ - if (q->memory == V4L2_MEMORY_USERPTR) { - mmap_sem = ¤t->mm->mmap_sem; - call_qop(q, wait_prepare, q); - down_read(mmap_sem); - call_qop(q, wait_finish, q); - } - - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", opname); - ret = -EBUSY; - goto unlock; - } - if (b->type != q->type) { dprintk(1, "%s(): invalid buffer type\n", opname); - ret = -EINVAL; - goto unlock; + return -EINVAL; } if (b->index >= q->num_buffers) { dprintk(1, "%s(): buffer index out of range\n", opname); - ret = -EINVAL; - goto unlock; + return -EINVAL; } - vb = q->bufs[b->index]; - if (NULL == vb) { + if (q->bufs[b->index] == NULL) { /* Should never happen */ dprintk(1, "%s(): buffer is NULL\n", opname); - ret = -EINVAL; - goto unlock; + return -EINVAL; } if (b->memory != q->memory) { dprintk(1, "%s(): invalid memory type\n", opname); - ret = -EINVAL; - goto unlock; - } - - ret = __verify_planes_array(vb, b); - if (ret) - goto unlock; - - ret = handler(q, b, vb); - if (ret) - goto unlock; - - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); - - dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); -unlock: - if (mmap_sem) - up_read(mmap_sem); - return ret; -} - -static int __vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, - struct vb2_buffer *vb) -{ - if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s(): invalid buffer state %d\n", __func__, - vb->state); return -EINVAL; } - return __buf_prepare(vb, b); + return __verify_planes_array(q->bufs[b->index], b); } /** @@ -1372,22 +1348,95 @@ static int __vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, */ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { - return vb2_queue_or_prepare_buf(q, b, "prepare_buf", __vb2_prepare_buf); + struct vb2_buffer *vb; + int ret; + + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); + if (ret) + return ret; + + vb = q->bufs[b->index]; + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, + vb->state); + return -EINVAL; + } + + ret = __buf_prepare(vb, b); + if (!ret) { + /* Fill buffer information for the userspace */ + __fill_v4l2_buffer(vb, b); + + dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); + } + return ret; } EXPORT_SYMBOL_GPL(vb2_prepare_buf); -static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, - struct vb2_buffer *vb) +/** + * vb2_start_streaming() - Attempt to start streaming. + * @q: videobuf2 queue + * + * If there are not enough buffers, then retry_start_streaming is set to + * 1 and 0 is returned. The next time a buffer is queued and + * retry_start_streaming is 1, this function will be called again to + * retry starting the DMA engine. + */ +static int vb2_start_streaming(struct vb2_queue *q) { int ret; + /* Tell the driver to start streaming */ + ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count)); + + /* + * If there are not enough buffers queued to start streaming, then + * the start_streaming operation will return -ENOBUFS and you have to + * retry when the next buffer is queued. + */ + if (ret == -ENOBUFS) { + dprintk(1, "qbuf: not enough buffers, retry when more buffers are queued.\n"); + q->retry_start_streaming = 1; + return 0; + } + if (ret) + dprintk(1, "qbuf: driver refused to start streaming\n"); + else + q->retry_start_streaming = 0; + return ret; +} + +static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); + struct vb2_buffer *vb; + + if (ret) + return ret; + + vb = q->bufs[b->index]; + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, + vb->state); + return -EINVAL; + } + switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: ret = __buf_prepare(vb, b); if (ret) return ret; + break; case VB2_BUF_STATE_PREPARED: break; + case VB2_BUF_STATE_PREPARING: + dprintk(1, "qbuf: buffer still being prepared\n"); + return -EINVAL; default: dprintk(1, "qbuf: buffer already in use\n"); return -EINVAL; @@ -1407,6 +1456,16 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, if (q->streaming) __enqueue_in_driver(vb); + /* Fill buffer information for the userspace */ + __fill_v4l2_buffer(vb, b); + + if (q->retry_start_streaming) { + ret = vb2_start_streaming(q); + if (ret) + return ret; + } + + dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); return 0; } @@ -1429,7 +1488,12 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, */ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { - return vb2_queue_or_prepare_buf(q, b, "qbuf", __vb2_qbuf); + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + return vb2_internal_qbuf(q, b); } EXPORT_SYMBOL_GPL(vb2_qbuf); @@ -1550,7 +1614,8 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q) return -EINVAL; } - wait_event(q->done_wq, !atomic_read(&q->queued_count)); + if (!q->retry_start_streaming) + wait_event(q->done_wq, !atomic_read(&q->queued_count)); return 0; } EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers); @@ -1579,37 +1644,11 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) } } -/** - * vb2_dqbuf() - Dequeue a buffer to the userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_dqbuf handler - * in driver - * @nonblocking: if true, this call will not sleep waiting for a buffer if no - * buffers ready for dequeuing are present. Normally the driver - * would be passing (file->f_flags & O_NONBLOCK) here - * - * Should be called from vidioc_dqbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_finish callback in the driver (if provided), in which - * driver can perform any additional operations that may be required before - * returning the buffer to userspace, such as cache sync, - * 3) the buffer struct members are filled with relevant information for - * the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_dqbuf handler in driver. - */ -int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) +static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { struct vb2_buffer *vb = NULL; int ret; - if (q->fileio) { - dprintk(1, "dqbuf: file io in progress\n"); - return -EBUSY; - } - if (b->type != q->type) { dprintk(1, "dqbuf: invalid buffer type\n"); return -EINVAL; @@ -1648,6 +1687,36 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) return 0; } + +/** + * vb2_dqbuf() - Dequeue a buffer to the userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_dqbuf handler + * in driver + * @nonblocking: if true, this call will not sleep waiting for a buffer if no + * buffers ready for dequeuing are present. Normally the driver + * would be passing (file->f_flags & O_NONBLOCK) here + * + * Should be called from vidioc_dqbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_finish callback in the driver (if provided), in which + * driver can perform any additional operations that may be required before + * returning the buffer to userspace, such as cache sync, + * 3) the buffer struct members are filled with relevant information for + * the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_dqbuf handler in driver. + */ +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) +{ + if (q->fileio) { + dprintk(1, "dqbuf: file io in progress\n"); + return -EBUSY; + } + return vb2_internal_dqbuf(q, b, nonblocking); +} EXPORT_SYMBOL_GPL(vb2_dqbuf); /** @@ -1660,6 +1729,11 @@ static void __vb2_queue_cancel(struct vb2_queue *q) { unsigned int i; + if (q->retry_start_streaming) { + q->retry_start_streaming = 0; + q->streaming = 0; + } + /* * Tell driver to stop all transactions and release all queued * buffers. @@ -1687,37 +1761,24 @@ static void __vb2_queue_cancel(struct vb2_queue *q) __vb2_dqbuf(q->bufs[i]); } -/** - * vb2_streamon - start streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamon handler - * - * Should be called from vidioc_streamon handler of a driver. - * This function: - * 1) verifies current state - * 2) passes any previously queued buffers to the driver and starts streaming - * - * The return values from this function are intended to be directly returned - * from vidioc_streamon handler in the driver. - */ -int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) +static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { struct vb2_buffer *vb; int ret; - if (q->fileio) { - dprintk(1, "streamon: file io in progress\n"); - return -EBUSY; - } - if (type != q->type) { dprintk(1, "streamon: invalid stream type\n"); return -EINVAL; } if (q->streaming) { - dprintk(1, "streamon: already streaming\n"); - return -EBUSY; + dprintk(3, "streamon successful: already streaming\n"); + return 0; + } + + if (!q->num_buffers) { + dprintk(1, "streamon: no buffers have been allocated\n"); + return -EINVAL; } /* @@ -1727,12 +1788,9 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) list_for_each_entry(vb, &q->queued_list, queued_entry) __enqueue_in_driver(vb); - /* - * Let driver notice that streaming state has been enabled. - */ - ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count)); + /* Tell driver to start streaming. */ + ret = vb2_start_streaming(q); if (ret) { - dprintk(1, "streamon: driver refused to start streaming\n"); __vb2_queue_cancel(q); return ret; } @@ -1742,39 +1800,40 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) dprintk(3, "Streamon successful\n"); return 0; } -EXPORT_SYMBOL_GPL(vb2_streamon); - /** - * vb2_streamoff - stop streaming + * vb2_streamon - start streaming * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamoff handler + * @type: type argument passed from userspace to vidioc_streamon handler * - * Should be called from vidioc_streamoff handler of a driver. + * Should be called from vidioc_streamon handler of a driver. * This function: - * 1) verifies current state, - * 2) stop streaming and dequeues any queued buffers, including those previously - * passed to the driver (after waiting for the driver to finish). + * 1) verifies current state + * 2) passes any previously queued buffers to the driver and starts streaming * - * This call can be used for pausing playback. * The return values from this function are intended to be directly returned - * from vidioc_streamoff handler in the driver + * from vidioc_streamon handler in the driver. */ -int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { if (q->fileio) { - dprintk(1, "streamoff: file io in progress\n"); + dprintk(1, "streamon: file io in progress\n"); return -EBUSY; } + return vb2_internal_streamon(q, type); +} +EXPORT_SYMBOL_GPL(vb2_streamon); +static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +{ if (type != q->type) { dprintk(1, "streamoff: invalid stream type\n"); return -EINVAL; } if (!q->streaming) { - dprintk(1, "streamoff: not streaming\n"); - return -EINVAL; + dprintk(3, "streamoff successful: not streaming\n"); + return 0; } /* @@ -1786,6 +1845,30 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) dprintk(3, "Streamoff successful\n"); return 0; } + +/** + * vb2_streamoff - stop streaming + * @q: videobuf2 queue + * @type: type argument passed from userspace to vidioc_streamoff handler + * + * Should be called from vidioc_streamoff handler of a driver. + * This function: + * 1) verifies current state, + * 2) stop streaming and dequeues any queued buffers, including those previously + * passed to the driver (after waiting for the driver to finish). + * + * This call can be used for pausing playback. + * The return values from this function are intended to be directly returned + * from vidioc_streamoff handler in the driver + */ +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +{ + if (q->fileio) { + dprintk(1, "streamoff: file io in progress\n"); + return -EBUSY; + } + return vb2_internal_streamoff(q, type); +} EXPORT_SYMBOL_GPL(vb2_streamoff); /** @@ -2277,15 +2360,16 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) goto err_reqbufs; fileio->bufs[i].queued = 1; } - - /* - * Start streaming. - */ - ret = vb2_streamon(q, q->type); - if (ret) - goto err_reqbufs; + fileio->index = q->num_buffers; } + /* + * Start streaming. + */ + ret = vb2_streamon(q, q->type); + if (ret) + goto err_reqbufs; + q->fileio = fileio; return ret; @@ -2308,13 +2392,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) struct vb2_fileio_data *fileio = q->fileio; if (fileio) { - /* - * Hack fileio context to enable direct calls to vb2 ioctl - * interface. - */ + vb2_internal_streamoff(q, q->type); q->fileio = NULL; - - vb2_streamoff(q, q->type); fileio->req.count = 0; vb2_reqbufs(q, &fileio->req); kfree(fileio); @@ -2358,39 +2437,34 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ fileio = q->fileio; /* - * Hack fileio context to enable direct calls to vb2 ioctl interface. - * The pointer will be restored before returning from this function. - */ - q->fileio = NULL; - - index = fileio->index; - buf = &fileio->bufs[index]; - - /* * Check if we need to dequeue the buffer. */ - if (buf->queued) { - struct vb2_buffer *vb; - + index = fileio->index; + if (index >= q->num_buffers) { /* * Call vb2_dqbuf to get buffer back. */ memset(&fileio->b, 0, sizeof(fileio->b)); fileio->b.type = q->type; fileio->b.memory = q->memory; - fileio->b.index = index; - ret = vb2_dqbuf(q, &fileio->b, nonblock); + ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); if (ret) - goto end; + return ret; fileio->dq_count += 1; + index = fileio->b.index; + buf = &fileio->bufs[index]; + /* * Get number of bytes filled by the driver */ - vb = q->bufs[index]; - buf->size = vb2_get_plane_payload(vb, 0); + buf->pos = 0; buf->queued = 0; + buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0) + : vb2_plane_size(q->bufs[index], 0); + } else { + buf = &fileio->bufs[index]; } /* @@ -2412,8 +2486,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ ret = copy_from_user(buf->vaddr + buf->pos, data, count); if (ret) { dprintk(3, "file io: error copying data\n"); - ret = -EFAULT; - goto end; + return -EFAULT; } /* @@ -2433,10 +2506,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && fileio->dq_count == 1) { dprintk(3, "file io: read limit reached\n"); - /* - * Restore fileio pointer and release the context. - */ - q->fileio = fileio; return __vb2_cleanup_fileio(q); } @@ -2448,32 +2517,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ fileio->b.memory = q->memory; fileio->b.index = index; fileio->b.bytesused = buf->pos; - ret = vb2_qbuf(q, &fileio->b); + ret = vb2_internal_qbuf(q, &fileio->b); dprintk(5, "file io: vb2_dbuf result: %d\n", ret); if (ret) - goto end; + return ret; /* * Buffer has been queued, update the status */ buf->pos = 0; buf->queued = 1; - buf->size = q->bufs[0]->v4l2_planes[0].length; + buf->size = vb2_plane_size(q->bufs[index], 0); fileio->q_count += 1; - - /* - * Switch to the next buffer - */ - fileio->index = (index + 1) % q->num_buffers; - - /* - * Start streaming if required. - */ - if (!read && !q->streaming) { - ret = vb2_streamon(q, q->type); - if (ret) - goto end; - } + if (fileio->index < q->num_buffers) + fileio->index++; } /* @@ -2481,11 +2538,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (ret == 0) ret = count; -end: - /* - * Restore the fileio context and block vb2 ioctl interface. - */ - q->fileio = fileio; return ret; } @@ -2649,16 +2701,29 @@ int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) } EXPORT_SYMBOL_GPL(vb2_fop_mmap); -int vb2_fop_release(struct file *file) +int _vb2_fop_release(struct file *file, struct mutex *lock) { struct video_device *vdev = video_devdata(file); if (file->private_data == vdev->queue->owner) { + if (lock) + mutex_lock(lock); vb2_queue_release(vdev->queue); vdev->queue->owner = NULL; + if (lock) + mutex_unlock(lock); } return v4l2_fh_release(file); } +EXPORT_SYMBOL_GPL(_vb2_fop_release); + +int vb2_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + + return _vb2_fop_release(file, lock); +} EXPORT_SYMBOL_GPL(vb2_fop_release); ssize_t vb2_fop_write(struct file *file, const char __user *buf, diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 0d3a8ffe47a3..c779f210d2c6 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -40,6 +40,7 @@ struct vb2_dma_sg_buf { unsigned int num_pages; atomic_t refcount; struct vb2_vmarea_handler handler; + struct vm_area_struct *vma; }; static void vb2_dma_sg_put(void *buf_priv); @@ -155,12 +156,18 @@ static void vb2_dma_sg_put(void *buf_priv) } } +static inline int vma_is_io(struct vm_area_struct *vma) +{ + return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); +} + static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write) { struct vb2_dma_sg_buf *buf; unsigned long first, last; int num_pages_from_user; + struct vm_area_struct *vma; buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) @@ -180,7 +187,38 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, if (!buf->pages) goto userptr_fail_alloc_pages; - num_pages_from_user = get_user_pages(current, current->mm, + vma = find_vma(current->mm, vaddr); + if (!vma) { + dprintk(1, "no vma for address %lu\n", vaddr); + goto userptr_fail_find_vma; + } + + if (vma->vm_end < vaddr + size) { + dprintk(1, "vma at %lu is too small for %lu bytes\n", + vaddr, size); + goto userptr_fail_find_vma; + } + + buf->vma = vb2_get_vma(vma); + if (!buf->vma) { + dprintk(1, "failed to copy vma\n"); + goto userptr_fail_find_vma; + } + + if (vma_is_io(buf->vma)) { + for (num_pages_from_user = 0; + num_pages_from_user < buf->num_pages; + ++num_pages_from_user, vaddr += PAGE_SIZE) { + unsigned long pfn; + + if (follow_pfn(buf->vma, vaddr, &pfn)) { + dprintk(1, "no page for address %lu\n", vaddr); + break; + } + buf->pages[num_pages_from_user] = pfn_to_page(pfn); + } + } else + num_pages_from_user = get_user_pages(current, current->mm, vaddr & PAGE_MASK, buf->num_pages, write, @@ -200,9 +238,12 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, userptr_fail_alloc_table_from_pages: userptr_fail_get_user_pages: dprintk(1, "get_user_pages requested/got: %d/%d]\n", - num_pages_from_user, buf->num_pages); - while (--num_pages_from_user >= 0) - put_page(buf->pages[num_pages_from_user]); + buf->num_pages, num_pages_from_user); + if (!vma_is_io(buf->vma)) + while (--num_pages_from_user >= 0) + put_page(buf->pages[num_pages_from_user]); + vb2_put_vma(buf->vma); +userptr_fail_find_vma: kfree(buf->pages); userptr_fail_alloc_pages: kfree(buf); @@ -226,9 +267,11 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) while (--i >= 0) { if (buf->write) set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); + if (!vma_is_io(buf->vma)) + put_page(buf->pages[i]); } kfree(buf->pages); + vb2_put_vma(buf->vma); kfree(buf); } |