From c6f8e5575511967a94c917c8934503253ebf6d5b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 28 Oct 2013 09:16:05 -0300 Subject: [media] radio-shark: Mark shark_resume_leds() inline to kill compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If SHARK_USE_LEDS=1, but CONFIG_PM=n: drivers/media/radio/radio-shark.c:275: warning: ‘shark_resume_leds’ defined but not used Instead of making the #ifdef logic even more complicated (there are already two definitions of shark_resume_leds()), mark shark_resume_leds() inline to kill the compiler warning. shark_resume_leds() is small and it has only one caller. Signed-off-by: Geert Uytterhoeven Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c index 3db8a8c..050b3bb 100644 --- a/drivers/media/radio/radio-shark.c +++ b/drivers/media/radio/radio-shark.c @@ -271,8 +271,7 @@ static void shark_unregister_leds(struct shark_device *shark) cancel_work_sync(&shark->led_work); } -#ifdef CONFIG_PM -static void shark_resume_leds(struct shark_device *shark) +static inline void shark_resume_leds(struct shark_device *shark) { if (test_bit(BLUE_IS_PULSE, &shark->brightness_new)) set_bit(BLUE_PULSE_LED, &shark->brightness_new); @@ -281,7 +280,6 @@ static void shark_resume_leds(struct shark_device *shark) set_bit(RED_LED, &shark->brightness_new); schedule_work(&shark->led_work); } -#endif #else static int shark_register_leds(struct shark_device *shark, struct device *dev) { -- cgit v0.10.2 From d8eec74a229f4e3448bfdff9ff3cd3f61bb1e63d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 17 Nov 2013 10:48:29 -0300 Subject: [media] radio-shark2: Mark shark_resume_leds() inline to kill compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mirrors the patch to the radio-shark driver by Geert Uytterhoeven. If SHARK_USE_LEDS=1, but CONFIG_PM=n: drivers/media/radio/radio-shark2.c:240: warning: ‘shark_resume_leds’ defined but not used Instead of making the #ifdef logic even more complicated (there are already two definitions of shark_resume_leds()), mark shark_resume_leds() inline to kill the compiler warning. shark_resume_leds() is small and it has only one caller. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c index d86d90d..8654e0d 100644 --- a/drivers/media/radio/radio-shark2.c +++ b/drivers/media/radio/radio-shark2.c @@ -237,8 +237,7 @@ static void shark_unregister_leds(struct shark_device *shark) cancel_work_sync(&shark->led_work); } -#ifdef CONFIG_PM -static void shark_resume_leds(struct shark_device *shark) +static inline void shark_resume_leds(struct shark_device *shark) { int i; @@ -247,7 +246,6 @@ static void shark_resume_leds(struct shark_device *shark) schedule_work(&shark->led_work); } -#endif #else static int shark_register_leds(struct shark_device *shark, struct device *dev) { -- cgit v0.10.2 From ea3aba8482be6f3e4815f4014fa7302fc77c9c3f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 21 May 2013 05:11:35 -0300 Subject: [media] videobuf2: Add support for file access mode flags for DMABUF exporting Currently it is not possible for userspace to map a DMABUF exported buffer with write permissions. This patch allows to also pass O_RDONLY/O_RDWR when exporting the buffer, so that userspace may map it with write permissions. Signed-off-by: Philipp Zabel Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml index e287c8f..4165e7b 100644 --- a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -73,7 +73,8 @@ range from zero to the maximal number of valid planes for the currently active format. For the single-planar API, applications must set plane to zero. Additional flags may be posted in the flags field. Refer to a manual for open() for details. -Currently only O_CLOEXEC is supported. All other fields must be set to zero. +Currently only O_CLOEXEC, O_RDONLY, O_WRONLY, and O_RDWR are supported. All +other fields must be set to zero. In the case of multi-planar API, every plane is exported separately using multiple VIDIOC_EXPBUF calls. @@ -170,8 +171,9 @@ multi-planar API. Otherwise this value must be set to zero. __u32 flags Flags for the newly created file, currently only -O_CLOEXEC is supported, refer to the manual of open() for more -details. +O_CLOEXEC , O_RDONLY, O_WRONLY +, and O_RDWR are supported, refer to the manual +of open() for more details. __s32 diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index b19b306..57ba131 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1824,8 +1824,8 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EINVAL; } - if (eb->flags & ~O_CLOEXEC) { - dprintk(1, "Queue does support only O_CLOEXEC flag\n"); + if (eb->flags & ~(O_CLOEXEC | O_ACCMODE)) { + dprintk(1, "Queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } @@ -1848,14 +1848,14 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) vb_plane = &vb->planes[eb->plane]; - dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv); + dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { dprintk(1, "Failed to export buffer %d, plane %d\n", eb->index, eb->plane); return -EINVAL; } - ret = dma_buf_fd(dbuf, eb->flags); + ret = dma_buf_fd(dbuf, eb->flags & ~O_ACCMODE); if (ret < 0) { dprintk(3, "buffer %d, plane %d failed to export (%d)\n", eb->index, eb->plane, ret); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 646f08f..33d3871d 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -393,7 +393,7 @@ static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf) return sgt; } -static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv) +static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) { struct vb2_dc_buf *buf = buf_priv; struct dma_buf *dbuf; @@ -404,7 +404,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv) if (WARN_ON(!buf->sgt_base)) return NULL; - dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, 0); + dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags); if (IS_ERR(dbuf)) return NULL; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index bd8218b..941055e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -83,7 +83,7 @@ struct vb2_fileio_data; struct vb2_mem_ops { void *(*alloc)(void *alloc_ctx, unsigned long size, gfp_t gfp_flags); void (*put)(void *buf_priv); - struct dma_buf *(*get_dmabuf)(void *buf_priv); + struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags); void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write); -- cgit v0.10.2 From 39c1cb2b191f56a963103d715797fca70f2fb26e Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sun, 20 Oct 2013 21:34:01 -0300 Subject: [media] media_tree: Fix spelling errors Fix various spelling errors in strings and comments throughout the media tree. The majority of these were found using Lucas De Marchi's codespell tool. [m.chehab@samsung.com: discard hunks with conflicts] Signed-off-by: Jonathan McCrohan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index d0799e3..9c9063c 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -955,7 +955,7 @@ struct sms_rx_stats { u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ s32 SNR; /* dB */ u32 ber; /* Post Viterbi ber [1E-5] */ - u32 ber_error_count; /* Number of erronous SYNC bits. */ + u32 ber_error_count; /* Number of erroneous SYNC bits. */ u32 ber_bit_count; /* Total number of SYNC bits. */ u32 ts_per; /* Transport stream PER, 0xFFFFFFFF indicate N/A */ @@ -981,7 +981,7 @@ struct sms_rx_stats_ex { u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ s32 SNR; /* dB */ u32 ber; /* Post Viterbi ber [1E-5] */ - u32 ber_error_count; /* Number of erronous SYNC bits. */ + u32 ber_error_count; /* Number of erroneous SYNC bits. */ u32 ber_bit_count; /* Total number of SYNC bits. */ u32 ts_per; /* Transport stream PER, 0xFFFFFFFF indicate N/A */ diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h index 92c413b..ae36d0a 100644 --- a/drivers/media/common/siano/smsdvb.h +++ b/drivers/media/common/siano/smsdvb.h @@ -95,7 +95,7 @@ struct RECEPTION_STATISTICS_PER_SLICES_S { u32 is_demod_locked; /* 0 - not locked, 1 - locked */ u32 ber_bit_count; /* Total number of SYNC bits. */ - u32 ber_error_count; /* Number of erronous SYNC bits. */ + u32 ber_error_count; /* Number of erroneous SYNC bits. */ s32 MRC_SNR; /* dB */ s32 mrc_in_band_pwr; /* In band power in dBM */ diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 58de441..53ee319 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -435,7 +435,7 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) dprintk_tscheck("TEI detected. " "PID=0x%x data1=0x%x\n", pid, buf[1]); - /* data in this packet cant be trusted - drop it unless + /* data in this packet can't be trusted - drop it unless * module option dvb_demux_feed_err_pkts is set */ if (!dvb_demux_feed_err_pkts) return; diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 9053614..6dbbee4 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3048,7 +3048,7 @@ static int dib8000_tune(struct dvb_frontend *fe) dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ - /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this lenght to lock */ + /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this length to lock */ *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); *tune_state = CT_DEMOD_STEP_5; break; @@ -3115,7 +3115,7 @@ static int dib8000_tune(struct dvb_frontend *fe) case CT_DEMOD_STEP_9: /* 39 */ if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ - /* defines timeout for mpeg lock depending on interleaver lenght of longest layer */ + /* defines timeout for mpeg lock depending on interleaver length of longest layer */ for (i = 0; i < 3; i++) { if (c->layer[i].interleaving >= deeper_interleaver) { dprintk("layer%i: time interleaver = %d ", i, c->layer[i].interleaving); diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index d416c15..bf29a3f 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1191,7 +1191,7 @@ static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable) goto error; if (state->m_enable_parallel == true) { - /* paralel -> enable MD1 to MD7 */ + /* parallel -> enable MD1 to MD7 */ status = write16(state, SIO_PDR_MD1_CFG__A, sio_pdr_mdx_cfg); if (status < 0) @@ -1428,7 +1428,7 @@ static int mpegts_stop(struct drxk_state *state) dprintk(1, "\n"); - /* Gracefull shutdown (byte boundaries) */ + /* Graceful shutdown (byte boundaries) */ status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode); if (status < 0) goto error; @@ -2021,7 +2021,7 @@ static int mpegts_dto_setup(struct drxk_state *state, fec_oc_dto_burst_len = 204; } - /* Check serial or parrallel output */ + /* Check serial or parallel output */ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); if (state->m_enable_parallel == false) { /* MPEG data output is serial -> set ipr_mode[0] */ @@ -2908,7 +2908,7 @@ static int adc_synchronization(struct drxk_state *state) goto error; if (count == 1) { - /* Try sampling on a diffrent edge */ + /* Try sampling on a different edge */ u16 clk_neg = 0; status = read16(state, IQM_AF_CLKNEG__A, &clk_neg); @@ -3306,7 +3306,7 @@ static int dvbt_sc_command(struct drxk_state *state, if (status < 0) goto error; - /* Retreive results parameters from SC */ + /* Retrieve results parameters from SC */ switch (cmd) { /* All commands yielding 5 results */ /* All commands yielding 4 results */ @@ -3849,7 +3849,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, break; } #if 0 - /* No hierachical channels support in BDA */ + /* No hierarchical channels support in BDA */ /* Priority (only for hierarchical channels) */ switch (channel->priority) { case DRX_PRIORITY_LOW: @@ -4081,7 +4081,7 @@ error: /*============================================================================*/ /** -* \brief Retreive lock status . +* \brief Retrieve lock status . * \param demod Pointer to demodulator instance. * \param lockStat Pointer to lock status structure. * \return DRXStatus_t. @@ -6174,7 +6174,7 @@ static int init_drxk(struct drxk_state *state) goto error; /* Stamp driver version number in SCU data RAM in BCD code - Done to enable field application engineers to retreive drxdriver version + Done to enable field application engineers to retrieve drxdriver version via I2C from SCU RAM. Not using SCU command interface for SCU register access since no microcode may be present. @@ -6399,7 +6399,7 @@ static int drxk_set_parameters(struct dvb_frontend *fe) fe->ops.tuner_ops.get_if_frequency(fe, &IF); start(state, 0, IF); - /* After set_frontend, stats aren't avaliable */ + /* After set_frontend, stats aren't available */ p->strength.stat[0].scale = FE_SCALE_RELATIVE; p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h index 4a5b7d2..b253d40 100644 --- a/drivers/media/i2c/adv7183_regs.h +++ b/drivers/media/i2c/adv7183_regs.h @@ -52,9 +52,9 @@ #define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ #define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ #define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ -#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ -#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ -#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ +#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync position control 1 */ +#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync position control 2 */ +#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync position control 3 */ #define ADV7183_POLARITY 0x37 /* Polarity */ #define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ #define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index fbfdd2f..a324106b 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -877,7 +877,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, break; case ADV7604_MODE_HDMI: /* set default prim_mode/vid_std for HDMI - accoring to [REF_03, c. 4.2] */ + according to [REF_03, c. 4.2] */ io_write(sd, 0x00, 0x02); /* video std */ io_write(sd, 0x01, 0x06); /* prim mode */ break; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 22f729d..b154f36 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1013,7 +1013,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, break; case ADV7842_MODE_HDMI: /* set default prim_mode/vid_std for HDMI - accoring to [REF_03, c. 4.2] */ + according to [REF_03, c. 4.2] */ io_write(sd, 0x00, 0x02); /* video std */ io_write(sd, 0x01, 0x06); /* prim mode */ break; diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 82bf567..99ee456 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -394,7 +394,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!rc) { /* - * If platform_data doesn't specify rc_dev, initilize it + * If platform_data doesn't specify rc_dev, initialize it * internally */ rc = rc_allocate_device(); diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c index f34429e..a60931e 100644 --- a/drivers/media/i2c/m5mols/m5mols_controls.c +++ b/drivers/media/i2c/m5mols/m5mols_controls.c @@ -544,7 +544,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd) u16 zoom_step; int ret; - /* Determine the firmware dependant control range and step values */ + /* Determine the firmware dependent control range and step values */ ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 6fec938..e7f555c 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1460,7 +1460,7 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) mutex_unlock(&state->lock); v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", - __func__, ret ? "failed" : "succeded", ret); + __func__, ret ? "failed" : "succeeded", ret); return ret; } diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h index 9d2c086..9dfa516 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3.h +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -393,7 +393,7 @@ struct s5c73m3 { /* External master clock frequency */ u32 mclk_frequency; - /* Video bus type - MIPI-CSI2/paralell */ + /* Video bus type - MIPI-CSI2/parallel */ enum v4l2_mbus_type bus_type; const struct s5c73m3_frame_size *sensor_pix_size[2]; diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 637d026..afdbcb0 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1699,7 +1699,7 @@ static void saa711x_write_platform_data(struct saa711x_state *state, * the analog demod. * If the tuner is not found, it returns -ENODEV. * If auto-detection is disabled and the tuner doesn't match what it was - * requred, it returns -EINVAL and fills 'name'. + * required, it returns -EINVAL and fills 'name'. * If the chip is found, it returns the chip ID and fills 'name'. */ static int saa711x_detect_chip(struct i2c_client *client, diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 0a5c5d4..d2daa6a 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -642,7 +642,7 @@ static const struct ov5642_datafmt static int reg_read(struct i2c_client *client, u16 reg, u8 *val) { int ret; - /* We have 16-bit i2c addresses - care for endianess */ + /* We have 16-bit i2c addresses - care for endianness */ unsigned char data[2] = { reg >> 8, reg & 0xff }; ret = i2c_master_send(client, data, 2); diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h index 2767c64..57f4688 100644 --- a/drivers/media/pci/cx18/cx18-driver.h +++ b/drivers/media/pci/cx18/cx18-driver.h @@ -262,7 +262,7 @@ struct cx18_options { }; /* per-mdl bit flags */ -#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ +#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianness swapped */ /* per-stream, s_flags */ #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index e3fc2c7..95666ee 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -427,7 +427,7 @@ int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) cx_write(MC417_RWD, regval); /* Transition RD to effect read transaction across bus. - * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)? + * Transition 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)? * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its * input only...) */ diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c index 8164d74..655d6854 100644 --- a/drivers/media/pci/pluto2/pluto2.c +++ b/drivers/media/pci/pluto2/pluto2.c @@ -401,7 +401,7 @@ static int pluto_hw_init(struct pluto *pluto) /* set automatic LED control by FPGA */ pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); - /* set data endianess */ + /* set data endianness */ #ifdef __LITTLE_ENDIAN pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); #else diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index bd72fb9..61f3dbc 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1434,7 +1434,7 @@ static void coda_buf_queue(struct vb2_buffer *vb) if (q_data->fourcc == V4L2_PIX_FMT_H264 && vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { /* - * For backwards compatiblity, queuing an empty buffer marks + * For backwards compatibility, queuing an empty buffer marks * the stream end */ if (vb2_get_plane_payload(vb, 0) == 0) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 3d66d88..f791569 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1039,7 +1039,7 @@ static int fimc_runtime_resume(struct device *dev) dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); - /* Enable clocks and perform basic initalization */ + /* Enable clocks and perform basic initialization */ clk_enable(fimc->clock[CLK_GATE]); fimc_hw_reset(fimc); diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 7a4ee4c..c1bce17 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -759,7 +759,7 @@ static int fimc_md_register_platform_entity(struct fimc_md *fmd, goto dev_unlock; drvdata = dev_get_drvdata(dev); - /* Some subdev didn't probe succesfully id drvdata is NULL */ + /* Some subdev didn't probe successfully id drvdata is NULL */ if (drvdata) { switch (plat_entity) { case IDX_FIMC: diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 1c36080..561bce8 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1673,7 +1673,7 @@ void omap3isp_print_status(struct isp_device *isp) * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in * resume(), and the the pipelines are restarted in complete(). * - * TODO: PM dependencies between the ISP and sensors are not modeled explicitly + * TODO: PM dependencies between the ISP and sensors are not modelled explicitly * yet. */ static int isp_pm_prepare(struct device *dev) diff --git a/drivers/media/platform/s5p-mfc/regs-mfc.h b/drivers/media/platform/s5p-mfc/regs-mfc.h index 9319e93..6ccc3f8 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc.h @@ -382,7 +382,7 @@ #define S5P_FIMV_R2H_CMD_EDFU_INIT_RET 16 #define S5P_FIMV_R2H_CMD_ERR_RET 32 -/* Dummy definition for MFCv6 compatibilty */ +/* Dummy definition for MFCv6 compatibility */ #define S5P_FIMV_CODEC_H264_MVC_DEC -1 #define S5P_FIMV_R2H_CMD_FIELD_DONE_RET -1 #define S5P_FIMV_MFC_RESET -1 diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 5f2c4ad..e46067a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -239,7 +239,7 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); /* Copy timestamp / timecode from decoded src to dst and set - appropraite flags */ + appropriate flags */ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { @@ -428,7 +428,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, case MFCINST_FINISHING: case MFCINST_FINISHED: case MFCINST_RUNNING: - /* It is higly probable that an error occured + /* It is highly probable that an error occurred * while decoding a frame */ clear_work_bit(ctx); ctx->state = MFCINST_ERROR; @@ -611,7 +611,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err); switch (reason) { case S5P_MFC_R2H_CMD_ERR_RET: - /* An error has occured */ + /* An error has occurred */ if (ctx->state == MFCINST_RUNNING && s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >= dev->warn_start) @@ -840,7 +840,7 @@ static int s5p_mfc_open(struct file *file) mutex_unlock(&dev->mfc_mutex); mfc_debug_leave(); return ret; - /* Deinit when failure occured */ + /* Deinit when failure occurred */ err_queue_init: if (dev->num_inst == 1) s5p_mfc_deinit_hw(dev); @@ -881,14 +881,14 @@ static int s5p_mfc_release(struct file *file) /* Mark context as idle */ clear_work_bit_irqsave(ctx); /* If instance was initialised then - * return instance and free reosurces */ + * return instance and free resources */ if (ctx->inst_no != MFC_NO_INSTANCE_SET) { mfc_debug(2, "Has to free instance\n"); ctx->state = MFCINST_RETURN_INST; set_work_bit_irqsave(ctx); s5p_mfc_clean_ctx_int_flags(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - /* Wait until instance is returned or timeout occured */ + /* Wait until instance is returned or timeout occurred */ if (s5p_mfc_wait_for_done_ctx (ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) { s5p_mfc_clock_off(); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 7cab684..2475a3c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -69,7 +69,7 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) } else { /* In this case bank2 can point to the same address as bank1. - * Firmware will always occupy the beggining of this area so it is + * Firmware will always occupy the beginning of this area so it is * impossible having a video frame buffer with zero address. */ dev->bank2 = dev->bank1; } diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index 04e6490..fb2acc5 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -65,7 +65,7 @@ struct mxr_format { int num_subframes; /** specifies to which subframe belong given plane */ int plane2subframe[MXR_MAX_PLANES]; - /** internal code, driver dependant */ + /** internal code, driver dependent */ unsigned long cookie; }; diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 641b1f0..81b97db 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -528,7 +528,7 @@ static int mxr_s_dv_timings(struct file *file, void *fh, mutex_lock(&mdev->mutex); /* timings change cannot be done while there is an entity - * dependant on output configuration + * dependent on output configuration */ if (mdev->n_output > 0) { mutex_unlock(&mdev->mutex); @@ -585,7 +585,7 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm) mutex_lock(&mdev->mutex); /* standard change cannot be done while there is an entity - * dependant on output configuration + * dependent on output configuration */ if (mdev->n_output > 0) { mutex_unlock(&mdev->mutex); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 6769193..74ce8b6 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1495,7 +1495,7 @@ static int omap1_cam_set_bus_param(struct soc_camera_device *icd) if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - /* select bus endianess */ + /* select bus endianness */ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); fmt = xlate->host_fmt; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 1d3f119..2d4e73b 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1108,7 +1108,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -/* timeperframe is arbitrary and continous */ +/* timeperframe is arbitrary and continuous */ static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { @@ -1125,7 +1125,7 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ + /* fill in stepwise (step=1.0 is required by V4L2 spec) */ fival->stepwise.min = tpf_min; fival->stepwise.max = tpf_max; fival->stepwise.step = (struct v4l2_fract) {1, 1}; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 1c9e771..d16bf0f 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -323,7 +323,7 @@ static void vsp1_clocks_disable(struct vsp1_device *vsp1) * Increment the VSP1 reference count and initialize the device if the first * reference is taken. * - * Return a pointer to the VSP1 device or NULL if an error occured. + * Return a pointer to the VSP1 device or NULL if an error occurred. */ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) { diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 9c9084c..2fd9009 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -268,8 +268,8 @@ struct si476x_radio; * * @tune_freq: Tune chip to a specific frequency * @seek_start: Star station seeking - * @rsq_status: Get Recieved Signal Quality(RSQ) status - * @rds_blckcnt: Get recived RDS blocks count + * @rsq_status: Get Received Signal Quality(RSQ) status + * @rds_blckcnt: Get received RDS blocks count * @phase_diversity: Change phase diversity mode of the tuner * @phase_div_status: Get phase diversity mode status * @acf_status: Get the status of Automatically Controlled diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 72e3fa6..f329485 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1370,7 +1370,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) * 0x68nnnnB7 to 0x6AnnnnB7, the left mouse button generates * 0x688301b7 and the right one 0x688481b7. All other keys generate * 0x2nnnnnnn. Position coordinate is encoded in buf[1] and buf[2] with - * reversed endianess. Extract direction from buffer, rotate endianess, + * reversed endianness. Extract direction from buffer, rotate endianness, * adjust sign and feed the values into stabilize(). The resulting codes * will be 0x01008000, 0x01007F00, which match the newer devices. */ diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 094484f..a5d4f88 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -118,7 +118,7 @@ static int debug; #define RR3_IR_IO_LENGTH_FUZZ 0x04 /* Timeout for end of signal detection */ #define RR3_IR_IO_SIG_TIMEOUT 0x05 -/* Minumum value for pause recognition. */ +/* Minimum value for pause recognition. */ #define RR3_IR_IO_MIN_PAUSE 0x06 /* Clock freq. of EZ-USB chip */ diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 2e1a02e..20cca40 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -1195,7 +1195,7 @@ static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state, * DNC Output is selected, the other is always off) * * @state: ptr to mt2063_state structure - * @Mode: desired reciever delivery system + * @Mode: desired receiver delivery system * * Note: Register cache must be valid for it to work */ @@ -2119,7 +2119,7 @@ static int mt2063_set_analog_params(struct dvb_frontend *fe, /* * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. - * So, the amount of the needed bandwith is given by: + * So, the amount of the needed bandwidth is given by: * Bw = Symbol_rate * (1 + 0.15) * As such, the maximum symbol rate supported by 6 MHz is given by: * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds diff --git a/drivers/media/tuners/tuner-xc2028-types.h b/drivers/media/tuners/tuner-xc2028-types.h index 74dc46a..7e47987 100644 --- a/drivers/media/tuners/tuner-xc2028-types.h +++ b/drivers/media/tuners/tuner-xc2028-types.h @@ -119,7 +119,7 @@ #define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B) #define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B) -/* To preserve backward compatibilty, +/* To preserve backward compatibility, (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 2627553..08240e4 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -266,7 +266,7 @@ static int mxl111sf_adap_fe_init(struct dvb_frontend *fe) struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; int err; - /* exit if we didnt initialize the driver yet */ + /* exit if we didn't initialize the driver yet */ if (!state->chip_id) { mxl_debug("driver not yet initialized, exit."); goto fail; @@ -322,7 +322,7 @@ static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe) struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; int err; - /* exit if we didnt initialize the driver yet */ + /* exit if we didn't initialize the driver yet */ if (!state->chip_id) { mxl_debug("driver not yet initialized, exit."); goto fail; diff --git a/drivers/media/usb/gspca/gl860/gl860.c b/drivers/media/usb/gspca/gl860/gl860.c index cb1e64c..cea8d7f 100644 --- a/drivers/media/usb/gspca/gl860/gl860.c +++ b/drivers/media/usb/gspca/gl860/gl860.c @@ -438,7 +438,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, s32 nToSkip = sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); - /* Test only against 0202h, so endianess does not matter */ + /* Test only against 0202h, so endianness does not matter */ switch (*(s16 *) data) { case 0x0202: /* End of frame, start a new one */ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index cd79c18..07529e5 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -416,7 +416,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, #if IS_ENABLED(CONFIG_INPUT) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ + int len) /* interrupt packet length */ { int ret = -EINVAL; diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index a915096..2fd1c5e 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -874,7 +874,7 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, #if IS_ENABLED(CONFIG_INPUT) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ + int len) /* interrupt packet length */ { int ret = -EINVAL; u8 data0, data1; diff --git a/drivers/media/usb/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c index 9c08276..7f94ec7 100644 --- a/drivers/media/usb/gspca/stv0680.c +++ b/drivers/media/usb/gspca/stv0680.c @@ -139,7 +139,7 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; - /* Give the camera some time to settle, otherwise initalization will + /* Give the camera some time to settle, otherwise initialization will fail on hotplug, and yes it really needs a full second. */ msleep(1000); diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index 7b95d8e..d3e1b6d 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -6905,7 +6905,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, #if IS_ENABLED(CONFIG_INPUT) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ + int len) /* interrupt packet length */ { if (len == 8 && data[4] == 1) { input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 77bbf78..78c9bc8 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1039,7 +1039,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* Set the leds off */ pwc_set_leds(pdev, 0, 0); - /* Setup intial videomode */ + /* Setup initial videomode */ rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, V4L2_PIX_FMT_YUV420, 30, &compression, 1); if (rc) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 899cb6d..898c208 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -556,7 +556,7 @@ static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) * * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1) * - * to avoid loosing precision in the division. Similarly, the host timestamp is + * to avoid losing precision in the division. Similarly, the host timestamp is * computed with * * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 60dcc0f..fb46790 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -420,7 +420,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Advanced Simple", "Core", "Simple Scalable", - "Advanced Coding Efficency", + "Advanced Coding Efficiency", NULL, }; -- cgit v0.10.2 From 0e0fe3958fdd13dbf55c3a787acafde6efd04272 Mon Sep 17 00:00:00 2001 From: Georg Kaindl Date: Mon, 21 Oct 2013 12:01:36 -0300 Subject: [media] usbtv: Add support for PAL video source Signed-off-by: Georg Kaindl Tested-by: Lubomir Rintel Tested-by: Marcin Nowak Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c index 8a505a9..6222a4a 100644 --- a/drivers/media/usb/usbtv/usbtv.c +++ b/drivers/media/usb/usbtv/usbtv.c @@ -50,13 +50,8 @@ #define USBTV_ISOC_TRANSFERS 16 #define USBTV_ISOC_PACKETS 8 -#define USBTV_WIDTH 720 -#define USBTV_HEIGHT 480 - #define USBTV_CHUNK_SIZE 256 #define USBTV_CHUNK 240 -#define USBTV_CHUNKS (USBTV_WIDTH * USBTV_HEIGHT \ - / 4 / USBTV_CHUNK) /* Chunk header. */ #define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \ @@ -65,6 +60,27 @@ #define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15) #define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff) +#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL) + +/* parameters for supported TV norms */ +struct usbtv_norm_params { + v4l2_std_id norm; + int cap_width, cap_height; +}; + +static struct usbtv_norm_params norm_params[] = { + { + .norm = V4L2_STD_525_60, + .cap_width = 720, + .cap_height = 480, + }, + { + .norm = V4L2_STD_PAL, + .cap_width = 720, + .cap_height = 576, + } +}; + /* A single videobuf2 frame buffer. */ struct usbtv_buf { struct vb2_buffer vb; @@ -94,11 +110,38 @@ struct usbtv { USBTV_COMPOSITE_INPUT, USBTV_SVIDEO_INPUT, } input; + v4l2_std_id norm; + int width, height; + int n_chunks; int iso_size; unsigned int sequence; struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS]; }; +static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm) +{ + int i, ret = 0; + struct usbtv_norm_params *params = NULL; + + for (i = 0; i < ARRAY_SIZE(norm_params); i++) { + if (norm_params[i].norm & norm) { + params = &norm_params[i]; + break; + } + } + + if (params) { + usbtv->width = params->cap_width; + usbtv->height = params->cap_height; + usbtv->n_chunks = usbtv->width * usbtv->height + / 4 / USBTV_CHUNK; + usbtv->norm = params->norm; + } else + ret = -EINVAL; + + return ret; +} + static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size) { int ret; @@ -158,6 +201,57 @@ static int usbtv_select_input(struct usbtv *usbtv, int input) return ret; } +static int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm) +{ + int ret; + static const u16 pal[][2] = { + { USBTV_BASE + 0x001a, 0x0068 }, + { USBTV_BASE + 0x010e, 0x0072 }, + { USBTV_BASE + 0x010f, 0x00a2 }, + { USBTV_BASE + 0x0112, 0x00b0 }, + { USBTV_BASE + 0x0117, 0x0001 }, + { USBTV_BASE + 0x0118, 0x002c }, + { USBTV_BASE + 0x012d, 0x0010 }, + { USBTV_BASE + 0x012f, 0x0020 }, + { USBTV_BASE + 0x024f, 0x0002 }, + { USBTV_BASE + 0x0254, 0x0059 }, + { USBTV_BASE + 0x025a, 0x0016 }, + { USBTV_BASE + 0x025b, 0x0035 }, + { USBTV_BASE + 0x0263, 0x0017 }, + { USBTV_BASE + 0x0266, 0x0016 }, + { USBTV_BASE + 0x0267, 0x0036 } + }; + + static const u16 ntsc[][2] = { + { USBTV_BASE + 0x001a, 0x0079 }, + { USBTV_BASE + 0x010e, 0x0068 }, + { USBTV_BASE + 0x010f, 0x009c }, + { USBTV_BASE + 0x0112, 0x00f0 }, + { USBTV_BASE + 0x0117, 0x0000 }, + { USBTV_BASE + 0x0118, 0x00fc }, + { USBTV_BASE + 0x012d, 0x0004 }, + { USBTV_BASE + 0x012f, 0x0008 }, + { USBTV_BASE + 0x024f, 0x0001 }, + { USBTV_BASE + 0x0254, 0x005f }, + { USBTV_BASE + 0x025a, 0x0012 }, + { USBTV_BASE + 0x025b, 0x0001 }, + { USBTV_BASE + 0x0263, 0x001c }, + { USBTV_BASE + 0x0266, 0x0011 }, + { USBTV_BASE + 0x0267, 0x0005 } + }; + + ret = usbtv_configure_for_norm(usbtv, norm); + + if (!ret) { + if (norm & V4L2_STD_525_60) + ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc)); + else if (norm & V4L2_STD_PAL) + ret = usbtv_set_regs(usbtv, pal, ARRAY_SIZE(pal)); + } + + return ret; +} + static int usbtv_setup_capture(struct usbtv *usbtv) { int ret; @@ -225,26 +319,11 @@ static int usbtv_setup_capture(struct usbtv *usbtv) { USBTV_BASE + 0x0284, 0x0088 }, { USBTV_BASE + 0x0003, 0x0004 }, - { USBTV_BASE + 0x001a, 0x0079 }, { USBTV_BASE + 0x0100, 0x00d3 }, - { USBTV_BASE + 0x010e, 0x0068 }, - { USBTV_BASE + 0x010f, 0x009c }, - { USBTV_BASE + 0x0112, 0x00f0 }, { USBTV_BASE + 0x0115, 0x0015 }, - { USBTV_BASE + 0x0117, 0x0000 }, - { USBTV_BASE + 0x0118, 0x00fc }, - { USBTV_BASE + 0x012d, 0x0004 }, - { USBTV_BASE + 0x012f, 0x0008 }, { USBTV_BASE + 0x0220, 0x002e }, { USBTV_BASE + 0x0225, 0x0008 }, { USBTV_BASE + 0x024e, 0x0002 }, - { USBTV_BASE + 0x024f, 0x0001 }, - { USBTV_BASE + 0x0254, 0x005f }, - { USBTV_BASE + 0x025a, 0x0012 }, - { USBTV_BASE + 0x025b, 0x0001 }, - { USBTV_BASE + 0x0263, 0x001c }, - { USBTV_BASE + 0x0266, 0x0011 }, - { USBTV_BASE + 0x0267, 0x0005 }, { USBTV_BASE + 0x024e, 0x0002 }, { USBTV_BASE + 0x024f, 0x0002 }, }; @@ -253,6 +332,10 @@ static int usbtv_setup_capture(struct usbtv *usbtv) if (ret) return ret; + ret = usbtv_select_norm(usbtv, usbtv->norm); + if (ret) + return ret; + ret = usbtv_select_input(usbtv, usbtv->input); if (ret) return ret; @@ -296,7 +379,7 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) frame_id = USBTV_FRAME_ID(chunk); odd = USBTV_ODD(chunk); chunk_no = USBTV_CHUNK_NO(chunk); - if (chunk_no >= USBTV_CHUNKS) + if (chunk_no >= usbtv->n_chunks) return; /* Beginning of a frame. */ @@ -324,10 +407,10 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) usbtv->chunks_done++; /* Last chunk in a frame, signalling an end */ - if (odd && chunk_no == USBTV_CHUNKS-1) { + if (odd && chunk_no == usbtv->n_chunks-1) { int size = vb2_plane_size(&buf->vb, 0); enum vb2_buffer_state state = usbtv->chunks_done == - USBTV_CHUNKS ? + usbtv->n_chunks ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; @@ -500,6 +583,8 @@ static int usbtv_querycap(struct file *file, void *priv, static int usbtv_enum_input(struct file *file, void *priv, struct v4l2_input *i) { + struct usbtv *dev = video_drvdata(file); + switch (i->index) { case USBTV_COMPOSITE_INPUT: strlcpy(i->name, "Composite", sizeof(i->name)); @@ -512,7 +597,7 @@ static int usbtv_enum_input(struct file *file, void *priv, } i->type = V4L2_INPUT_TYPE_CAMERA; - i->std = V4L2_STD_525_60; + i->std = dev->vdev.tvnorms; return 0; } @@ -531,23 +616,37 @@ static int usbtv_enum_fmt_vid_cap(struct file *file, void *priv, static int usbtv_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - f->fmt.pix.width = USBTV_WIDTH; - f->fmt.pix.height = USBTV_HEIGHT; + struct usbtv *usbtv = video_drvdata(file); + + f->fmt.pix.width = usbtv->width; + f->fmt.pix.height = usbtv->height; f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.bytesperline = USBTV_WIDTH * 2; + f->fmt.pix.bytesperline = usbtv->width * 2; f->fmt.pix.sizeimage = (f->fmt.pix.bytesperline * f->fmt.pix.height); f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; + return 0; } static int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm) { - *norm = V4L2_STD_525_60; + struct usbtv *usbtv = video_drvdata(file); + *norm = usbtv->norm; return 0; } +static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) +{ + int ret = -EINVAL; + struct usbtv *usbtv = video_drvdata(file); + + if ((norm & V4L2_STD_525_60) || (norm & V4L2_STD_PAL)) + ret = usbtv_select_norm(usbtv, norm); + + return ret; +} + static int usbtv_g_input(struct file *file, void *priv, unsigned int *i) { struct usbtv *usbtv = video_drvdata(file); @@ -561,13 +660,6 @@ static int usbtv_s_input(struct file *file, void *priv, unsigned int i) return usbtv_select_input(usbtv, i); } -static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) -{ - if (norm & V4L2_STD_525_60) - return 0; - return -EINVAL; -} - struct v4l2_ioctl_ops usbtv_ioctl_ops = { .vidioc_querycap = usbtv_querycap, .vidioc_enum_input = usbtv_enum_input, @@ -604,10 +696,12 @@ static int usbtv_queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + struct usbtv *usbtv = vb2_get_drv_priv(vq); + if (*nbuffers < 2) *nbuffers = 2; *nplanes = 1; - sizes[0] = USBTV_WIDTH * USBTV_HEIGHT / 2 * sizeof(u32); + sizes[0] = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); return 0; } @@ -690,7 +784,11 @@ static int usbtv_probe(struct usb_interface *intf, return -ENOMEM; usbtv->dev = dev; usbtv->udev = usb_get_dev(interface_to_usbdev(intf)); + usbtv->iso_size = size; + + (void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60); + spin_lock_init(&usbtv->buflock); mutex_init(&usbtv->v4l2_lock); mutex_init(&usbtv->vb2q_lock); @@ -727,7 +825,7 @@ static int usbtv_probe(struct usb_interface *intf, usbtv->vdev.release = video_device_release_empty; usbtv->vdev.fops = &usbtv_fops; usbtv->vdev.ioctl_ops = &usbtv_ioctl_ops; - usbtv->vdev.tvnorms = V4L2_STD_525_60; + usbtv->vdev.tvnorms = USBTV_TV_STD; usbtv->vdev.queue = &usbtv->vb2q; usbtv->vdev.lock = &usbtv->v4l2_lock; set_bit(V4L2_FL_USE_FH_PRIO, &usbtv->vdev.flags); -- cgit v0.10.2 From 98c24dcd6392784b1cd0dca462abe6f91f0cadc9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 6 Nov 2013 05:39:35 -0300 Subject: [media] em28xx-video: Swap release order to avoid lock nesting vb2_fop_release might take the video queue mutex lock. In order to avoid nesting mutexes the private mutex is taken after the fop_release has finished. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index fc5d60e..dd19c9f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1664,8 +1664,8 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); - mutex_lock(&dev->lock); vb2_fop_release(filp); + mutex_lock(&dev->lock); if (dev->users == 1) { /* the device is already disconnect, -- cgit v0.10.2 From a3d94dafbba04f498beacec68a32b063bbf62d08 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 6 Nov 2013 11:40:18 -0300 Subject: [media] ths7303: Declare as static a private function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git grep shows that the function is only called from ths7303.c Fix this build warning: CC drivers/media/i2c/ths7303.o drivers/media/i2c/ths7303.c:86:5: warning: no previous prototype for ‘ths7303_setval’ [-Wmissing-prototypes] int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) ^ Signed-off-by: Ricardo Ribalda Delgado Acked-by: Laurent Pinchart Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 42276d9..ed9ae88 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -83,7 +83,8 @@ static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val) } /* following function is used to set ths7303 */ -int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) +static int ths7303_setval(struct v4l2_subdev *sd, + enum ths7303_filter_mode mode) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ths7303_state *state = to_state(sd); -- cgit v0.10.2 From fe4a0f1c7d316734b4614ce626e6126bc302a4b6 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Mon, 4 Nov 2013 23:18:15 -0300 Subject: [media] marvell-ccic: drop resource free in driver remove The mmp-driver is using devm_* to allocate the resource. The old resource release methods are not appropriate here. Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Cc: stable@vger.kernel.org # for v3.12 and up Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 3458fa0..70cb57f 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -478,18 +478,11 @@ out_deinit_clk: static int mmpcam_remove(struct mmp_camera *cam) { struct mcam_camera *mcam = &cam->mcam; - struct mmp_camera_platform_data *pdata; mmpcam_remove_device(cam); mccic_shutdown(mcam); mmpcam_power_down(mcam); - pdata = cam->pdev->dev.platform_data; - gpio_free(pdata->sensor_reset_gpio); - gpio_free(pdata->sensor_power_gpio); mcam_deinit_clk(mcam); - iounmap(cam->power_regs); - iounmap(mcam->regs); - kfree(cam); return 0; } -- cgit v0.10.2 From fa507e4d32bf6c35eb5fe7dbc0593ae3723c9575 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 5 Nov 2013 05:29:07 -0300 Subject: [media] media: marvell-ccic: use devm to release clk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch uses devm to release the clks instead of releasing manually. And it adds enable/disable mipi_clk when getting its rate. Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Acked-by: Uwe Kleine-König Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 70cb57f..054507f 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -142,12 +142,6 @@ static int mmpcam_power_up(struct mcam_camera *mcam) struct mmp_camera *cam = mcam_to_cam(mcam); struct mmp_camera_platform_data *pdata; - if (mcam->bus_type == V4L2_MBUS_CSI2) { - cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); - if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) - return PTR_ERR(cam->mipi_clk); - } - /* * Turn on power and clocks to the controller. */ @@ -186,12 +180,6 @@ static void mmpcam_power_down(struct mcam_camera *mcam) gpio_set_value(pdata->sensor_power_gpio, 0); gpio_set_value(pdata->sensor_reset_gpio, 0); - if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) { - if (cam->mipi_clk) - devm_clk_put(mcam->dev, cam->mipi_clk); - cam->mipi_clk = NULL; - } - mcam_clk_disable(mcam); } @@ -292,8 +280,9 @@ void mmpcam_calc_dphy(struct mcam_camera *mcam) return; /* get the escape clk, this is hard coded */ + clk_prepare_enable(cam->mipi_clk); tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12; - + clk_disable_unprepare(cam->mipi_clk); /* * dphy[2] - CSI2_DPHY6: * bit 0 ~ bit 7: CK Term Enable @@ -325,19 +314,6 @@ static irqreturn_t mmpcam_irq(int irq, void *data) return IRQ_RETVAL(handled); } -static void mcam_deinit_clk(struct mcam_camera *mcam) -{ - unsigned int i; - - for (i = 0; i < NR_MCAM_CLK; i++) { - if (!IS_ERR(mcam->clk[i])) { - if (mcam->clk[i]) - devm_clk_put(mcam->dev, mcam->clk[i]); - } - mcam->clk[i] = NULL; - } -} - static void mcam_init_clk(struct mcam_camera *mcam) { unsigned int i; @@ -371,7 +347,6 @@ static int mmpcam_probe(struct platform_device *pdev) if (cam == NULL) return -ENOMEM; cam->pdev = pdev; - cam->mipi_clk = NULL; INIT_LIST_HEAD(&cam->devlist); mcam = &cam->mcam; @@ -387,6 +362,11 @@ static int mmpcam_probe(struct platform_device *pdev) mcam->mclk_div = pdata->mclk_div; mcam->bus_type = pdata->bus_type; mcam->dphy = pdata->dphy; + if (mcam->bus_type == V4L2_MBUS_CSI2) { + cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); + if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) + return PTR_ERR(cam->mipi_clk); + } mcam->mipi_enabled = false; mcam->lane = pdata->lane; mcam->chip_id = MCAM_ARMADA610; @@ -444,7 +424,7 @@ static int mmpcam_probe(struct platform_device *pdev) */ ret = mmpcam_power_up(mcam); if (ret) - goto out_deinit_clk; + return ret; ret = mccic_register(mcam); if (ret) goto out_power_down; @@ -469,8 +449,6 @@ out_unregister: mccic_shutdown(mcam); out_power_down: mmpcam_power_down(mcam); -out_deinit_clk: - mcam_deinit_clk(mcam); return ret; } @@ -482,7 +460,6 @@ static int mmpcam_remove(struct mmp_camera *cam) mmpcam_remove_device(cam); mccic_shutdown(mcam); mmpcam_power_down(mcam); - mcam_deinit_clk(mcam); return 0; } -- cgit v0.10.2 From 68fd02d15bed4a680c23f0aaeee9bc871bf1630c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 21 Oct 2013 16:56:36 -0300 Subject: [media] rtl2830: add parent for I2C adapter i2c i2c-6: adapter [RTL2830 tuner I2C adapter] registered BUG: unable to handle kernel NULL pointer dereference at 0000000000000220 IP: [] i2c_register_adapter+0x130/0x390 [i2c_core] Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 7efb796..50e8b63 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -710,6 +710,7 @@ struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg, sizeof(priv->tuner_i2c_adapter.name)); priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo; priv->tuner_i2c_adapter.algo_data = NULL; + priv->tuner_i2c_adapter.dev.parent = &i2c->dev; i2c_set_adapdata(&priv->tuner_i2c_adapter, priv); if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) { dev_err(&i2c->dev, -- cgit v0.10.2 From 646959059c56374896010fb245bec5338cbac44d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 18 Oct 2013 00:07:11 -0300 Subject: [media] mt9p031: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly to avoid build breakage in the future. Signed-off-by: Sachin Kamat Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 4734836..1c2303d 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 59f0ad8076816d13f7cba80d2b178ff5ab787e2e Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - Core This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds the driver core, registers definitions and documentation. Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt new file mode 100644 index 0000000..25d9b40 --- /dev/null +++ b/Documentation/video4linux/omap4_camera.txt @@ -0,0 +1,60 @@ + OMAP4 ISS Driver + ================ + +Introduction +------------ + +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS), +Which contains several components that can be categorized in 3 big groups: + +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2) +- ISP (Image Signal Processor) +- SIMCOP (Still Image Coprocessor) + +For more information, please look in [1] for latest version of: + "OMAP4430 Multimedia Device Silicon Revision 2.x" + +As of Revision AB, the ISS is described in detail in section 8. + +This driver is supporting _only_ the CSI2-A/B interfaces for now. + +It makes use of the Media Controller framework [2], and inherited most of the +code from OMAP3 ISP driver (found under drivers/media/platform/omap3isp/*), +except that it doesn't need an IOMMU now for ISS buffers memory mapping. + +Supports usage of MMAP buffers only (for now). + +Tested platforms +---------------- + +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in + which only the last one is supported, outputting YUV422 frames). + +- TI Blaze MDP, w/ OMAP4430 ES2.2 EMU (Contains 1 IMX060 & 2 OV5650 sensors, in + which only the OV5650 are supported, outputting RAW10 frames). + +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with + following sensors: + * OV5640 + * OV5650 + +- Tested on mainline kernel: + + http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary + + Tag: v3.3 (commit c16fa4f2ad19908a47c63d8fa436a1178438c7e7) + +File list +--------- +drivers/staging/media/omap4iss/ +include/media/omap4iss.h + +References +---------- + +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62 +[2] http://lwn.net/Articles/420485/ +[3] http://www.spinics.net/lists/linux-media/msg44370.html +-- +Author: Sergio Aguirre +Copyright (C) 2012, Texas Instruments diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c new file mode 100644 index 0000000..d054d9b --- /dev/null +++ b/drivers/staging/media/omap4iss/iss.c @@ -0,0 +1,1477 @@ +/* + * TI OMAP4 ISS V4L2 Driver + * + * Copyright (C) 2012, Texas Instruments + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" + +#define ISS_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###ISS " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_##name)) + +static void iss_print_status(struct iss_device *iss) +{ + dev_dbg(iss->dev, "-------------ISS HL Register dump-------------\n"); + + ISS_PRINT_REGISTER(iss, HL_REVISION); + ISS_PRINT_REGISTER(iss, HL_SYSCONFIG); + ISS_PRINT_REGISTER(iss, HL_IRQSTATUS_5); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_SET); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_CLR); + ISS_PRINT_REGISTER(iss, CTRL); + ISS_PRINT_REGISTER(iss, CLKCTRL); + ISS_PRINT_REGISTER(iss, CLKSTAT); + + dev_dbg(iss->dev, "-----------------------------------------------\n"); +} + +/* + * omap4iss_flush - Post pending L3 bus writes by doing a register readback + * @iss: OMAP4 ISS device + * + * In order to force posting of pending writes, we need to write and + * readback the same register, in this case the revision register. + * + * See this link for reference: + * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html + */ +void omap4iss_flush(struct iss_device *iss) +{ + writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); +} + +/* + * iss_enable_interrupts - Enable ISS interrupts. + * @iss: OMAP4 ISS device + */ +static void iss_enable_interrupts(struct iss_device *iss) +{ + static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB | ISS_HL_IRQ_ISP(0); + + /* Enable HL interrupts */ + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET); + +} + +/* + * iss_disable_interrupts - Disable ISS interrupts. + * @iss: OMAP4 ISS device + */ +static void iss_disable_interrupts(struct iss_device *iss) +{ + writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR); +} + +/* + * iss_isp_enable_interrupts - Enable ISS ISP interrupts. + * @iss: OMAP4 ISS device + */ +void omap4iss_isp_enable_interrupts(struct iss_device *iss) +{ + static const u32 isp_irq = ISP5_IRQ_OCP_ERR | + ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_OVF | + ISP5_IRQ_RSZ_INT_DMA | + ISP5_IRQ_ISIF0; + + /* Enable ISP interrupts */ + writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQSTATUS(0)); + writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_SET(0)); +} + +/* + * iss_isp_disable_interrupts - Disable ISS interrupts. + * @iss: OMAP4 ISS device + */ +void omap4iss_isp_disable_interrupts(struct iss_device *iss) +{ + writel(-1, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_CLR(0)); +} + +int omap4iss_get_external_info(struct iss_pipeline *pipe, + struct media_link *link) +{ + struct iss_device *iss = + container_of(pipe, struct iss_video, pipe)->iss; + struct v4l2_subdev_format fmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + int ret; + + if (!pipe->external) + return 0; + + if (pipe->external_rate) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + + fmt.pad = link->source->index; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(link->sink->entity), + pad, get_fmt, NULL, &fmt); + if (ret < 0) + return -EPIPE; + + pipe->external_bpp = omap4iss_video_format_info(fmt.format.code)->bpp; + + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.id = V4L2_CID_PIXEL_RATE; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id); + ctrls.count = 1; + ctrls.controls = &ctrl; + + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); + if (ret < 0) { + dev_warn(iss->dev, "no pixel rate control in subdev %s\n", + pipe->external->name); + return ret; + } + + pipe->external_rate = ctrl.value64; + + return 0; +} + +/* + * Configure the bridge. Valid inputs are + * + * IPIPEIF_INPUT_CSI2A: CSI2a receiver + * IPIPEIF_INPUT_CSI2B: CSI2b receiver + * + * The bridge and lane shifter are configured according to the selected input + * and the ISP platform data. + */ +void omap4iss_configure_bridge(struct iss_device *iss, + enum ipipeif_input_entity input) +{ + u32 issctrl_val; + u32 isp5ctrl_val; + + issctrl_val = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); + issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK; + issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK; + + isp5ctrl_val = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + switch (input) { + case IPIPEIF_INPUT_CSI2A: + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A; + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; + break; + + case IPIPEIF_INPUT_CSI2B: + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B; + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; + break; + + default: + return; + } + + issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING; + + isp5ctrl_val |= ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE; + + writel(issctrl_val, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); + writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); +} + +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) +{ + static const char *name[] = { + "ISP_IRQ0", + "ISP_IRQ1", + "ISP_IRQ2", + "ISP_IRQ3", + "CSIA_IRQ", + "CSIB_IRQ", + "CCP2_IRQ0", + "CCP2_IRQ1", + "CCP2_IRQ2", + "CCP2_IRQ3", + "CBUFF_IRQ", + "BTE_IRQ", + "SIMCOP_IRQ0", + "SIMCOP_IRQ1", + "SIMCOP_IRQ2", + "SIMCOP_IRQ3", + "CCP2_IRQ8", + "HS_VS_IRQ", + "res18", + "res19", + "res20", + "res21", + "res22", + "res23", + "res24", + "res25", + "res26", + "res27", + "res28", + "res29", + "res30", + "res31", + }; + int i; + + dev_dbg(iss->dev, "ISS IRQ: "); + + for (i = 0; i < ARRAY_SIZE(name); i++) { + if ((1 << i) & irqstatus) + pr_cont("%s ", name[i]); + } + pr_cont("\n"); +} + +/* + * iss_isr - Interrupt Service Routine for ISS module. + * @irq: Not used currently. + * @_iss: Pointer to the OMAP4 ISS device + * + * Handles the corresponding callback if plugged in. + * + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the + * IRQ wasn't handled. + */ +static irqreturn_t iss_isr(int irq, void *_iss) +{ + static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF | + ISP5_IRQ_ISIF0; + static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_OVF | + ISP5_IRQ_RSZ_INT_DMA; + struct iss_device *iss = _iss; + u32 irqstatus; + + irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + + if (irqstatus & ISS_HL_IRQ_CSIA) + omap4iss_csi2_isr(&iss->csi2a); + + if (irqstatus & ISS_HL_IRQ_CSIB) + omap4iss_csi2_isr(&iss->csi2b); + + if (irqstatus & ISS_HL_IRQ_ISP(0)) { + u32 isp_irqstatus = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + + ISP5_IRQSTATUS(0)); + writel(isp_irqstatus, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + + ISP5_IRQSTATUS(0)); + + if (isp_irqstatus & ISP5_IRQ_OCP_ERR) + dev_dbg(iss->dev, "ISP5 OCP Error!\n"); + + if (isp_irqstatus & ipipeif_events) { + omap4iss_ipipeif_isr(&iss->ipipeif, + isp_irqstatus & ipipeif_events); + } + + if (isp_irqstatus & resizer_events) + omap4iss_resizer_isr(&iss->resizer, + isp_irqstatus & resizer_events); + } + + omap4iss_flush(iss); + +#if defined(DEBUG) && defined(ISS_ISR_DEBUG) + iss_isr_dbg(iss, irqstatus); +#endif + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Pipeline power management + * + * Entities must be powered up when part of a pipeline that contains at least + * one open video device node. + * + * To achieve this use the entity use_count field to track the number of users. + * For entities corresponding to video device nodes the use_count field stores + * the users count of the node. For entities corresponding to subdevs the + * use_count field stores the total number of users of all video device nodes + * in the pipeline. + * + * The omap4iss_pipeline_pm_use() function must be called in the open() and + * close() handlers of video device nodes. It increments or decrements the use + * count of all subdev entities in the pipeline. + * + * To react to link management on powered pipelines, the link setup notification + * callback updates the use count of all entities in the source and sink sides + * of the link. + */ + +/* + * iss_pipeline_pm_use_count - Count the number of users of a pipeline + * @entity: The entity + * + * Return the total number of users of all video device nodes in the pipeline. + */ +static int iss_pipeline_pm_use_count(struct media_entity *entity) +{ + struct media_entity_graph graph; + int use = 0; + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + use += entity->use_count; + } + + return use; +} + +/* + * iss_pipeline_pm_power_one - Apply power change to an entity + * @entity: The entity + * @change: Use count change + * + * Change the entity use count by @change. If the entity is a subdev update its + * power state by calling the core::s_power operation when the use count goes + * from 0 to != 0 or from != 0 to 0. + * + * Return 0 on success or a negative error code on failure. + */ +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + + subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev != NULL) { + int ret; + + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev != NULL) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +/* + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline + * @entity: The entity + * @change: Use count change + * + * Walk the pipeline to update the use count and the power state of all non-node + * entities. + * + * Return 0 on success or a negative error code on failure. + */ +static int iss_pipeline_pm_power(struct media_entity *entity, int change) +{ + struct media_entity_graph graph; + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + + media_entity_graph_walk_start(&graph, entity); + + while (!ret && (entity = media_entity_graph_walk_next(&graph))) + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + ret = iss_pipeline_pm_power_one(entity, change); + + if (!ret) + return 0; + + media_entity_graph_walk_start(&graph, first); + + while ((first = media_entity_graph_walk_next(&graph)) + && first != entity) + if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + iss_pipeline_pm_power_one(first, -change); + + return ret; +} + +/* + * omap4iss_pipeline_pm_use - Update the use count of an entity + * @entity: The entity + * @use: Use (1) or stop using (0) the entity + * + * Update the use count of all entities in the pipeline and power entities on or + * off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. No failure can occur when the use parameter is + * set to 0. + */ +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) +{ + int change = use ? 1 : -1; + int ret; + + mutex_lock(&entity->parent->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = iss_pipeline_pm_power(entity, change); + if (ret < 0) + entity->use_count -= change; + + mutex_unlock(&entity->parent->graph_mutex); + + return ret; +} + +/* + * iss_pipeline_link_notify - Link management notification callback + * @link: The link + * @flags: New link flags that will be applied + * + * React to link management on powered pipelines by updating the use count of + * all entities in the source and sink sides of the link. Entities are powered + * on or off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. This function will not fail for disconnection + * events. + */ +static int iss_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use = iss_pipeline_pm_use_count(source); + int sink_use = iss_pipeline_pm_use_count(sink); + int ret; + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(link->flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + iss_pipeline_pm_power(source, -sink_use); + iss_pipeline_pm_power(sink, -source_use); + return 0; + } + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + ret = iss_pipeline_pm_power(source, sink_use); + if (ret < 0) + return ret; + + ret = iss_pipeline_pm_power(sink, source_use); + if (ret < 0) + iss_pipeline_pm_power(source, -sink_use); + + return ret; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pipeline stream management + */ + +/* + * iss_pipeline_enable - Enable streaming on a pipeline + * @pipe: ISS pipeline + * @mode: Stream mode (single shot or continuous) + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int iss_pipeline_enable(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state mode) +{ + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); + spin_unlock_irqrestore(&pipe->lock, flags); + + pipe->do_propagation = false; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, mode); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + iss_print_status(pipe->output->iss); + return 0; +} + +/* + * iss_pipeline_disable - Disable streaming on a pipeline + * @pipe: ISS pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. Wait synchronously for the modules to be stopped if + * necessary. + */ +static int iss_pipeline_disable(struct iss_pipeline *pipe) +{ + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int failure = 0; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + return failure; +} + +/* + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: ISS pipeline + * @state: Stream state (stopped, single shot or continuous) + * + * Set the pipeline to the given stream state. Pipelines can be started in + * single-shot or continuous mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. The pipeline state is not updated when the operation + * fails, except when stopping the pipeline. + */ +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state state) +{ + int ret; + + if (state == ISS_PIPELINE_STREAM_STOPPED) + ret = iss_pipeline_disable(pipe); + else + ret = iss_pipeline_enable(pipe, state); + + if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED) + pipe->stream_state = state; + + return ret; +} + +/* + * iss_pipeline_is_last - Verify if entity has an enabled link to the output + * video node + * @me: ISS module's media entity + * + * Returns 1 if the entity has an enabled link to the output video node or 0 + * otherwise. It's true only while pipeline can have no more than one output + * node. + */ +static int iss_pipeline_is_last(struct media_entity *me) +{ + struct iss_pipeline *pipe; + struct media_pad *pad; + + if (!me->pipe) + return 0; + pipe = to_iss_pipeline(me); + if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + pad = media_entity_remote_pad(&pipe->output->pad); + return pad->entity == me; +} + +static int iss_reset(struct iss_device *iss) +{ + unsigned long timeout = 0; + + writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) | + ISS_HL_SYSCONFIG_SOFTRESET, + iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG); + + while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) & + ISS_HL_SYSCONFIG_SOFTRESET) { + if (timeout++ > 10000) { + dev_alert(iss->dev, "cannot reset ISS\n"); + return -ETIMEDOUT; + } + udelay(1); + } + + return 0; +} + +static int iss_isp_reset(struct iss_device *iss) +{ + unsigned long timeout = 0; + + /* Fist, ensure that the ISP is IDLE (no transactions happening) */ + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & + ~ISP5_SYSCONFIG_STANDBYMODE_MASK) | + ISP5_SYSCONFIG_STANDBYMODE_SMART, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) | + ISP5_CTRL_MSTANDBY, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + for (;;) { + if (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & + ISP5_CTRL_MSTANDBY_WAIT) + break; + if (timeout++ > 1000) { + dev_alert(iss->dev, "cannot set ISP5 to standby\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + /* Now finally, do the reset */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) | + ISP5_SYSCONFIG_SOFTRESET, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + + timeout = 0; + while (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & + ISP5_SYSCONFIG_SOFTRESET) { + if (timeout++ > 1000) { + dev_alert(iss->dev, "cannot reset ISP5\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +/* + * iss_module_sync_idle - Helper to sync module with its idle state + * @me: ISS submodule's media entity + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISS submodule needs to wait for next interrupt. If + * yes, makes the caller to sleep while waiting for such event. + */ +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping) +{ + struct iss_pipeline *pipe = to_iss_pipeline(me); + struct iss_video *video = pipe->output; + unsigned long flags; + + if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED || + (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT && + !iss_pipeline_ready(pipe))) + return 0; + + /* + * atomic_set() doesn't include memory barrier on ARM platform for SMP + * scenario. We'll call it here to avoid race conditions. + */ + atomic_set(stopping, 1); + smp_wmb(); + + /* + * If module is the last one, it's writing to memory. In this case, + * it's necessary to check if the module is already paused due to + * DMA queue underrun or if it has to wait for next interrupt to be + * idle. + * If it isn't the last one, the function won't sleep but *stopping + * will still be set to warn next submodule caller's interrupt the + * module wants to be idle. + */ + if (!iss_pipeline_is_last(me)) + return 0; + + spin_lock_irqsave(&video->qlock, flags); + if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { + spin_unlock_irqrestore(&video->qlock, flags); + atomic_set(stopping, 0); + smp_wmb(); + return 0; + } + spin_unlock_irqrestore(&video->qlock, flags); + if (!wait_event_timeout(*wait, !atomic_read(stopping), + msecs_to_jiffies(1000))) { + atomic_set(stopping, 0); + smp_wmb(); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISS submodule was stopping. In case of yes, it + * notices the caller by setting stopping to 0 and waking up the wait queue. + * Returns 1 if it was stopping or 0 otherwise. + */ +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping) +{ + if (atomic_cmpxchg(stopping, 1, 0)) { + wake_up(wait); + return 1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Clock management + */ + +#define ISS_CLKCTRL_MASK (ISS_CLKCTRL_CSI2_A |\ + ISS_CLKCTRL_CSI2_B |\ + ISS_CLKCTRL_ISP) + +static int __iss_subclk_update(struct iss_device *iss) +{ + u32 clk = 0; + int ret = 0, timeout = 1000; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A) + clk |= ISS_CLKCTRL_CSI2_A; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_B) + clk |= ISS_CLKCTRL_CSI2_B; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_ISP) + clk |= ISS_CLKCTRL_ISP; + + writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) & + ~ISS_CLKCTRL_MASK) | clk, + iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL); + + /* Wait for HW assertion */ + while (--timeout > 0) { + udelay(1); + if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) & + ISS_CLKCTRL_MASK) == clk) + break; + } + + if (!timeout) + ret = -EBUSY; + + return ret; +} + +int omap4iss_subclk_enable(struct iss_device *iss, + enum iss_subclk_resource res) +{ + iss->subclk_resources |= res; + + return __iss_subclk_update(iss); +} + +int omap4iss_subclk_disable(struct iss_device *iss, + enum iss_subclk_resource res) +{ + iss->subclk_resources &= ~res; + + return __iss_subclk_update(iss); +} + +#define ISS_ISP5_CLKCTRL_MASK (ISP5_CTRL_BL_CLK_ENABLE |\ + ISP5_CTRL_ISIF_CLK_ENABLE |\ + ISP5_CTRL_H3A_CLK_ENABLE |\ + ISP5_CTRL_RSZ_CLK_ENABLE |\ + ISP5_CTRL_IPIPE_CLK_ENABLE |\ + ISP5_CTRL_IPIPEIF_CLK_ENABLE) + +static int __iss_isp_subclk_update(struct iss_device *iss) +{ + u32 clk = 0; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_ISIF) + clk |= ISP5_CTRL_ISIF_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_H3A) + clk |= ISP5_CTRL_H3A_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_RSZ) + clk |= ISP5_CTRL_RSZ_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPE) + clk |= ISP5_CTRL_IPIPE_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPEIF) + clk |= ISP5_CTRL_IPIPEIF_CLK_ENABLE; + + if (clk) + clk |= ISP5_CTRL_BL_CLK_ENABLE; + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & + ~ISS_ISP5_CLKCTRL_MASK) | clk, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + return 0; +} + +int omap4iss_isp_subclk_enable(struct iss_device *iss, + enum iss_isp_subclk_resource res) +{ + iss->isp_subclk_resources |= res; + + return __iss_isp_subclk_update(iss); +} + +int omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res) +{ + iss->isp_subclk_resources &= ~res; + + return __iss_isp_subclk_update(iss); +} + +/* + * iss_enable_clocks - Enable ISS clocks + * @iss: OMAP4 ISS device + * + * Return 0 if successful, or clk_enable return value if any of tthem fails. + */ +static int iss_enable_clocks(struct iss_device *iss) +{ + int r; + + r = clk_enable(iss->iss_fck); + if (r) { + dev_err(iss->dev, "clk_enable iss_fck failed\n"); + return r; + } + + r = clk_enable(iss->iss_ctrlclk); + if (r) { + dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n"); + goto out_clk_enable_ctrlclk; + } + return 0; + +out_clk_enable_ctrlclk: + clk_disable(iss->iss_fck); + return r; +} + +/* + * iss_disable_clocks - Disable ISS clocks + * @iss: OMAP4 ISS device + */ +static void iss_disable_clocks(struct iss_device *iss) +{ + clk_disable(iss->iss_ctrlclk); + clk_disable(iss->iss_fck); +} + +static void iss_put_clocks(struct iss_device *iss) +{ + if (iss->iss_fck) { + clk_put(iss->iss_fck); + iss->iss_fck = NULL; + } + + if (iss->iss_ctrlclk) { + clk_put(iss->iss_ctrlclk); + iss->iss_ctrlclk = NULL; + } +} + +static int iss_get_clocks(struct iss_device *iss) +{ + iss->iss_fck = clk_get(iss->dev, "iss_fck"); + if (IS_ERR(iss->iss_fck)) { + dev_err(iss->dev, "Unable to get iss_fck clock info\n"); + iss_put_clocks(iss); + return PTR_ERR(iss->iss_fck); + } + + iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk"); + if (IS_ERR(iss->iss_ctrlclk)) { + dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n"); + iss_put_clocks(iss); + return PTR_ERR(iss->iss_fck); + } + + return 0; +} + +/* + * omap4iss_get - Acquire the ISS resource. + * + * Initializes the clocks for the first acquire. + * + * Increment the reference count on the ISS. If the first reference is taken, + * enable clocks and power-up all submodules. + * + * Return a pointer to the ISS device structure, or NULL if an error occurred. + */ +struct iss_device *omap4iss_get(struct iss_device *iss) +{ + struct iss_device *__iss = iss; + + if (iss == NULL) + return NULL; + + mutex_lock(&iss->iss_mutex); + if (iss->ref_count > 0) + goto out; + + if (iss_enable_clocks(iss) < 0) { + __iss = NULL; + goto out; + } + + iss_enable_interrupts(iss); + +out: + if (__iss != NULL) + iss->ref_count++; + mutex_unlock(&iss->iss_mutex); + + return __iss; +} + +/* + * omap4iss_put - Release the ISS + * + * Decrement the reference count on the ISS. If the last reference is released, + * power-down all submodules, disable clocks and free temporary buffers. + */ +void omap4iss_put(struct iss_device *iss) +{ + if (iss == NULL) + return; + + mutex_lock(&iss->iss_mutex); + BUG_ON(iss->ref_count == 0); + if (--iss->ref_count == 0) { + iss_disable_interrupts(iss); + iss_disable_clocks(iss); + } + mutex_unlock(&iss->iss_mutex); +} + +static int iss_map_mem_resource(struct platform_device *pdev, + struct iss_device *iss, + enum iss_mem_resources res) +{ + struct resource *mem; + + /* request the mem region for the camera registers */ + + mem = platform_get_resource(pdev, IORESOURCE_MEM, res); + if (!mem) { + dev_err(iss->dev, "no mem resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(iss->dev, + "cannot reserve camera register I/O region\n"); + return -ENODEV; + } + iss->res[res] = mem; + + /* map the region */ + iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem)); + if (!iss->regs[res]) { + dev_err(iss->dev, "cannot map camera register I/O region\n"); + return -ENODEV; + } + + return 0; +} + +static void iss_unregister_entities(struct iss_device *iss) +{ + omap4iss_resizer_unregister_entities(&iss->resizer); + omap4iss_ipipe_unregister_entities(&iss->ipipe); + omap4iss_ipipeif_unregister_entities(&iss->ipipeif); + omap4iss_csi2_unregister_entities(&iss->csi2a); + omap4iss_csi2_unregister_entities(&iss->csi2b); + + v4l2_device_unregister(&iss->v4l2_dev); + media_device_unregister(&iss->media_dev); +} + +/* + * iss_register_subdev_group - Register a group of subdevices + * @iss: OMAP4 ISS device + * @board_info: I2C subdevs board information array + * + * Register all I2C subdevices in the board_info array. The array must be + * terminated by a NULL entry, and the first entry must be the sensor. + * + * Return a pointer to the sensor media entity if it has been successfully + * registered, or NULL otherwise. + */ +static struct v4l2_subdev * +iss_register_subdev_group(struct iss_device *iss, + struct iss_subdev_i2c_board_info *board_info) +{ + struct v4l2_subdev *sensor = NULL; + unsigned int first; + + if (board_info->board_info == NULL) + return NULL; + + for (first = 1; board_info->board_info; ++board_info, first = 0) { + struct v4l2_subdev *subdev; + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(board_info->i2c_adapter_id); + if (adapter == NULL) { + dev_err(iss->dev, "%s: Unable to get I2C adapter %d for " + "device %s\n", __func__, + board_info->i2c_adapter_id, + board_info->board_info->type); + continue; + } + + subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter, + board_info->board_info, NULL); + if (subdev == NULL) { + dev_err(iss->dev, "%s: Unable to register subdev %s\n", + __func__, board_info->board_info->type); + continue; + } + + if (first) + sensor = subdev; + } + + return sensor; +} + +static int iss_register_entities(struct iss_device *iss) +{ + struct iss_platform_data *pdata = iss->pdata; + struct iss_v4l2_subdevs_group *subdevs; + int ret; + + iss->media_dev.dev = iss->dev; + strlcpy(iss->media_dev.model, "TI OMAP4 ISS", + sizeof(iss->media_dev.model)); + iss->media_dev.hw_revision = iss->revision; + iss->media_dev.link_notify = iss_pipeline_link_notify; + ret = media_device_register(&iss->media_dev); + if (ret < 0) { + printk(KERN_ERR "%s: Media device registration failed (%d)\n", + __func__, ret); + return ret; + } + + iss->v4l2_dev.mdev = &iss->media_dev; + ret = v4l2_device_register(iss->dev, &iss->v4l2_dev); + if (ret < 0) { + printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", + __func__, ret); + goto done; + } + + /* Register internal entities */ + ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_csi2_register_entities(&iss->csi2b, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_ipipeif_register_entities(&iss->ipipeif, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_ipipe_register_entities(&iss->ipipe, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_resizer_register_entities(&iss->resizer, &iss->v4l2_dev); + if (ret < 0) + goto done; + + /* Register external entities */ + for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) { + struct v4l2_subdev *sensor; + struct media_entity *input; + unsigned int flags; + unsigned int pad; + + sensor = iss_register_subdev_group(iss, subdevs->subdevs); + if (sensor == NULL) + continue; + + sensor->host_priv = subdevs; + + /* Connect the sensor to the correct interface module. + * CSI2a receiver through CSIPHY1, or + * CSI2b receiver through CSIPHY2 + */ + switch (subdevs->interface) { + case ISS_INTERFACE_CSI2A_PHY1: + input = &iss->csi2a.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + case ISS_INTERFACE_CSI2B_PHY2: + input = &iss->csi2b.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + default: + printk(KERN_ERR "%s: invalid interface type %u\n", + __func__, subdevs->interface); + ret = -EINVAL; + goto done; + } + + ret = media_entity_create_link(&sensor->entity, 0, input, pad, + flags); + if (ret < 0) + goto done; + } + + ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev); + +done: + if (ret < 0) + iss_unregister_entities(iss); + + return ret; +} + +static void iss_cleanup_modules(struct iss_device *iss) +{ + omap4iss_csi2_cleanup(iss); + omap4iss_ipipeif_cleanup(iss); + omap4iss_ipipe_cleanup(iss); + omap4iss_resizer_cleanup(iss); +} + +static int iss_initialize_modules(struct iss_device *iss) +{ + int ret; + + ret = omap4iss_csiphy_init(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI PHY initialization failed\n"); + goto error_csiphy; + } + + ret = omap4iss_csi2_init(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI2 initialization failed\n"); + goto error_csi2; + } + + ret = omap4iss_ipipeif_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPEIF initialization failed\n"); + goto error_ipipeif; + } + + ret = omap4iss_ipipe_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPE initialization failed\n"); + goto error_ipipe; + } + + ret = omap4iss_resizer_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP RESIZER initialization failed\n"); + goto error_resizer; + } + + /* Connect the submodules. */ + ret = media_entity_create_link( + &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + return 0; + +error_link: + omap4iss_resizer_cleanup(iss); +error_resizer: + omap4iss_ipipe_cleanup(iss); +error_ipipe: + omap4iss_ipipeif_cleanup(iss); +error_ipipeif: + omap4iss_csi2_cleanup(iss); +error_csi2: +error_csiphy: + return ret; +} + +static int iss_probe(struct platform_device *pdev) +{ + struct iss_platform_data *pdata = pdev->dev.platform_data; + struct iss_device *iss; + int i, ret; + + if (pdata == NULL) + return -EINVAL; + + iss = kzalloc(sizeof(*iss), GFP_KERNEL); + if (!iss) { + dev_err(&pdev->dev, "Could not allocate memory\n"); + return -ENOMEM; + } + + mutex_init(&iss->iss_mutex); + + iss->dev = &pdev->dev; + iss->pdata = pdata; + iss->ref_count = 0; + + iss->raw_dmamask = DMA_BIT_MASK(32); + iss->dev->dma_mask = &iss->raw_dmamask; + iss->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + platform_set_drvdata(pdev, iss); + + /* Clocks */ + ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP); + if (ret < 0) + goto error; + + ret = iss_get_clocks(iss); + if (ret < 0) + goto error; + + if (omap4iss_get(iss) == NULL) + goto error; + + ret = iss_reset(iss); + if (ret < 0) + goto error_iss; + + iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + dev_info(iss->dev, "Revision %08x found\n", iss->revision); + + for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) { + ret = iss_map_mem_resource(pdev, iss, i); + if (ret) + goto error_iss; + } + + /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */ + writel((readl(iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL) & ~BTE_CTRL_BW_LIMITER_MASK) | + (18 << BTE_CTRL_BW_LIMITER_SHIFT), + iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL); + + /* Perform ISP reset */ + ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP); + if (ret < 0) + goto error_iss; + + ret = iss_isp_reset(iss); + if (ret < 0) + goto error_iss; + + dev_info(iss->dev, "ISP Revision %08x found\n", + readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_REVISION)); + + /* Interrupt */ + iss->irq_num = platform_get_irq(pdev, 0); + if (iss->irq_num <= 0) { + dev_err(iss->dev, "No IRQ resource\n"); + ret = -ENODEV; + goto error_iss; + } + + if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) { + dev_err(iss->dev, "Unable to request IRQ\n"); + ret = -EINVAL; + goto error_iss; + } + + /* Entities */ + ret = iss_initialize_modules(iss); + if (ret < 0) + goto error_irq; + + ret = iss_register_entities(iss); + if (ret < 0) + goto error_modules; + + omap4iss_put(iss); + + return 0; + +error_modules: + iss_cleanup_modules(iss); +error_irq: + free_irq(iss->irq_num, iss); +error_iss: + omap4iss_put(iss); +error: + iss_put_clocks(iss); + + for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { + if (iss->regs[i]) { + iounmap(iss->regs[i]); + iss->regs[i] = NULL; + } + + if (iss->res[i]) { + release_mem_region(iss->res[i]->start, + resource_size(iss->res[i])); + iss->res[i] = NULL; + } + } + platform_set_drvdata(pdev, NULL); + + mutex_destroy(&iss->iss_mutex); + kfree(iss); + + return ret; +} + +static int iss_remove(struct platform_device *pdev) +{ + struct iss_device *iss = platform_get_drvdata(pdev); + int i; + + iss_unregister_entities(iss); + iss_cleanup_modules(iss); + + free_irq(iss->irq_num, iss); + iss_put_clocks(iss); + + for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { + if (iss->regs[i]) { + iounmap(iss->regs[i]); + iss->regs[i] = NULL; + } + + if (iss->res[i]) { + release_mem_region(iss->res[i]->start, + resource_size(iss->res[i])); + iss->res[i] = NULL; + } + } + + kfree(iss); + + return 0; +} + +static struct platform_device_id omap4iss_id_table[] = { + { "omap4iss", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, omap4iss_id_table); + +static struct platform_driver iss_driver = { + .probe = iss_probe, + .remove = iss_remove, + .id_table = omap4iss_id_table, + .driver = { + .owner = THIS_MODULE, + .name = "omap4iss", + }, +}; + +module_platform_driver(iss_driver); + +MODULE_DESCRIPTION("TI OMAP4 ISS driver"); +MODULE_AUTHOR("Sergio Aguirre "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ISS_VIDEO_DRIVER_VERSION); diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h new file mode 100644 index 0000000..cc24f1a --- /dev/null +++ b/drivers/staging/media/omap4iss/iss.h @@ -0,0 +1,153 @@ +/* + * TI OMAP4 ISS V4L2 Driver + * + * Copyright (C) 2012 Texas Instruments. + * + * Author: Sergio Aguirre + * + * 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 _OMAP4_ISS_H_ +#define _OMAP4_ISS_H_ + +#include +#include +#include +#include +#include + +#include + +#include "iss_regs.h" +#include "iss_csiphy.h" +#include "iss_csi2.h" +#include "iss_ipipeif.h" +#include "iss_ipipe.h" +#include "iss_resizer.h" + +#define to_iss_device(ptr_module) \ + container_of(ptr_module, struct iss_device, ptr_module) +#define to_device(ptr_module) \ + (to_iss_device(ptr_module)->dev) + +enum iss_mem_resources { + OMAP4_ISS_MEM_TOP, + OMAP4_ISS_MEM_CSI2_A_REGS1, + OMAP4_ISS_MEM_CAMERARX_CORE1, + OMAP4_ISS_MEM_CSI2_B_REGS1, + OMAP4_ISS_MEM_CAMERARX_CORE2, + OMAP4_ISS_MEM_BTE, + OMAP4_ISS_MEM_ISP_SYS1, + OMAP4_ISS_MEM_ISP_RESIZER, + OMAP4_ISS_MEM_ISP_IPIPE, + OMAP4_ISS_MEM_ISP_ISIF, + OMAP4_ISS_MEM_ISP_IPIPEIF, + OMAP4_ISS_MEM_LAST, +}; + +enum iss_subclk_resource { + OMAP4_ISS_SUBCLK_SIMCOP = (1 << 0), + OMAP4_ISS_SUBCLK_ISP = (1 << 1), + OMAP4_ISS_SUBCLK_CSI2_A = (1 << 2), + OMAP4_ISS_SUBCLK_CSI2_B = (1 << 3), + OMAP4_ISS_SUBCLK_CCP2 = (1 << 4), +}; + +enum iss_isp_subclk_resource { + OMAP4_ISS_ISP_SUBCLK_BL = (1 << 0), + OMAP4_ISS_ISP_SUBCLK_ISIF = (1 << 1), + OMAP4_ISS_ISP_SUBCLK_H3A = (1 << 2), + OMAP4_ISS_ISP_SUBCLK_RSZ = (1 << 3), + OMAP4_ISS_ISP_SUBCLK_IPIPE = (1 << 4), + OMAP4_ISS_ISP_SUBCLK_IPIPEIF = (1 << 5), +}; + +/* + * struct iss_reg - Structure for ISS register values. + * @reg: 32-bit Register address. + * @val: 32-bit Register value. + */ +struct iss_reg { + enum iss_mem_resources mmio_range; + u32 reg; + u32 val; +}; + +struct iss_device { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + u32 revision; + + /* platform HW resources */ + struct iss_platform_data *pdata; + unsigned int irq_num; + + struct resource *res[OMAP4_ISS_MEM_LAST]; + void __iomem *regs[OMAP4_ISS_MEM_LAST]; + + u64 raw_dmamask; + + struct mutex iss_mutex; /* For handling ref_count field */ + int has_context; + int ref_count; + + struct clk *iss_fck; + struct clk *iss_ctrlclk; + + /* ISS modules */ + struct iss_csi2_device csi2a; + struct iss_csi2_device csi2b; + struct iss_csiphy csiphy1; + struct iss_csiphy csiphy2; + struct iss_ipipeif_device ipipeif; + struct iss_ipipe_device ipipe; + struct iss_resizer_device resizer; + + unsigned int subclk_resources; + unsigned int isp_subclk_resources; +}; + +#define v4l2_dev_to_iss_device(dev) \ + container_of(dev, struct iss_device, v4l2_dev) + +int omap4iss_get_external_info(struct iss_pipeline *pipe, + struct media_link *link); + +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping); + +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping); + +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state state); + +void omap4iss_configure_bridge(struct iss_device *iss, + enum ipipeif_input_entity input); + +struct iss_device *omap4iss_get(struct iss_device *iss); +void omap4iss_put(struct iss_device *iss); +int omap4iss_subclk_enable(struct iss_device *iss, + enum iss_subclk_resource res); +int omap4iss_subclk_disable(struct iss_device *iss, + enum iss_subclk_resource res); +int omap4iss_isp_subclk_enable(struct iss_device *iss, + enum iss_isp_subclk_resource res); +int omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res); + +void omap4iss_isp_enable_interrupts(struct iss_device *iss); +void omap4iss_isp_disable_interrupts(struct iss_device *iss); + +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use); + +int omap4iss_register_entities(struct platform_device *pdev, + struct v4l2_device *v4l2_dev); +void omap4iss_unregister_entities(struct platform_device *pdev); + +#endif /* _OMAP4_ISS_H_ */ diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h new file mode 100644 index 0000000..7327d0c --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -0,0 +1,883 @@ +/* + * TI OMAP4 ISS V4L2 Driver - Register defines + * + * Copyright (C) 2012 Texas Instruments. + * + * Author: Sergio Aguirre + * + * 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 _OMAP4_ISS_REGS_H_ +#define _OMAP4_ISS_REGS_H_ + +/* ISS */ +#define ISS_HL_REVISION 0x0 + +#define ISS_HL_SYSCONFIG 0x10 +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT 2 +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE 0x0 +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE 0x1 +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE 0x2 +#define ISS_HL_SYSCONFIG_SOFTRESET (1 << 0) + +#define ISS_HL_IRQSTATUS_5 (0x24 + (0x10 * 5)) +#define ISS_HL_IRQENABLE_5_SET (0x28 + (0x10 * 5)) +#define ISS_HL_IRQENABLE_5_CLR (0x2C + (0x10 * 5)) + +#define ISS_HL_IRQ_BTE (1 << 11) +#define ISS_HL_IRQ_CBUFF (1 << 10) +#define ISS_HL_IRQ_CSIB (1 << 5) +#define ISS_HL_IRQ_CSIA (1 << 4) +#define ISS_HL_IRQ_ISP(i) (1 << (i)) + +#define ISS_CTRL 0x80 +#define ISS_CTRL_CLK_DIV_MASK (3 << 4) +#define ISS_CTRL_INPUT_SEL_MASK (3 << 2) +#define ISS_CTRL_INPUT_SEL_CSI2A (0 << 2) +#define ISS_CTRL_INPUT_SEL_CSI2B (1 << 2) +#define ISS_CTRL_SYNC_DETECT_VS_RAISING (3 << 0) + +#define ISS_CLKCTRL 0x84 +#define ISS_CLKCTRL_VPORT2_CLK (1 << 30) +#define ISS_CLKCTRL_VPORT1_CLK (1 << 29) +#define ISS_CLKCTRL_VPORT0_CLK (1 << 28) +#define ISS_CLKCTRL_CCP2 (1 << 4) +#define ISS_CLKCTRL_CSI2_B (1 << 3) +#define ISS_CLKCTRL_CSI2_A (1 << 2) +#define ISS_CLKCTRL_ISP (1 << 1) +#define ISS_CLKCTRL_SIMCOP (1 << 0) + +#define ISS_CLKSTAT 0x88 +#define ISS_CLKSTAT_VPORT2_CLK (1 << 30) +#define ISS_CLKSTAT_VPORT1_CLK (1 << 29) +#define ISS_CLKSTAT_VPORT0_CLK (1 << 28) +#define ISS_CLKSTAT_CCP2 (1 << 4) +#define ISS_CLKSTAT_CSI2_B (1 << 3) +#define ISS_CLKSTAT_CSI2_A (1 << 2) +#define ISS_CLKSTAT_ISP (1 << 1) +#define ISS_CLKSTAT_SIMCOP (1 << 0) + +#define ISS_PM_STATUS 0x8C +#define ISS_PM_STATUS_CBUFF_PM_MASK (3 << 12) +#define ISS_PM_STATUS_BTE_PM_MASK (3 << 10) +#define ISS_PM_STATUS_SIMCOP_PM_MASK (3 << 8) +#define ISS_PM_STATUS_ISP_PM_MASK (3 << 6) +#define ISS_PM_STATUS_CCP2_PM_MASK (3 << 4) +#define ISS_PM_STATUS_CSI2_B_PM_MASK (3 << 2) +#define ISS_PM_STATUS_CSI2_A_PM_MASK (3 << 0) + +#define REGISTER0 0x0 +#define REGISTER0_HSCLOCKCONFIG (1 << 24) +#define REGISTER0_THS_TERM_MASK (0xFF << 8) +#define REGISTER0_THS_TERM_SHIFT 8 +#define REGISTER0_THS_SETTLE_MASK (0xFF << 0) +#define REGISTER0_THS_SETTLE_SHIFT 0 + +#define REGISTER1 0x4 +#define REGISTER1_RESET_DONE_CTRLCLK (1 << 29) +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) +#define REGISTER1_TCLK_TERM_MASK (0x3F << 18) +#define REGISTER1_TCLK_TERM_SHIFT 18 +#define REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT 10 +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK (0x3 << 8) +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT 8 +#define REGISTER1_TCLK_SETTLE_MASK (0xFF << 0) +#define REGISTER1_TCLK_SETTLE_SHIFT 0 + +#define REGISTER2 0x8 + +#define CSI2_SYSCONFIG 0x10 +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK (3 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE (0 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO (1 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART (2 << 12) +#define CSI2_SYSCONFIG_SOFT_RESET (1 << 1) +#define CSI2_SYSCONFIG_AUTO_IDLE (1 << 0) + +#define CSI2_SYSSTATUS 0x14 +#define CSI2_SYSSTATUS_RESET_DONE (1 << 0) + +#define CSI2_IRQSTATUS 0x18 +#define CSI2_IRQENABLE 0x1C + +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */ + +#define CSI2_IRQ_OCP_ERR (1 << 14) +#define CSI2_IRQ_SHORT_PACKET (1 << 13) +#define CSI2_IRQ_ECC_CORRECTION (1 << 12) +#define CSI2_IRQ_ECC_NO_CORRECTION (1 << 11) +#define CSI2_IRQ_COMPLEXIO_ERR (1 << 9) +#define CSI2_IRQ_FIFO_OVF (1 << 8) +#define CSI2_IRQ_CONTEXT0 (1 << 0) + +#define CSI2_CTRL 0x40 +#define CSI2_CTRL_MFLAG_LEVH_MASK (7 << 20) +#define CSI2_CTRL_MFLAG_LEVH_SHIFT 20 +#define CSI2_CTRL_MFLAG_LEVL_MASK (7 << 17) +#define CSI2_CTRL_MFLAG_LEVL_SHIFT 17 +#define CSI2_CTRL_BURST_SIZE_EXPAND (1 << 16) +#define CSI2_CTRL_VP_CLK_EN (1 << 15) +#define CSI2_CTRL_NON_POSTED_WRITE (1 << 13) +#define CSI2_CTRL_VP_ONLY_EN (1 << 11) +#define CSI2_CTRL_VP_OUT_CTRL_MASK (3 << 8) +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT 8 +#define CSI2_CTRL_DBG_EN (1 << 7) +#define CSI2_CTRL_BURST_SIZE_MASK (3 << 5) +#define CSI2_CTRL_ENDIANNESS (1 << 4) +#define CSI2_CTRL_FRAME (1 << 3) +#define CSI2_CTRL_ECC_EN (1 << 2) +#define CSI2_CTRL_IF_EN (1 << 0) + +#define CSI2_DBG_H 0x44 + +#define CSI2_COMPLEXIO_CFG 0x50 +#define CSI2_COMPLEXIO_CFG_RESET_CTRL (1 << 30) +#define CSI2_COMPLEXIO_CFG_RESET_DONE (1 << 29) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK (3 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF (0 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON (1 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP (2 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK (3 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF (0 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON (1 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP (2 << 25) +#define CSI2_COMPLEXIO_CFG_PWR_AUTO (1 << 24) +#define CSI2_COMPLEXIO_CFG_DATA_POL(i) (1 << (((i) * 4) + 3)) +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i) (7 << ((i) * 4)) +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i) ((i) * 4) +#define CSI2_COMPLEXIO_CFG_CLOCK_POL (1 << 3) +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK (7 << 0) +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT 0 + +#define CSI2_COMPLEXIO_IRQSTATUS 0x54 + +#define CSI2_SHORT_PACKET 0x5C + +#define CSI2_COMPLEXIO_IRQENABLE 0x60 + +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */ +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT (1 << 26) +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER (1 << 25) +#define CSI2_COMPLEXIO_IRQ_STATEULPM5 (1 << 24) +#define CSI2_COMPLEXIO_IRQ_STATEULPM4 (1 << 23) +#define CSI2_COMPLEXIO_IRQ_STATEULPM3 (1 << 22) +#define CSI2_COMPLEXIO_IRQ_STATEULPM2 (1 << 21) +#define CSI2_COMPLEXIO_IRQ_STATEULPM1 (1 << 20) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5 (1 << 19) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4 (1 << 18) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3 (1 << 17) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2 (1 << 16) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1 (1 << 15) +#define CSI2_COMPLEXIO_IRQ_ERRESC5 (1 << 14) +#define CSI2_COMPLEXIO_IRQ_ERRESC4 (1 << 13) +#define CSI2_COMPLEXIO_IRQ_ERRESC3 (1 << 12) +#define CSI2_COMPLEXIO_IRQ_ERRESC2 (1 << 11) +#define CSI2_COMPLEXIO_IRQ_ERRESC1 (1 << 10) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 (1 << 9) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 (1 << 8) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 (1 << 7) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 (1 << 6) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 (1 << 5) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5 (1 << 4) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4 (1 << 3) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3 (1 << 2) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2 (1 << 1) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1 (1 << 0) + +#define CSI2_DBG_P 0x68 + +#define CSI2_TIMING 0x6C +#define CSI2_TIMING_FORCE_RX_MODE_IO1 (1 << 15) +#define CSI2_TIMING_STOP_STATE_X16_IO1 (1 << 14) +#define CSI2_TIMING_STOP_STATE_X4_IO1 (1 << 13) +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK (0x1FFF << 0) +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT 0 + +#define CSI2_CTX_CTRL1(i) (0x70 + (0x20 * i)) +#define CSI2_CTX_CTRL1_GENERIC (1 << 30) +#define CSI2_CTX_CTRL1_TRANSCODE (0xF << 24) +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK (0xFF << 16) +#define CSI2_CTX_CTRL1_COUNT_MASK (0xFF << 8) +#define CSI2_CTX_CTRL1_COUNT_SHIFT 8 +#define CSI2_CTX_CTRL1_EOF_EN (1 << 7) +#define CSI2_CTX_CTRL1_EOL_EN (1 << 6) +#define CSI2_CTX_CTRL1_CS_EN (1 << 5) +#define CSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) +#define CSI2_CTX_CTRL1_PING_PONG (1 << 3) +#define CSI2_CTX_CTRL1_CTX_EN (1 << 0) + +#define CSI2_CTX_CTRL2(i) (0x74 + (0x20 * i)) +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ + (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK (3 << 11) +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 +#define CSI2_CTX_CTRL2_DPCM_PRED (1 << 10) +#define CSI2_CTX_CTRL2_FORMAT_MASK (0x3FF << 0) +#define CSI2_CTX_CTRL2_FORMAT_SHIFT 0 + +#define CSI2_CTX_DAT_OFST(i) (0x78 + (0x20 * i)) +#define CSI2_CTX_DAT_OFST_MASK (0xFFF << 5) + +#define CSI2_CTX_PING_ADDR(i) (0x7C + (0x20 * i)) +#define CSI2_CTX_PING_ADDR_MASK 0xFFFFFFE0 + +#define CSI2_CTX_PONG_ADDR(i) (0x80 + (0x20 * i)) +#define CSI2_CTX_PONG_ADDR_MASK CSI2_CTX_PING_ADDR_MASK + +#define CSI2_CTX_IRQENABLE(i) (0x84 + (0x20 * i)) +#define CSI2_CTX_IRQSTATUS(i) (0x88 + (0x20 * i)) + +#define CSI2_CTX_CTRL3(i) (0x8C + (0x20 * i)) +#define CSI2_CTX_CTRL3_ALPHA_SHIFT 5 +#define CSI2_CTX_CTRL3_ALPHA_MASK \ + (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT) + +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */ +#define CSI2_CTX_IRQ_ECC_CORRECTION (1 << 8) +#define CSI2_CTX_IRQ_LINE_NUMBER (1 << 7) +#define CSI2_CTX_IRQ_FRAME_NUMBER (1 << 6) +#define CSI2_CTX_IRQ_CS (1 << 5) +#define CSI2_CTX_IRQ_LE (1 << 3) +#define CSI2_CTX_IRQ_LS (1 << 2) +#define CSI2_CTX_IRQ_FE (1 << 1) +#define CSI2_CTX_IRQ_FS (1 << 0) + +/* ISS BTE */ +#define BTE_CTRL (0x0030) +#define BTE_CTRL_BW_LIMITER_MASK (0x3FF << 22) +#define BTE_CTRL_BW_LIMITER_SHIFT 22 + +/* ISS ISP_SYS1 */ +#define ISP5_REVISION (0x0000) +#define ISP5_SYSCONFIG (0x0010) +#define ISP5_SYSCONFIG_STANDBYMODE_MASK (3 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_FORCE (0 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_NO (1 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_SMART (2 << 4) +#define ISP5_SYSCONFIG_SOFTRESET (1 << 1) + +#define ISP5_IRQSTATUS(i) (0x0028 + (0x10 * (i))) +#define ISP5_IRQENABLE_SET(i) (0x002C + (0x10 * (i))) +#define ISP5_IRQENABLE_CLR(i) (0x0030 + (0x10 * (i))) + +/* Bits shared for ISP5_IRQ* registers */ +#define ISP5_IRQ_OCP_ERR (1 << 31) +#define ISP5_IRQ_RSZ_INT_EOF0 (1 << 22) +#define ISP5_IRQ_RSZ_FIFO_IN_BLK (1 << 19) +#define ISP5_IRQ_RSZ_FIFO_OVF (1 << 18) +#define ISP5_IRQ_RSZ_INT_CYC_RSZA (1 << 16) +#define ISP5_IRQ_RSZ_INT_DMA (1 << 15) +#define ISP5_IRQ_IPIPEIF (1 << 9) +#define ISP5_IRQ_ISIF3 (1 << 3) +#define ISP5_IRQ_ISIF2 (1 << 2) +#define ISP5_IRQ_ISIF1 (1 << 1) +#define ISP5_IRQ_ISIF0 (1 << 0) + +#define ISP5_CTRL (0x006C) +#define ISP5_CTRL_MSTANDBY (1 << 24) +#define ISP5_CTRL_VD_PULSE_EXT (1 << 23) +#define ISP5_CTRL_MSTANDBY_WAIT (1 << 20) +#define ISP5_CTRL_BL_CLK_ENABLE (1 << 15) +#define ISP5_CTRL_ISIF_CLK_ENABLE (1 << 14) +#define ISP5_CTRL_H3A_CLK_ENABLE (1 << 13) +#define ISP5_CTRL_RSZ_CLK_ENABLE (1 << 12) +#define ISP5_CTRL_IPIPE_CLK_ENABLE (1 << 11) +#define ISP5_CTRL_IPIPEIF_CLK_ENABLE (1 << 10) +#define ISP5_CTRL_SYNC_ENABLE (1 << 9) +#define ISP5_CTRL_PSYNC_CLK_SEL (1 << 8) + +/* ISS ISP ISIF register offsets */ +#define ISIF_SYNCEN (0x0000) +#define ISIF_SYNCEN_DWEN (1 << 1) +#define ISIF_SYNCEN_SYEN (1 << 0) + +#define ISIF_MODESET (0x0004) +#define ISIF_MODESET_INPMOD_MASK (3 << 12) +#define ISIF_MODESET_INPMOD_RAW (0 << 12) +#define ISIF_MODESET_INPMOD_YCBCR16 (1 << 12) +#define ISIF_MODESET_INPMOD_YCBCR8 (2 << 12) +#define ISIF_MODESET_CCDW_MASK (7 << 8) +#define ISIF_MODESET_CCDW_2BIT (2 << 8) +#define ISIF_MODESET_CCDMD (1 << 7) +#define ISIF_MODESET_SWEN (1 << 5) +#define ISIF_MODESET_HDPOL (1 << 3) +#define ISIF_MODESET_VDPOL (1 << 2) + +#define ISIF_SPH (0x0018) +#define ISIF_SPH_MASK (0x7FFF) + +#define ISIF_LNH (0x001C) +#define ISIF_LNH_MASK (0x7FFF) + +#define ISIF_LNV (0x0028) +#define ISIF_LNV_MASK (0x7FFF) + +#define ISIF_HSIZE (0x0034) +#define ISIF_HSIZE_ADCR (1 << 12) +#define ISIF_HSIZE_HSIZE_MASK (0xFFF) + +#define ISIF_CADU (0x003C) +#define ISIF_CADU_MASK (0x7FF) + +#define ISIF_CADL (0x0040) +#define ISIF_CADL_MASK (0xFFFF) + +#define ISIF_CCOLP (0x004C) +#define ISIF_CCOLP_CP0_F0_R (0 << 6) +#define ISIF_CCOLP_CP0_F0_GR (1 << 6) +#define ISIF_CCOLP_CP0_F0_B (3 << 6) +#define ISIF_CCOLP_CP0_F0_GB (2 << 6) +#define ISIF_CCOLP_CP1_F0_R (0 << 4) +#define ISIF_CCOLP_CP1_F0_GR (1 << 4) +#define ISIF_CCOLP_CP1_F0_B (3 << 4) +#define ISIF_CCOLP_CP1_F0_GB (2 << 4) +#define ISIF_CCOLP_CP2_F0_R (0 << 2) +#define ISIF_CCOLP_CP2_F0_GR (1 << 2) +#define ISIF_CCOLP_CP2_F0_B (3 << 2) +#define ISIF_CCOLP_CP2_F0_GB (2 << 2) +#define ISIF_CCOLP_CP3_F0_R (0 << 0) +#define ISIF_CCOLP_CP3_F0_GR (1 << 0) +#define ISIF_CCOLP_CP3_F0_B (3 << 0) +#define ISIF_CCOLP_CP3_F0_GB (2 << 0) + +#define ISIF_VDINT0 (0x0070) +#define ISIF_VDINT0_MASK (0x7FFF) + +#define ISIF_CGAMMAWD (0x0080) +#define ISIF_CGAMMAWD_GWDI_MASK (0xF << 1) +#define ISIF_CGAMMAWD_GWDI_BIT11 (0x4 << 1) + +#define ISIF_CCDCFG (0x0088) +#define ISIF_CCDCFG_Y8POS (1 << 11) + +/* ISS ISP IPIPEIF register offsets */ +#define IPIPEIF_ENABLE (0x0000) + +#define IPIPEIF_CFG1 (0x0004) +#define IPIPEIF_CFG1_INPSRC1_MASK (3 << 14) +#define IPIPEIF_CFG1_INPSRC1_VPORT_RAW (0 << 14) +#define IPIPEIF_CFG1_INPSRC1_SDRAM_RAW (1 << 14) +#define IPIPEIF_CFG1_INPSRC1_ISIF_DARKFM (2 << 14) +#define IPIPEIF_CFG1_INPSRC1_SDRAM_YUV (3 << 14) +#define IPIPEIF_CFG1_INPSRC2_MASK (3 << 2) +#define IPIPEIF_CFG1_INPSRC2_ISIF (0 << 2) +#define IPIPEIF_CFG1_INPSRC2_SDRAM_RAW (1 << 2) +#define IPIPEIF_CFG1_INPSRC2_ISIF_DARKFM (2 << 2) +#define IPIPEIF_CFG1_INPSRC2_SDRAM_YUV (3 << 2) + +#define IPIPEIF_CFG2 (0x0030) +#define IPIPEIF_CFG2_YUV8P (1 << 7) +#define IPIPEIF_CFG2_YUV8 (1 << 6) +#define IPIPEIF_CFG2_YUV16 (1 << 3) +#define IPIPEIF_CFG2_VDPOL (1 << 2) +#define IPIPEIF_CFG2_HDPOL (1 << 1) +#define IPIPEIF_CFG2_INTSW (1 << 0) + +#define IPIPEIF_CLKDIV (0x0040) + +/* ISS ISP IPIPE register offsets */ +#define IPIPE_SRC_EN (0x0000) +#define IPIPE_SRC_EN_EN (1 << 0) + +#define IPIPE_SRC_MODE (0x0004) +#define IPIPE_SRC_MODE_WRT (1 << 1) +#define IPIPE_SRC_MODE_OST (1 << 0) + +#define IPIPE_SRC_FMT (0x0008) +#define IPIPE_SRC_FMT_RAW2YUV (0 << 0) +#define IPIPE_SRC_FMT_RAW2RAW (1 << 0) +#define IPIPE_SRC_FMT_RAW2STATS (2 << 0) +#define IPIPE_SRC_FMT_YUV2YUV (3 << 0) + +#define IPIPE_SRC_COL (0x000C) +#define IPIPE_SRC_COL_OO_R (0 << 6) +#define IPIPE_SRC_COL_OO_GR (1 << 6) +#define IPIPE_SRC_COL_OO_B (3 << 6) +#define IPIPE_SRC_COL_OO_GB (2 << 6) +#define IPIPE_SRC_COL_OE_R (0 << 4) +#define IPIPE_SRC_COL_OE_GR (1 << 4) +#define IPIPE_SRC_COL_OE_B (3 << 4) +#define IPIPE_SRC_COL_OE_GB (2 << 4) +#define IPIPE_SRC_COL_EO_R (0 << 2) +#define IPIPE_SRC_COL_EO_GR (1 << 2) +#define IPIPE_SRC_COL_EO_B (3 << 2) +#define IPIPE_SRC_COL_EO_GB (2 << 2) +#define IPIPE_SRC_COL_EE_R (0 << 0) +#define IPIPE_SRC_COL_EE_GR (1 << 0) +#define IPIPE_SRC_COL_EE_B (3 << 0) +#define IPIPE_SRC_COL_EE_GB (2 << 0) + +#define IPIPE_SRC_VPS (0x0010) +#define IPIPE_SRC_VPS_MASK (0xFFFF) + +#define IPIPE_SRC_VSZ (0x0014) +#define IPIPE_SRC_VSZ_MASK (0x1FFF) + +#define IPIPE_SRC_HPS (0x0018) +#define IPIPE_SRC_HPS_MASK (0xFFFF) + +#define IPIPE_SRC_HSZ (0x001C) +#define IPIPE_SRC_HSZ_MASK (0x1FFE) + +#define IPIPE_SEL_SBU (0x0020) + +#define IPIPE_SRC_STA (0x0024) + +#define IPIPE_GCK_MMR (0x0028) +#define IPIPE_GCK_MMR_REG (1 << 0) + +#define IPIPE_GCK_PIX (0x002C) +#define IPIPE_GCK_PIX_G3 (1 << 3) +#define IPIPE_GCK_PIX_G2 (1 << 2) +#define IPIPE_GCK_PIX_G1 (1 << 1) +#define IPIPE_GCK_PIX_G0 (1 << 0) + +#define IPIPE_DPC_LUT_EN (0x0034) +#define IPIPE_DPC_LUT_SEL (0x0038) +#define IPIPE_DPC_LUT_ADR (0x003C) +#define IPIPE_DPC_LUT_SIZ (0x0040) + +#define IPIPE_DPC_OTF_EN (0x0044) +#define IPIPE_DPC_OTF_TYP (0x0048) +#define IPIPE_DPC_OTF_2_D_THR_R (0x004C) +#define IPIPE_DPC_OTF_2_D_THR_GR (0x0050) +#define IPIPE_DPC_OTF_2_D_THR_GB (0x0054) +#define IPIPE_DPC_OTF_2_D_THR_B (0x0058) +#define IPIPE_DPC_OTF_2_C_THR_R (0x005C) +#define IPIPE_DPC_OTF_2_C_THR_GR (0x0060) +#define IPIPE_DPC_OTF_2_C_THR_GB (0x0064) +#define IPIPE_DPC_OTF_2_C_THR_B (0x0068) +#define IPIPE_DPC_OTF_3_SHF (0x006C) +#define IPIPE_DPC_OTF_3_D_THR (0x0070) +#define IPIPE_DPC_OTF_3_D_SPL (0x0074) +#define IPIPE_DPC_OTF_3_D_MIN (0x0078) +#define IPIPE_DPC_OTF_3_D_MAX (0x007C) +#define IPIPE_DPC_OTF_3_C_THR (0x0080) +#define IPIPE_DPC_OTF_3_C_SLP (0x0084) +#define IPIPE_DPC_OTF_3_C_MIN (0x0088) +#define IPIPE_DPC_OTF_3_C_MAX (0x008C) + +#define IPIPE_LSC_VOFT (0x0090) +#define IPIPE_LSC_VA2 (0x0094) +#define IPIPE_LSC_VA1 (0x0098) +#define IPIPE_LSC_VS (0x009C) +#define IPIPE_LSC_HOFT (0x00A0) +#define IPIPE_LSC_HA2 (0x00A4) +#define IPIPE_LSC_HA1 (0x00A8) +#define IPIPE_LSC_HS (0x00AC) +#define IPIPE_LSC_GAN_R (0x00B0) +#define IPIPE_LSC_GAN_GR (0x00B4) +#define IPIPE_LSC_GAN_GB (0x00B8) +#define IPIPE_LSC_GAN_B (0x00BC) +#define IPIPE_LSC_OFT_R (0x00C0) +#define IPIPE_LSC_OFT_GR (0x00C4) +#define IPIPE_LSC_OFT_GB (0x00C8) +#define IPIPE_LSC_OFT_B (0x00CC) +#define IPIPE_LSC_SHF (0x00D0) +#define IPIPE_LSC_MAX (0x00D4) + +#define IPIPE_D2F_1ST_EN (0x00D8) +#define IPIPE_D2F_1ST_TYP (0x00DC) +#define IPIPE_D2F_1ST_THR_00 (0x00E0) +#define IPIPE_D2F_1ST_THR_01 (0x00E4) +#define IPIPE_D2F_1ST_THR_02 (0x00E8) +#define IPIPE_D2F_1ST_THR_03 (0x00EC) +#define IPIPE_D2F_1ST_THR_04 (0x00F0) +#define IPIPE_D2F_1ST_THR_05 (0x00F4) +#define IPIPE_D2F_1ST_THR_06 (0x00F8) +#define IPIPE_D2F_1ST_THR_07 (0x00FC) +#define IPIPE_D2F_1ST_STR_00 (0x0100) +#define IPIPE_D2F_1ST_STR_01 (0x0104) +#define IPIPE_D2F_1ST_STR_02 (0x0108) +#define IPIPE_D2F_1ST_STR_03 (0x010C) +#define IPIPE_D2F_1ST_STR_04 (0x0110) +#define IPIPE_D2F_1ST_STR_05 (0x0114) +#define IPIPE_D2F_1ST_STR_06 (0x0118) +#define IPIPE_D2F_1ST_STR_07 (0x011C) +#define IPIPE_D2F_1ST_SPR_00 (0x0120) +#define IPIPE_D2F_1ST_SPR_01 (0x0124) +#define IPIPE_D2F_1ST_SPR_02 (0x0128) +#define IPIPE_D2F_1ST_SPR_03 (0x012C) +#define IPIPE_D2F_1ST_SPR_04 (0x0130) +#define IPIPE_D2F_1ST_SPR_05 (0x0134) +#define IPIPE_D2F_1ST_SPR_06 (0x0138) +#define IPIPE_D2F_1ST_SPR_07 (0x013C) +#define IPIPE_D2F_1ST_EDG_MIN (0x0140) +#define IPIPE_D2F_1ST_EDG_MAX (0x0144) +#define IPIPE_D2F_2ND_EN (0x0148) +#define IPIPE_D2F_2ND_TYP (0x014C) +#define IPIPE_D2F_2ND_THR00 (0x0150) +#define IPIPE_D2F_2ND_THR01 (0x0154) +#define IPIPE_D2F_2ND_THR02 (0x0158) +#define IPIPE_D2F_2ND_THR03 (0x015C) +#define IPIPE_D2F_2ND_THR04 (0x0160) +#define IPIPE_D2F_2ND_THR05 (0x0164) +#define IPIPE_D2F_2ND_THR06 (0x0168) +#define IPIPE_D2F_2ND_THR07 (0x016C) +#define IPIPE_D2F_2ND_STR_00 (0x0170) +#define IPIPE_D2F_2ND_STR_01 (0x0174) +#define IPIPE_D2F_2ND_STR_02 (0x0178) +#define IPIPE_D2F_2ND_STR_03 (0x017C) +#define IPIPE_D2F_2ND_STR_04 (0x0180) +#define IPIPE_D2F_2ND_STR_05 (0x0184) +#define IPIPE_D2F_2ND_STR_06 (0x0188) +#define IPIPE_D2F_2ND_STR_07 (0x018C) +#define IPIPE_D2F_2ND_SPR_00 (0x0190) +#define IPIPE_D2F_2ND_SPR_01 (0x0194) +#define IPIPE_D2F_2ND_SPR_02 (0x0198) +#define IPIPE_D2F_2ND_SPR_03 (0x019C) +#define IPIPE_D2F_2ND_SPR_04 (0x01A0) +#define IPIPE_D2F_2ND_SPR_05 (0x01A4) +#define IPIPE_D2F_2ND_SPR_06 (0x01A8) +#define IPIPE_D2F_2ND_SPR_07 (0x01AC) +#define IPIPE_D2F_2ND_EDG_MIN (0x01B0) +#define IPIPE_D2F_2ND_EDG_MAX (0x01B4) + +#define IPIPE_GIC_EN (0x01B8) +#define IPIPE_GIC_TYP (0x01BC) +#define IPIPE_GIC_GAN (0x01C0) +#define IPIPE_GIC_NFGAIN (0x01C4) +#define IPIPE_GIC_THR (0x01C8) +#define IPIPE_GIC_SLP (0x01CC) + +#define IPIPE_WB2_OFT_R (0x01D0) +#define IPIPE_WB2_OFT_GR (0x01D4) +#define IPIPE_WB2_OFT_GB (0x01D8) +#define IPIPE_WB2_OFT_B (0x01DC) + +#define IPIPE_WB2_WGN_R (0x01E0) +#define IPIPE_WB2_WGN_GR (0x01E4) +#define IPIPE_WB2_WGN_GB (0x01E8) +#define IPIPE_WB2_WGN_B (0x01EC) + +#define IPIPE_CFA_MODE (0x01F0) +#define IPIPE_CFA_2DIR_HPF_THR (0x01F4) +#define IPIPE_CFA_2DIR_HPF_SLP (0x01F8) +#define IPIPE_CFA_2DIR_MIX_THR (0x01FC) +#define IPIPE_CFA_2DIR_MIX_SLP (0x0200) +#define IPIPE_CFA_2DIR_DIR_TRH (0x0204) +#define IPIPE_CFA_2DIR_DIR_SLP (0x0208) +#define IPIPE_CFA_2DIR_NDWT (0x020C) +#define IPIPE_CFA_MONO_HUE_FRA (0x0210) +#define IPIPE_CFA_MONO_EDG_THR (0x0214) +#define IPIPE_CFA_MONO_THR_MIN (0x0218) + +#define IPIPE_CFA_MONO_THR_SLP (0x021C) +#define IPIPE_CFA_MONO_SLP_MIN (0x0220) +#define IPIPE_CFA_MONO_SLP_SLP (0x0224) +#define IPIPE_CFA_MONO_LPWT (0x0228) + +#define IPIPE_RGB1_MUL_RR (0x022C) +#define IPIPE_RGB1_MUL_GR (0x0230) +#define IPIPE_RGB1_MUL_BR (0x0234) +#define IPIPE_RGB1_MUL_RG (0x0238) +#define IPIPE_RGB1_MUL_GG (0x023C) +#define IPIPE_RGB1_MUL_BG (0x0240) +#define IPIPE_RGB1_MUL_RB (0x0244) +#define IPIPE_RGB1_MUL_GB (0x0248) +#define IPIPE_RGB1_MUL_BB (0x024C) +#define IPIPE_RGB1_OFT_OR (0x0250) +#define IPIPE_RGB1_OFT_OG (0x0254) +#define IPIPE_RGB1_OFT_OB (0x0258) +#define IPIPE_GMM_CFG (0x025C) +#define IPIPE_RGB2_MUL_RR (0x0260) +#define IPIPE_RGB2_MUL_GR (0x0264) +#define IPIPE_RGB2_MUL_BR (0x0268) +#define IPIPE_RGB2_MUL_RG (0x026C) +#define IPIPE_RGB2_MUL_GG (0x0270) +#define IPIPE_RGB2_MUL_BG (0x0274) +#define IPIPE_RGB2_MUL_RB (0x0278) +#define IPIPE_RGB2_MUL_GB (0x027C) +#define IPIPE_RGB2_MUL_BB (0x0280) +#define IPIPE_RGB2_OFT_OR (0x0284) +#define IPIPE_RGB2_OFT_OG (0x0288) +#define IPIPE_RGB2_OFT_OB (0x028C) + +#define IPIPE_YUV_ADJ (0x0294) +#define IPIPE_YUV_MUL_RY (0x0298) +#define IPIPE_YUV_MUL_GY (0x029C) +#define IPIPE_YUV_MUL_BY (0x02A0) +#define IPIPE_YUV_MUL_RCB (0x02A4) +#define IPIPE_YUV_MUL_GCB (0x02A8) +#define IPIPE_YUV_MUL_BCB (0x02AC) +#define IPIPE_YUV_MUL_RCR (0x02B0) +#define IPIPE_YUV_MUL_GCR (0x02B4) +#define IPIPE_YUV_MUL_BCR (0x02B8) +#define IPIPE_YUV_OFT_Y (0x02BC) +#define IPIPE_YUV_OFT_CB (0x02C0) +#define IPIPE_YUV_OFT_CR (0x02C4) + +#define IPIPE_YUV_PHS (0x02C8) +#define IPIPE_YUV_PHS_LPF (1 << 1) +#define IPIPE_YUV_PHS_POS (1 << 0) + +#define IPIPE_YEE_EN (0x02D4) +#define IPIPE_YEE_TYP (0x02D8) +#define IPIPE_YEE_SHF (0x02DC) +#define IPIPE_YEE_MUL_00 (0x02E0) +#define IPIPE_YEE_MUL_01 (0x02E4) +#define IPIPE_YEE_MUL_02 (0x02E8) +#define IPIPE_YEE_MUL_10 (0x02EC) +#define IPIPE_YEE_MUL_11 (0x02F0) +#define IPIPE_YEE_MUL_12 (0x02F4) +#define IPIPE_YEE_MUL_20 (0x02F8) +#define IPIPE_YEE_MUL_21 (0x02FC) +#define IPIPE_YEE_MUL_22 (0x0300) +#define IPIPE_YEE_THR (0x0304) +#define IPIPE_YEE_E_GAN (0x0308) +#define IPIPE_YEE_E_THR_1 (0x030C) +#define IPIPE_YEE_E_THR_2 (0x0310) +#define IPIPE_YEE_G_GAN (0x0314) +#define IPIPE_YEE_G_OFT (0x0318) + +#define IPIPE_CAR_EN (0x031C) +#define IPIPE_CAR_TYP (0x0320) +#define IPIPE_CAR_SW (0x0324) +#define IPIPE_CAR_HPF_TYP (0x0328) +#define IPIPE_CAR_HPF_SHF (0x032C) +#define IPIPE_CAR_HPF_THR (0x0330) +#define IPIPE_CAR_GN1_GAN (0x0334) +#define IPIPE_CAR_GN1_SHF (0x0338) +#define IPIPE_CAR_GN1_MIN (0x033C) +#define IPIPE_CAR_GN2_GAN (0x0340) +#define IPIPE_CAR_GN2_SHF (0x0344) +#define IPIPE_CAR_GN2_MIN (0x0348) +#define IPIPE_CGS_EN (0x034C) +#define IPIPE_CGS_GN1_L_THR (0x0350) +#define IPIPE_CGS_GN1_L_GAIN (0x0354) +#define IPIPE_CGS_GN1_L_SHF (0x0358) +#define IPIPE_CGS_GN1_L_MIN (0x035C) +#define IPIPE_CGS_GN1_H_THR (0x0360) +#define IPIPE_CGS_GN1_H_GAIN (0x0364) +#define IPIPE_CGS_GN1_H_SHF (0x0368) +#define IPIPE_CGS_GN1_H_MIN (0x036C) +#define IPIPE_CGS_GN2_L_THR (0x0370) +#define IPIPE_CGS_GN2_L_GAIN (0x0374) +#define IPIPE_CGS_GN2_L_SHF (0x0378) +#define IPIPE_CGS_GN2_L_MIN (0x037C) + +#define IPIPE_BOX_EN (0x0380) +#define IPIPE_BOX_MODE (0x0384) +#define IPIPE_BOX_TYP (0x0388) +#define IPIPE_BOX_SHF (0x038C) +#define IPIPE_BOX_SDR_SAD_H (0x0390) +#define IPIPE_BOX_SDR_SAD_L (0x0394) + +#define IPIPE_HST_EN (0x039C) +#define IPIPE_HST_MODE (0x03A0) +#define IPIPE_HST_SEL (0x03A4) +#define IPIPE_HST_PARA (0x03A8) +#define IPIPE_HST_0_VPS (0x03AC) +#define IPIPE_HST_0_VSZ (0x03B0) +#define IPIPE_HST_0_HPS (0x03B4) +#define IPIPE_HST_0_HSZ (0x03B8) +#define IPIPE_HST_1_VPS (0x03BC) +#define IPIPE_HST_1_VSZ (0x03C0) +#define IPIPE_HST_1_HPS (0x03C4) +#define IPIPE_HST_1_HSZ (0x03C8) +#define IPIPE_HST_2_VPS (0x03CC) +#define IPIPE_HST_2_VSZ (0x03D0) +#define IPIPE_HST_2_HPS (0x03D4) +#define IPIPE_HST_2_HSZ (0x03D8) +#define IPIPE_HST_3_VPS (0x03DC) +#define IPIPE_HST_3_VSZ (0x03E0) +#define IPIPE_HST_3_HPS (0x03E4) +#define IPIPE_HST_3_HSZ (0x03E8) +#define IPIPE_HST_TBL (0x03EC) +#define IPIPE_HST_MUL_R (0x03F0) +#define IPIPE_HST_MUL_GR (0x03F4) +#define IPIPE_HST_MUL_GB (0x03F8) +#define IPIPE_HST_MUL_B (0x03FC) + +#define IPIPE_BSC_EN (0x0400) +#define IPIPE_BSC_MODE (0x0404) +#define IPIPE_BSC_TYP (0x0408) +#define IPIPE_BSC_ROW_VCT (0x040C) +#define IPIPE_BSC_ROW_SHF (0x0410) +#define IPIPE_BSC_ROW_VPO (0x0414) +#define IPIPE_BSC_ROW_VNU (0x0418) +#define IPIPE_BSC_ROW_VSKIP (0x041C) +#define IPIPE_BSC_ROW_HPO (0x0420) +#define IPIPE_BSC_ROW_HNU (0x0424) +#define IPIPE_BSC_ROW_HSKIP (0x0428) +#define IPIPE_BSC_COL_VCT (0x042C) +#define IPIPE_BSC_COL_SHF (0x0430) +#define IPIPE_BSC_COL_VPO (0x0434) +#define IPIPE_BSC_COL_VNU (0x0438) +#define IPIPE_BSC_COL_VSKIP (0x043C) +#define IPIPE_BSC_COL_HPO (0x0440) +#define IPIPE_BSC_COL_HNU (0x0444) +#define IPIPE_BSC_COL_HSKIP (0x0448) + +#define IPIPE_BSC_EN (0x0400) + +/* ISS ISP Resizer register offsets */ +#define RSZ_REVISION (0x0000) +#define RSZ_SYSCONFIG (0x0004) +#define RSZ_SYSCONFIG_RSZB_CLK_EN (1 << 9) +#define RSZ_SYSCONFIG_RSZA_CLK_EN (1 << 8) + +#define RSZ_IN_FIFO_CTRL (0x000C) +#define RSZ_IN_FIFO_CTRL_THRLD_LOW_MASK (0x1FF << 16) +#define RSZ_IN_FIFO_CTRL_THRLD_LOW_SHIFT 16 +#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_MASK (0x1FF << 0) +#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_SHIFT 0 + +#define RSZ_FRACDIV (0x0008) +#define RSZ_FRACDIV_MASK (0xFFFF) + +#define RSZ_SRC_EN (0x0020) +#define RSZ_SRC_EN_SRC_EN (1 << 0) + +#define RSZ_SRC_MODE (0x0024) +#define RSZ_SRC_MODE_OST (1 << 0) +#define RSZ_SRC_MODE_WRT (1 << 1) + +#define RSZ_SRC_FMT0 (0x0028) +#define RSZ_SRC_FMT0_BYPASS (1 << 1) +#define RSZ_SRC_FMT0_SEL (1 << 0) + +#define RSZ_SRC_FMT1 (0x002C) +#define RSZ_SRC_FMT1_IN420 (1 << 1) + +#define RSZ_SRC_VPS (0x0030) +#define RSZ_SRC_VSZ (0x0034) +#define RSZ_SRC_HPS (0x0038) +#define RSZ_SRC_HSZ (0x003C) +#define RSZ_DMA_RZA (0x0040) +#define RSZ_DMA_RZB (0x0044) +#define RSZ_DMA_STA (0x0048) +#define RSZ_GCK_MMR (0x004C) +#define RSZ_GCK_MMR_MMR (1 << 0) + +#define RSZ_GCK_SDR (0x0054) +#define RSZ_GCK_SDR_CORE (1 << 0) + +#define RSZ_IRQ_RZA (0x0058) +#define RSZ_IRQ_RZA_MASK (0x1FFF) + +#define RSZ_IRQ_RZB (0x005C) +#define RSZ_IRQ_RZB_MASK (0x1FFF) + +#define RSZ_YUV_Y_MIN (0x0060) +#define RSZ_YUV_Y_MAX (0x0064) +#define RSZ_YUV_C_MIN (0x0068) +#define RSZ_YUV_C_MAX (0x006C) + +#define RSZ_SEQ (0x0074) +#define RSZ_SEQ_HRVB (1 << 2) +#define RSZ_SEQ_HRVA (1 << 0) + +#define RZA_EN (0x0078) +#define RZA_MODE (0x007C) +#define RZA_MODE_ONE_SHOT (1 << 0) + +#define RZA_420 (0x0080) +#define RZA_I_VPS (0x0084) +#define RZA_I_HPS (0x0088) +#define RZA_O_VSZ (0x008C) +#define RZA_O_HSZ (0x0090) +#define RZA_V_PHS_Y (0x0094) +#define RZA_V_PHS_C (0x0098) +#define RZA_V_DIF (0x009C) +#define RZA_V_TYP (0x00A0) +#define RZA_V_LPF (0x00A4) +#define RZA_H_PHS (0x00A8) +#define RZA_H_DIF (0x00B0) +#define RZA_H_TYP (0x00B4) +#define RZA_H_LPF (0x00B8) +#define RZA_DWN_EN (0x00BC) +#define RZA_SDR_Y_BAD_H (0x00D0) +#define RZA_SDR_Y_BAD_L (0x00D4) +#define RZA_SDR_Y_SAD_H (0x00D8) +#define RZA_SDR_Y_SAD_L (0x00DC) +#define RZA_SDR_Y_OFT (0x00E0) +#define RZA_SDR_Y_PTR_S (0x00E4) +#define RZA_SDR_Y_PTR_E (0x00E8) +#define RZA_SDR_C_BAD_H (0x00EC) +#define RZA_SDR_C_BAD_L (0x00F0) +#define RZA_SDR_C_SAD_H (0x00F4) +#define RZA_SDR_C_SAD_L (0x00F8) +#define RZA_SDR_C_OFT (0x00FC) +#define RZA_SDR_C_PTR_S (0x0100) +#define RZA_SDR_C_PTR_E (0x0104) + +#define RZB_EN (0x0108) +#define RZB_MODE (0x010C) +#define RZB_420 (0x0110) +#define RZB_I_VPS (0x0114) +#define RZB_I_HPS (0x0118) +#define RZB_O_VSZ (0x011C) +#define RZB_O_HSZ (0x0120) + +#define RZB_V_DIF (0x012C) +#define RZB_V_TYP (0x0130) +#define RZB_V_LPF (0x0134) + +#define RZB_H_DIF (0x0140) +#define RZB_H_TYP (0x0144) +#define RZB_H_LPF (0x0148) + +#define RZB_SDR_Y_BAD_H (0x0160) +#define RZB_SDR_Y_BAD_L (0x0164) +#define RZB_SDR_Y_SAD_H (0x0168) +#define RZB_SDR_Y_SAD_L (0x016C) +#define RZB_SDR_Y_OFT (0x0170) +#define RZB_SDR_Y_PTR_S (0x0174) +#define RZB_SDR_Y_PTR_E (0x0178) +#define RZB_SDR_C_BAD_H (0x017C) +#define RZB_SDR_C_BAD_L (0x0180) +#define RZB_SDR_C_SAD_H (0x0184) +#define RZB_SDR_C_SAD_L (0x0188) + +#define RZB_SDR_C_PTR_S (0x0190) +#define RZB_SDR_C_PTR_E (0x0194) + +/* Shared Bitmasks between RZA & RZB */ +#define RSZ_EN_EN (1 << 0) + +#define RSZ_420_CEN (1 << 1) +#define RSZ_420_YEN (1 << 0) + +#define RSZ_I_VPS_MASK (0x1FFF) + +#define RSZ_I_HPS_MASK (0x1FFF) + +#define RSZ_O_VSZ_MASK (0x1FFF) + +#define RSZ_O_HSZ_MASK (0x1FFE) + +#define RSZ_V_PHS_Y_MASK (0x3FFF) + +#define RSZ_V_PHS_C_MASK (0x3FFF) + +#define RSZ_V_DIF_MASK (0x3FFF) + +#define RSZ_V_TYP_C (1 << 1) +#define RSZ_V_TYP_Y (1 << 0) + +#define RSZ_V_LPF_C_MASK (0x3F << 6) +#define RSZ_V_LPF_C_SHIFT 6 +#define RSZ_V_LPF_Y_MASK (0x3F << 0) +#define RSZ_V_LPF_Y_SHIFT 0 + +#define RSZ_H_PHS_MASK (0x3FFF) + +#define RSZ_H_DIF_MASK (0x3FFF) + +#define RSZ_H_TYP_C (1 << 1) +#define RSZ_H_TYP_Y (1 << 0) + +#define RSZ_H_LPF_C_MASK (0x3F << 6) +#define RSZ_H_LPF_C_SHIFT 6 +#define RSZ_H_LPF_Y_MASK (0x3F << 0) +#define RSZ_H_LPF_Y_SHIFT 0 + +#define RSZ_DWN_EN_DWN_EN (1 << 0) + +#endif /* _OMAP4_ISS_REGS_H_ */ diff --git a/include/media/omap4iss.h b/include/media/omap4iss.h new file mode 100644 index 0000000..0d7620d --- /dev/null +++ b/include/media/omap4iss.h @@ -0,0 +1,65 @@ +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H +#define ARCH_ARM_PLAT_OMAP4_ISS_H + +#include + +struct iss_device; + +enum iss_interface_type { + ISS_INTERFACE_CSI2A_PHY1, + ISS_INTERFACE_CSI2B_PHY2, +}; + +/** + * struct iss_csiphy_lane: CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct iss_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISS_CSIPHY1_NUM_DATA_LANES 4 +#define ISS_CSIPHY2_NUM_DATA_LANES 1 + +/** + * struct iss_csiphy_lanes_cfg - CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct iss_csiphy_lanes_cfg { + struct iss_csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES]; + struct iss_csiphy_lane clk; +}; + +/** + * struct iss_csi2_platform_data - CSI2 interface platform data + * @crc: Enable the cyclic redundancy check + * @vpclk_div: Video port output clock control + */ +struct iss_csi2_platform_data { + unsigned crc:1; + unsigned vpclk_div:2; + struct iss_csiphy_lanes_cfg lanecfg; +}; + +struct iss_subdev_i2c_board_info { + struct i2c_board_info *board_info; + int i2c_adapter_id; +}; + +struct iss_v4l2_subdevs_group { + struct iss_subdev_i2c_board_info *subdevs; + enum iss_interface_type interface; + union { + struct iss_csi2_platform_data csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +struct iss_platform_data { + struct iss_v4l2_subdevs_group *subdevs; + void (*set_constraints)(struct iss_device *iss, bool enable); +}; + +#endif -- cgit v0.10.2 From fc96d58c10162d476138f135b5e9080fec41b478 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - Video devices This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds video devices support. Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c new file mode 100644 index 0000000..31f1b88 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -0,0 +1,1129 @@ +/* + * TI OMAP4 ISS V4L2 Driver - Generic video node + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iss_video.h" +#include "iss.h" + + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static struct iss_format_info formats[] = { + { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, + V4L2_PIX_FMT_GREY, 8, }, + { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10, + V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8, + V4L2_PIX_FMT_Y10, 10, }, + { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10, + V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8, + V4L2_PIX_FMT_Y12, 12, }, + { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_PIX_FMT_SBGGR8, 8, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, + V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, + V4L2_PIX_FMT_SGBRG8, 8, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_PIX_FMT_SGRBG8, 8, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_PIX_FMT_SRGGB8, 8, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_1X10, 0, + V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_PIX_FMT_SBGGR10, 10, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8, + V4L2_PIX_FMT_SGBRG10, 10, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_PIX_FMT_SGRBG10, 10, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_PIX_FMT_SRGGB10, 10, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_PIX_FMT_SBGGR12, 12, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8, + V4L2_PIX_FMT_SGBRG12, 12, }, + { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_PIX_FMT_SGRBG12, 12, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_PIX_FMT_SRGGB12, 12, }, + { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_UYVY8_1X16, 0, + V4L2_PIX_FMT_UYVY, 16, }, + { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, 0, + V4L2_PIX_FMT_YUYV, 16, }, + { V4L2_MBUS_FMT_YUYV8_1_5X8, V4L2_MBUS_FMT_YUYV8_1_5X8, + V4L2_MBUS_FMT_YUYV8_1_5X8, 0, + V4L2_PIX_FMT_NV12, 8, }, +}; + +const struct iss_format_info * +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +/* + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format + * @video: ISS video instance + * @mbus: v4l2_mbus_framefmt format (input) + * @pix: v4l2_pix_format format (output) + * + * Fill the output pix structure with information from the input mbus format. + * The bytesperline and sizeimage fields are computed from the requested bytes + * per line value in the pix format and information from the video instance. + * + * Return the number of padding bytes at end of line. + */ +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video, + const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + unsigned int bpl = pix->bytesperline; + unsigned int min_bpl; + unsigned int i; + + memset(pix, 0, sizeof(*pix)); + pix->width = mbus->width; + pix->height = mbus->height; + + /* Skip the last format in the loop so that it will be selected if no + * match is found. + */ + for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { + if (formats[i].code == mbus->code) + break; + } + + min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; + + /* Clamp the requested bytes per line value. If the maximum bytes per + * line value is zero, the module doesn't support user configurable line + * sizes. Override the requested value with the minimum in that case. + */ + if (video->bpl_max) + bpl = clamp(bpl, min_bpl, video->bpl_max); + else + bpl = min_bpl; + + if (!video->bpl_zero_padding || bpl != min_bpl) + bpl = ALIGN(bpl, video->bpl_alignment); + + pix->pixelformat = formats[i].pixelformat; + pix->bytesperline = bpl; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = mbus->colorspace; + pix->field = mbus->field; + + /* FIXME: Special case for NV12! We should make this nicer... */ + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage += (pix->bytesperline * pix->height) / 2; + + return bpl - min_bpl; +} + +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix, + struct v4l2_mbus_framefmt *mbus) +{ + unsigned int i; + + memset(mbus, 0, sizeof(*mbus)); + mbus->width = pix->width; + mbus->height = pix->height; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].pixelformat == pix->pixelformat) + break; + } + + if (WARN_ON(i == ARRAY_SIZE(formats))) + return; + + mbus->code = formats[i].code; + mbus->colorspace = pix->colorspace; + mbus->field = pix->field; +} + +static struct v4l2_subdev * +iss_video_remote_subdev(struct iss_video *video, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(&video->pad); + + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* Return a pointer to the ISS video instance at the far end of the pipeline. */ +static struct iss_video * +iss_video_far_end(struct iss_video *video) +{ + struct media_entity_graph graph; + struct media_entity *entity = &video->video.entity; + struct media_device *mdev = entity->parent; + struct iss_video *far_end = NULL; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video.entity) + continue; + + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + + far_end = to_iss_video(media_entity_to_video_device(entity)); + if (far_end->type != video->type) + break; + + far_end = NULL; + } + + mutex_unlock(&mdev->graph_mutex); + return far_end; +} + +static int +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + ret = -EINVAL; + + mutex_unlock(&video->mutex); + + if (ret) + return ret; + + format->type = video->type; + return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); +} + +static int +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh) +{ + struct v4l2_format format; + int ret; + + memcpy(&format, &vfh->format, sizeof(format)); + ret = __iss_video_get_format(video, &format); + if (ret < 0) + return ret; + + if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || + vfh->format.fmt.pix.height != format.fmt.pix.height || + vfh->format.fmt.pix.width != format.fmt.pix.width || + vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || + vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) + return -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct iss_video_fh *vfh = vb2_get_drv_priv(vq); + struct iss_video *video = vfh->video; + + /* Revisit multi-planar support for NV12 */ + *num_planes = 1; + + sizes[0] = vfh->format.fmt.pix.sizeimage; + if (sizes[0] == 0) + return -EINVAL; + + alloc_ctxs[0] = video->alloc_ctx; + + *count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0]))); + + return 0; +} + +static void iss_video_buf_cleanup(struct vb2_buffer *vb) +{ + struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); + + if (buffer->iss_addr) + buffer->iss_addr = 0; +} + +static int iss_video_buf_prepare(struct vb2_buffer *vb) +{ + struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); + struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); + struct iss_video *video = vfh->video; + unsigned long size = vfh->format.fmt.pix.sizeimage; + dma_addr_t addr; + + if (vb2_plane_size(vb, 0) < size) + return -ENOBUFS; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!IS_ALIGNED(addr, 32)) { + dev_dbg(video->iss->dev, "Buffer address must be " + "aligned to 32 bytes boundary.\n"); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + buffer->iss_addr = addr; + return 0; +} + +static void iss_video_buf_queue(struct vb2_buffer *vb) +{ + struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); + struct iss_video *video = vfh->video; + struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); + struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); + unsigned int empty; + unsigned long flags; + + spin_lock_irqsave(&video->qlock, flags); + empty = list_empty(&video->dmaqueue); + list_add_tail(&buffer->list, &video->dmaqueue); + spin_unlock_irqrestore(&video->qlock, flags); + + if (empty) { + enum iss_pipeline_state state; + unsigned int start; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISS_PIPELINE_QUEUE_OUTPUT; + else + state = ISS_PIPELINE_QUEUE_INPUT; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state |= state; + video->ops->queue(video, buffer); + video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED; + + start = iss_pipeline_ready(pipe); + if (start) + pipe->state |= ISS_PIPELINE_STREAM; + spin_unlock_irqrestore(&pipe->lock, flags); + + if (start) + omap4iss_pipeline_set_stream(pipe, + ISS_PIPELINE_STREAM_SINGLESHOT); + } +} + +static struct vb2_ops iss_video_vb2ops = { + .queue_setup = iss_video_queue_setup, + .buf_prepare = iss_video_buf_prepare, + .buf_queue = iss_video_buf_queue, + .buf_cleanup = iss_video_buf_cleanup, +}; + +/* + * omap4iss_video_buffer_next - Complete the current buffer and return the next + * @video: ISS video object + * + * Remove the current video buffer from the DMA queue and fill its timestamp, + * field count and state fields before waking up its completion handler. + * + * For capture video nodes, the buffer state is set to VB2_BUF_STATE_DONE if no + * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. + * + * The DMA queue is expected to contain at least one buffer. + * + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is + * empty. + */ +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) +{ + struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); + enum iss_pipeline_state state; + struct iss_buffer *buf; + unsigned long flags; + struct timespec ts; + + spin_lock_irqsave(&video->qlock, flags); + if (WARN_ON(list_empty(&video->dmaqueue))) { + spin_unlock_irqrestore(&video->qlock, flags); + return NULL; + } + + buf = list_first_entry(&video->dmaqueue, struct iss_buffer, + list); + list_del(&buf->list); + spin_unlock_irqrestore(&video->qlock, flags); + + ktime_get_ts(&ts); + buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec; + buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + + /* Do frame number propagation only if this is the output video node. + * Frame number either comes from the CSI receivers or it gets + * incremented here if H3A is not active. + * Note: There is no guarantee that the output buffer will finish + * first, so the input number might lag behind by 1 in some cases. + */ + if (video == pipe->output && !pipe->do_propagation) + buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number); + else + buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); + + vb2_buffer_done(&buf->vb, pipe->error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + pipe->error = false; + + spin_lock_irqsave(&video->qlock, flags); + if (list_empty(&video->dmaqueue)) { + spin_unlock_irqrestore(&video->qlock, flags); + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISS_PIPELINE_QUEUE_OUTPUT + | ISS_PIPELINE_STREAM; + else + state = ISS_PIPELINE_QUEUE_INPUT + | ISS_PIPELINE_STREAM; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~state; + if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS) + video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; + spin_unlock_irqrestore(&pipe->lock, flags); + return NULL; + } + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~ISS_PIPELINE_STREAM; + spin_unlock_irqrestore(&pipe->lock, flags); + } + + buf = list_first_entry(&video->dmaqueue, struct iss_buffer, + list); + spin_unlock_irqrestore(&video->qlock, flags); + buf->vb.state = VB2_BUF_STATE_ACTIVE; + return buf; +} + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct iss_video *video = video_drvdata(file); + + strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, video->video.name, sizeof(cap->card)); + strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + return 0; +} + +static int +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + + if (format->type != video->type) + return -EINVAL; + + mutex_lock(&video->mutex); + *format = vfh->format; + mutex_unlock(&video->mutex); + + return 0; +} + +static int +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + struct v4l2_mbus_framefmt fmt; + + if (format->type != video->type) + return -EINVAL; + + mutex_lock(&video->mutex); + + /* Fill the bytesperline and sizeimage fields by converting to media bus + * format and back to pixel format. + */ + iss_video_pix_to_mbus(&format->fmt.pix, &fmt); + iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix); + + vfh->format = *format; + + mutex_unlock(&video->mutex); + return 0; +} + +static int +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + if (format->type != video->type) + return -EINVAL; + + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format); + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); + return 0; +} + +static int +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + int ret; + + subdev = iss_video_remote_subdev(video, NULL); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); + mutex_unlock(&video->mutex); + + return ret == -ENOIOCTLCMD ? -EINVAL : ret; +} + +static int +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev_format format; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + /* Try the get crop operation first and fallback to get format if not + * implemented. + */ + ret = v4l2_subdev_call(subdev, video, g_crop, crop); + if (ret != -ENOIOCTLCMD) + return ret; + + format.pad = pad; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + crop->c.left = 0; + crop->c.top = 0; + crop->c.width = format.format.width; + crop->c.height = format.format.height; + + return 0; +} + +static int +iss_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + int ret; + + subdev = iss_video_remote_subdev(video, NULL); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, video, s_crop, crop); + mutex_unlock(&video->mutex); + + return ret == -ENOIOCTLCMD ? -EINVAL : ret; +} + +static int +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + video->type != a->type) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe = vfh->timeperframe; + + return 0; +} + +static int +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + video->type != a->type) + return -EINVAL; + + if (a->parm.output.timeperframe.denominator == 0) + a->parm.output.timeperframe.denominator = 1; + + vfh->timeperframe = a->parm.output.timeperframe; + + return 0; +} + +static int +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_reqbufs(&vfh->queue, rb); +} + +static int +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_querybuf(&vfh->queue, b); +} + +static int +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_qbuf(&vfh->queue, b); +} + +static int +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); +} + +/* + * Stream management + * + * Every ISS pipeline has a single input and a single output. The input can be + * either a sensor or a video node. The output is always a video node. + * + * As every pipeline has an output video node, the ISS video objects at the + * pipeline output stores the pipeline state. It tracks the streaming state of + * both the input and output, as well as the availability of buffers. + * + * In sensor-to-memory mode, frames are always available at the pipeline input. + * Starting the sensor usually requires I2C transfers and must be done in + * interruptible context. The pipeline is started and stopped synchronously + * to the stream on/off commands. All modules in the pipeline will get their + * subdev set stream handler called. The module at the end of the pipeline must + * delay starting the hardware until buffers are available at its output. + * + * In memory-to-memory mode, starting/stopping the stream requires + * synchronization between the input and output. ISS modules can't be stopped + * in the middle of a frame, and at least some of the modules seem to become + * busy as soon as they're started, even if they don't receive a frame start + * event. For that reason frames need to be processed in single-shot mode. The + * driver needs to wait until a frame is completely processed and written to + * memory before restarting the pipeline for the next frame. Pipelined + * processing might be possible but requires more testing. + * + * Stream start must be delayed until buffers are available at both the input + * and output. The pipeline must be started in the videobuf queue callback with + * the buffers queue spinlock held. The modules subdev set stream operation must + * not sleep. + */ +static int +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + enum iss_pipeline_state state; + struct iss_pipeline *pipe; + struct iss_video *far_end; + unsigned long flags; + int ret; + + if (type != video->type) + return -EINVAL; + + mutex_lock(&video->stream_lock); + + if (video->streaming) { + mutex_unlock(&video->stream_lock); + return -EBUSY; + } + + /* Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + */ + pipe = video->video.entity.pipe + ? to_iss_pipeline(&video->video.entity) : &video->pipe; + pipe->external = NULL; + pipe->external_rate = 0; + pipe->external_bpp = 0; + + if (video->iss->pdata->set_constraints) + video->iss->pdata->set_constraints(video->iss, true); + + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + goto err_media_entity_pipeline_start; + + /* Verify that the currently configured format matches the output of + * the connected subdev. + */ + ret = iss_video_check_format(video, vfh); + if (ret < 0) + goto err_iss_video_check_format; + + video->bpl_padding = ret; + video->bpl_value = vfh->format.fmt.pix.bytesperline; + + /* Find the ISS video node connected at the far end of the pipeline and + * update the pipeline. + */ + far_end = iss_video_far_end(video); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT; + pipe->input = far_end; + pipe->output = video; + } else { + if (far_end == NULL) { + ret = -EPIPE; + goto err_iss_video_check_format; + } + + state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT; + pipe->input = video; + pipe->output = far_end; + } + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~ISS_PIPELINE_STREAM; + pipe->state |= state; + spin_unlock_irqrestore(&pipe->lock, flags); + + /* Set the maximum time per frame as the value requested by userspace. + * This is a soft limit that can be overridden if the hardware doesn't + * support the request limit. + */ + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->max_timeperframe = vfh->timeperframe; + + video->queue = &vfh->queue; + INIT_LIST_HEAD(&video->dmaqueue); + spin_lock_init(&video->qlock); + atomic_set(&pipe->frame_number, -1); + + ret = vb2_streamon(&vfh->queue, type); + if (ret < 0) + goto err_iss_video_check_format; + + /* In sensor-to-memory mode, the stream can be started synchronously + * to the stream on command. In memory-to-memory mode, it will be + * started when buffers are queued on both the input and output. + */ + if (pipe->input == NULL) { + unsigned long flags; + ret = omap4iss_pipeline_set_stream(pipe, + ISS_PIPELINE_STREAM_CONTINUOUS); + if (ret < 0) + goto err_omap4iss_set_stream; + spin_lock_irqsave(&video->qlock, flags); + if (list_empty(&video->dmaqueue)) + video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; + spin_unlock_irqrestore(&video->qlock, flags); + } + + if (ret < 0) { +err_omap4iss_set_stream: + vb2_streamoff(&vfh->queue, type); +err_iss_video_check_format: + media_entity_pipeline_stop(&video->video.entity); +err_media_entity_pipeline_start: + if (video->iss->pdata->set_constraints) + video->iss->pdata->set_constraints(video->iss, false); + video->queue = NULL; + } + + if (!ret) + video->streaming = 1; + + mutex_unlock(&video->stream_lock); + return ret; +} + +static int +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + struct iss_video *video = video_drvdata(file); + struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); + enum iss_pipeline_state state; + unsigned long flags; + + if (type != video->type) + return -EINVAL; + + mutex_lock(&video->stream_lock); + + if (!vb2_is_streaming(&vfh->queue)) + goto done; + + /* Update the pipeline state. */ + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISS_PIPELINE_STREAM_OUTPUT + | ISS_PIPELINE_QUEUE_OUTPUT; + else + state = ISS_PIPELINE_STREAM_INPUT + | ISS_PIPELINE_QUEUE_INPUT; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~state; + spin_unlock_irqrestore(&pipe->lock, flags); + + /* Stop the stream. */ + omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED); + vb2_streamoff(&vfh->queue, type); + video->queue = NULL; + video->streaming = 0; + + if (video->iss->pdata->set_constraints) + video->iss->pdata->set_constraints(video->iss, false); + media_entity_pipeline_stop(&video->video.entity); + +done: + mutex_unlock(&video->stream_lock); + return 0; +} + +static int +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int +iss_video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { + .vidioc_querycap = iss_video_querycap, + .vidioc_g_fmt_vid_cap = iss_video_get_format, + .vidioc_s_fmt_vid_cap = iss_video_set_format, + .vidioc_try_fmt_vid_cap = iss_video_try_format, + .vidioc_g_fmt_vid_out = iss_video_get_format, + .vidioc_s_fmt_vid_out = iss_video_set_format, + .vidioc_try_fmt_vid_out = iss_video_try_format, + .vidioc_cropcap = iss_video_cropcap, + .vidioc_g_crop = iss_video_get_crop, + .vidioc_s_crop = iss_video_set_crop, + .vidioc_g_parm = iss_video_get_param, + .vidioc_s_parm = iss_video_set_param, + .vidioc_reqbufs = iss_video_reqbufs, + .vidioc_querybuf = iss_video_querybuf, + .vidioc_qbuf = iss_video_qbuf, + .vidioc_dqbuf = iss_video_dqbuf, + .vidioc_streamon = iss_video_streamon, + .vidioc_streamoff = iss_video_streamoff, + .vidioc_enum_input = iss_video_enum_input, + .vidioc_g_input = iss_video_g_input, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int iss_video_open(struct file *file) +{ + struct iss_video *video = video_drvdata(file); + struct iss_video_fh *handle; + struct vb2_queue *q; + int ret = 0; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video); + v4l2_fh_add(&handle->vfh); + + /* If this is the first user, initialise the pipeline. */ + if (omap4iss_get(video->iss) == NULL) { + ret = -EBUSY; + goto done; + } + + ret = omap4iss_pipeline_pm_use(&video->video.entity, 1); + if (ret < 0) { + omap4iss_put(video->iss); + goto done; + } + + video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev); + if (IS_ERR(video->alloc_ctx)) { + ret = PTR_ERR(video->alloc_ctx); + omap4iss_put(video->iss); + goto done; + } + + q = &handle->queue; + + q->type = video->type; + q->io_modes = VB2_MMAP; + q->drv_priv = handle; + q->ops = &iss_video_vb2ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct iss_buffer); + + ret = vb2_queue_init(q); + if (ret) { + omap4iss_put(video->iss); + goto done; + } + + memset(&handle->format, 0, sizeof(handle->format)); + handle->format.type = video->type; + handle->timeperframe.denominator = 1; + + handle->video = video; + file->private_data = &handle->vfh; + +done: + if (ret < 0) { + v4l2_fh_del(&handle->vfh); + kfree(handle); + } + + return ret; +} + +static int iss_video_release(struct file *file) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct iss_video_fh *handle = to_iss_video_fh(vfh); + + /* Disable streaming and free the buffers queue resources. */ + iss_video_streamoff(file, vfh, video->type); + + omap4iss_pipeline_pm_use(&video->video.entity, 0); + + /* Release the videobuf2 queue */ + vb2_queue_release(&handle->queue); + + /* Release the file handle. */ + v4l2_fh_del(vfh); + kfree(handle); + file->private_data = NULL; + + omap4iss_put(video->iss); + + return 0; +} + +static unsigned int iss_video_poll(struct file *file, poll_table *wait) +{ + struct iss_video_fh *vfh = to_iss_video_fh(file->private_data); + + return vb2_poll(&vfh->queue, file, wait); +} + +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct iss_video_fh *vfh = to_iss_video_fh(file->private_data); + + return vb2_mmap(&vfh->queue, vma);; +} + +static struct v4l2_file_operations iss_video_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = iss_video_open, + .release = iss_video_release, + .poll = iss_video_poll, + .mmap = iss_video_mmap, +}; + +/* ----------------------------------------------------------------------------- + * ISS video core + */ + +static const struct iss_video_operations iss_video_dummy_ops = { +}; + +int omap4iss_video_init(struct iss_video *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + break; + + default: + return -EINVAL; + } + + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + if (ret < 0) + return ret; + + mutex_init(&video->mutex); + atomic_set(&video->active, 0); + + spin_lock_init(&video->pipe.lock); + mutex_init(&video->stream_lock); + + /* Initialize the video device. */ + if (video->ops == NULL) + video->ops = &iss_video_dummy_ops; + + video->video.fops = &iss_video_fops; + snprintf(video->video.name, sizeof(video->video.name), + "OMAP4 ISS %s %s", name, direction); + video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.release = video_device_release_empty; + video->video.ioctl_ops = &iss_video_ioctl_ops; + video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED; + + video_set_drvdata(&video->video, video); + + return 0; +} + +void omap4iss_video_cleanup(struct iss_video *video) +{ + media_entity_cleanup(&video->video.entity); + mutex_destroy(&video->stream_lock); + mutex_destroy(&video->mutex); +} + +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) +{ + int ret; + + video->video.v4l2_dev = vdev; + + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) + printk(KERN_ERR "%s: could not register video device (%d)\n", + __func__, ret); + + return ret; +} + +void omap4iss_video_unregister(struct iss_video *video) +{ + if (video_is_registered(&video->video)) + video_unregister_device(&video->video); +} diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h new file mode 100644 index 0000000..8cf1b35 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -0,0 +1,201 @@ +/* + * TI OMAP4 ISS V4L2 Driver - Generic video node + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_VIDEO_H +#define OMAP4_ISS_VIDEO_H + +#include +#include +#include +#include +#include +#include + +#define ISS_VIDEO_DRIVER_NAME "issvideo" +#define ISS_VIDEO_DRIVER_VERSION "0.0.2" + +struct iss_device; +struct iss_video; +struct v4l2_mbus_framefmt; +struct v4l2_pix_format; + +/* + * struct iss_format_info - ISS media bus format information + * @code: V4L2 media bus format code + * @truncated: V4L2 media bus format code for the same format truncated to 10 + * bits. Identical to @code if the format is 10 bits wide or less. + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed + * format. Identical to @code if the format is not DPCM compressed. + * @flavor: V4L2 media bus format code for the same pixel layout but + * shifted to be 8 bits per pixel. =0 if format is not shiftable. + * @pixelformat: V4L2 pixel format FCC identifier + * @bpp: Bits per pixel + */ +struct iss_format_info { + enum v4l2_mbus_pixelcode code; + enum v4l2_mbus_pixelcode truncated; + enum v4l2_mbus_pixelcode uncompressed; + enum v4l2_mbus_pixelcode flavor; + u32 pixelformat; + unsigned int bpp; +}; + +enum iss_pipeline_stream_state { + ISS_PIPELINE_STREAM_STOPPED = 0, + ISS_PIPELINE_STREAM_CONTINUOUS = 1, + ISS_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum iss_pipeline_state { + /* The stream has been started on the input video node. */ + ISS_PIPELINE_STREAM_INPUT = 1, + /* The stream has been started on the output video node. */ + ISS_PIPELINE_STREAM_OUTPUT = (1 << 1), + /* At least one buffer is queued on the input video node. */ + ISS_PIPELINE_QUEUE_INPUT = (1 << 2), + /* At least one buffer is queued on the output video node. */ + ISS_PIPELINE_QUEUE_OUTPUT = (1 << 3), + /* The input entity is idle, ready to be started. */ + ISS_PIPELINE_IDLE_INPUT = (1 << 4), + /* The output entity is idle, ready to be started. */ + ISS_PIPELINE_IDLE_OUTPUT = (1 << 5), + /* The pipeline is currently streaming. */ + ISS_PIPELINE_STREAM = (1 << 6), +}; + +/* + * struct iss_pipeline - An OMAP4 ISS hardware pipeline + * @error: A hardware error occurred during capture + */ +struct iss_pipeline { + struct media_pipeline pipe; + spinlock_t lock; /* Pipeline state and queue flags */ + unsigned int state; + enum iss_pipeline_stream_state stream_state; + struct iss_video *input; + struct iss_video *output; + atomic_t frame_number; + bool do_propagation; /* of frame number */ + bool error; + struct v4l2_fract max_timeperframe; + struct v4l2_subdev *external; + unsigned int external_rate; + int external_bpp; +}; + +#define to_iss_pipeline(__e) \ + container_of((__e)->pipe, struct iss_pipeline, pipe) + +static inline int iss_pipeline_ready(struct iss_pipeline *pipe) +{ + return pipe->state == (ISS_PIPELINE_STREAM_INPUT | + ISS_PIPELINE_STREAM_OUTPUT | + ISS_PIPELINE_QUEUE_INPUT | + ISS_PIPELINE_QUEUE_OUTPUT | + ISS_PIPELINE_IDLE_INPUT | + ISS_PIPELINE_IDLE_OUTPUT); +} + +/* + * struct iss_buffer - ISS buffer + * @buffer: ISS video buffer + * @iss_addr: Physical address of the buffer. + */ +struct iss_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + struct list_head list; + dma_addr_t iss_addr; +}; + +#define to_iss_buffer(buf) container_of(buf, struct iss_buffer, buffer) + +enum iss_video_dmaqueue_flags { + /* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */ + ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), + /* Set when queuing buffer to an empty DMA queue */ + ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1), +}; + +#define iss_video_dmaqueue_flags_clr(video) \ + ({ (video)->dmaqueue_flags = 0; }) + +/* + * struct iss_video_operations - ISS video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct iss_video_operations { + int(*queue)(struct iss_video *video, struct iss_buffer *buffer); +}; + +struct iss_video { + struct video_device video; + enum v4l2_buf_type type; + struct media_pad pad; + + struct mutex mutex; /* format and crop settings */ + atomic_t active; + + struct iss_device *iss; + + unsigned int capture_mem; + unsigned int bpl_alignment; /* alignment value */ + unsigned int bpl_zero_padding; /* whether the alignment is optional */ + unsigned int bpl_max; /* maximum bytes per line value */ + unsigned int bpl_value; /* bytes per line value */ + unsigned int bpl_padding; /* padding at end of line */ + + /* Entity video node streaming */ + unsigned int streaming:1; + + /* Pipeline state */ + struct iss_pipeline pipe; + struct mutex stream_lock; /* pipeline and stream states */ + + /* Video buffers queue */ + struct vb2_queue *queue; + spinlock_t qlock; /* Spinlock for dmaqueue */ + struct list_head dmaqueue; + enum iss_video_dmaqueue_flags dmaqueue_flags; + struct vb2_alloc_ctx *alloc_ctx; + + const struct iss_video_operations *ops; +}; + +#define to_iss_video(vdev) container_of(vdev, struct iss_video, video) + +struct iss_video_fh { + struct v4l2_fh vfh; + struct iss_video *video; + struct vb2_queue queue; + struct v4l2_format format; + struct v4l2_fract timeperframe; +}; + +#define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh) +#define iss_video_queue_to_iss_video_fh(q) \ + container_of(q, struct iss_video_fh, queue) + +int omap4iss_video_init(struct iss_video *video, const char *name); +void omap4iss_video_cleanup(struct iss_video *video); +int omap4iss_video_register(struct iss_video *video, + struct v4l2_device *vdev); +void omap4iss_video_unregister(struct iss_video *video); +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video); +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video); + +const struct iss_format_info * +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code); + +#endif /* OMAP4_ISS_VIDEO_H */ -- cgit v0.10.2 From b4a0477c0b87c5b7a20a84df8cf81311d1efb226 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - CSI receivers This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds CSI receivers support. Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c new file mode 100644 index 0000000..0ee8381 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -0,0 +1,1368 @@ +/* + * TI OMAP4 ISS V4L2 Driver - CSI PHY module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" +#include "iss_csi2.h" + +/* + * csi2_if_enable - Enable CSI2 Receiver interface. + * @enable: enable flag + * + */ +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable) +{ + struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl; + + writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) | + (enable ? CSI2_CTRL_IF_EN : 0), + csi2->regs1 + CSI2_CTRL); + + currctrl->if_enable = enable; +} + +/* + * csi2_recv_config - CSI2 receiver module configuration. + * @currctrl: iss_csi2_ctrl_cfg structure + * + */ +static void csi2_recv_config(struct iss_csi2_device *csi2, + struct iss_csi2_ctrl_cfg *currctrl) +{ + u32 reg = 0; + + if (currctrl->frame_mode) + reg |= CSI2_CTRL_FRAME; + else + reg &= ~CSI2_CTRL_FRAME; + + if (currctrl->vp_clk_enable) + reg |= CSI2_CTRL_VP_CLK_EN; + else + reg &= ~CSI2_CTRL_VP_CLK_EN; + + if (currctrl->vp_only_enable) + reg |= CSI2_CTRL_VP_ONLY_EN; + else + reg &= ~CSI2_CTRL_VP_ONLY_EN; + + reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK; + reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT; + + if (currctrl->ecc_enable) + reg |= CSI2_CTRL_ECC_EN; + else + reg &= ~CSI2_CTRL_ECC_EN; + + /* + * Set MFlag assertion boundaries to: + * Low: 4/8 of FIFO size + * High: 6/8 of FIFO size + */ + reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK); + reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) | + (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT); + + /* Generation of 16x64-bit bursts (Recommended) */ + reg |= CSI2_CTRL_BURST_SIZE_EXPAND; + + /* Do Non-Posted writes (Recommended) */ + reg |= CSI2_CTRL_NON_POSTED_WRITE; + + /* + * Enforce Little endian for all formats, including: + * YUV4:2:2 8-bit and YUV4:2:0 Legacy + */ + reg |= CSI2_CTRL_ENDIANNESS; + + writel(reg, csi2->regs1 + CSI2_CTRL); +} + +static const unsigned int csi2_input_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, + V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGBRG8_1X8, + V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, +}; + +/* To set the format on the CSI2 requires a mapping function that takes + * the following inputs: + * - 3 different formats (at this time) + * - 2 destinations (mem, vp+mem) (vp only handled separately) + * - 2 decompression options (on, off) + * Output should be CSI2 frame format code + * Array indices as follows: [format][dest][decompr] + * Not all combinations are valid. 0 means invalid. + */ +static const u16 __csi2_fmt_map[][2][2] = { + /* RAW10 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_RAW10_EXP16, + /* DPCM decompression */ + 0, + }, + /* Output to both */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_RAW10_EXP16_VP, + /* DPCM decompression */ + 0, + }, + }, + /* RAW10 DPCM8 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + CSI2_USERDEF_8BIT_DATA1, + /* DPCM decompression */ + CSI2_USERDEF_8BIT_DATA1_DPCM10, + }, + /* Output to both */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_RAW8_VP, + /* DPCM decompression */ + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP, + }, + }, + /* RAW8 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_RAW8, + /* DPCM decompression */ + 0, + }, + /* Output to both */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_RAW8_VP, + /* DPCM decompression */ + 0, + }, + }, + /* YUV422 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_YUV422_8BIT, + /* DPCM decompression */ + 0, + }, + /* Output to both */ + { + /* No DPCM decompression */ + CSI2_PIX_FMT_YUV422_8BIT_VP16, + /* DPCM decompression */ + 0, + }, + }, +}; + +/* + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID + * @csi2: ISS CSI2 device + * + * Returns CSI2 physical format id + */ +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2) +{ + const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; + int fmtidx, destidx; + + switch (fmt->code) { + case V4L2_MBUS_FMT_SGRBG10_1X10: + case V4L2_MBUS_FMT_SRGGB10_1X10: + case V4L2_MBUS_FMT_SBGGR10_1X10: + case V4L2_MBUS_FMT_SGBRG10_1X10: + fmtidx = 0; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8: + case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8: + fmtidx = 1; + break; + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGBRG8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + case V4L2_MBUS_FMT_SRGGB8_1X8: + fmtidx = 2; + break; + case V4L2_MBUS_FMT_UYVY8_1X16: + case V4L2_MBUS_FMT_YUYV8_1X16: + fmtidx = 3; + break; + default: + WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", + fmt->code); + return 0; + } + + if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) && + !(csi2->output & CSI2_OUTPUT_MEMORY)) { + /* Neither output enabled is a valid combination */ + return CSI2_PIX_FMT_OTHERS; + } + + /* If we need to skip frames at the beginning of the stream disable the + * video port to avoid sending the skipped frames to the IPIPEIF. + */ + destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF); + + return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress]; +} + +/* + * csi2_set_outaddr - Set memory address to save output image + * @csi2: Pointer to ISS CSI2a device. + * @addr: 32-bit memory address aligned on 32 byte boundary. + * + * Sets the memory address where the output will be saved. + * + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte + * boundary. + */ +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr) +{ + struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0]; + + ctx->ping_addr = addr; + ctx->pong_addr = addr; + writel(ctx->ping_addr, + csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum)); + writel(ctx->pong_addr, + csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum)); +} + +/* + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should + * be enabled by CSI2. + * @format_id: mapped format id + * + */ +static inline int is_usr_def_mapping(u32 format_id) +{ + return ((format_id & 0xF0) == 0x40) ? 1 : 0; +} + +/* + * csi2_ctx_enable - Enable specified CSI2 context + * @ctxnum: Context number, valid between 0 and 7 values. + * @enable: enable + * + */ +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable) +{ + struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; + u32 reg; + + reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum)); + + if (enable) { + unsigned int skip = 0; + + if (csi2->frame_skip) + skip = csi2->frame_skip; + else if (csi2->output & CSI2_OUTPUT_MEMORY) + skip = 1; + + reg &= ~CSI2_CTX_CTRL1_COUNT_MASK; + reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK + | (skip << CSI2_CTX_CTRL1_COUNT_SHIFT) + | CSI2_CTX_CTRL1_CTX_EN; + } else { + reg &= ~CSI2_CTX_CTRL1_CTX_EN; + } + + writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum)); + ctx->enabled = enable; +} + +/* + * csi2_ctx_config - CSI2 context configuration. + * @ctx: context configuration + * + */ +static void csi2_ctx_config(struct iss_csi2_device *csi2, + struct iss_csi2_ctx_cfg *ctx) +{ + u32 reg; + + /* Set up CSI2_CTx_CTRL1 */ + if (ctx->eof_enabled) + reg = CSI2_CTX_CTRL1_EOF_EN; + + if (ctx->eol_enabled) + reg |= CSI2_CTX_CTRL1_EOL_EN; + + if (ctx->checksum_enabled) + reg |= CSI2_CTX_CTRL1_CS_EN; + + writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum)); + + /* Set up CSI2_CTx_CTRL2 */ + reg = ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; + reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT; + + if (ctx->dpcm_decompress && ctx->dpcm_predictor) + reg |= CSI2_CTX_CTRL2_DPCM_PRED; + + if (is_usr_def_mapping(ctx->format_id)) + reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; + + writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum)); + + /* Set up CSI2_CTx_CTRL3 */ + writel(ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT, + csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum)); + + /* Set up CSI2_CTx_DAT_OFST */ + reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum)); + reg &= ~CSI2_CTX_DAT_OFST_MASK; + reg |= ctx->data_offset; + writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum)); + + writel(ctx->ping_addr, + csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum)); + + writel(ctx->pong_addr, + csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum)); +} + +/* + * csi2_timing_config - CSI2 timing configuration. + * @timing: csi2_timing_cfg structure + */ +static void csi2_timing_config(struct iss_csi2_device *csi2, + struct iss_csi2_timing_cfg *timing) +{ + u32 reg; + + reg = readl(csi2->regs1 + CSI2_TIMING); + + if (timing->force_rx_mode) + reg |= CSI2_TIMING_FORCE_RX_MODE_IO1; + else + reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1; + + if (timing->stop_state_16x) + reg |= CSI2_TIMING_STOP_STATE_X16_IO1; + else + reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1; + + if (timing->stop_state_4x) + reg |= CSI2_TIMING_STOP_STATE_X4_IO1; + else + reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1; + + reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK; + reg |= timing->stop_state_counter << + CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT; + + writel(reg, csi2->regs1 + CSI2_TIMING); +} + +/* + * csi2_irq_ctx_set - Enables CSI2 Context IRQs. + * @enable: Enable/disable CSI2 Context interrupts + */ +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable) +{ + u32 reg = CSI2_CTX_IRQ_FE; + int i; + + if (csi2->use_fs_irq) + reg |= CSI2_CTX_IRQ_FS; + + for (i = 0; i < 8; i++) { + writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i)); + if (enable) + writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg, + csi2->regs1 + CSI2_CTX_IRQENABLE(i)); + else + writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) & + ~reg, + csi2->regs1 + CSI2_CTX_IRQENABLE(i)); + } +} + +/* + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts + */ +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable) +{ + u32 reg; + reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT | + CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER | + CSI2_COMPLEXIO_IRQ_STATEULPM5 | + CSI2_COMPLEXIO_IRQ_ERRCONTROL5 | + CSI2_COMPLEXIO_IRQ_ERRESC5 | + CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 | + CSI2_COMPLEXIO_IRQ_ERRSOTHS5 | + CSI2_COMPLEXIO_IRQ_STATEULPM4 | + CSI2_COMPLEXIO_IRQ_ERRCONTROL4 | + CSI2_COMPLEXIO_IRQ_ERRESC4 | + CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 | + CSI2_COMPLEXIO_IRQ_ERRSOTHS4 | + CSI2_COMPLEXIO_IRQ_STATEULPM3 | + CSI2_COMPLEXIO_IRQ_ERRCONTROL3 | + CSI2_COMPLEXIO_IRQ_ERRESC3 | + CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 | + CSI2_COMPLEXIO_IRQ_ERRSOTHS3 | + CSI2_COMPLEXIO_IRQ_STATEULPM2 | + CSI2_COMPLEXIO_IRQ_ERRCONTROL2 | + CSI2_COMPLEXIO_IRQ_ERRESC2 | + CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 | + CSI2_COMPLEXIO_IRQ_ERRSOTHS2 | + CSI2_COMPLEXIO_IRQ_STATEULPM1 | + CSI2_COMPLEXIO_IRQ_ERRCONTROL1 | + CSI2_COMPLEXIO_IRQ_ERRESC1 | + CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 | + CSI2_COMPLEXIO_IRQ_ERRSOTHS1; + writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS); + if (enable) + reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE); + else + reg = 0; + writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE); +} + +/* + * csi2_irq_status_set - Enables CSI2 Status IRQs. + * @enable: Enable/disable CSI2 Status interrupts + */ +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable) +{ + u32 reg; + reg = CSI2_IRQ_OCP_ERR | + CSI2_IRQ_SHORT_PACKET | + CSI2_IRQ_ECC_CORRECTION | + CSI2_IRQ_ECC_NO_CORRECTION | + CSI2_IRQ_COMPLEXIO_ERR | + CSI2_IRQ_FIFO_OVF | + CSI2_IRQ_CONTEXT0; + writel(reg, csi2->regs1 + CSI2_IRQSTATUS); + if (enable) + reg |= readl(csi2->regs1 + CSI2_IRQENABLE); + else + reg = 0; + + writel(reg, csi2->regs1 + CSI2_IRQENABLE); +} + +/* + * omap4iss_csi2_reset - Resets the CSI2 module. + * + * Must be called with the phy lock held. + * + * Returns 0 if successful, or -EBUSY if power command didn't respond. + */ +int omap4iss_csi2_reset(struct iss_csi2_device *csi2) +{ + u8 soft_reset_retries = 0; + u32 reg; + int i; + + if (!csi2->available) + return -ENODEV; + + if (csi2->phy->phy_in_use) + return -EBUSY; + + writel(readl(csi2->regs1 + CSI2_SYSCONFIG) | + CSI2_SYSCONFIG_SOFT_RESET, + csi2->regs1 + CSI2_SYSCONFIG); + + do { + reg = readl(csi2->regs1 + CSI2_SYSSTATUS) & + CSI2_SYSSTATUS_RESET_DONE; + if (reg == CSI2_SYSSTATUS_RESET_DONE) + break; + soft_reset_retries++; + if (soft_reset_retries < 5) + usleep_range(100, 100); + } while (soft_reset_retries < 5); + + if (soft_reset_retries == 5) { + printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); + return -EBUSY; + } + + writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) | + CSI2_COMPLEXIO_CFG_RESET_CTRL, + csi2->regs1 + CSI2_COMPLEXIO_CFG); + + i = 100; + do { + reg = readl(csi2->phy->phy_regs + REGISTER1) + & REGISTER1_RESET_DONE_CTRLCLK; + if (reg == REGISTER1_RESET_DONE_CTRLCLK) + break; + usleep_range(100, 100); + } while (--i > 0); + + if (i == 0) { + printk(KERN_ERR + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); + return -EBUSY; + } + + writel((readl(csi2->regs1 + CSI2_SYSCONFIG) & + ~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK | + CSI2_SYSCONFIG_AUTO_IDLE)) | + CSI2_SYSCONFIG_MSTANDBY_MODE_NO, + csi2->regs1 + CSI2_SYSCONFIG); + + return 0; +} + +static int csi2_configure(struct iss_csi2_device *csi2) +{ + const struct iss_v4l2_subdevs_group *pdata; + struct iss_csi2_timing_cfg *timing = &csi2->timing[0]; + struct v4l2_subdev *sensor; + struct media_pad *pad; + + /* + * CSI2 fields that can be updated while the context has + * been enabled or the interface has been enabled are not + * updated dynamically currently. So we do not allow to + * reconfigure if either has been enabled + */ + if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) + return -EBUSY; + + pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]); + sensor = media_entity_to_v4l2_subdev(pad->entity); + pdata = sensor->host_priv; + + csi2->frame_skip = 0; + v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); + + csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; + csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE; + csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; + + timing->force_rx_mode = 1; + timing->stop_state_16x = 1; + timing->stop_state_4x = 1; + timing->stop_state_counter = 0x1FF; + + /* + * The CSI2 receiver can't do any format conversion except DPCM + * decompression, so every set_format call configures both pads + * and enables DPCM decompression as a special case: + */ + if (csi2->formats[CSI2_PAD_SINK].code != + csi2->formats[CSI2_PAD_SOURCE].code) + csi2->dpcm_decompress = true; + else + csi2->dpcm_decompress = false; + + csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); + + if (csi2->video_out.bpl_padding == 0) + csi2->contexts[0].data_offset = 0; + else + csi2->contexts[0].data_offset = csi2->video_out.bpl_value; + + /* + * Enable end of frame and end of line signals generation for + * context 0. These signals are generated from CSI2 receiver to + * qualify the last pixel of a frame and the last pixel of a line. + * Without enabling the signals CSI2 receiver writes data to memory + * beyond buffer size and/or data line offset is not handled correctly. + */ + csi2->contexts[0].eof_enabled = 1; + csi2->contexts[0].eol_enabled = 1; + + csi2_irq_complexio1_set(csi2, 1); + csi2_irq_ctx_set(csi2, 1); + csi2_irq_status_set(csi2, 1); + + /* Set configuration (timings, format and links) */ + csi2_timing_config(csi2, timing); + csi2_recv_config(csi2, &csi2->ctrl); + csi2_ctx_config(csi2, &csi2->contexts[0]); + + return 0; +} + +/* + * csi2_print_status - Prints CSI2 debug information. + */ +#define CSI2_PRINT_REGISTER(iss, regs, name)\ + dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \ + readl(regs + CSI2_##name)) + +static void csi2_print_status(struct iss_csi2_device *csi2) +{ + struct iss_device *iss = csi2->iss; + + if (!csi2->available) + return; + + dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n"); + + CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG); + CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS); + CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE); + CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL); + CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H); + CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG); + CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS); + CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET); + CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE); + CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P); + CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0)); + CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0)); + + dev_dbg(iss->dev, "--------------------------------------------\n"); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +/* + * csi2_isr_buffer - Does buffer handling at end-of-frame + * when writing to memory. + */ +static void csi2_isr_buffer(struct iss_csi2_device *csi2) +{ + struct iss_buffer *buffer; + + csi2_ctx_enable(csi2, 0, 0); + + buffer = omap4iss_video_buffer_next(&csi2->video_out); + + /* + * Let video queue operation restart engine if there is an underrun + * condition. + */ + if (buffer == NULL) + return; + + csi2_set_outaddr(csi2, buffer->iss_addr); + csi2_ctx_enable(csi2, 0, 1); +} + +static void csi2_isr_ctx(struct iss_csi2_device *csi2, + struct iss_csi2_ctx_cfg *ctx) +{ + unsigned int n = ctx->ctxnum; + u32 status; + + status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n)); + writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n)); + + /* Propagate frame number */ + if (status & CSI2_CTX_IRQ_FS) { + struct iss_pipeline *pipe = + to_iss_pipeline(&csi2->subdev.entity); + if (pipe->do_propagation) + atomic_inc(&pipe->frame_number); + } + + if (!(status & CSI2_CTX_IRQ_FE)) + return; + + /* Skip interrupts until we reach the frame skip count. The CSI2 will be + * automatically disabled, as the frame skip count has been programmed + * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. + * + * It would have been nice to rely on the FRAME_NUMBER interrupt instead + * but it turned out that the interrupt is only generated when the CSI2 + * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased + * correctly and reaches 0 when data is forwarded to the video port only + * but no interrupt arrives). Maybe a CSI2 hardware bug. + */ + if (csi2->frame_skip) { + csi2->frame_skip--; + if (csi2->frame_skip == 0) { + ctx->format_id = csi2_ctx_map_format(csi2); + csi2_ctx_config(csi2, ctx); + csi2_ctx_enable(csi2, n, 1); + } + return; + } + + if (csi2->output & CSI2_OUTPUT_MEMORY) + csi2_isr_buffer(csi2); +} + +/* + * omap4iss_csi2_isr - CSI2 interrupt handling. + */ +void omap4iss_csi2_isr(struct iss_csi2_device *csi2) +{ + struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); + u32 csi2_irqstatus, cpxio1_irqstatus; + struct iss_device *iss = csi2->iss; + + if (!csi2->available) + return; + + csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS); + writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS); + + /* Failure Cases */ + if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) { + cpxio1_irqstatus = readl(csi2->regs1 + + CSI2_COMPLEXIO_IRQSTATUS); + writel(cpxio1_irqstatus, + csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS); + dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ " + "%x\n", cpxio1_irqstatus); + pipe->error = true; + } + + if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR | + CSI2_IRQ_SHORT_PACKET | + CSI2_IRQ_ECC_NO_CORRECTION | + CSI2_IRQ_COMPLEXIO_ERR | + CSI2_IRQ_FIFO_OVF)) { + dev_dbg(iss->dev, "CSI2 Err:" + " OCP:%d," + " Short_pack:%d," + " ECC:%d," + " CPXIO:%d," + " FIFO_OVF:%d," + "\n", + (csi2_irqstatus & + CSI2_IRQ_OCP_ERR) ? 1 : 0, + (csi2_irqstatus & + CSI2_IRQ_SHORT_PACKET) ? 1 : 0, + (csi2_irqstatus & + CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0, + (csi2_irqstatus & + CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0, + (csi2_irqstatus & + CSI2_IRQ_FIFO_OVF) ? 1 : 0); + pipe->error = true; + } + + if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) + return; + + /* Successful cases */ + if (csi2_irqstatus & CSI2_IRQ_CONTEXT0) + csi2_isr_ctx(csi2, &csi2->contexts[0]); + + if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION) + dev_dbg(iss->dev, "CSI2: ECC correction done\n"); +} + +/* ----------------------------------------------------------------------------- + * ISS video operations + */ + +/* + * csi2_queue - Queues the first buffer when using memory output + * @video: The video node + * @buffer: buffer to queue + */ +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer) +{ + struct iss_csi2_device *csi2 = container_of(video, + struct iss_csi2_device, video_out); + + csi2_set_outaddr(csi2, buffer->iss_addr); + + /* + * If streaming was enabled before there was a buffer queued + * or underrun happened in the ISR, the hardware was not enabled + * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set. + * Enable it now. + */ + if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { + /* Enable / disable context 0 and IRQs */ + csi2_if_enable(csi2, 1); + csi2_ctx_enable(csi2, 0, 1); + iss_video_dmaqueue_flags_clr(&csi2->video_out); + } + + return 0; +} + +static const struct iss_video_operations csi2_issvideo_ops = { + .queue = csi2_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static struct v4l2_mbus_framefmt * +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &csi2->formats[pad]; +} + +static void +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + enum v4l2_mbus_pixelcode pixelcode; + struct v4l2_mbus_framefmt *format; + const struct iss_format_info *info; + unsigned int i; + + switch (pad) { + case CSI2_PAD_SINK: + /* Clamp the width and height to valid range (1-8191). */ + for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { + if (fmt->code == csi2_input_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(csi2_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + break; + + case CSI2_PAD_SOURCE: + /* Source format same as sink format, except for DPCM + * compression. + */ + pixelcode = fmt->code; + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* + * Only Allow DPCM decompression, and check that the + * pattern is preserved + */ + info = omap4iss_video_format_info(fmt->code); + if (info->uncompressed == pixelcode) + fmt->code = pixelcode; + break; + } + + /* RGB, non-interlaced */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * csi2_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int csi2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + const struct iss_format_info *info; + + if (code->pad == CSI2_PAD_SINK) { + if (code->index >= ARRAY_SIZE(csi2_input_fmts)) + return -EINVAL; + + code->code = csi2_input_fmts[code->index]; + } else { + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + switch (code->index) { + case 0: + /* Passthrough sink pad code */ + code->code = format->code; + break; + case 1: + /* Uncompressed code */ + info = omap4iss_video_format_info(format->code); + if (info->uncompressed == format->code) + return -EINVAL; + + code->code = info->uncompressed; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int csi2_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * csi2_get_format - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * csi2_set_format - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == CSI2_PAD_SINK) { + format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, + fmt->which); + *format = fmt->format; + csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); + } + + return 0; +} + +static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); + int rval; + + pipe->external = media_entity_to_v4l2_subdev(link->source->entity); + rval = omap4iss_get_external_info(pipe, link); + if (rval < 0) + return rval; + + return v4l2_subdev_link_validate_default(sd, link, source_fmt, + sink_fmt); +} + +/* + * csi2_init_formats - Initialize formats on all pads + * @sd: ISS CSI2 V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = CSI2_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + csi2_set_format(sd, fh, &format); + + return 0; +} + +/* + * csi2_set_stream - Enable/Disable streaming on the CSI2 module + * @sd: ISS CSI2 V4L2 subdevice + * @enable: ISS pipeline stream state + * + * Return 0 on success or a negative error code otherwise. + */ +static int csi2_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct iss_device *iss = csi2->iss; + struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); + struct iss_video *video_out = &csi2->video_out; + + if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) { + if (enable == ISS_PIPELINE_STREAM_STOPPED) + return 0; + + if (csi2 == &iss->csi2a) + omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A); + else if (csi2 == &iss->csi2b) + omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_B); + } + + switch (enable) { + case ISS_PIPELINE_STREAM_CONTINUOUS: { + int ret; + + ret = omap4iss_csiphy_config(iss, sd); + if (ret < 0) + return ret; + + if (omap4iss_csiphy_acquire(csi2->phy) < 0) + return -ENODEV; + csi2->use_fs_irq = pipe->do_propagation; + csi2_configure(csi2); + csi2_print_status(csi2); + + /* + * When outputting to memory with no buffer available, let the + * buffer queue handler start the hardware. A DMA queue flag + * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is + * a buffer available. + */ + if (csi2->output & CSI2_OUTPUT_MEMORY && + !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED)) + break; + /* Enable context 0 and IRQs */ + atomic_set(&csi2->stopping, 0); + csi2_ctx_enable(csi2, 0, 1); + csi2_if_enable(csi2, 1); + iss_video_dmaqueue_flags_clr(video_out); + break; + } + case ISS_PIPELINE_STREAM_STOPPED: + if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait, + &csi2->stopping)) + dev_dbg(iss->dev, "%s: module stop timeout.\n", + sd->name); + csi2_ctx_enable(csi2, 0, 0); + csi2_if_enable(csi2, 0); + csi2_irq_ctx_set(csi2, 0); + omap4iss_csiphy_release(csi2->phy); + if (csi2 == &iss->csi2a) + omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A); + else if (csi2 == &iss->csi2b) + omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_B); + iss_video_dmaqueue_flags_clr(video_out); + break; + } + + csi2->state = enable; + return 0; +} + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops csi2_video_ops = { + .s_stream = csi2_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops csi2_pad_ops = { + .enum_mbus_code = csi2_enum_mbus_code, + .enum_frame_size = csi2_enum_frame_size, + .get_fmt = csi2_get_format, + .set_fmt = csi2_set_format, + .link_validate = csi2_link_validate, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops csi2_ops = { + .video = &csi2_video_ops, + .pad = &csi2_pad_ops, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .open = csi2_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * csi2_link_setup - Setup CSI2 connections. + * @entity : Pointer to media entity structure + * @local : Pointer to local pad array + * @remote : Pointer to remote pad array + * @flags : Link flags + * return -EINVAL or zero on success + */ +static int csi2_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + + /* + * The ISS core doesn't support pipelines with multiple video outputs. + * Revisit this when it will be implemented, and return -EBUSY for now. + */ + + switch (local->index | media_entity_type(remote->entity)) { + case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi2->output & ~CSI2_OUTPUT_MEMORY) + return -EBUSY; + csi2->output |= CSI2_OUTPUT_MEMORY; + } else { + csi2->output &= ~CSI2_OUTPUT_MEMORY; + } + break; + + case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi2->output & ~CSI2_OUTPUT_IPIPEIF) + return -EBUSY; + csi2->output |= CSI2_OUTPUT_IPIPEIF; + } else { + csi2->output &= ~CSI2_OUTPUT_IPIPEIF; + } + break; + + default: + /* Link from camera to CSI2 is fixed... */ + return -EINVAL; + } + + ctrl->vp_only_enable = + (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; + ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF); + + return 0; +} + +/* media operations */ +static const struct media_entity_operations csi2_media_ops = { + .link_setup = csi2_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2) +{ + v4l2_device_unregister_subdev(&csi2->subdev); + omap4iss_video_unregister(&csi2->video_out); +} + +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &csi2->subdev); + if (ret < 0) + goto error; + + ret = omap4iss_video_register(&csi2->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap4iss_csi2_unregister_entities(csi2); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISS CSI2 initialisation and cleanup + */ + +/* + * csi2_init_entities - Initialize subdev and media entity. + * @csi2: Pointer to csi2 structure. + * return -ENOMEM or zero on success + */ +static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) +{ + struct v4l2_subdev *sd = &csi2->subdev; + struct media_pad *pads = csi2->pads; + struct media_entity *me = &sd->entity; + int ret; + char name[V4L2_SUBDEV_NAME_SIZE]; + + v4l2_subdev_init(sd, &csi2_ops); + sd->internal_ops = &csi2_internal_ops; + sprintf(name, "CSI2%s", subname); + strlcpy(sd->name, "", sizeof(sd->name)); + sprintf(sd->name, "OMAP4 ISS %s", name); + + sd->grp_id = 1 << 16; /* group ID for iss subdevs */ + v4l2_set_subdevdata(sd, 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; + + me->ops = &csi2_media_ops; + ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + csi2_init_formats(sd, NULL); + + /* Video device node */ + csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + csi2->video_out.ops = &csi2_issvideo_ops; + csi2->video_out.bpl_alignment = 32; + csi2->video_out.bpl_zero_padding = 1; + csi2->video_out.bpl_max = 0x1ffe0; + csi2->video_out.iss = csi2->iss; + csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + + ret = omap4iss_video_init(&csi2->video_out, name); + if (ret < 0) + goto error_video; + + /* Connect the CSI2 subdev to the video node. */ + ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, + &csi2->video_out.video.entity, 0, 0); + if (ret < 0) + goto error_link; + + return 0; + +error_link: + omap4iss_video_cleanup(&csi2->video_out); +error_video: + media_entity_cleanup(&csi2->subdev.entity); + return ret; +} + +/* + * omap4iss_csi2_init - Routine for module driver init + */ +int omap4iss_csi2_init(struct iss_device *iss) +{ + struct iss_csi2_device *csi2a = &iss->csi2a; + struct iss_csi2_device *csi2b = &iss->csi2b; + int ret; + + csi2a->iss = iss; + csi2a->available = 1; + csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1]; + csi2a->phy = &iss->csiphy1; + csi2a->state = ISS_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&csi2a->wait); + + ret = csi2_init_entities(csi2a, "a"); + if (ret < 0) + return ret; + + csi2b->iss = iss; + csi2b->available = 1; + csi2b->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_B_REGS1]; + csi2b->phy = &iss->csiphy2; + csi2b->state = ISS_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&csi2b->wait); + + ret = csi2_init_entities(csi2b, "b"); + if (ret < 0) + return ret; + + return 0; +} + +/* + * omap4iss_csi2_cleanup - Routine for module driver cleanup + */ +void omap4iss_csi2_cleanup(struct iss_device *iss) +{ + struct iss_csi2_device *csi2a = &iss->csi2a; + struct iss_csi2_device *csi2b = &iss->csi2b; + + omap4iss_video_cleanup(&csi2a->video_out); + media_entity_cleanup(&csi2a->subdev.entity); + + omap4iss_video_cleanup(&csi2b->video_out); + media_entity_cleanup(&csi2b->subdev.entity); +} diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h new file mode 100644 index 0000000..d1d077b --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -0,0 +1,156 @@ +/* + * TI OMAP4 ISS V4L2 Driver - CSI2 module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_CSI2_H +#define OMAP4_ISS_CSI2_H + +#include +#include + +#include "iss_video.h" + +struct iss_csiphy; + +/* This is not an exhaustive list */ +enum iss_csi2_pix_formats { + CSI2_PIX_FMT_OTHERS = 0, + CSI2_PIX_FMT_YUV422_8BIT = 0x1e, + CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e, + CSI2_PIX_FMT_YUV422_8BIT_VP16 = 0xde, + CSI2_PIX_FMT_RAW10_EXP16 = 0xab, + CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f, + CSI2_PIX_FMT_RAW8 = 0x2a, + CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa, + CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a, + CSI2_PIX_FMT_RAW8_VP = 0x12a, + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340, + CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0, + CSI2_USERDEF_8BIT_DATA1 = 0x40, +}; + +enum iss_csi2_irqevents { + OCP_ERR_IRQ = 0x4000, + SHORT_PACKET_IRQ = 0x2000, + ECC_CORRECTION_IRQ = 0x1000, + ECC_NO_CORRECTION_IRQ = 0x800, + COMPLEXIO2_ERR_IRQ = 0x400, + COMPLEXIO1_ERR_IRQ = 0x200, + FIFO_OVF_IRQ = 0x100, + CONTEXT7 = 0x80, + CONTEXT6 = 0x40, + CONTEXT5 = 0x20, + CONTEXT4 = 0x10, + CONTEXT3 = 0x8, + CONTEXT2 = 0x4, + CONTEXT1 = 0x2, + CONTEXT0 = 0x1, +}; + +enum iss_csi2_ctx_irqevents { + CTX_ECC_CORRECTION = 0x100, + CTX_LINE_NUMBER = 0x80, + CTX_FRAME_NUMBER = 0x40, + CTX_CS = 0x20, + CTX_LE = 0x8, + CTX_LS = 0x4, + CTX_FE = 0x2, + CTX_FS = 0x1, +}; + +enum iss_csi2_frame_mode { + ISS_CSI2_FRAME_IMMEDIATE, + ISS_CSI2_FRAME_AFTERFEC, +}; + +#define ISS_CSI2_MAX_CTX_NUM 7 + +struct iss_csi2_ctx_cfg { + u8 ctxnum; /* context number 0 - 7 */ + u8 dpcm_decompress; + + /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */ + u8 virtual_id; + u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ + u8 dpcm_predictor; /* 1: simple, 0: advanced */ + + /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ + u16 alpha; + u16 data_offset; + u32 ping_addr; + u32 pong_addr; + u8 eof_enabled; + u8 eol_enabled; + u8 checksum_enabled; + u8 enabled; +}; + +struct iss_csi2_timing_cfg { + u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */ + unsigned force_rx_mode:1; + unsigned stop_state_16x:1; + unsigned stop_state_4x:1; + u16 stop_state_counter; +}; + +struct iss_csi2_ctrl_cfg { + bool vp_clk_enable; + bool vp_only_enable; + u8 vp_out_ctrl; + enum iss_csi2_frame_mode frame_mode; + bool ecc_enable; + bool if_enable; +}; + +#define CSI2_PAD_SINK 0 +#define CSI2_PAD_SOURCE 1 +#define CSI2_PADS_NUM 2 + +#define CSI2_OUTPUT_IPIPEIF (1 << 0) +#define CSI2_OUTPUT_MEMORY (1 << 1) + +struct iss_csi2_device { + struct v4l2_subdev subdev; + struct media_pad pads[CSI2_PADS_NUM]; + struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM]; + + struct iss_video video_out; + struct iss_device *iss; + + u8 available; /* Is the IP present on the silicon? */ + + /* Pointer to register remaps into kernel space */ + void __iomem *regs1; + void __iomem *regs2; + + u32 output; /* output to IPIPEIF, memory or both? */ + bool dpcm_decompress; + unsigned int frame_skip; + bool use_fs_irq; + + struct iss_csiphy *phy; + struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1]; + struct iss_csi2_timing_cfg timing[2]; + struct iss_csi2_ctrl_cfg ctrl; + enum iss_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +void omap4iss_csi2_isr(struct iss_csi2_device *csi2); +int omap4iss_csi2_reset(struct iss_csi2_device *csi2); +int omap4iss_csi2_init(struct iss_device *iss); +void omap4iss_csi2_cleanup(struct iss_device *iss); +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2); +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2, + struct v4l2_device *vdev); +#endif /* OMAP4_ISS_CSI2_H */ diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c new file mode 100644 index 0000000..2afea98 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -0,0 +1,278 @@ +/* + * TI OMAP4 ISS V4L2 Driver - CSI PHY module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include + +#include "../../../../arch/arm/mach-omap2/control.h" + +#include "iss.h" +#include "iss_regs.h" +#include "iss_csiphy.h" + +/* + * csiphy_lanes_config - Configuration of CSIPHY lanes. + * + * Updates HW configuration. + * Called with phy->mutex taken. + */ +static void csiphy_lanes_config(struct iss_csiphy *phy) +{ + unsigned int i; + u32 reg; + + reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG); + + for (i = 0; i < phy->max_data_lanes; i++) { + reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) | + CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1)); + reg |= (phy->lanes.data[i].pol ? + CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0); + reg |= (phy->lanes.data[i].pos << + CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1)); + } + + reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL | + CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK); + reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0; + reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT; + + writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG); +} + +/* + * csiphy_set_power + * @power: Power state to be set. + * + * Returns 0 if successful, or -EBUSY if the retry count is exceeded. + */ +static int csiphy_set_power(struct iss_csiphy *phy, u32 power) +{ + u32 reg; + u8 retry_count; + + writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) & + ~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) | + power, + phy->cfg_regs + CSI2_COMPLEXIO_CFG); + + retry_count = 0; + do { + udelay(1); + reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) & + CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK; + + if (reg != power >> 2) + retry_count++; + + } while ((reg != power >> 2) && (retry_count < 250)); + + if (retry_count == 250) { + printk(KERN_ERR "CSI2 CIO set power failed!\n"); + return -EBUSY; + } + + return 0; +} + +/* + * csiphy_dphy_config - Configure CSI2 D-PHY parameters. + * + * Called with phy->mutex taken. + */ +static void csiphy_dphy_config(struct iss_csiphy *phy) +{ + u32 reg; + + /* Set up REGISTER0 */ + reg = phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT; + reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT; + + writel(reg, phy->phy_regs + REGISTER0); + + /* Set up REGISTER1 */ + reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT; + reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT; + reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT; + reg |= 0xB8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT; + + writel(reg, phy->phy_regs + REGISTER1); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +int omap4iss_csiphy_config(struct iss_device *iss, + struct v4l2_subdev *csi2_subdev) +{ + struct iss_csi2_device *csi2 = v4l2_get_subdevdata(csi2_subdev); + struct iss_pipeline *pipe = to_iss_pipeline(&csi2_subdev->entity); + struct iss_v4l2_subdevs_group *subdevs = pipe->external->host_priv; + struct iss_csiphy_dphy_cfg csi2phy; + int csi2_ddrclk_khz; + struct iss_csiphy_lanes_cfg *lanes; + unsigned int used_lanes = 0; + u32 cam_rx_ctrl; + unsigned int i; + + lanes = &subdevs->bus.csi2.lanecfg; + + /* + * SCM.CONTROL_CAMERA_RX + * - bit [31] : CSIPHY2 lane 2 enable (4460+ only) + * - bit [30:29] : CSIPHY2 per-lane enable (1 to 0) + * - bit [28:24] : CSIPHY1 per-lane enable (4 to 0) + * - bit [21] : CSIPHY2 CTRLCLK enable + * - bit [20:19] : CSIPHY2 config: 00 d-phy, 01/10 ccp2 + * - bit [18] : CSIPHY1 CTRLCLK enable + * - bit [17:16] : CSIPHY1 config: 00 d-phy, 01/10 ccp2 + */ + cam_rx_ctrl = omap4_ctrl_pad_readl( + OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX); + + + if (subdevs->interface == ISS_INTERFACE_CSI2A_PHY1) { + cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK | + OMAP4_CAMERARX_CSI21_CAMMODE_MASK); + /* NOTE: Leave CSIPHY1 config to 0x0: D-PHY mode */ + /* Enable all lanes for now */ + cam_rx_ctrl |= + 0x1F << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT; + /* Enable CTRLCLK */ + cam_rx_ctrl |= OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK; + } + + if (subdevs->interface == ISS_INTERFACE_CSI2B_PHY2) { + cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI22_LANEENABLE_MASK | + OMAP4_CAMERARX_CSI22_CAMMODE_MASK); + /* NOTE: Leave CSIPHY2 config to 0x0: D-PHY mode */ + /* Enable all lanes for now */ + cam_rx_ctrl |= + 0x3 << OMAP4_CAMERARX_CSI22_LANEENABLE_SHIFT; + /* Enable CTRLCLK */ + cam_rx_ctrl |= OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK; + } + + omap4_ctrl_pad_writel(cam_rx_ctrl, + OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX); + + /* Reset used lane count */ + csi2->phy->used_data_lanes = 0; + + /* Clock and data lanes verification */ + for (i = 0; i < csi2->phy->max_data_lanes; i++) { + if (lanes->data[i].pos == 0) + continue; + + if (lanes->data[i].pol > 1 || lanes->data[i].pos > (csi2->phy->max_data_lanes + 1)) + return -EINVAL; + + if (used_lanes & (1 << lanes->data[i].pos)) + return -EINVAL; + + used_lanes |= 1 << lanes->data[i].pos; + csi2->phy->used_data_lanes++; + } + + if (lanes->clk.pol > 1 || lanes->clk.pos > (csi2->phy->max_data_lanes + 1)) + return -EINVAL; + + if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) + return -EINVAL; + + csi2_ddrclk_khz = pipe->external_rate / 1000 + / (2 * csi2->phy->used_data_lanes) + * pipe->external_bpp; + + /* + * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. + * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. + */ + csi2phy.ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1; + csi2phy.ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3; + csi2phy.tclk_term = TCLK_TERM; + csi2phy.tclk_miss = TCLK_MISS; + csi2phy.tclk_settle = TCLK_SETTLE; + + mutex_lock(&csi2->phy->mutex); + csi2->phy->dphy = csi2phy; + csi2->phy->lanes = *lanes; + mutex_unlock(&csi2->phy->mutex); + + return 0; +} + +int omap4iss_csiphy_acquire(struct iss_csiphy *phy) +{ + int rval; + + mutex_lock(&phy->mutex); + + rval = omap4iss_csi2_reset(phy->csi2); + if (rval) + goto done; + + csiphy_dphy_config(phy); + csiphy_lanes_config(phy); + + rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON); + if (rval) + goto done; + + phy->phy_in_use = 1; + +done: + mutex_unlock(&phy->mutex); + return rval; +} + +void omap4iss_csiphy_release(struct iss_csiphy *phy) +{ + mutex_lock(&phy->mutex); + if (phy->phy_in_use) { + csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF); + phy->phy_in_use = 0; + } + mutex_unlock(&phy->mutex); +} + +/* + * omap4iss_csiphy_init - Initialize the CSI PHY frontends + */ +int omap4iss_csiphy_init(struct iss_device *iss) +{ + struct iss_csiphy *phy1 = &iss->csiphy1; + struct iss_csiphy *phy2 = &iss->csiphy2; + + phy1->iss = iss; + phy1->csi2 = &iss->csi2a; + phy1->max_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES; + phy1->used_data_lanes = 0; + phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1]; + phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1]; + mutex_init(&phy1->mutex); + + phy2->iss = iss; + phy2->csi2 = &iss->csi2b; + phy2->max_data_lanes = ISS_CSIPHY2_NUM_DATA_LANES; + phy2->used_data_lanes = 0; + phy2->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_B_REGS1]; + phy2->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE2]; + mutex_init(&phy2->mutex); + + return 0; +} diff --git a/drivers/staging/media/omap4iss/iss_csiphy.h b/drivers/staging/media/omap4iss/iss_csiphy.h new file mode 100644 index 0000000..df63eda --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_csiphy.h @@ -0,0 +1,51 @@ +/* + * TI OMAP4 ISS V4L2 Driver - CSI PHY module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_CSI_PHY_H +#define OMAP4_ISS_CSI_PHY_H + +#include + +struct iss_csi2_device; + +struct iss_csiphy_dphy_cfg { + u8 ths_term; + u8 ths_settle; + u8 tclk_term; + unsigned tclk_miss:1; + u8 tclk_settle; +}; + +struct iss_csiphy { + struct iss_device *iss; + struct mutex mutex; /* serialize csiphy configuration */ + u8 phy_in_use; + struct iss_csi2_device *csi2; + + /* Pointer to register remaps into kernel space */ + void __iomem *cfg_regs; + void __iomem *phy_regs; + + u8 max_data_lanes; /* number of CSI2 Data Lanes supported */ + u8 used_data_lanes; /* number of CSI2 Data Lanes used */ + struct iss_csiphy_lanes_cfg lanes; + struct iss_csiphy_dphy_cfg dphy; +}; + +int omap4iss_csiphy_config(struct iss_device *iss, + struct v4l2_subdev *csi2_subdev); +int omap4iss_csiphy_acquire(struct iss_csiphy *phy); +void omap4iss_csiphy_release(struct iss_csiphy *phy); +int omap4iss_csiphy_init(struct iss_device *iss); + +#endif /* OMAP4_ISS_CSI_PHY_H */ -- cgit v0.10.2 From 714148260d0585aedf72bd4d6d0a909886b0e9e1 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - IPIPE(IF) This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds the IPIPEIF and IPIPE processing blocks support. Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c new file mode 100644 index 0000000..fc38a5c5 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -0,0 +1,581 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" +#include "iss_ipipe.h" + +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which); + +static const unsigned int ipipe_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, +}; + +/* + * ipipe_print_status - Print current IPIPE Module register values. + * @ipipe: Pointer to ISS ISP IPIPE device. + * + * Also prints other debug information stored in the IPIPE module. + */ +#define IPIPE_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###IPIPE " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_##name)) + +static void ipipe_print_status(struct iss_ipipe_device *ipipe) +{ + struct iss_device *iss = to_iss_device(ipipe); + + dev_dbg(iss->dev, "-------------IPIPE Register dump-------------\n"); + + IPIPE_PRINT_REGISTER(iss, SRC_EN); + IPIPE_PRINT_REGISTER(iss, SRC_MODE); + IPIPE_PRINT_REGISTER(iss, SRC_FMT); + IPIPE_PRINT_REGISTER(iss, SRC_COL); + IPIPE_PRINT_REGISTER(iss, SRC_VPS); + IPIPE_PRINT_REGISTER(iss, SRC_VSZ); + IPIPE_PRINT_REGISTER(iss, SRC_HPS); + IPIPE_PRINT_REGISTER(iss, SRC_HSZ); + IPIPE_PRINT_REGISTER(iss, GCK_MMR); + IPIPE_PRINT_REGISTER(iss, YUV_PHS); + + dev_dbg(iss->dev, "-----------------------------------------------\n"); +} + +/* + * ipipe_enable - Enable/Disable IPIPE. + * @enable: enable flag + * + */ +static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable) +{ + struct iss_device *iss = to_iss_device(ipipe); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN) & + ~IPIPE_SRC_EN_EN) | + enable ? IPIPE_SRC_EN_EN : 0, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN); +} + +/* ----------------------------------------------------------------------------- + * Format- and pipeline-related configuration helpers + */ + +static void ipipe_configure(struct iss_ipipe_device *ipipe) +{ + struct iss_device *iss = to_iss_device(ipipe); + struct v4l2_mbus_framefmt *format; + + /* IPIPE_PAD_SINK */ + format = &ipipe->formats[IPIPE_PAD_SINK]; + + /* NOTE: Currently just supporting pipeline IN: RGB, OUT: YUV422 */ + writel(IPIPE_SRC_FMT_RAW2YUV, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_FMT); + + /* Enable YUV444 -> YUV422 conversion */ + writel(IPIPE_YUV_PHS_LPF, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_YUV_PHS); + + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_VPS); + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_HPS); + writel((format->height - 2) & IPIPE_SRC_VSZ_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_VSZ); + writel((format->width - 1) & IPIPE_SRC_HSZ_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_HSZ); + + /* Ignore ipipeif_wrt signal, and operate on-the-fly. */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_MODE) & + ~(IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST), + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_MODE); + + /* HACK: Values tuned for Ducati SW (OV) */ + writel(IPIPE_SRC_COL_EE_B | + IPIPE_SRC_COL_EO_GB | + IPIPE_SRC_COL_OE_GR | + IPIPE_SRC_COL_OO_R, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_COL); + + /* IPIPE_PAD_SOURCE_VP */ + format = &ipipe->formats[IPIPE_PAD_SOURCE_VP]; + /* Do nothing? */ + + omap4iss_isp_enable_interrupts(iss); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +/* + * ipipe_set_stream - Enable/Disable streaming on the IPIPE module + * @sd: ISP IPIPE V4L2 subdevice + * @enable: Enable/disable stream + */ +static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(ipipe); + int ret = 0; + + if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) { + if (enable == ISS_PIPELINE_STREAM_STOPPED) + return 0; + + omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE); + + /* Enable clk_arm_g0 */ + writel(IPIPE_GCK_MMR_REG, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_GCK_MMR); + + /* Enable clk_pix_g[3:0] */ + writel(IPIPE_GCK_PIX_G3 | + IPIPE_GCK_PIX_G2 | + IPIPE_GCK_PIX_G1 | + IPIPE_GCK_PIX_G0, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_GCK_PIX); + } + + switch (enable) { + case ISS_PIPELINE_STREAM_CONTINUOUS: + + ipipe_configure(ipipe); + ipipe_print_status(ipipe); + + atomic_set(&ipipe->stopping, 0); + ipipe_enable(ipipe, 1); + break; + + case ISS_PIPELINE_STREAM_STOPPED: + if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + if (omap4iss_module_sync_idle(&sd->entity, &ipipe->wait, + &ipipe->stopping)) + dev_dbg(iss->dev, "%s: module stop timeout.\n", + sd->name); + + ipipe_enable(ipipe, 0); + omap4iss_isp_disable_interrupts(iss); + omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE); + break; + } + + ipipe->state = enable; + return ret; +} + +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &ipipe->formats[pad]; +} + +/* + * ipipe_try_format - Try video format on a pad + * @ipipe: ISS IPIPE device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +ipipe_try_format(struct iss_ipipe_device *ipipe, 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 width = fmt->width; + unsigned int height = fmt->height; + unsigned int i; + + switch (pad) { + case IPIPE_PAD_SINK: + for (i = 0; i < ARRAY_SIZE(ipipe_fmts); i++) { + if (fmt->code == ipipe_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(ipipe_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + /* Clamp the input size. */ + fmt->width = clamp_t(u32, width, 1, 8192); + fmt->height = clamp_t(u32, height, 1, 8192); + fmt->colorspace = V4L2_COLORSPACE_SRGB; + break; + + case IPIPE_PAD_SOURCE_VP: + format = __ipipe_get_format(ipipe, fh, IPIPE_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + fmt->code = V4L2_MBUS_FMT_UYVY8_1X16; + fmt->width = clamp_t(u32, width, 32, fmt->width); + fmt->height = clamp_t(u32, height, 32, fmt->height); + fmt->colorspace = V4L2_COLORSPACE_JPEG; + break; + } + + fmt->field = V4L2_FIELD_NONE; +} + +/* + * ipipe_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipe_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPE_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipe_fmts)) + return -EINVAL; + + code->code = ipipe_fmts[code->index]; + break; + + case IPIPE_PAD_SOURCE_VP: + /* FIXME: Forced format conversion inside IPIPE ? */ + if (code->index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_UYVY8_1X16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int ipipe_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipe_try_format(ipipe, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipe_try_format(ipipe, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipe_get_format - Retrieve the video format on a pad + * @sd : ISP IPIPE V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * ipipe_set_format - Set the video format on a pad + * @sd : ISP IPIPE V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == IPIPE_PAD_SINK) { + format = __ipipe_get_format(ipipe, fh, IPIPE_PAD_SOURCE_VP, + fmt->which); + *format = fmt->format; + ipipe_try_format(ipipe, fh, IPIPE_PAD_SOURCE_VP, format, + fmt->which); + } + + return 0; +} + +static int ipipe_link_validate(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + if (source_fmt->format.code != sink_fmt->format.code) + return -EPIPE; + + return 0; +} + +/* + * ipipe_init_formats - Initialize formats on all pads + * @sd: ISP IPIPE V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + ipipe_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { + .s_stream = ipipe_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { + .enum_mbus_code = ipipe_enum_mbus_code, + .enum_frame_size = ipipe_enum_frame_size, + .get_fmt = ipipe_get_format, + .set_fmt = ipipe_set_format, + .link_validate = ipipe_link_validate, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops ipipe_v4l2_ops = { + .video = &ipipe_v4l2_video_ops, + .pad = &ipipe_v4l2_pad_ops, +}; + +/* V4L2 subdev internal operations */ +static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { + .open = ipipe_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * ipipe_link_setup - Setup IPIPE connections + * @entity: IPIPE media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int ipipe_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(ipipe); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from IPIPEIF. */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipe->input = IPIPE_INPUT_NONE; + break; + } + + if (ipipe->input != IPIPE_INPUT_NONE) + return -EBUSY; + + if (remote->entity == &iss->ipipeif.subdev.entity) + ipipe->input = IPIPE_INPUT_IPIPEIF; + + break; + + case IPIPE_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + /* Send to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ipipe->output & ~IPIPE_OUTPUT_VP) + return -EBUSY; + ipipe->output |= IPIPE_OUTPUT_VP; + } else { + ipipe->output &= ~IPIPE_OUTPUT_VP; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations ipipe_media_ops = { + .link_setup = ipipe_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * ipipe_init_entities - Initialize V4L2 subdev and media entity + * @ipipe: ISS ISP IPIPE module + * + * Return 0 on success and a negative error code on failure. + */ +static int ipipe_init_entities(struct iss_ipipe_device *ipipe) +{ + struct v4l2_subdev *sd = &ipipe->subdev; + struct media_pad *pads = ipipe->pads; + struct media_entity *me = &sd->entity; + int ret; + + ipipe->input = IPIPE_INPUT_NONE; + + v4l2_subdev_init(sd, &ipipe_v4l2_ops); + sd->internal_ops = &ipipe_v4l2_internal_ops; + strlcpy(sd->name, "OMAP4 ISS ISP IPIPE", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for iss subdevs */ + v4l2_set_subdevdata(sd, ipipe); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &ipipe_media_ops; + ret = media_entity_init(me, IPIPE_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + ipipe_init_formats(sd, NULL); + + return 0; +} + +void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe) +{ + media_entity_cleanup(&ipipe->subdev.entity); + + v4l2_device_unregister_subdev(&ipipe->subdev); +} + +int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video node. */ + ret = v4l2_device_register_subdev(vdev, &ipipe->subdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap4iss_ipipe_unregister_entities(ipipe); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP IPIPE initialisation and cleanup + */ + +/* + * omap4iss_ipipe_init - IPIPE module initialization. + * @iss: Device pointer specific to the OMAP4 ISS. + * + * TODO: Get the initialisation values from platform data. + * + * Return 0 on success or a negative error code otherwise. + */ +int omap4iss_ipipe_init(struct iss_device *iss) +{ + struct iss_ipipe_device *ipipe = &iss->ipipe; + + ipipe->state = ISS_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&ipipe->wait); + + return ipipe_init_entities(ipipe); +} + +/* + * omap4iss_ipipe_cleanup - IPIPE module cleanup. + * @iss: Device pointer specific to the OMAP4 ISS. + */ +void omap4iss_ipipe_cleanup(struct iss_device *iss) +{ + /* FIXME: are you sure there's nothing to do? */ +} diff --git a/drivers/staging/media/omap4iss/iss_ipipe.h b/drivers/staging/media/omap4iss/iss_ipipe.h new file mode 100644 index 0000000..c22d904 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_ipipe.h @@ -0,0 +1,67 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_IPIPE_H +#define OMAP4_ISS_IPIPE_H + +#include "iss_video.h" + +enum ipipe_input_entity { + IPIPE_INPUT_NONE, + IPIPE_INPUT_IPIPEIF, +}; + +#define IPIPE_OUTPUT_VP (1 << 0) + +/* Sink and source IPIPE pads */ +#define IPIPE_PAD_SINK 0 +#define IPIPE_PAD_SOURCE_VP 1 +#define IPIPE_PADS_NUM 2 + +/* + * struct iss_ipipe_device - Structure for the IPIPE module to store its own + * information + * @subdev: V4L2 subdevice + * @pads: Sink and source media entity pads + * @formats: Active video formats + * @input: Active input + * @output: Active outputs + * @error: A hardware error occurred during capture + * @state: Streaming state + * @wait: Wait queue used to stop the module + * @stopping: Stopping state + */ +struct iss_ipipe_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPE_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM]; + + enum ipipe_input_entity input; + unsigned int output; + unsigned int error; + + enum iss_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +struct iss_device; + +int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe, + struct v4l2_device *vdev); +void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe); + +int omap4iss_ipipe_init(struct iss_device *iss); +void omap4iss_ipipe_cleanup(struct iss_device *iss); + +#endif /* OMAP4_ISS_IPIPE_H */ diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c new file mode 100644 index 0000000..acc6005 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -0,0 +1,847 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" +#include "iss_ipipeif.h" + +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which); + +static const unsigned int ipipeif_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, +}; + +/* + * ipipeif_print_status - Print current IPIPEIF Module register values. + * @ipipeif: Pointer to ISS ISP IPIPEIF device. + * + * Also prints other debug information stored in the IPIPEIF module. + */ +#define IPIPEIF_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###IPIPEIF " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_##name)) + +#define ISIF_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###ISIF " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_##name)) + +#define ISP5_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###ISP5 " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_##name)) + +static void ipipeif_print_status(struct iss_ipipeif_device *ipipeif) +{ + struct iss_device *iss = to_iss_device(ipipeif); + + dev_dbg(iss->dev, "-------------IPIPEIF Register dump-------------\n"); + + IPIPEIF_PRINT_REGISTER(iss, CFG1); + IPIPEIF_PRINT_REGISTER(iss, CFG2); + + ISIF_PRINT_REGISTER(iss, SYNCEN); + ISIF_PRINT_REGISTER(iss, CADU); + ISIF_PRINT_REGISTER(iss, CADL); + ISIF_PRINT_REGISTER(iss, MODESET); + ISIF_PRINT_REGISTER(iss, CCOLP); + ISIF_PRINT_REGISTER(iss, SPH); + ISIF_PRINT_REGISTER(iss, LNH); + ISIF_PRINT_REGISTER(iss, LNV); + ISIF_PRINT_REGISTER(iss, VDINT0); + ISIF_PRINT_REGISTER(iss, HSIZE); + + ISP5_PRINT_REGISTER(iss, SYSCONFIG); + ISP5_PRINT_REGISTER(iss, CTRL); + ISP5_PRINT_REGISTER(iss, IRQSTATUS(0)); + ISP5_PRINT_REGISTER(iss, IRQENABLE_SET(0)); + ISP5_PRINT_REGISTER(iss, IRQENABLE_CLR(0)); + + dev_dbg(iss->dev, "-----------------------------------------------\n"); +} + +static void ipipeif_write_enable(struct iss_ipipeif_device *ipipeif, u8 enable) +{ + struct iss_device *iss = to_iss_device(ipipeif); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & + ~ISIF_SYNCEN_DWEN) | + enable ? ISIF_SYNCEN_DWEN : 0, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); +} + +/* + * ipipeif_enable - Enable/Disable IPIPEIF. + * @enable: enable flag + * + */ +static void ipipeif_enable(struct iss_ipipeif_device *ipipeif, u8 enable) +{ + struct iss_device *iss = to_iss_device(ipipeif); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & + ~ISIF_SYNCEN_SYEN) | + enable ? ISIF_SYNCEN_SYEN : 0, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); +} + +/* ----------------------------------------------------------------------------- + * Format- and pipeline-related configuration helpers + */ + +/* + * ipipeif_set_outaddr - Set memory address to save output image + * @ipipeif: Pointer to ISP IPIPEIF device. + * @addr: 32-bit memory address aligned on 32 byte boundary. + * + * Sets the memory address where the output will be saved. + */ +static void ipipeif_set_outaddr(struct iss_ipipeif_device *ipipeif, u32 addr) +{ + struct iss_device *iss = to_iss_device(ipipeif); + + /* Save address splitted in Base Address H & L */ + writel((addr >> (16 + 5)) & ISIF_CADU_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CADU); + writel((addr >> 5) & ISIF_CADL_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CADL); +} + +static void ipipeif_configure(struct iss_ipipeif_device *ipipeif) +{ + struct iss_device *iss = to_iss_device(ipipeif); + struct v4l2_mbus_framefmt *format; + u32 isif_ccolp = 0; + + omap4iss_configure_bridge(iss, ipipeif->input); + + /* IPIPEIF_PAD_SINK */ + format = &ipipeif->formats[IPIPEIF_PAD_SINK]; + + /* IPIPEIF with YUV422 input from ISIF */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG1) & + ~(IPIPEIF_CFG1_INPSRC1_MASK | IPIPEIF_CFG1_INPSRC2_MASK), + iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG1); + + /* Select ISIF/IPIPEIF input format */ + switch (format->code) { + case V4L2_MBUS_FMT_UYVY8_1X16: + case V4L2_MBUS_FMT_YUYV8_1X16: + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET) & + ~(ISIF_MODESET_CCDMD | + ISIF_MODESET_INPMOD_MASK | + ISIF_MODESET_CCDW_MASK)) | + ISIF_MODESET_INPMOD_YCBCR16, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2) & + ~IPIPEIF_CFG2_YUV8) | + IPIPEIF_CFG2_YUV16, + iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2); + + break; + case V4L2_MBUS_FMT_SGRBG10_1X10: + isif_ccolp = ISIF_CCOLP_CP0_F0_GR | + ISIF_CCOLP_CP1_F0_R | + ISIF_CCOLP_CP2_F0_B | + ISIF_CCOLP_CP3_F0_GB; + goto cont_raw; + case V4L2_MBUS_FMT_SRGGB10_1X10: + isif_ccolp = ISIF_CCOLP_CP0_F0_R | + ISIF_CCOLP_CP1_F0_GR | + ISIF_CCOLP_CP2_F0_GB | + ISIF_CCOLP_CP3_F0_B; + goto cont_raw; + case V4L2_MBUS_FMT_SBGGR10_1X10: + isif_ccolp = ISIF_CCOLP_CP0_F0_B | + ISIF_CCOLP_CP1_F0_GB | + ISIF_CCOLP_CP2_F0_GR | + ISIF_CCOLP_CP3_F0_R; + goto cont_raw; + case V4L2_MBUS_FMT_SGBRG10_1X10: + isif_ccolp = ISIF_CCOLP_CP0_F0_GB | + ISIF_CCOLP_CP1_F0_B | + ISIF_CCOLP_CP2_F0_R | + ISIF_CCOLP_CP3_F0_GR; +cont_raw: + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2) & + ~IPIPEIF_CFG2_YUV16), + iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET) & + ~(ISIF_MODESET_CCDMD | + ISIF_MODESET_INPMOD_MASK | + ISIF_MODESET_CCDW_MASK)) | + ISIF_MODESET_INPMOD_RAW | ISIF_MODESET_CCDW_2BIT, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD) & + ~ISIF_CGAMMAWD_GWDI_MASK) | + ISIF_CGAMMAWD_GWDI_BIT11, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD); + + /* Set RAW Bayer pattern */ + writel(isif_ccolp, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CCOLP); + break; + } + + writel(0 & ISIF_SPH_MASK, iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SPH); + writel((format->width - 1) & ISIF_LNH_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_LNH); + writel((format->height - 1) & ISIF_LNV_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_LNV); + + /* Generate ISIF0 on the last line of the image */ + writel(format->height - 1, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_VDINT0); + + /* IPIPEIF_PAD_SOURCE_ISIF_SF */ + format = &ipipeif->formats[IPIPEIF_PAD_SOURCE_ISIF_SF]; + + writel((ipipeif->video_out.bpl_value >> 5) & ISIF_HSIZE_HSIZE_MASK, + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_HSIZE); + + /* IPIPEIF_PAD_SOURCE_VP */ + /* Do nothing? */ + + omap4iss_isp_enable_interrupts(iss); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void ipipeif_isr_buffer(struct iss_ipipeif_device *ipipeif) +{ + struct iss_buffer *buffer; + + ipipeif_write_enable(ipipeif, 0); + + buffer = omap4iss_video_buffer_next(&ipipeif->video_out); + if (buffer == NULL) + return; + + ipipeif_set_outaddr(ipipeif, buffer->iss_addr); + + ipipeif_write_enable(ipipeif, 1); +} + +/* + * ipipeif_isif0_isr - Handle ISIF0 event + * @ipipeif: Pointer to ISP IPIPEIF device. + * + * Executes LSC deferred enablement before next frame starts. + */ +static void ipipeif_isif0_isr(struct iss_ipipeif_device *ipipeif) +{ + struct iss_pipeline *pipe = + to_iss_pipeline(&ipipeif->subdev.entity); + if (pipe->do_propagation) + atomic_inc(&pipe->frame_number); + + if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) + ipipeif_isr_buffer(ipipeif); +} + +/* + * omap4iss_ipipeif_isr - Configure ipipeif during interframe time. + * @ipipeif: Pointer to ISP IPIPEIF device. + * @events: IPIPEIF events + */ +void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) +{ + if (omap4iss_module_sync_is_stopping(&ipipeif->wait, &ipipeif->stopping)) + return; + + if (events & ISP5_IRQ_ISIF0) + ipipeif_isif0_isr(ipipeif); +} + +/* ----------------------------------------------------------------------------- + * ISP video operations + */ + +static int ipipeif_video_queue(struct iss_video *video, struct iss_buffer *buffer) +{ + struct iss_ipipeif_device *ipipeif = container_of(video, + struct iss_ipipeif_device, video_out); + + if (!(ipipeif->output & IPIPEIF_OUTPUT_MEMORY)) + return -ENODEV; + + ipipeif_set_outaddr(ipipeif, buffer->iss_addr); + + /* + * If streaming was enabled before there was a buffer queued + * or underrun happened in the ISR, the hardware was not enabled + * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set. + * Enable it now. + */ + if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { + if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) + ipipeif_write_enable(ipipeif, 1); + ipipeif_enable(ipipeif, 1); + iss_video_dmaqueue_flags_clr(video); + } + + return 0; +} + +static const struct iss_video_operations ipipeif_video_ops = { + .queue = ipipeif_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +#define IPIPEIF_DRV_SUBCLK_MASK (OMAP4_ISS_ISP_SUBCLK_IPIPEIF |\ + OMAP4_ISS_ISP_SUBCLK_ISIF) +/* + * ipipeif_set_stream - Enable/Disable streaming on the IPIPEIF module + * @sd: ISP IPIPEIF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(ipipeif); + struct iss_video *video_out = &ipipeif->video_out; + int ret = 0; + + if (ipipeif->state == ISS_PIPELINE_STREAM_STOPPED) { + if (enable == ISS_PIPELINE_STREAM_STOPPED) + return 0; + + omap4iss_isp_subclk_enable(iss, IPIPEIF_DRV_SUBCLK_MASK); + } + + switch (enable) { + case ISS_PIPELINE_STREAM_CONTINUOUS: + + ipipeif_configure(ipipeif); + ipipeif_print_status(ipipeif); + + /* + * When outputting to memory with no buffer available, let the + * buffer queue handler start the hardware. A DMA queue flag + * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is + * a buffer available. + */ + if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY && + !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED)) + break; + + atomic_set(&ipipeif->stopping, 0); + if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) + ipipeif_write_enable(ipipeif, 1); + ipipeif_enable(ipipeif, 1); + iss_video_dmaqueue_flags_clr(video_out); + break; + + case ISS_PIPELINE_STREAM_STOPPED: + if (ipipeif->state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + if (omap4iss_module_sync_idle(&sd->entity, &ipipeif->wait, + &ipipeif->stopping)) + dev_dbg(iss->dev, "%s: module stop timeout.\n", + sd->name); + + if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) + ipipeif_write_enable(ipipeif, 0); + ipipeif_enable(ipipeif, 0); + omap4iss_isp_disable_interrupts(iss); + omap4iss_isp_subclk_disable(iss, IPIPEIF_DRV_SUBCLK_MASK); + iss_video_dmaqueue_flags_clr(video_out); + break; + } + + ipipeif->state = enable; + return ret; +} + +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &ipipeif->formats[pad]; +} + +/* + * ipipeif_try_format - Try video format on a pad + * @ipipeif: ISS IPIPEIF device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +ipipeif_try_format(struct iss_ipipeif_device *ipipeif, 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 width = fmt->width; + unsigned int height = fmt->height; + unsigned int i; + + switch (pad) { + case IPIPEIF_PAD_SINK: + /* TODO: If the IPIPEIF output formatter pad is connected directly + * to the resizer, only YUV formats can be used. + */ + for (i = 0; i < ARRAY_SIZE(ipipeif_fmts); i++) { + if (fmt->code == ipipeif_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(ipipeif_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + /* Clamp the input size. */ + fmt->width = clamp_t(u32, width, 1, 8192); + fmt->height = clamp_t(u32, height, 1, 8192); + break; + + case IPIPEIF_PAD_SOURCE_ISIF_SF: + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); + fmt->width &= ~15; + fmt->height = clamp_t(u32, height, 32, fmt->height); + break; + + case IPIPEIF_PAD_SOURCE_VP: + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + fmt->width = clamp_t(u32, width, 32, fmt->width); + fmt->height = clamp_t(u32, height, 32, fmt->height); + break; + } + + /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is + * stored on 2 bytes. + */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * ipipeif_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + switch (code->pad) { + case IPIPEIF_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipeif_fmts)) + return -EINVAL; + + code->code = ipipeif_fmts[code->index]; + break; + + case IPIPEIF_PAD_SOURCE_ISIF_SF: + case IPIPEIF_PAD_SOURCE_VP: + /* No format conversion inside IPIPEIF */ + if (code->index != 0) + return -EINVAL; + + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + + code->code = format->code; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipeif_get_format - Retrieve the video format on a pad + * @sd : ISP IPIPEIF V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * ipipeif_set_format - Set the video format on a pad + * @sd : ISP IPIPEIF V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == IPIPEIF_PAD_SINK) { + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, + fmt->which); + *format = fmt->format; + ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, format, + fmt->which); + + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_VP, + fmt->which); + *format = fmt->format; + ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_VP, format, + fmt->which); + } + + return 0; +} + +static int ipipeif_link_validate(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + if (source_fmt->format.code != sink_fmt->format.code) + return -EPIPE; + + return 0; +} + +/* + * ipipeif_init_formats - Initialize formats on all pads + * @sd: ISP IPIPEIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + ipipeif_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = { + .s_stream = ipipeif_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = { + .enum_mbus_code = ipipeif_enum_mbus_code, + .enum_frame_size = ipipeif_enum_frame_size, + .get_fmt = ipipeif_get_format, + .set_fmt = ipipeif_set_format, + .link_validate = ipipeif_link_validate, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops ipipeif_v4l2_ops = { + .video = &ipipeif_v4l2_video_ops, + .pad = &ipipeif_v4l2_pad_ops, +}; + +/* V4L2 subdev internal operations */ +static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = { + .open = ipipeif_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * ipipeif_link_setup - Setup IPIPEIF connections + * @entity: IPIPEIF media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int ipipeif_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(ipipeif); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from the sensor CSI2a or CSI2b. */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + + if (ipipeif->input != IPIPEIF_INPUT_NONE) + return -EBUSY; + + if (remote->entity == &iss->csi2a.subdev.entity) + ipipeif->input = IPIPEIF_INPUT_CSI2A; + else if (remote->entity == &iss->csi2b.subdev.entity) + ipipeif->input = IPIPEIF_INPUT_CSI2B; + + break; + + case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY) + return -EBUSY; + ipipeif->output |= IPIPEIF_OUTPUT_MEMORY; + } else { + ipipeif->output &= ~IPIPEIF_OUTPUT_MEMORY; + } + break; + + case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + /* Send to IPIPE/RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ipipeif->output & ~IPIPEIF_OUTPUT_VP) + return -EBUSY; + ipipeif->output |= IPIPEIF_OUTPUT_VP; + } else { + ipipeif->output &= ~IPIPEIF_OUTPUT_VP; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations ipipeif_media_ops = { + .link_setup = ipipeif_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * ipipeif_init_entities - Initialize V4L2 subdev and media entity + * @ipipeif: ISS ISP IPIPEIF module + * + * Return 0 on success and a negative error code on failure. + */ +static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) +{ + struct v4l2_subdev *sd = &ipipeif->subdev; + struct media_pad *pads = ipipeif->pads; + struct media_entity *me = &sd->entity; + int ret; + + ipipeif->input = IPIPEIF_INPUT_NONE; + + v4l2_subdev_init(sd, &ipipeif_v4l2_ops); + sd->internal_ops = &ipipeif_v4l2_internal_ops; + strlcpy(sd->name, "OMAP4 ISS ISP IPIPEIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for iss subdevs */ + v4l2_set_subdevdata(sd, ipipeif); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPEIF_PAD_SOURCE_ISIF_SF].flags = MEDIA_PAD_FL_SOURCE; + pads[IPIPEIF_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &ipipeif_media_ops; + ret = media_entity_init(me, IPIPEIF_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + ipipeif_init_formats(sd, NULL); + + ipipeif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ipipeif->video_out.ops = &ipipeif_video_ops; + ipipeif->video_out.iss = to_iss_device(ipipeif); + ipipeif->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + ipipeif->video_out.bpl_alignment = 32; + ipipeif->video_out.bpl_zero_padding = 1; + ipipeif->video_out.bpl_max = 0x1ffe0; + + ret = omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF"); + if (ret < 0) + return ret; + + /* Connect the IPIPEIF subdev to the video node. */ + ret = media_entity_create_link(&ipipeif->subdev.entity, IPIPEIF_PAD_SOURCE_ISIF_SF, + &ipipeif->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif) +{ + media_entity_cleanup(&ipipeif->subdev.entity); + + v4l2_device_unregister_subdev(&ipipeif->subdev); + omap4iss_video_unregister(&ipipeif->video_out); +} + +int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video node. */ + ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev); + if (ret < 0) + goto error; + + ret = omap4iss_video_register(&ipipeif->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap4iss_ipipeif_unregister_entities(ipipeif); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP IPIPEIF initialisation and cleanup + */ + +/* + * omap4iss_ipipeif_init - IPIPEIF module initialization. + * @iss: Device pointer specific to the OMAP4 ISS. + * + * TODO: Get the initialisation values from platform data. + * + * Return 0 on success or a negative error code otherwise. + */ +int omap4iss_ipipeif_init(struct iss_device *iss) +{ + struct iss_ipipeif_device *ipipeif = &iss->ipipeif; + + ipipeif->state = ISS_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&ipipeif->wait); + + return ipipeif_init_entities(ipipeif); +} + +/* + * omap4iss_ipipeif_cleanup - IPIPEIF module cleanup. + * @iss: Device pointer specific to the OMAP4 ISS. + */ +void omap4iss_ipipeif_cleanup(struct iss_device *iss) +{ + /* FIXME: are you sure there's nothing to do? */ +} diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h new file mode 100644 index 0000000..cbdccb9 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_ipipeif.h @@ -0,0 +1,92 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_IPIPEIF_H +#define OMAP4_ISS_IPIPEIF_H + +#include "iss_video.h" + +enum ipipeif_input_entity { + IPIPEIF_INPUT_NONE, + IPIPEIF_INPUT_CSI2A, + IPIPEIF_INPUT_CSI2B +}; + +#define IPIPEIF_OUTPUT_MEMORY (1 << 0) +#define IPIPEIF_OUTPUT_VP (1 << 1) + +/* Sink and source IPIPEIF pads */ +#define IPIPEIF_PAD_SINK 0 +#define IPIPEIF_PAD_SOURCE_ISIF_SF 1 +#define IPIPEIF_PAD_SOURCE_VP 2 +#define IPIPEIF_PADS_NUM 3 + +/* + * struct iss_ipipeif_device - Structure for the IPIPEIF module to store its own + * information + * @subdev: V4L2 subdevice + * @pads: Sink and source media entity pads + * @formats: Active video formats + * @input: Active input + * @output: Active outputs + * @video_out: Output video node + * @error: A hardware error occurred during capture + * @alaw: A-law compression enabled (1) or disabled (0) + * @lpf: Low pass filter enabled (1) or disabled (0) + * @obclamp: Optical-black clamp enabled (1) or disabled (0) + * @fpc_en: Faulty pixels correction enabled (1) or disabled (0) + * @blcomp: Black level compensation configuration + * @clamp: Optical-black or digital clamp configuration + * @fpc: Faulty pixels correction configuration + * @lsc: Lens shading compensation configuration + * @update: Bitmask of controls to update during the next interrupt + * @shadow_update: Controls update in progress by userspace + * @syncif: Interface synchronization configuration + * @vpcfg: Video port configuration + * @underrun: A buffer underrun occurred and a new buffer has been queued + * @state: Streaming state + * @lock: Serializes shadow_update with interrupt handler + * @wait: Wait queue used to stop the module + * @stopping: Stopping state + * @ioctl_lock: Serializes ioctl calls and LSC requests freeing + */ +struct iss_ipipeif_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPEIF_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPEIF_PADS_NUM]; + + enum ipipeif_input_entity input; + unsigned int output; + struct iss_video video_out; + unsigned int error; + + enum iss_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +struct iss_device; + +int omap4iss_ipipeif_init(struct iss_device *iss); +void omap4iss_ipipeif_cleanup(struct iss_device *iss); +int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif, + struct v4l2_device *vdev); +void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif); + +int omap4iss_ipipeif_busy(struct iss_ipipeif_device *ipipeif); +void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events); +void omap4iss_ipipeif_restore_context(struct iss_device *iss); +void omap4iss_ipipeif_max_rate(struct iss_ipipeif_device *ipipeif, + unsigned int *max_rate); + +#endif /* OMAP4_ISS_IPIPEIF_H */ -- cgit v0.10.2 From 69c536b2c2b338e0a162626aee5059e4e1c197eb Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - Resizer This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds resizer support. [Port the driver to v3.12-rc3, including the following changes - Don't include plat/ headers - Don't use cpu_is_omap44xx() macro - Don't depend on EXPERIMENTAL - Fix s_crop operation prototype - Update link_notify prototype - Rename media_entity_remote_source to media_entity_remote_pad] Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c new file mode 100644 index 0000000..cb5df52 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -0,0 +1,905 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" +#include "iss_resizer.h" + +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which); + +static const unsigned int resizer_fmts[] = { + V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, +}; + +/* + * resizer_print_status - Print current RESIZER Module register values. + * @resizer: Pointer to ISS ISP RESIZER device. + * + * Also prints other debug information stored in the RESIZER module. + */ +#define RSZ_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###RSZ " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_##name)) + +#define RZA_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###RZA " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_##name)) + +static void resizer_print_status(struct iss_resizer_device *resizer) +{ + struct iss_device *iss = to_iss_device(resizer); + + dev_dbg(iss->dev, "-------------RESIZER Register dump-------------\n"); + + RSZ_PRINT_REGISTER(iss, SYSCONFIG); + RSZ_PRINT_REGISTER(iss, IN_FIFO_CTRL); + RSZ_PRINT_REGISTER(iss, FRACDIV); + RSZ_PRINT_REGISTER(iss, SRC_EN); + RSZ_PRINT_REGISTER(iss, SRC_MODE); + RSZ_PRINT_REGISTER(iss, SRC_FMT0); + RSZ_PRINT_REGISTER(iss, SRC_FMT1); + RSZ_PRINT_REGISTER(iss, SRC_VPS); + RSZ_PRINT_REGISTER(iss, SRC_VSZ); + RSZ_PRINT_REGISTER(iss, SRC_HPS); + RSZ_PRINT_REGISTER(iss, SRC_HSZ); + RSZ_PRINT_REGISTER(iss, DMA_RZA); + RSZ_PRINT_REGISTER(iss, DMA_RZB); + RSZ_PRINT_REGISTER(iss, DMA_STA); + RSZ_PRINT_REGISTER(iss, GCK_MMR); + RSZ_PRINT_REGISTER(iss, GCK_SDR); + RSZ_PRINT_REGISTER(iss, IRQ_RZA); + RSZ_PRINT_REGISTER(iss, IRQ_RZB); + RSZ_PRINT_REGISTER(iss, YUV_Y_MIN); + RSZ_PRINT_REGISTER(iss, YUV_Y_MAX); + RSZ_PRINT_REGISTER(iss, YUV_C_MIN); + RSZ_PRINT_REGISTER(iss, YUV_C_MAX); + RSZ_PRINT_REGISTER(iss, SEQ); + + RZA_PRINT_REGISTER(iss, EN); + RZA_PRINT_REGISTER(iss, MODE); + RZA_PRINT_REGISTER(iss, 420); + RZA_PRINT_REGISTER(iss, I_VPS); + RZA_PRINT_REGISTER(iss, I_HPS); + RZA_PRINT_REGISTER(iss, O_VSZ); + RZA_PRINT_REGISTER(iss, O_HSZ); + RZA_PRINT_REGISTER(iss, V_PHS_Y); + RZA_PRINT_REGISTER(iss, V_PHS_C); + RZA_PRINT_REGISTER(iss, V_DIF); + RZA_PRINT_REGISTER(iss, V_TYP); + RZA_PRINT_REGISTER(iss, V_LPF); + RZA_PRINT_REGISTER(iss, H_PHS); + RZA_PRINT_REGISTER(iss, H_DIF); + RZA_PRINT_REGISTER(iss, H_TYP); + RZA_PRINT_REGISTER(iss, H_LPF); + RZA_PRINT_REGISTER(iss, DWN_EN); + RZA_PRINT_REGISTER(iss, SDR_Y_BAD_H); + RZA_PRINT_REGISTER(iss, SDR_Y_BAD_L); + RZA_PRINT_REGISTER(iss, SDR_Y_SAD_H); + RZA_PRINT_REGISTER(iss, SDR_Y_SAD_L); + RZA_PRINT_REGISTER(iss, SDR_Y_OFT); + RZA_PRINT_REGISTER(iss, SDR_Y_PTR_S); + RZA_PRINT_REGISTER(iss, SDR_Y_PTR_E); + RZA_PRINT_REGISTER(iss, SDR_C_BAD_H); + RZA_PRINT_REGISTER(iss, SDR_C_BAD_L); + RZA_PRINT_REGISTER(iss, SDR_C_SAD_H); + RZA_PRINT_REGISTER(iss, SDR_C_SAD_L); + RZA_PRINT_REGISTER(iss, SDR_C_OFT); + RZA_PRINT_REGISTER(iss, SDR_C_PTR_S); + RZA_PRINT_REGISTER(iss, SDR_C_PTR_E); + + dev_dbg(iss->dev, "-----------------------------------------------\n"); +} + +/* + * resizer_enable - Enable/Disable RESIZER. + * @enable: enable flag + * + */ +static void resizer_enable(struct iss_resizer_device *resizer, u8 enable) +{ + struct iss_device *iss = to_iss_device(resizer); + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN) & + ~RSZ_SRC_EN_SRC_EN) | + enable ? RSZ_SRC_EN_SRC_EN : 0, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN); + + /* TODO: Enable RSZB */ + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) & + ~RSZ_EN_EN) | + enable ? RSZ_EN_EN : 0, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); +} + +/* ----------------------------------------------------------------------------- + * Format- and pipeline-related configuration helpers + */ + +/* + * resizer_set_outaddr - Set memory address to save output image + * @resizer: Pointer to ISP RESIZER device. + * @addr: 32-bit memory address aligned on 32 byte boundary. + * + * Sets the memory address where the output will be saved. + */ +static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr) +{ + struct iss_device *iss = to_iss_device(resizer); + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->formats[RESIZER_PAD_SINK]; + outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM]; + + /* Save address splitted in Base Address H & L */ + writel((addr >> 16) & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_BAD_H); + writel(addr & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_BAD_L); + + /* SAD = BAD */ + writel((addr >> 16) & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_SAD_H); + writel(addr & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_SAD_L); + + /* Program UV buffer address... Hardcoded to be contiguous! */ + if ((informat->code == V4L2_MBUS_FMT_UYVY8_1X16) && + (outformat->code == V4L2_MBUS_FMT_YUYV8_1_5X8)) { + u32 c_addr = addr + (resizer->video_out.bpl_value * + (outformat->height - 1)); + + /* Ensure Y_BAD_L[6:0] = C_BAD_L[6:0]*/ + if ((c_addr ^ addr) & 0x7f) { + c_addr &= ~0x7f; + c_addr += 0x80; + c_addr |= addr & 0x7f; + } + + /* Save address splitted in Base Address H & L */ + writel((c_addr >> 16) & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_BAD_H); + writel(c_addr & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_BAD_L); + + /* SAD = BAD */ + writel((c_addr >> 16) & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_SAD_H); + writel(c_addr & 0xffff, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_SAD_L); + } +} + +static void resizer_configure(struct iss_resizer_device *resizer) +{ + struct iss_device *iss = to_iss_device(resizer); + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->formats[RESIZER_PAD_SINK]; + outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM]; + + /* Make sure we don't bypass the resizer */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0) & + ~RSZ_SRC_FMT0_BYPASS, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0); + + /* Select RSZ input */ + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0) & + ~RSZ_SRC_FMT0_SEL) | + (resizer->input == RESIZER_INPUT_IPIPEIF) ? RSZ_SRC_FMT0_SEL : 0, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0); + + /* RSZ ignores WEN signal from IPIPE/IPIPEIF */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE) & + ~RSZ_SRC_MODE_WRT, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE); + + /* Set Resizer in free-running mode */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE) & + ~RSZ_SRC_MODE_OST, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE); + + /* Init Resizer A */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_MODE) & + ~RZA_MODE_ONE_SHOT, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_MODE); + + /* Set size related things now */ + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_VPS); + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_HPS); + writel(informat->height - 2, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_VSZ); + writel(informat->width - 1, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_HSZ); + + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_I_VPS); + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_I_HPS); + + writel(outformat->height - 2, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_O_VSZ); + writel(outformat->width - 1, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_O_HSZ); + + writel(0x100, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_V_DIF); + writel(0x100, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_H_DIF); + + /* Buffer output settings */ + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_PTR_S); + writel(outformat->height - 1, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_PTR_E); + + writel(resizer->video_out.bpl_value, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_OFT); + + /* UYVY -> NV12 conversion */ + if ((informat->code == V4L2_MBUS_FMT_UYVY8_1X16) && + (outformat->code == V4L2_MBUS_FMT_YUYV8_1_5X8)) { + writel(RSZ_420_CEN | RSZ_420_YEN, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_420); + + /* UV Buffer output settings */ + writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_PTR_S); + writel(outformat->height - 1, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_PTR_E); + + writel(resizer->video_out.bpl_value, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_OFT); + } else { + writel(0, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_420); + } + + omap4iss_isp_enable_interrupts(iss); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void resizer_isr_buffer(struct iss_resizer_device *resizer) +{ + struct iss_device *iss = to_iss_device(resizer); + struct iss_buffer *buffer; + + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) & + ~RSZ_EN_EN, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); + + buffer = omap4iss_video_buffer_next(&resizer->video_out); + if (buffer == NULL) + return; + + resizer_set_outaddr(resizer, buffer->iss_addr); + + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) | + RSZ_EN_EN, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); +} + +/* + * resizer_isif0_isr - Handle ISIF0 event + * @resizer: Pointer to ISP RESIZER device. + * + * Executes LSC deferred enablement before next frame starts. + */ +static void resizer_int_dma_isr(struct iss_resizer_device *resizer) +{ + struct iss_pipeline *pipe = + to_iss_pipeline(&resizer->subdev.entity); + if (pipe->do_propagation) + atomic_inc(&pipe->frame_number); + + resizer_isr_buffer(resizer); +} + +/* + * omap4iss_resizer_isr - Configure resizer during interframe time. + * @resizer: Pointer to ISP RESIZER device. + * @events: RESIZER events + */ +void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) +{ + struct iss_device *iss = to_iss_device(resizer); + struct iss_pipeline *pipe = + to_iss_pipeline(&resizer->subdev.entity); + + if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_OVF)) { + dev_dbg(iss->dev, "RSZ Err:" + " FIFO_IN_BLK:%d," + " FIFO_OVF:%d," + "\n", + (events & + ISP5_IRQ_RSZ_FIFO_IN_BLK) ? 1 : 0, + (events & + ISP5_IRQ_RSZ_FIFO_OVF) ? 1 : 0); + pipe->error = true; + } + + if (omap4iss_module_sync_is_stopping(&resizer->wait, &resizer->stopping)) + return; + + if (events & ISP5_IRQ_RSZ_INT_DMA) + resizer_int_dma_isr(resizer); +} + +/* ----------------------------------------------------------------------------- + * ISS video operations + */ + +static int resizer_video_queue(struct iss_video *video, struct iss_buffer *buffer) +{ + struct iss_resizer_device *resizer = container_of(video, + struct iss_resizer_device, video_out); + + if (!(resizer->output & RESIZER_OUTPUT_MEMORY)) + return -ENODEV; + + resizer_set_outaddr(resizer, buffer->iss_addr); + + /* + * If streaming was enabled before there was a buffer queued + * or underrun happened in the ISR, the hardware was not enabled + * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set. + * Enable it now. + */ + if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { + resizer_enable(resizer, 1); + iss_video_dmaqueue_flags_clr(video); + } + + return 0; +} + +static const struct iss_video_operations resizer_video_ops = { + .queue = resizer_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +/* + * resizer_set_stream - Enable/Disable streaming on the RESIZER module + * @sd: ISP RESIZER V4L2 subdevice + * @enable: Enable/disable stream + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(resizer); + struct iss_video *video_out = &resizer->video_out; + int ret = 0; + + if (resizer->state == ISS_PIPELINE_STREAM_STOPPED) { + if (enable == ISS_PIPELINE_STREAM_STOPPED) + return 0; + + omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ); + + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR) | + RSZ_GCK_MMR_MMR, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR); + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR) | + RSZ_GCK_SDR_CORE, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR); + + /* FIXME: Enable RSZB also */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG) | + RSZ_SYSCONFIG_RSZA_CLK_EN, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG); + } + + switch (enable) { + case ISS_PIPELINE_STREAM_CONTINUOUS: + + resizer_configure(resizer); + resizer_print_status(resizer); + + /* + * When outputting to memory with no buffer available, let the + * buffer queue handler start the hardware. A DMA queue flag + * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is + * a buffer available. + */ + if (resizer->output & RESIZER_OUTPUT_MEMORY && + !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED)) + break; + + atomic_set(&resizer->stopping, 0); + resizer_enable(resizer, 1); + iss_video_dmaqueue_flags_clr(video_out); + break; + + case ISS_PIPELINE_STREAM_STOPPED: + if (resizer->state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + if (omap4iss_module_sync_idle(&sd->entity, &resizer->wait, + &resizer->stopping)) + dev_dbg(iss->dev, "%s: module stop timeout.\n", + sd->name); + + resizer_enable(resizer, 0); + omap4iss_isp_disable_interrupts(iss); + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG) & + ~RSZ_SYSCONFIG_RSZA_CLK_EN, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG); + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR) & + ~RSZ_GCK_SDR_CORE, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR); + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR) & + ~RSZ_GCK_MMR_MMR, + iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR); + omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ); + iss_video_dmaqueue_flags_clr(video_out); + break; + } + + resizer->state = enable; + return ret; +} + +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &resizer->formats[pad]; +} + +/* + * resizer_try_format - Try video format on a pad + * @resizer: ISS RESIZER device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +resizer_try_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + enum v4l2_mbus_pixelcode pixelcode; + struct v4l2_mbus_framefmt *format; + unsigned int width = fmt->width; + unsigned int height = fmt->height; + unsigned int i; + + switch (pad) { + case RESIZER_PAD_SINK: + for (i = 0; i < ARRAY_SIZE(resizer_fmts); i++) { + if (fmt->code == resizer_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_1X16; + + /* Clamp the input size. */ + fmt->width = clamp_t(u32, width, 1, 8192); + fmt->height = clamp_t(u32, height, 1, 8192); + break; + + case RESIZER_PAD_SOURCE_MEM: + pixelcode = fmt->code; + format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + if ((pixelcode == V4L2_MBUS_FMT_YUYV8_1_5X8) && + (fmt->code == V4L2_MBUS_FMT_UYVY8_1X16)) + fmt->code = pixelcode; + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); + fmt->width &= ~15; + fmt->height = clamp_t(u32, height, 32, fmt->height); + break; + + } + + fmt->colorspace = V4L2_COLORSPACE_JPEG; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * resizer_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + switch (code->pad) { + case RESIZER_PAD_SINK: + if (code->index >= ARRAY_SIZE(resizer_fmts)) + return -EINVAL; + + code->code = resizer_fmts[code->index]; + break; + + case RESIZER_PAD_SOURCE_MEM: + format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + + if (code->index == 0) { + code->code = format->code; + break; + } + + switch (format->code) { + case V4L2_MBUS_FMT_UYVY8_1X16: + if (code->index == 1) + code->code = V4L2_MBUS_FMT_YUYV8_1_5X8; + else + return -EINVAL; + break; + default: + if (code->index != 0) + return -EINVAL; + } + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(resizer, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(resizer, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_get_format - Retrieve the video format on a pad + * @sd : ISP RESIZER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * resizer_set_format - Set the video format on a pad + * @sd : ISP RESIZER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(resizer, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == RESIZER_PAD_SINK) { + format = __resizer_get_format(resizer, fh, RESIZER_PAD_SOURCE_MEM, + fmt->which); + *format = fmt->format; + resizer_try_format(resizer, fh, RESIZER_PAD_SOURCE_MEM, format, + fmt->which); + } + + 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) +{ + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + if (source_fmt->format.code != sink_fmt->format.code) + return -EPIPE; + + return 0; +} + +/* + * resizer_init_formats - Initialize formats on all pads + * @sd: ISP RESIZER V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_1X16; + format.format.width = 4096; + format.format.height = 4096; + resizer_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, + .link_validate = resizer_link_validate, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* V4L2 subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * resizer_link_setup - Setup RESIZER connections + * @entity: RESIZER media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct iss_device *iss = to_iss_device(resizer); + + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from IPIPE or IPIPEIF. */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->input = RESIZER_INPUT_NONE; + break; + } + + if (resizer->input != RESIZER_INPUT_NONE) + return -EBUSY; + + if (remote->entity == &iss->ipipeif.subdev.entity) + resizer->input = RESIZER_INPUT_IPIPEIF; + else if (remote->entity == &iss->ipipe.subdev.entity) + resizer->input = RESIZER_INPUT_IPIPE; + + + break; + + case RESIZER_PAD_SOURCE_MEM | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (resizer->output & ~RESIZER_OUTPUT_MEMORY) + return -EBUSY; + resizer->output |= RESIZER_OUTPUT_MEMORY; + } else { + resizer->output &= ~RESIZER_OUTPUT_MEMORY; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * resizer_init_entities - Initialize V4L2 subdev and media entity + * @resizer: ISS ISP RESIZER module + * + * Return 0 on success and a negative error code on failure. + */ +static int resizer_init_entities(struct iss_resizer_device *resizer) +{ + struct v4l2_subdev *sd = &resizer->subdev; + struct media_pad *pads = resizer->pads; + struct media_entity *me = &sd->entity; + int ret; + + resizer->input = RESIZER_INPUT_NONE; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for iss subdevs */ + v4l2_set_subdevdata(sd, resizer); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + resizer_init_formats(sd, NULL); + + resizer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + resizer->video_out.ops = &resizer_video_ops; + resizer->video_out.iss = to_iss_device(resizer); + resizer->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + resizer->video_out.bpl_alignment = 32; + resizer->video_out.bpl_zero_padding = 1; + resizer->video_out.bpl_max = 0x1ffe0; + + ret = omap4iss_video_init(&resizer->video_out, "ISP resizer a"); + if (ret < 0) + return ret; + + /* Connect the RESIZER subdev to the video node. */ + ret = media_entity_create_link(&resizer->subdev.entity, RESIZER_PAD_SOURCE_MEM, + &resizer->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer) +{ + media_entity_cleanup(&resizer->subdev.entity); + + v4l2_device_unregister_subdev(&resizer->subdev); + omap4iss_video_unregister(&resizer->video_out); +} + +int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video node. */ + ret = v4l2_device_register_subdev(vdev, &resizer->subdev); + if (ret < 0) + goto error; + + ret = omap4iss_video_register(&resizer->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap4iss_resizer_unregister_entities(resizer); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP RESIZER initialisation and cleanup + */ + +/* + * omap4iss_resizer_init - RESIZER module initialization. + * @iss: Device pointer specific to the OMAP4 ISS. + * + * TODO: Get the initialisation values from platform data. + * + * Return 0 on success or a negative error code otherwise. + */ +int omap4iss_resizer_init(struct iss_device *iss) +{ + struct iss_resizer_device *resizer = &iss->resizer; + + resizer->state = ISS_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&resizer->wait); + + return resizer_init_entities(resizer); +} + +/* + * omap4iss_resizer_cleanup - RESIZER module cleanup. + * @iss: Device pointer specific to the OMAP4 ISS. + */ +void omap4iss_resizer_cleanup(struct iss_device *iss) +{ + /* FIXME: are you sure there's nothing to do? */ +} diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h new file mode 100644 index 0000000..3727498 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_resizer.h @@ -0,0 +1,75 @@ +/* + * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Author: Sergio Aguirre + * + * 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 OMAP4_ISS_RESIZER_H +#define OMAP4_ISS_RESIZER_H + +#include "iss_video.h" + +enum resizer_input_entity { + RESIZER_INPUT_NONE, + RESIZER_INPUT_IPIPE, + RESIZER_INPUT_IPIPEIF +}; + +#define RESIZER_OUTPUT_MEMORY (1 << 0) + +/* Sink and source RESIZER pads */ +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE_MEM 1 +#define RESIZER_PADS_NUM 2 + +/* + * struct iss_resizer_device - Structure for the RESIZER module to store its own + * information + * @subdev: V4L2 subdevice + * @pads: Sink and source media entity pads + * @formats: Active video formats + * @input: Active input + * @output: Active outputs + * @video_out: Output video node + * @error: A hardware error occurred during capture + * @state: Streaming state + * @wait: Wait queue used to stop the module + * @stopping: Stopping state + */ +struct iss_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + + enum resizer_input_entity input; + unsigned int output; + struct iss_video video_out; + unsigned int error; + + enum iss_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +struct iss_device; + +int omap4iss_resizer_init(struct iss_device *iss); +void omap4iss_resizer_cleanup(struct iss_device *iss); +int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer, + struct v4l2_device *vdev); +void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer); + +int omap4iss_resizer_busy(struct iss_resizer_device *resizer); +void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events); +void omap4iss_resizer_restore_context(struct iss_device *iss); +void omap4iss_resizer_max_rate(struct iss_resizer_device *resizer, + unsigned int *max_rate); + +#endif /* OMAP4_ISS_RESIZER_H */ -- cgit v0.10.2 From d632dfefd36f0794dd69a41f86bfff2cc0e5af73 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 2 Oct 2013 20:27:07 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - Build system This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds and updates Kconfig's and Makefile's, as well as a TODO list. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 46f1e61..bc4c798 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -33,6 +33,8 @@ source "drivers/staging/media/msi3101/Kconfig" source "drivers/staging/media/solo6x10/Kconfig" +source "drivers/staging/media/omap4iss/Kconfig" + # Keep LIRC at the end, as it has sub-menus source "drivers/staging/media/lirc/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index eb7f30b..a528d3f 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ +obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig new file mode 100644 index 0000000..b9fe753 --- /dev/null +++ b/drivers/staging/media/omap4iss/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_OMAP4 + bool "OMAP 4 Camera support" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && I2C && ARCH_OMAP4 + select VIDEOBUF2_DMA_CONTIG + ---help--- + Driver for an OMAP 4 ISS controller. + +config VIDEO_OMAP4_DEBUG + bool "OMAP 4 Camera debug messages" + depends on VIDEO_OMAP4 + ---help--- + Enable debug messages on OMAP 4 ISS controller driver. diff --git a/drivers/staging/media/omap4iss/Makefile b/drivers/staging/media/omap4iss/Makefile new file mode 100644 index 0000000..a716ce9 --- /dev/null +++ b/drivers/staging/media/omap4iss/Makefile @@ -0,0 +1,6 @@ +# Makefile for OMAP4 ISS driver + +omap4-iss-objs += \ + iss.o iss_csi2.o iss_csiphy.o iss_ipipeif.o iss_ipipe.o iss_resizer.o iss_video.o + +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o diff --git a/drivers/staging/media/omap4iss/TODO b/drivers/staging/media/omap4iss/TODO new file mode 100644 index 0000000..fcde888 --- /dev/null +++ b/drivers/staging/media/omap4iss/TODO @@ -0,0 +1,4 @@ +* Make the driver compile as a module +* Fix FIFO/buffer overflows and underflows +* Replace dummy resizer code with a real implementation +* Fix checkpatch errors and warnings -- cgit v0.10.2 From ea72717e961d1166882370a87876bfeacc967eb0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:13:32 -0300 Subject: [media] v4l: omap4iss: Don't use v4l2_g_ext_ctrls() internally Instead of using the extended control API internally to get the sensor pixel rate, use the dedicated in-kernel APIs (find the control with v4l2_ctrl_find() and get its value with v4l2_ctrl_g_ctrl_int64()). Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index d054d9b..1a5cac9 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -121,8 +121,7 @@ int omap4iss_get_external_info(struct iss_pipeline *pipe, struct iss_device *iss = container_of(pipe, struct iss_video, pipe)->iss; struct v4l2_subdev_format fmt; - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; + struct v4l2_ctrl *ctrl; int ret; if (!pipe->external) @@ -142,23 +141,15 @@ int omap4iss_get_external_info(struct iss_pipeline *pipe, pipe->external_bpp = omap4iss_video_format_info(fmt.format.code)->bpp; - memset(&ctrls, 0, sizeof(ctrls)); - memset(&ctrl, 0, sizeof(ctrl)); - - ctrl.id = V4L2_CID_PIXEL_RATE; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id); - ctrls.count = 1; - ctrls.controls = &ctrl; - - ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); - if (ret < 0) { + ctrl = v4l2_ctrl_find(pipe->external->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (ctrl == NULL) { dev_warn(iss->dev, "no pixel rate control in subdev %s\n", pipe->external->name); - return ret; + return -EPIPE; } - pipe->external_rate = ctrl.value64; + pipe->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); return 0; } -- cgit v0.10.2 From cce093ee559779d376fbd767d70a5b0ce4cf015a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:17:51 -0300 Subject: [media] v4l: omap4iss: Move common code out of switch...case Code common to all cases can be moved out of the switch...case statement. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 1a5cac9..243fcb8 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -178,12 +178,10 @@ void omap4iss_configure_bridge(struct iss_device *iss, switch (input) { case IPIPEIF_INPUT_CSI2A: issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A; - isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; break; case IPIPEIF_INPUT_CSI2B: issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B; - isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; break; default: @@ -192,7 +190,8 @@ void omap4iss_configure_bridge(struct iss_device *iss, issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING; - isp5ctrl_val |= ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE; + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT | ISP5_CTRL_PSYNC_CLK_SEL | + ISP5_CTRL_SYNC_ENABLE; writel(issctrl_val, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); -- cgit v0.10.2 From 35c71be8c75dbf80d30f37347a5b2be19761f83e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Report device caps in response to VIDIOC_QUERYCAP Set the v4l2_capability capabilities and device_caps fields correctly. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 31f1b88..bab62d7 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -480,9 +480,12 @@ iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; return 0; } -- cgit v0.10.2 From f33354f0451dc6bf339052f28078a5e139fcf38c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Remove iss_video streaming field The vb2 queue already keeps track of the streaming state, there's no need to duplicate that in the driver. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index bab62d7..1cdfc43 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -743,11 +743,6 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) mutex_lock(&video->stream_lock); - if (video->streaming) { - mutex_unlock(&video->stream_lock); - return -EBUSY; - } - /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ @@ -842,9 +837,6 @@ err_media_entity_pipeline_start: video->queue = NULL; } - if (!ret) - video->streaming = 1; - mutex_unlock(&video->stream_lock); return ret; } @@ -882,7 +874,6 @@ iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED); vb2_streamoff(&vfh->queue, type); video->queue = NULL; - video->streaming = 0; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 8cf1b35..35f14d8 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -156,9 +156,6 @@ struct iss_video { unsigned int bpl_value; /* bytes per line value */ unsigned int bpl_padding; /* padding at end of line */ - /* Entity video node streaming */ - unsigned int streaming:1; - /* Pipeline state */ struct iss_pipeline pipe; struct mutex stream_lock; /* pipeline and stream states */ -- cgit v0.10.2 From bb4e7d6ea2b1e97348f8ac4be5b9493b70d9d54e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Set the vb2 timestamp type Timestamps use the monotonic clock, configure the vb2 queue accordingly. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 1cdfc43..ee30054 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -972,6 +972,7 @@ static int iss_video_open(struct file *file) q->ops = &iss_video_vb2ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct iss_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { -- cgit v0.10.2 From 2b7f0b641ab125e06db707bee47ddf3f19218cbb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Remove duplicate video_is_registered() check The video_unregister_device() function checks if the video device is registered before proceeding, remote the duplicate check from the driver. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index ee30054..ccbdecd 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1119,6 +1119,5 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) void omap4iss_video_unregister(struct iss_video *video) { - if (video_is_registered(&video->video)) - video_unregister_device(&video->video); + video_unregister_device(&video->video); } -- cgit v0.10.2 From 57da5e47ccf3d727958aeef7675154d48635fa09 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Remove unneeded status variable The failure variable is initialized with 0 and used as a return value without ever being modified. Remove it and return 0 directly. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 243fcb8..320bfd4 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -560,7 +560,6 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; - int failure = 0; entity = &pipe->output->video.entity; while (1) { @@ -579,7 +578,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) v4l2_subdev_call(subdev, video, s_stream, 0); } - return failure; + return 0; } /* -- cgit v0.10.2 From ca6f19b1cf0fac0fdf1ef06d6bed0f07f8f37ae9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Replace udelay/msleep with usleep_range The only udelay() call takes place in a sleepable context, we can sleep instead. Use usleep_range(). Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 320bfd4..3103093 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -642,11 +642,11 @@ static int iss_reset(struct iss_device *iss) while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) & ISS_HL_SYSCONFIG_SOFTRESET) { - if (timeout++ > 10000) { + if (timeout++ > 100) { dev_alert(iss->dev, "cannot reset ISS\n"); return -ETIMEDOUT; } - udelay(1); + usleep_range(10, 10); } return 0; @@ -674,7 +674,7 @@ static int iss_isp_reset(struct iss_device *iss) dev_alert(iss->dev, "cannot set ISP5 to standby\n"); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 1500); } /* Now finally, do the reset */ @@ -689,7 +689,7 @@ static int iss_isp_reset(struct iss_device *iss) dev_alert(iss->dev, "cannot reset ISP5\n"); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 1500); } return 0; -- cgit v0.10.2 From 68c03a666c36068428fed43010bde521d4341079 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Make omap4iss_isp_subclk_(en|dis)able() functions void The functions always succeed, there's no need to return an error value. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 3103093..043a3f3 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -836,7 +836,7 @@ int omap4iss_subclk_disable(struct iss_device *iss, ISP5_CTRL_IPIPE_CLK_ENABLE |\ ISP5_CTRL_IPIPEIF_CLK_ENABLE) -static int __iss_isp_subclk_update(struct iss_device *iss) +static void __iss_isp_subclk_update(struct iss_device *iss) { u32 clk = 0; @@ -861,24 +861,22 @@ static int __iss_isp_subclk_update(struct iss_device *iss) writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & ~ISS_ISP5_CLKCTRL_MASK) | clk, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); - - return 0; } -int omap4iss_isp_subclk_enable(struct iss_device *iss, +void omap4iss_isp_subclk_enable(struct iss_device *iss, enum iss_isp_subclk_resource res) { iss->isp_subclk_resources |= res; - return __iss_isp_subclk_update(iss); + __iss_isp_subclk_update(iss); } -int omap4iss_isp_subclk_disable(struct iss_device *iss, - enum iss_isp_subclk_resource res) +void omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res) { iss->isp_subclk_resources &= ~res; - return __iss_isp_subclk_update(iss); + __iss_isp_subclk_update(iss); } /* diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index cc24f1a..f33664d 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -136,10 +136,10 @@ int omap4iss_subclk_enable(struct iss_device *iss, enum iss_subclk_resource res); int omap4iss_subclk_disable(struct iss_device *iss, enum iss_subclk_resource res); -int omap4iss_isp_subclk_enable(struct iss_device *iss, - enum iss_isp_subclk_resource res); -int omap4iss_isp_subclk_disable(struct iss_device *iss, +void omap4iss_isp_subclk_enable(struct iss_device *iss, enum iss_isp_subclk_resource res); +void omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res); void omap4iss_isp_enable_interrupts(struct iss_device *iss); void omap4iss_isp_disable_interrupts(struct iss_device *iss); -- cgit v0.10.2 From 4334fd18e2bcf9c6aa7c535c25b05b462aab05b3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Make loop counters unsigned where appropriate Loop counters that can only take positive values should be unsigned. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 043a3f3..c7dffa6 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1290,7 +1290,8 @@ static int iss_probe(struct platform_device *pdev) { struct iss_platform_data *pdata = pdev->dev.platform_data; struct iss_device *iss; - int i, ret; + unsigned int i; + int ret; if (pdata == NULL) return -EINVAL; @@ -1414,7 +1415,7 @@ error: static int iss_remove(struct platform_device *pdev) { struct iss_device *iss = platform_get_drvdata(pdev); - int i; + unsigned int i; iss_unregister_entities(iss); iss_cleanup_modules(iss); -- cgit v0.10.2 From 245d6b2d505b0575b34e5c02b694bf2e476904b4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Don't initialize fields to 0 manually The iss_device structure is allocated with kzalloc, there's no need to initialize its fields to 0 explicitly. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index c7dffa6..7d427d5 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1306,7 +1306,6 @@ static int iss_probe(struct platform_device *pdev) iss->dev = &pdev->dev; iss->pdata = pdata; - iss->ref_count = 0; iss->raw_dmamask = DMA_BIT_MASK(32); iss->dev->dma_mask = &iss->raw_dmamask; -- cgit v0.10.2 From 2b16b44a1c44814ed00cf750d20f3c404b5d4e48 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Simplify error paths Get rid of a goto statement for a simple error path that can be inlined, and split spaghetti error code to a separate section. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 7d427d5..b7c8a6b 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -887,24 +887,22 @@ void omap4iss_isp_subclk_disable(struct iss_device *iss, */ static int iss_enable_clocks(struct iss_device *iss) { - int r; + int ret; - r = clk_enable(iss->iss_fck); - if (r) { + ret = clk_enable(iss->iss_fck); + if (ret) { dev_err(iss->dev, "clk_enable iss_fck failed\n"); - return r; + return ret; } - r = clk_enable(iss->iss_ctrlclk); - if (r) { + ret = clk_enable(iss->iss_ctrlclk); + if (ret) { dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n"); - goto out_clk_enable_ctrlclk; + clk_disable(iss->iss_fck); + return ret; } - return 0; -out_clk_enable_ctrlclk: - clk_disable(iss->iss_fck); - return r; + return 0; } /* diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index ccbdecd..a527330a 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -826,16 +826,17 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) spin_unlock_irqrestore(&video->qlock, flags); } - if (ret < 0) { + mutex_unlock(&video->stream_lock); + return 0; + err_omap4iss_set_stream: - vb2_streamoff(&vfh->queue, type); + vb2_streamoff(&vfh->queue, type); err_iss_video_check_format: - media_entity_pipeline_stop(&video->video.entity); + media_entity_pipeline_stop(&video->video.entity); err_media_entity_pipeline_start: - if (video->iss->pdata->set_constraints) - video->iss->pdata->set_constraints(video->iss, false); - video->queue = NULL; - } + if (video->iss->pdata->set_constraints) + video->iss->pdata->set_constraints(video->iss, false); + video->queue = NULL; mutex_unlock(&video->stream_lock); return ret; -- cgit v0.10.2 From e43484e42d5537ad2db11a1973790a105bed54ca Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Don't check for missing get_fmt op on remote subdev The remote subdev of any video node in the OMAP4 ISS is an internal subdev that is guaranteed to implement get_fmt. Don't check the return value for -ENOIOCTLCMD, as this can't happen. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index a527330a..0cb5820 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -248,8 +248,6 @@ __iss_video_get_format(struct iss_video *video, struct v4l2_format *format) fmt.pad = pad; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); - if (ret == -ENOIOCTLCMD) - ret = -EINVAL; mutex_unlock(&video->mutex); @@ -552,7 +550,7 @@ iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); if (ret) - return ret == -ENOIOCTLCMD ? -EINVAL : ret; + return ret; iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); return 0; -- cgit v0.10.2 From 4a62361f825325bd6dd228d3c05e254d40f74e47 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 3 Nov 2013 20:28:24 -0300 Subject: [media] v4l: omap4iss: Translate -ENOIOCTLCMD to -ENOTTY Translating -ENOIOCTLCMD to -EINVAL is invalid, the correct ioctl return value is -ENOTTY. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 0cb5820..63419b3 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -571,7 +571,7 @@ iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); mutex_unlock(&video->mutex); - return ret == -ENOIOCTLCMD ? -EINVAL : ret; + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; } static int @@ -598,7 +598,7 @@ iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) format.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); if (ret < 0) - return ret == -ENOIOCTLCMD ? -EINVAL : ret; + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; crop->c.left = 0; crop->c.top = 0; @@ -623,7 +623,7 @@ iss_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) ret = v4l2_subdev_call(subdev, video, s_crop, crop); mutex_unlock(&video->mutex); - return ret == -ENOIOCTLCMD ? -EINVAL : ret; + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; } static int -- cgit v0.10.2 From c3dbc70d825968da10cf6fdde40447aeec632a2d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 4 Nov 2013 06:52:10 -0300 Subject: [media] v4l: omap4iss: Move code out of mutex-protected section The pad::get_fmt call must be protected by a mutex, but preparing its arguments doesn't need to be. Move the non-critical code out of the mutex-protected section. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 63419b3..6800623 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -243,12 +243,11 @@ __iss_video_get_format(struct iss_video *video, struct v4l2_format *format) if (subdev == NULL) return -EINVAL; - mutex_lock(&video->mutex); - fmt.pad = pad; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); mutex_unlock(&video->mutex); if (ret) -- cgit v0.10.2 From 16422f552d4e70fa0953917e05f72033d629bdb8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 4 Nov 2013 06:59:26 -0300 Subject: [media] v4l: omap4iss: Implement VIDIOC_S_INPUT The ioctl is (at least currently) mandatory. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 6800623..766491e 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -902,6 +902,12 @@ iss_video_g_input(struct file *file, void *fh, unsigned int *input) return 0; } +static int +iss_video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_querycap = iss_video_querycap, .vidioc_g_fmt_vid_cap = iss_video_get_format, @@ -923,6 +929,7 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_streamoff = iss_video_streamoff, .vidioc_enum_input = iss_video_enum_input, .vidioc_g_input = iss_video_g_input, + .vidioc_s_input = iss_video_s_input, }; /* ----------------------------------------------------------------------------- -- cgit v0.10.2 From d0700c5175b0684c9935ca57deae733c2758667c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 13 Oct 2013 07:58:43 -0300 Subject: [media] media: Add pad flag MEDIA_PAD_FL_MUST_CONNECT Pads that set this flag must be connected by an active link for the entity to stream. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml index 355df43..cf85485 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml @@ -134,6 +134,15 @@ Output pad, relative to the entity. Output pads source data and are origins of links. + + MEDIA_PAD_FL_MUST_CONNECT + If this flag is set and the pad is linked to any other + pad, then at least one of those links must be enabled for the + entity to be able to stream. There could be temporary reasons + (e.g. device configuration dependent) for the pad to need + enabled links even when this flag isn't set; the absence of the + flag doesn't imply there is none. + diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index ed49574..d847c76 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -98,6 +98,7 @@ struct media_entity_desc { #define MEDIA_PAD_FL_SINK (1 << 0) #define MEDIA_PAD_FL_SOURCE (1 << 1) +#define MEDIA_PAD_FL_MUST_CONNECT (1 << 2) struct media_pad_desc { __u32 entity; /* entity ID */ -- cgit v0.10.2 From de49c285a3604955dcbf746edd871f2a4f128122 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 13 Oct 2013 08:00:26 -0300 Subject: [media] media: Check for active links on pads with MEDIA_PAD_FL_MUST_CONNECT flag Do not allow streaming if a pad with MEDIA_PAD_FL_MUST_CONNECT flag is not connected by an active link. This patch makes it possible to avoid drivers having to check for the most common case of link state validation: a sink pad that must be connected. Signed-off-by: Sakari Ailus Tested-by: Sylwester Nawrocki Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 2c286c3..37c334e 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); -- cgit v0.10.2 From 8dad936ab3e28b1fd396972c70f523d4b50dfcf4 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 Oct 2013 20:17:52 -0300 Subject: [media] omap3isp: Mark which pads must connect Mark pads that must be connected. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 907a205..561c991 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -2484,7 +2484,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 e716514..e84fe05 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 6db245d..62056082 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 cd8831a..1c776c1 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/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index d11fb26..fa35f2c 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -1701,7 +1701,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 61e17f9..a75407c 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 a908d00..d45af5c 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1335,11 +1335,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; -- cgit v0.10.2 From 2e1ab9cb94a257dcd7561601be44ca8b35b27365 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 Oct 2013 20:17:53 -0300 Subject: [media] omap3isp: Add resizer data rate configuration to resizer_link_validate The configuration of many other blocks depend on resizer maximum data rate. Get the value from resizer at link validation time. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index fa35f2c..0d36b8b 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 */ diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index d45af5c..9319e4d 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) { @@ -1054,11 +1005,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); -- cgit v0.10.2 From 8e6e8f93f7cb3452b1c00da8a30d01558628d913 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 14 Sep 2013 18:39:04 -0300 Subject: [media] V4L: Add mem2mem ioctl and file operation helpers This patch adds ioctl helpers to the V4L2 mem-to-mem API, so we can avoid several ioctl handlers in the mem-to-mem video node drivers that are simply a pass-through to the v4l2_m2m_* calls. These helpers will only be useful for drivers that use same mutex for both OUTPUT and CAPTURE queue, which is the case for all currently in tree v4l2 m2m drivers. In order to use the helpers the drivers are required to use struct v4l2_fh. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 73035ee..178ce96 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/include/media/v4l2-fh.h b/include/media/v4l2-fh.h index 528cdaf..8035167 100644 --- a/include/media/v4l2-fh.h +++ b/include/media/v4l2-fh.h @@ -45,6 +45,10 @@ struct v4l2_fh { struct list_head available; /* Dequeueable event */ unsigned int navailable; u32 sequence; + +#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) + struct v4l2_m2m_ctx *m2m_ctx; +#endif }; /* diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 44542a2..12ea5a6 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -64,6 +64,9 @@ struct v4l2_m2m_queue_ctx { }; struct v4l2_m2m_ctx { + /* optional cap/out vb2 queues lock */ + struct mutex *q_lock; + /* private: internal use only */ struct v4l2_m2m_dev *m2m_dev; @@ -229,5 +232,26 @@ static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx); } +/* v4l2 ioctl helpers */ + +int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int v4l2_m2m_ioctl_create_bufs(struct file *file, void *fh, + struct v4l2_create_buffers *create); +int v4l2_m2m_ioctl_querybuf(struct file *file, void *fh, + struct v4l2_buffer *buf); +int v4l2_m2m_ioctl_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb); +int v4l2_m2m_ioctl_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buf); +int v4l2_m2m_ioctl_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *buf); +int v4l2_m2m_ioctl_streamon(struct file *file, void *fh, + enum v4l2_buf_type type); +int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type); +int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma); +unsigned int v4l2_m2m_fop_poll(struct file *file, poll_table *wait); + #endif /* _MEDIA_V4L2_MEM2MEM_H */ -- cgit v0.10.2 From 20702ebc2c3bd12235db90f3e0348698fe4c8fb5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 25 Aug 2013 17:27:45 -0300 Subject: [media] mem2mem_testdev: Use mem-to-mem ioctl and vb2 helpers Simplify the driver by using the m2m ioctl and vb2 helpers. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Acked-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 8df5975..08e2437 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); - -- cgit v0.10.2 From 43894d848a40075320898bc2bdfdd99c6d3b7b35 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 25 Aug 2013 16:55:58 -0300 Subject: [media] exynos4-is: Use mem-to-mem ioctl helpers Simplify the FIMC mem-to-mem driver by using the m2m ioctl and vb2 helpers. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Acked-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 3d376fa..1790fb4 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-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 8d33b68..9da95bd 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 = { -- cgit v0.10.2 From 718cf4a9da22b2abf3d4f220b7a0a4e4a5b6287d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 25 Aug 2013 17:16:56 -0300 Subject: [media] s5p-jpeg: Use mem-to-mem ioctl helpers Simplify the driver by using the m2m ioctl and vb2 helpers. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Acked-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 9b88a460..c818cbe 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -316,9 +316,9 @@ static int s5p_jpeg_open(struct file *file) 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; } @@ -342,7 +342,7 @@ 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); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); mutex_unlock(&jpeg->lock); v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh); @@ -352,39 +352,13 @@ static int s5p_jpeg_release(struct file *file) 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; -} - 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, }; /* @@ -589,7 +563,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; @@ -745,7 +719,7 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; - 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; @@ -792,53 +766,6 @@ 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) { @@ -972,14 +899,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, }; @@ -997,8 +923,8 @@ static void s5p_jpeg_device_run(void *priv) struct vb2_buffer *src_buf, *dst_buf; unsigned long src_addr, dst_addr; - 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); @@ -1170,22 +1096,8 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) ); 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 +1123,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 +1142,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 +1155,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,8 +1181,8 @@ 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); @@ -1296,7 +1210,7 @@ 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); spin_unlock(&jpeg->slock); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 8a4013e..4a4776b 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -115,7 +115,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 +126,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; -- cgit v0.10.2 From febeaa45a692e93275b166e6a6f5852a46196ec9 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 25 Aug 2013 18:13:17 -0300 Subject: [media] s5p-g2d: Use mem-to-mem ioctl helpers Simplify the driver by using the m2m ioctl and vb2 helpers. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Acked-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 0b29483..0fcf7d7 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 300ca05..b0e52ab 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; -- cgit v0.10.2 From 0495d405f319171ac1cb6276019fc49a382fa295 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Sat, 21 Sep 2013 11:00:47 -0300 Subject: [media] s5p-tv: sdo: Restore vpll clock rate after streamoff Restore vpll clock rate if start stream fail or stream is off. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park [s.nawrocki@samsung.com: fixed whitespace error reported by checkpath.pl] Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 0afa90f..fbc6d7a 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_enable(sdev->dacphy); + if (ret < 0) { + dev_err(sdev->dev, "clk_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) @@ -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; } -- cgit v0.10.2 From a889c11519b425dce284c6233cfb4629f519ccac Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Sat, 21 Sep 2013 11:00:48 -0300 Subject: [media] s5p-tv: sdo: Prepare for common clock framework Replace clk_enable() by clock_enable_prepare() and clk_disable() with clk_disable_unprepare(). clk_{prepare/unprepare} calls are required by common clock framework and this driver was missed while converting all users of the Samsung original clocks driver to its new implementation based on the common clock API. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index fbc6d7a..5a7c379 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -208,9 +208,9 @@ static int sdo_streamon(struct sdo_device *sdev) clk_get_rate(sdev->fout_vpll)); /* enable clock in SDO */ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON); - ret = clk_enable(sdev->dacphy); + ret = clk_prepare_enable(sdev->dacphy); if (ret < 0) { - dev_err(sdev->dev, "clk_enable(dacphy) failed\n"); + dev_err(sdev->dev, "clk_prepare_enable(dacphy) failed\n"); goto fail; } /* enable DAC */ @@ -229,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) @@ -273,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; } @@ -285,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; @@ -318,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; } @@ -424,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); @@ -463,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); -- cgit v0.10.2 From 03ce1a13ccdfa61bf8f3bdcc7f10e2580db71149 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Sat, 21 Sep 2013 11:00:49 -0300 Subject: [media] s5p-tv: mixer: Prepare for common clock framework Replace clk_enable() by clock_enable_prepare() and clk_disable() with clk_disable_unprepare(). clk_{prepare/unprepare} calls are required by common clock framework and this driver was missed while converting all users of the Samsung original clocks driver to its new implementation based on the common clock API. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park Acked-by: Tomasz Stanislawski [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index 51805a5..bc08b5f 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; -- cgit v0.10.2 From e1163236f5214e3447f1132a355a26db3db7d9f3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 Nov 2013 06:52:24 -0300 Subject: [media] exynos4-is: Cleanup a define in mipi-csis driver This define is only used in s5pcsis_irq_handler(): if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { The problem is that "status" is a 32 bit and (0xff << 28) is larger than 32 bits and that sets off a static checker warning. I consulted with Sylwester Nawrocki and the define should actually be (0xf << 28). Signed-off-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 9fc2af6..31dfc50 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) -- cgit v0.10.2 From b1fbe051c1ff608eaab3d46cf38be03ac374294b Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sun, 13 Oct 2013 07:50:40 -0300 Subject: [media] exynos4-is: fimc-lite: Index out of bounds if no pixelcode found In case no valid pixelcode is found, an i of -1 after the loop is out of bounds for the array. Signed-off-by: Roel Kluin Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index 72a343e3b..d0dc7ee 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; -- cgit v0.10.2 From 3f823e094b935c1882605f8720336ee23433a16d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Oct 2013 11:14:32 -0300 Subject: [media] exynos4-is: Simplify fimc-is hardware polling helpers The fimc_is_hw_wait_intsr0_intsd0() function is currently unused and can be safely removed. The other polling function simplified and ETIME error code is replaced with more commonly used ETIMEDOUT. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index f758e26..2628733 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 5fa2fda..1d9d4ff 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); -- cgit v0.10.2 From 9fa7c419643b8c291d9c56718804cb8cf4237e97 Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Wed, 13 Nov 2013 15:02:28 -0300 Subject: [media] cx24117: Add complete demod command list This patch adds the complete list of all the commands known for the cx24117 demodulator. Signed-off-by: Luis Alves Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index 476b422..a6fe1af 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,8 +917,8 @@ 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; + /* Set GPIO direction */ + cmd.args[0] = CMD_SET_GPIODIR; cmd.args[1] = reg; cmd.args[2] = reg; cmd.len = 3; @@ -910,8 +928,8 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, if ((voltage == SEC_VOLTAGE_13) || (voltage == SEC_VOLTAGE_18)) { - /* CMD 33 */ - cmd.args[0] = 0x33; + /* Set GPIO logic level */ + cmd.args[0] = CMD_SET_GPIOOUT; cmd.args[1] = reg; cmd.args[2] = reg; cmd.len = 3; @@ -926,7 +944,7 @@ 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); @@ -935,7 +953,7 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, /* Min delay time before DiSEqC send */ msleep(20); } else { - cmd.args[0] = 0x33; + cmd.args[0] = CMD_SET_GPIOOUT; cmd.args[1] = 0x00; cmd.args[2] = reg; cmd.len = 3; @@ -968,8 +986,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; @@ -1231,8 +1248,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 +1261,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,8 +1271,8 @@ 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; @@ -1278,8 +1295,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 +1575,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); -- cgit v0.10.2 From 71b6aaafd534995255d65d6433cbae7938a8131f Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Wed, 13 Nov 2013 15:06:44 -0300 Subject: [media] cx24117: Fix LNB set_voltage function This patch should fix/enhance the set_voltage function for the cx24117 demodulator. Signed-off-by: Luis Alves Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index a6fe1af..68f768a 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -917,22 +917,15 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "SEC_VOLTAGE_OFF"); - /* Set GPIO direction */ - cmd.args[0] = CMD_SET_GPIODIR; - 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)) { - /* Set GPIO logic level */ - cmd.args[0] = CMD_SET_GPIOOUT; + /* 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; @@ -949,17 +942,17 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, 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] = CMD_SET_GPIOOUT; + /* 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, @@ -1277,6 +1270,16 @@ static int cx24117_initfe(struct dvb_frontend *fe) 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); -- cgit v0.10.2 From 899127b67df098e6d878f27be05dc91401cc6685 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Nov 2013 08:34:42 -0300 Subject: [media] This adds support for the BCM2048 radio module found in Nokia N900 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add suport for Nokia N900 radio. This driver is far from being ready to be added at the main tree, as it creates its own sysfs interface, and violates lots of Coding Style rules, doing even evil things like returning from a function inside a macro. So, it is being added at staging with the condition that it will be soon be fixed. [m.chehab@samsung.com: added a description for the patch] Signed-off-by: Eero Nurkkala Signed-off-by: Nils Faerber Signed-off-by: Joni Lapilainen Signed-off-by: Pali Rohár Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: moved to staging, added slab.h include] Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index bc4c798..7728879 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -21,6 +21,8 @@ if STAGING_MEDIA # Please keep them in alphabetic order source "drivers/staging/media/as102/Kconfig" +source "drivers/staging/media/bcm2048/Kconfig" + source "drivers/staging/media/cxd2099/Kconfig" source "drivers/staging/media/davinci_vpfe/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index a528d3f..0bd1a88 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_DVB_AS102) += as102/ +obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ diff --git a/drivers/staging/media/bcm2048/Kconfig b/drivers/staging/media/bcm2048/Kconfig new file mode 100644 index 0000000..a9fc6e1 --- /dev/null +++ b/drivers/staging/media/bcm2048/Kconfig @@ -0,0 +1,13 @@ +# +# Multimedia Video device configuration +# + +config I2C_BCM2048 + tristate "Broadcom BCM2048 FM Radio Receiver support" + depends on I2C && VIDEO_V4L2 && RADIO_ADAPTERS + ---help--- + Say Y here if you want support to BCM2048 FM Radio Receiver. + This device driver supports only i2c bus. + + To compile this driver as a module, choose M here: the + module will be called radio-bcm2048. diff --git a/drivers/staging/media/bcm2048/Makefile b/drivers/staging/media/bcm2048/Makefile new file mode 100644 index 0000000..b4f5663 --- /dev/null +++ b/drivers/staging/media/bcm2048/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_I2C_BCM2048) += radio-bcm2048.o diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c new file mode 100644 index 0000000..782cc11 --- /dev/null +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -0,0 +1,2745 @@ +/* + * drivers/staging/media/radio-bcm2048.c + * + * Driver for I2C Broadcom BCM2048 FM Radio Receiver: + * + * Copyright (C) Nokia Corporation + * Contact: Eero Nurkkala + * + * Copyright (C) Nils Faerber + * + * 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 + */ + +/* + * History: + * Eero Nurkkala + * Version 0.0.1 + * - Initial implementation + * 2010-02-21 Nils Faerber + * Version 0.0.2 + * - Add support for interrupt driven rds data reading + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "radio-bcm2048.h" + +/* driver definitions */ +#define BCM2048_DRIVER_AUTHOR "Eero Nurkkala " +#define BCM2048_DRIVER_NAME BCM2048_NAME +#define BCM2048_DRIVER_VERSION KERNEL_VERSION(0, 0, 1) +#define BCM2048_DRIVER_CARD "Broadcom bcm2048 FM Radio Receiver" +#define BCM2048_DRIVER_DESC "I2C driver for BCM2048 FM Radio Receiver" + +/* I2C Control Registers */ +#define BCM2048_I2C_FM_RDS_SYSTEM 0x00 +#define BCM2048_I2C_FM_CTRL 0x01 +#define BCM2048_I2C_RDS_CTRL0 0x02 +#define BCM2048_I2C_RDS_CTRL1 0x03 +#define BCM2048_I2C_FM_AUDIO_PAUSE 0x04 +#define BCM2048_I2C_FM_AUDIO_CTRL0 0x05 +#define BCM2048_I2C_FM_AUDIO_CTRL1 0x06 +#define BCM2048_I2C_FM_SEARCH_CTRL0 0x07 +#define BCM2048_I2C_FM_SEARCH_CTRL1 0x08 +#define BCM2048_I2C_FM_SEARCH_TUNE_MODE 0x09 +#define BCM2048_I2C_FM_FREQ0 0x0a +#define BCM2048_I2C_FM_FREQ1 0x0b +#define BCM2048_I2C_FM_AF_FREQ0 0x0c +#define BCM2048_I2C_FM_AF_FREQ1 0x0d +#define BCM2048_I2C_FM_CARRIER 0x0e +#define BCM2048_I2C_FM_RSSI 0x0f +#define BCM2048_I2C_FM_RDS_MASK0 0x10 +#define BCM2048_I2C_FM_RDS_MASK1 0x11 +#define BCM2048_I2C_FM_RDS_FLAG0 0x12 +#define BCM2048_I2C_FM_RDS_FLAG1 0x13 +#define BCM2048_I2C_RDS_WLINE 0x14 +#define BCM2048_I2C_RDS_BLKB_MATCH0 0x16 +#define BCM2048_I2C_RDS_BLKB_MATCH1 0x17 +#define BCM2048_I2C_RDS_BLKB_MASK0 0x18 +#define BCM2048_I2C_RDS_BLKB_MASK1 0x19 +#define BCM2048_I2C_RDS_PI_MATCH0 0x1a +#define BCM2048_I2C_RDS_PI_MATCH1 0x1b +#define BCM2048_I2C_RDS_PI_MASK0 0x1c +#define BCM2048_I2C_RDS_PI_MASK1 0x1d +#define BCM2048_I2C_SPARE1 0x20 +#define BCM2048_I2C_SPARE2 0x21 +#define BCM2048_I2C_FM_RDS_REV 0x28 +#define BCM2048_I2C_SLAVE_CONFIGURATION 0x29 +#define BCM2048_I2C_RDS_DATA 0x80 +#define BCM2048_I2C_FM_BEST_TUNE_MODE 0x90 + +/* BCM2048_I2C_FM_RDS_SYSTEM */ +#define BCM2048_FM_ON 0x01 +#define BCM2048_RDS_ON 0x02 + +/* BCM2048_I2C_FM_CTRL */ +#define BCM2048_BAND_SELECT 0x01 +#define BCM2048_STEREO_MONO_AUTO_SELECT 0x02 +#define BCM2048_STEREO_MONO_MANUAL_SELECT 0x04 +#define BCM2048_STEREO_MONO_BLEND_SWITCH 0x08 +#define BCM2048_HI_LO_INJECTION 0x10 + +/* BCM2048_I2C_RDS_CTRL0 */ +#define BCM2048_RBDS_RDS_SELECT 0x01 +#define BCM2048_FLUSH_FIFO 0x02 + +/* BCM2048_I2C_FM_AUDIO_PAUSE */ +#define BCM2048_AUDIO_PAUSE_RSSI_TRESH 0x0f +#define BCM2048_AUDIO_PAUSE_DURATION 0xf0 + +/* BCM2048_I2C_FM_AUDIO_CTRL0 */ +#define BCM2048_RF_MUTE 0x01 +#define BCM2048_MANUAL_MUTE 0x02 +#define BCM2048_DAC_OUTPUT_LEFT 0x04 +#define BCM2048_DAC_OUTPUT_RIGHT 0x08 +#define BCM2048_AUDIO_ROUTE_DAC 0x10 +#define BCM2048_AUDIO_ROUTE_I2S 0x20 +#define BCM2048_DE_EMPHASIS_SELECT 0x40 +#define BCM2048_AUDIO_BANDWIDTH_SELECT 0x80 + +/* BCM2048_I2C_FM_SEARCH_CTRL0 */ +#define BCM2048_SEARCH_RSSI_THRESHOLD 0x7f +#define BCM2048_SEARCH_DIRECTION 0x80 + +/* BCM2048_I2C_FM_SEARCH_TUNE_MODE */ +#define BCM2048_FM_AUTO_SEARCH 0x03 + +/* BCM2048_I2C_FM_RSSI */ +#define BCM2048_RSSI_VALUE 0xff + +/* BCM2048_I2C_FM_RDS_MASK0 */ +/* BCM2048_I2C_FM_RDS_MASK1 */ +#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED 0x01 +#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL 0x02 +#define BCM2048_FM_FLAG_RSSI_LOW 0x04 +#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH 0x08 +#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION 0x10 +#define BCM2048_FLAG_STEREO_DETECTED 0x20 +#define BCM2048_FLAG_STEREO_ACTIVE 0x40 + +/* BCM2048_I2C_RDS_DATA */ +#define BCM2048_SLAVE_ADDRESS 0x3f +#define BCM2048_SLAVE_ENABLE 0x80 + +/* BCM2048_I2C_FM_BEST_TUNE_MODE */ +#define BCM2048_BEST_TUNE_MODE 0x80 + +#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED 0x01 +#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL 0x02 +#define BCM2048_FM_FLAG_RSSI_LOW 0x04 +#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH 0x08 +#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION 0x10 +#define BCM2048_FLAG_STEREO_DETECTED 0x20 +#define BCM2048_FLAG_STEREO_ACTIVE 0x40 + +#define BCM2048_RDS_FLAG_FIFO_WLINE 0x02 +#define BCM2048_RDS_FLAG_B_BLOCK_MATCH 0x08 +#define BCM2048_RDS_FLAG_SYNC_LOST 0x10 +#define BCM2048_RDS_FLAG_PI_MATCH 0x20 + +#define BCM2048_RDS_MARK_END_BYTE0 0x7C +#define BCM2048_RDS_MARK_END_BYTEN 0xFF + +#define BCM2048_FM_FLAGS_ALL (FM_FLAG_SEARCH_TUNE_FINISHED | \ + FM_FLAG_SEARCH_TUNE_FAIL | \ + FM_FLAG_RSSI_LOW | \ + FM_FLAG_CARRIER_ERROR_HIGH | \ + FM_FLAG_AUDIO_PAUSE_INDICATION | \ + FLAG_STEREO_DETECTED | FLAG_STEREO_ACTIVE) + +#define BCM2048_RDS_FLAGS_ALL (RDS_FLAG_FIFO_WLINE | \ + RDS_FLAG_B_BLOCK_MATCH | \ + RDS_FLAG_SYNC_LOST | RDS_FLAG_PI_MATCH) + +#define BCM2048_DEFAULT_TIMEOUT 1500 +#define BCM2048_AUTO_SEARCH_TIMEOUT 3000 + + +#define BCM2048_FREQDEV_UNIT 10000 +#define BCM2048_FREQV4L2_MULTI 625 +#define dev_to_v4l2(f) ((f * BCM2048_FREQDEV_UNIT) / BCM2048_FREQV4L2_MULTI) +#define v4l2_to_dev(f) ((f * BCM2048_FREQV4L2_MULTI) / BCM2048_FREQDEV_UNIT) + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) +#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) + +#define BCM2048_DEFAULT_POWERING_DELAY 20 +#define BCM2048_DEFAULT_REGION 0x02 +#define BCM2048_DEFAULT_MUTE 0x01 +#define BCM2048_DEFAULT_RSSI_THRESHOLD 0x64 +#define BCM2048_DEFAULT_RDS_WLINE 0x7E + +#define BCM2048_FM_SEARCH_INACTIVE 0x00 +#define BCM2048_FM_PRE_SET_MODE 0x01 +#define BCM2048_FM_AUTO_SEARCH_MODE 0x02 +#define BCM2048_FM_AF_JUMP_MODE 0x03 + +#define BCM2048_FREQUENCY_BASE 64000 + +#define BCM2048_POWER_ON 0x01 +#define BCM2048_POWER_OFF 0x00 + +#define BCM2048_ITEM_ENABLED 0x01 +#define BCM2048_SEARCH_DIRECTION_UP 0x01 + +#define BCM2048_DE_EMPHASIS_75us 75 +#define BCM2048_DE_EMPHASIS_50us 50 + +#define BCM2048_SCAN_FAIL 0x00 +#define BCM2048_SCAN_OK 0x01 + +#define BCM2048_FREQ_ERROR_FLOOR -20 +#define BCM2048_FREQ_ERROR_ROOF 20 + +/* -60 dB is reported as full signal strenght */ +#define BCM2048_RSSI_LEVEL_BASE -60 +#define BCM2048_RSSI_LEVEL_ROOF -100 +#define BCM2048_RSSI_LEVEL_ROOF_NEG 100 +#define BCM2048_SIGNAL_MULTIPLIER (0xFFFF / \ + (BCM2048_RSSI_LEVEL_ROOF_NEG + \ + BCM2048_RSSI_LEVEL_BASE)) + +#define BCM2048_RDS_FIFO_DUPLE_SIZE 0x03 +#define BCM2048_RDS_CRC_MASK 0x0F +#define BCM2048_RDS_CRC_NONE 0x00 +#define BCM2048_RDS_CRC_MAX_2BITS 0x04 +#define BCM2048_RDS_CRC_LEAST_2BITS 0x08 +#define BCM2048_RDS_CRC_UNRECOVARABLE 0x0C + +#define BCM2048_RDS_BLOCK_MASK 0xF0 +#define BCM2048_RDS_BLOCK_A 0x00 +#define BCM2048_RDS_BLOCK_B 0x10 +#define BCM2048_RDS_BLOCK_C 0x20 +#define BCM2048_RDS_BLOCK_D 0x30 +#define BCM2048_RDS_BLOCK_C_SCORED 0x40 +#define BCM2048_RDS_BLOCK_E 0x60 + +#define BCM2048_RDS_RT 0x20 +#define BCM2048_RDS_PS 0x00 + +#define BCM2048_RDS_GROUP_AB_MASK 0x08 +#define BCM2048_RDS_GROUP_A 0x00 +#define BCM2048_RDS_GROUP_B 0x08 + +#define BCM2048_RDS_RT_AB_MASK 0x10 +#define BCM2048_RDS_RT_A 0x00 +#define BCM2048_RDS_RT_B 0x10 +#define BCM2048_RDS_RT_INDEX 0x0F + +#define BCM2048_RDS_PS_INDEX 0x03 + +struct rds_info { + u16 rds_pi; +#define BCM2048_MAX_RDS_RT (64 + 1) + u8 rds_rt[BCM2048_MAX_RDS_RT]; + u8 rds_rt_group_b; + u8 rds_rt_ab; +#define BCM2048_MAX_RDS_PS (8 + 1) + u8 rds_ps[BCM2048_MAX_RDS_PS]; + u8 rds_ps_group; + u8 rds_ps_group_cnt; +#define BCM2048_MAX_RDS_RADIO_TEXT 255 + u8 radio_text[BCM2048_MAX_RDS_RADIO_TEXT + 3]; + u8 text_len; +}; + +struct region_info { + u32 bottom_frequency; + u32 top_frequency; + u8 deemphasis; + u8 channel_spacing; + u8 region; +}; + +struct bcm2048_device { + struct i2c_client *client; + struct video_device *videodev; + struct work_struct work; + struct completion compl; + struct mutex mutex; + struct bcm2048_platform_data *platform_data; + struct rds_info rds_info; + struct region_info region_info; + u16 frequency; + u8 cache_fm_rds_system; + u8 cache_fm_ctrl; + u8 cache_fm_audio_ctrl0; + u8 cache_fm_search_ctrl0; + u8 power_state; + u8 rds_state; + u8 fifo_size; + u8 scan_state; + u8 mute_state; + + /* for rds data device read */ + wait_queue_head_t read_queue; + unsigned int users; + unsigned char rds_data_available; + unsigned int rd_index; +}; + +static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */ +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, + "Minor number for radio device (-1 ==> auto assign)"); + +static struct region_info region_configs[] = { + /* USA */ + { + .channel_spacing = 20, + .bottom_frequency = 87500, + .top_frequency = 108000, + .deemphasis = 75, + .region = 0, + }, + /* Australia */ + { + .channel_spacing = 20, + .bottom_frequency = 87500, + .top_frequency = 108000, + .deemphasis = 50, + .region = 1, + }, + /* Europe */ + { + .channel_spacing = 10, + .bottom_frequency = 87500, + .top_frequency = 108000, + .deemphasis = 50, + .region = 2, + }, + /* Japan */ + { + .channel_spacing = 10, + .bottom_frequency = 76000, + .top_frequency = 90000, + .deemphasis = 50, + .region = 3, + }, + /* Japan wide band */ + { + .channel_spacing = 10, + .bottom_frequency = 76000, + .top_frequency = 108000, + .deemphasis = 50, + .region = 4, + }, +}; + +/* + * I2C Interface read / write + */ +static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = bdev->client; + u8 data[2]; + + if (!bdev->power_state) { + dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); + return -EIO; + } + + data[0] = reg & 0xff; + data[1] = value & 0xff; + + if (i2c_master_send(client, data, 2) == 2) { + return 0; + } else { + dev_err(&bdev->client->dev, "BCM I2C error!\n"); + dev_err(&bdev->client->dev, "Is Bluetooth up and running?\n"); + return -EIO; + } +} + +static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg, + u8 *value) +{ + struct i2c_client *client = bdev->client; + + if (!bdev->power_state) { + dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); + return -EIO; + } + + value[0] = i2c_smbus_read_byte_data(client, reg & 0xff); + + return 0; +} + +static int bcm2048_recv_duples(struct bcm2048_device *bdev, unsigned int reg, + u8 *value, u8 duples) +{ + struct i2c_client *client = bdev->client; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg[2]; + u8 buf; + + if (!bdev->power_state) { + dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); + return -EIO; + } + + buf = reg & 0xff; + + msg[0].addr = client->addr; + msg[0].flags = client->flags & I2C_M_TEN; + msg[0].len = 1; + msg[0].buf = &buf; + + msg[1].addr = client->addr; + msg[1].flags = client->flags & I2C_M_TEN; + msg[1].flags |= I2C_M_RD; + msg[1].len = duples; + msg[1].buf = value; + + return i2c_transfer(adap, msg, 2); +} + +/* + * BCM2048 - I2C register programming helpers + */ +static int bcm2048_set_power_state(struct bcm2048_device *bdev, u8 power) +{ + int err = 0; + + mutex_lock(&bdev->mutex); + + if (power) { + bdev->power_state = BCM2048_POWER_ON; + bdev->cache_fm_rds_system |= BCM2048_FM_ON; + } else { + bdev->cache_fm_rds_system &= ~BCM2048_FM_ON; + } + + /* + * Warning! FM cannot be turned off because then + * the I2C communications get ruined! + * Comment off the "if (power)" when the chip works! + */ + if (power) + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, + bdev->cache_fm_rds_system); + msleep(BCM2048_DEFAULT_POWERING_DELAY); + + if (!power) + bdev->power_state = BCM2048_POWER_OFF; + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_power_state(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value); + + mutex_unlock(&bdev->mutex); + + if (!err && (value & BCM2048_FM_ON)) + return BCM2048_POWER_ON; + + return err; +} + +static int bcm2048_set_rds_no_lock(struct bcm2048_device *bdev, u8 rds_on) +{ + int err; + u8 flags; + + bdev->cache_fm_rds_system &= ~BCM2048_RDS_ON; + + if (rds_on) { + bdev->cache_fm_rds_system |= BCM2048_RDS_ON; + bdev->rds_state = BCM2048_RDS_ON; + flags = BCM2048_RDS_FLAG_FIFO_WLINE; + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, + flags); + } else { + flags = 0; + bdev->rds_state = 0; + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, + flags); + memset(&bdev->rds_info, 0, sizeof(bdev->rds_info)); + } + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, + bdev->cache_fm_rds_system); + + return err; +} + +static int bcm2048_get_rds_no_lock(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value); + + if (!err && (value & BCM2048_RDS_ON)) + return BCM2048_ITEM_ENABLED; + + return err; +} + +static int bcm2048_set_rds(struct bcm2048_device *bdev, u8 rds_on) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_set_rds_no_lock(bdev, rds_on); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds(struct bcm2048_device *bdev) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_get_rds_no_lock(bdev); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_pi(struct bcm2048_device *bdev) +{ + return bdev->rds_info.rds_pi; +} + +static int bcm2048_set_fm_automatic_stereo_mono(struct bcm2048_device *bdev, + u8 enabled) +{ + int err; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_ctrl &= ~BCM2048_STEREO_MONO_AUTO_SELECT; + + if (enabled) + bdev->cache_fm_ctrl |= BCM2048_STEREO_MONO_AUTO_SELECT; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, + bdev->cache_fm_ctrl); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_set_fm_hi_lo_injection(struct bcm2048_device *bdev, + u8 hi_lo) +{ + int err; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_ctrl &= ~BCM2048_HI_LO_INJECTION; + + if (hi_lo) + bdev->cache_fm_ctrl |= BCM2048_HI_LO_INJECTION; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, + bdev->cache_fm_ctrl); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_hi_lo_injection(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CTRL, &value); + + mutex_unlock(&bdev->mutex); + + if (!err && (value & BCM2048_HI_LO_INJECTION)) + return BCM2048_ITEM_ENABLED; + + return err; +} + +static int bcm2048_set_fm_frequency(struct bcm2048_device *bdev, u32 frequency) +{ + int err; + + if (frequency < bdev->region_info.bottom_frequency || + frequency > bdev->region_info.top_frequency) + return -EDOM; + + frequency -= BCM2048_FREQUENCY_BASE; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ0, lsb(frequency)); + err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ1, + msb(frequency)); + + if (!err) + bdev->frequency = frequency; + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_frequency(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ0, &lsb); + err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ1, &msb); + + mutex_unlock(&bdev->mutex); + + if (err) + return err; + + err = compose_u16(msb, lsb); + err += BCM2048_FREQUENCY_BASE; + + return err; +} + +static int bcm2048_set_fm_af_frequency(struct bcm2048_device *bdev, + u32 frequency) +{ + int err; + + if (frequency < bdev->region_info.bottom_frequency || + frequency > bdev->region_info.top_frequency) + return -EDOM; + + frequency -= BCM2048_FREQUENCY_BASE; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ0, + lsb(frequency)); + err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ1, + msb(frequency)); + if (!err) + bdev->frequency = frequency; + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_af_frequency(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ0, &lsb); + err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ1, &msb); + + mutex_unlock(&bdev->mutex); + + if (err) + return err; + + err = compose_u16(msb, lsb); + err += BCM2048_FREQUENCY_BASE; + + return err; +} + +static int bcm2048_set_fm_deemphasis(struct bcm2048_device *bdev, int d) +{ + int err; + u8 deemphasis; + + if (d == BCM2048_DE_EMPHASIS_75us) + deemphasis = BCM2048_DE_EMPHASIS_SELECT; + else + deemphasis = 0; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_audio_ctrl0 &= ~BCM2048_DE_EMPHASIS_SELECT; + bdev->cache_fm_audio_ctrl0 |= deemphasis; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, + bdev->cache_fm_audio_ctrl0); + + if (!err) + bdev->region_info.deemphasis = d; + + mutex_unlock(&bdev->mutex); + + return err; +} + +static int bcm2048_get_fm_deemphasis(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); + + mutex_unlock(&bdev->mutex); + + if (!err) { + if (value & BCM2048_DE_EMPHASIS_SELECT) + return BCM2048_DE_EMPHASIS_75us; + else + return BCM2048_DE_EMPHASIS_50us; + } + + return err; +} + +static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region) +{ + int err; + u32 new_frequency = 0; + + if (region > ARRAY_SIZE(region_configs)) + return -EINVAL; + + mutex_lock(&bdev->mutex); + memcpy(&bdev->region_info, ®ion_configs[region], + sizeof(struct region_info)); + mutex_unlock(&bdev->mutex); + + if (bdev->frequency < region_configs[region].bottom_frequency || + bdev->frequency > region_configs[region].top_frequency) + new_frequency = region_configs[region].bottom_frequency; + + if (new_frequency > 0) { + err = bcm2048_set_fm_frequency(bdev, new_frequency); + + if (err) + goto done; + } + + err = bcm2048_set_fm_deemphasis(bdev, + region_configs[region].deemphasis); + +done: + return err; +} + +static int bcm2048_get_region(struct bcm2048_device *bdev) +{ + int err; + + mutex_lock(&bdev->mutex); + err = bdev->region_info.region; + mutex_unlock(&bdev->mutex); + + return err; +} + +static int bcm2048_set_mute(struct bcm2048_device *bdev, u16 mute) +{ + int err; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE); + + if (mute) + bdev->cache_fm_audio_ctrl0 |= (BCM2048_RF_MUTE | + BCM2048_MANUAL_MUTE); + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, + bdev->cache_fm_audio_ctrl0); + + if (!err) + bdev->mute_state = mute; + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_mute(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + if (bdev->power_state) { + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, + &value); + if (!err) + err = value & (BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE); + } else { + err = bdev->mute_state; + } + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_set_audio_route(struct bcm2048_device *bdev, u8 route) +{ + int err; + + mutex_lock(&bdev->mutex); + + route &= (BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S); + bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_AUDIO_ROUTE_DAC | + BCM2048_AUDIO_ROUTE_I2S); + bdev->cache_fm_audio_ctrl0 |= route; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, + bdev->cache_fm_audio_ctrl0); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_audio_route(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); + + mutex_unlock(&bdev->mutex); + + if (!err) + return value & (BCM2048_AUDIO_ROUTE_DAC | + BCM2048_AUDIO_ROUTE_I2S); + + return err; +} + +static int bcm2048_set_dac_output(struct bcm2048_device *bdev, u8 channels) +{ + int err; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_DAC_OUTPUT_LEFT | + BCM2048_DAC_OUTPUT_RIGHT); + bdev->cache_fm_audio_ctrl0 |= channels; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, + bdev->cache_fm_audio_ctrl0); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_dac_output(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); + + mutex_unlock(&bdev->mutex); + + if (!err) + return value & (BCM2048_DAC_OUTPUT_LEFT | + BCM2048_DAC_OUTPUT_RIGHT); + + return err; +} + +static int bcm2048_set_fm_search_rssi_threshold(struct bcm2048_device *bdev, + u8 threshold) +{ + int err; + + mutex_lock(&bdev->mutex); + + threshold &= BCM2048_SEARCH_RSSI_THRESHOLD; + bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_RSSI_THRESHOLD; + bdev->cache_fm_search_ctrl0 |= threshold; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, + bdev->cache_fm_search_ctrl0); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_search_rssi_threshold(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value); + + mutex_unlock(&bdev->mutex); + + if (!err) + return value & BCM2048_SEARCH_RSSI_THRESHOLD; + + return err; +} + +static int bcm2048_set_fm_search_mode_direction(struct bcm2048_device *bdev, + u8 direction) +{ + int err; + + mutex_lock(&bdev->mutex); + + bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_DIRECTION; + + if (direction) + bdev->cache_fm_search_ctrl0 |= BCM2048_SEARCH_DIRECTION; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, + bdev->cache_fm_search_ctrl0); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_search_mode_direction(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value); + + mutex_unlock(&bdev->mutex); + + if (!err && (value & BCM2048_SEARCH_DIRECTION)) + return BCM2048_SEARCH_DIRECTION_UP; + + return err; +} + +static int bcm2048_set_fm_search_tune_mode(struct bcm2048_device *bdev, + u8 mode) +{ + int err, timeout, restart_rds = 0; + u8 value, flags; + + value = mode & BCM2048_FM_AUTO_SEARCH; + + flags = BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED | + BCM2048_FM_FLAG_SEARCH_TUNE_FAIL; + + mutex_lock(&bdev->mutex); + + /* + * If RDS is enabled, and frequency is changed, RDS quits working. + * Thus, always restart RDS if it's enabled. Moreover, RDS must + * not be enabled while changing the frequency because it can + * provide a race to the mutex from the workqueue handler if RDS + * IRQ occurs while waiting for frequency changed IRQ. + */ + if (bcm2048_get_rds_no_lock(bdev)) { + err = bcm2048_set_rds_no_lock(bdev, 0); + if (err) + goto unlock; + restart_rds = 1; + } + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK0, flags); + + if (err) + goto unlock; + + bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE, value); + + if (mode != BCM2048_FM_AUTO_SEARCH_MODE) + timeout = BCM2048_DEFAULT_TIMEOUT; + else + timeout = BCM2048_AUTO_SEARCH_TIMEOUT; + + if (!wait_for_completion_timeout(&bdev->compl, + msecs_to_jiffies(timeout))) + dev_err(&bdev->client->dev, "IRQ timeout.\n"); + + if (value) + if (!bdev->scan_state) + err = -EIO; + +unlock: + if (restart_rds) + err |= bcm2048_set_rds_no_lock(bdev, 1); + + mutex_unlock(&bdev->mutex); + + return err; +} + +static int bcm2048_get_fm_search_tune_mode(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE, + &value); + + mutex_unlock(&bdev->mutex); + + if (!err) + return value & BCM2048_FM_AUTO_SEARCH; + + return err; +} + +static int bcm2048_set_rds_b_block_mask(struct bcm2048_device *bdev, u16 mask) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, + BCM2048_I2C_RDS_BLKB_MASK0, lsb(mask)); + err |= bcm2048_send_command(bdev, + BCM2048_I2C_RDS_BLKB_MASK1, msb(mask)); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_b_block_mask(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_BLKB_MASK0, &lsb); + err |= bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_BLKB_MASK1, &msb); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(msb, lsb); + + return err; +} + +static int bcm2048_set_rds_b_block_match(struct bcm2048_device *bdev, + u16 match) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, + BCM2048_I2C_RDS_BLKB_MATCH0, lsb(match)); + err |= bcm2048_send_command(bdev, + BCM2048_I2C_RDS_BLKB_MATCH1, msb(match)); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_b_block_match(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_BLKB_MATCH0, &lsb); + err |= bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_BLKB_MATCH1, &msb); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(msb, lsb); + + return err; +} + +static int bcm2048_set_rds_pi_mask(struct bcm2048_device *bdev, u16 mask) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, + BCM2048_I2C_RDS_PI_MASK0, lsb(mask)); + err |= bcm2048_send_command(bdev, + BCM2048_I2C_RDS_PI_MASK1, msb(mask)); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_pi_mask(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_PI_MASK0, &lsb); + err |= bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_PI_MASK1, &msb); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(msb, lsb); + + return err; +} + +static int bcm2048_set_rds_pi_match(struct bcm2048_device *bdev, u16 match) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, + BCM2048_I2C_RDS_PI_MATCH0, lsb(match)); + err |= bcm2048_send_command(bdev, + BCM2048_I2C_RDS_PI_MATCH1, msb(match)); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_pi_match(struct bcm2048_device *bdev) +{ + int err; + u8 lsb, msb; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_PI_MATCH0, &lsb); + err |= bcm2048_recv_command(bdev, + BCM2048_I2C_RDS_PI_MATCH1, &msb); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(msb, lsb); + + return err; +} + +static int bcm2048_set_fm_rds_mask(struct bcm2048_device *bdev, u16 mask) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, + BCM2048_I2C_FM_RDS_MASK0, lsb(mask)); + err |= bcm2048_send_command(bdev, + BCM2048_I2C_FM_RDS_MASK1, msb(mask)); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_rds_mask(struct bcm2048_device *bdev) +{ + int err; + u8 value0, value1; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK0, &value0); + err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK1, &value1); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(value1, value0); + + return err; +} + +static int bcm2048_get_fm_rds_flags(struct bcm2048_device *bdev) +{ + int err; + u8 value0, value1; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &value0); + err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &value1); + + mutex_unlock(&bdev->mutex); + + if (!err) + return compose_u16(value1, value0); + + return err; +} + +static int bcm2048_get_region_bottom_frequency(struct bcm2048_device *bdev) +{ + return bdev->region_info.bottom_frequency; +} + +static int bcm2048_get_region_top_frequency(struct bcm2048_device *bdev) +{ + return bdev->region_info.top_frequency; +} + +static int bcm2048_set_fm_best_tune_mode(struct bcm2048_device *bdev, u8 mode) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + /* Perform read as the manual indicates */ + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, + &value); + value &= ~BCM2048_BEST_TUNE_MODE; + + if (mode) + value |= BCM2048_BEST_TUNE_MODE; + err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, + value); + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_fm_best_tune_mode(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, + &value); + + mutex_unlock(&bdev->mutex); + + if (!err && (value & BCM2048_BEST_TUNE_MODE)) + return BCM2048_ITEM_ENABLED; + + return err; +} + +static int bcm2048_get_fm_carrier_error(struct bcm2048_device *bdev) +{ + int err = 0; + s8 value; + + mutex_lock(&bdev->mutex); + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CARRIER, &value); + mutex_unlock(&bdev->mutex); + + if (!err) + return value; + + return err; +} + +static int bcm2048_get_fm_rssi(struct bcm2048_device *bdev) +{ + int err; + s8 value; + + mutex_lock(&bdev->mutex); + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RSSI, &value); + mutex_unlock(&bdev->mutex); + + if (!err) + return value; + + return err; +} + +static int bcm2048_set_rds_wline(struct bcm2048_device *bdev, u8 wline) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_WLINE, wline); + + if (!err) + bdev->fifo_size = wline; + + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_wline(struct bcm2048_device *bdev) +{ + int err; + u8 value; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_WLINE, &value); + + mutex_unlock(&bdev->mutex); + + if (!err) { + bdev->fifo_size = value; + return value; + } + + return err; +} + +static int bcm2048_checkrev(struct bcm2048_device *bdev) +{ + int err; + u8 version; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_REV, &version); + + mutex_unlock(&bdev->mutex); + + if (!err) { + dev_info(&bdev->client->dev, "BCM2048 Version 0x%x\n", + version); + return version; + } + + return err; +} + +static int bcm2048_get_rds_rt(struct bcm2048_device *bdev, char *data) +{ + int err = 0, i, j = 0, ce = 0, cr = 0; + char data_buffer[BCM2048_MAX_RDS_RT+1]; + + mutex_lock(&bdev->mutex); + + if (!bdev->rds_info.text_len) { + err = -EINVAL; + goto unlock; + } + + for (i = 0; i < BCM2048_MAX_RDS_RT; i++) { + if (bdev->rds_info.rds_rt[i]) { + ce = i; + /* Skip the carriage return */ + if (bdev->rds_info.rds_rt[i] != 0x0d) { + data_buffer[j++] = bdev->rds_info.rds_rt[i]; + } else { + cr = i; + break; + } + } + } + + if (j <= BCM2048_MAX_RDS_RT) + data_buffer[j] = 0; + + for (i = 0; i < BCM2048_MAX_RDS_RT; i++) { + if (!bdev->rds_info.rds_rt[i]) { + if (cr && (i < cr)) { + err = -EBUSY; + goto unlock; + } + if (i < ce) { + if (cr && (i >= cr)) + break; + err = -EBUSY; + goto unlock; + } + } + } + + memcpy(data, data_buffer, sizeof(data_buffer)); + +unlock: + mutex_unlock(&bdev->mutex); + return err; +} + +static int bcm2048_get_rds_ps(struct bcm2048_device *bdev, char *data) +{ + int err = 0, i, j = 0; + char data_buffer[BCM2048_MAX_RDS_PS+1]; + + mutex_lock(&bdev->mutex); + + if (!bdev->rds_info.text_len) { + err = -EINVAL; + goto unlock; + } + + for (i = 0; i < BCM2048_MAX_RDS_PS; i++) { + if (bdev->rds_info.rds_ps[i]) { + data_buffer[j++] = bdev->rds_info.rds_ps[i]; + } else { + if (i < (BCM2048_MAX_RDS_PS - 1)) { + err = -EBUSY; + goto unlock; + } + } + } + + if (j <= BCM2048_MAX_RDS_PS) + data_buffer[j] = 0; + + memcpy(data, data_buffer, sizeof(data_buffer)); + +unlock: + mutex_unlock(&bdev->mutex); + return err; +} + +static void bcm2048_parse_rds_pi(struct bcm2048_device *bdev) +{ + int i, cnt = 0; + u16 pi; + + for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { + + /* Block A match, only data without crc errors taken */ + if (bdev->rds_info.radio_text[i] == BCM2048_RDS_BLOCK_A) { + + pi = ((bdev->rds_info.radio_text[i+1] << 8) + + bdev->rds_info.radio_text[i+2]); + + if (!bdev->rds_info.rds_pi) { + bdev->rds_info.rds_pi = pi; + return; + } + if (pi != bdev->rds_info.rds_pi) { + cnt++; + if (cnt > 3) { + bdev->rds_info.rds_pi = pi; + cnt = 0; + } + } else { + cnt = 0; + } + } + } +} + +static int bcm2048_rds_block_crc(struct bcm2048_device *bdev, int i) +{ + return bdev->rds_info.radio_text[i] & BCM2048_RDS_CRC_MASK; +} + +static void bcm2048_parse_rds_rt_block(struct bcm2048_device *bdev, int i, + int index, int crc) +{ + /* Good data will overwrite poor data */ + if (crc) { + if (!bdev->rds_info.rds_rt[index]) + bdev->rds_info.rds_rt[index] = + bdev->rds_info.radio_text[i+1]; + if (!bdev->rds_info.rds_rt[index+1]) + bdev->rds_info.rds_rt[index+1] = + bdev->rds_info.radio_text[i+2]; + } else { + bdev->rds_info.rds_rt[index] = bdev->rds_info.radio_text[i+1]; + bdev->rds_info.rds_rt[index+1] = + bdev->rds_info.radio_text[i+2]; + } +} + +static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i) +{ + int crc, rt_id, rt_group_b, rt_ab, index = 0; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return -EIO; + + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_B) { + + rt_id = (bdev->rds_info.radio_text[i+1] & + BCM2048_RDS_BLOCK_MASK); + rt_group_b = bdev->rds_info.radio_text[i+1] & + BCM2048_RDS_GROUP_AB_MASK; + rt_ab = bdev->rds_info.radio_text[i+2] & + BCM2048_RDS_RT_AB_MASK; + + if (rt_group_b != bdev->rds_info.rds_rt_group_b) { + memset(bdev->rds_info.rds_rt, 0, + sizeof(bdev->rds_info.rds_rt)); + bdev->rds_info.rds_rt_group_b = rt_group_b; + } + + if (rt_id == BCM2048_RDS_RT) { + /* A to B or (vice versa), means: clear screen */ + if (rt_ab != bdev->rds_info.rds_rt_ab) { + memset(bdev->rds_info.rds_rt, 0, + sizeof(bdev->rds_info.rds_rt)); + bdev->rds_info.rds_rt_ab = rt_ab; + } + + index = bdev->rds_info.radio_text[i+2] & + BCM2048_RDS_RT_INDEX; + + if (bdev->rds_info.rds_rt_group_b) + index <<= 1; + else + index <<= 2; + + return index; + } + } + + return -EIO; +} + +static int bcm2048_parse_rt_match_c(struct bcm2048_device *bdev, int i, + int index) +{ + int crc; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return 0; + + BUG_ON((index+2) >= BCM2048_MAX_RDS_RT); + + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_C) { + if (bdev->rds_info.rds_rt_group_b) + return 1; + bcm2048_parse_rds_rt_block(bdev, i, index, crc); + return 1; + } + + return 0; +} + +static void bcm2048_parse_rt_match_d(struct bcm2048_device *bdev, int i, + int index) +{ + int crc; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return; + + BUG_ON((index+4) >= BCM2048_MAX_RDS_RT); + + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_D) + bcm2048_parse_rds_rt_block(bdev, i, index+2, crc); +} + +static int bcm2048_parse_rds_rt(struct bcm2048_device *bdev) +{ + int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0; + + for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { + + if (match_b) { + match_b = 0; + index = bcm2048_parse_rt_match_b(bdev, i); + if (index >= 0 && index <= (BCM2048_MAX_RDS_RT - 5)) + match_c = 1; + continue; + } else if (match_c) { + match_c = 0; + if (bcm2048_parse_rt_match_c(bdev, i, index)) + match_d = 1; + continue; + } else if (match_d) { + match_d = 0; + bcm2048_parse_rt_match_d(bdev, i, index); + continue; + } + + /* Skip erroneous blocks due to messed up A block altogether */ + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) + == BCM2048_RDS_BLOCK_A) { + crc = bcm2048_rds_block_crc(bdev, i); + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + continue; + /* Syncronize to a good RDS PI */ + if (((bdev->rds_info.radio_text[i+1] << 8) + + bdev->rds_info.radio_text[i+2]) == + bdev->rds_info.rds_pi) + match_b = 1; + } + } + + return 0; +} + +static void bcm2048_parse_rds_ps_block(struct bcm2048_device *bdev, int i, + int index, int crc) +{ + /* Good data will overwrite poor data */ + if (crc) { + if (!bdev->rds_info.rds_ps[index]) + bdev->rds_info.rds_ps[index] = + bdev->rds_info.radio_text[i+1]; + if (!bdev->rds_info.rds_ps[index+1]) + bdev->rds_info.rds_ps[index+1] = + bdev->rds_info.radio_text[i+2]; + } else { + bdev->rds_info.rds_ps[index] = bdev->rds_info.radio_text[i+1]; + bdev->rds_info.rds_ps[index+1] = + bdev->rds_info.radio_text[i+2]; + } +} + +static int bcm2048_parse_ps_match_c(struct bcm2048_device *bdev, int i, + int index) +{ + int crc; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return 0; + + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_C) + return 1; + + return 0; +} + +static void bcm2048_parse_ps_match_d(struct bcm2048_device *bdev, int i, + int index) +{ + int crc; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return; + + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_D) + bcm2048_parse_rds_ps_block(bdev, i, index, crc); +} + +static int bcm2048_parse_ps_match_b(struct bcm2048_device *bdev, int i) +{ + int crc, index, ps_id, ps_group; + + crc = bcm2048_rds_block_crc(bdev, i); + + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + return -EIO; + + /* Block B Radio PS match */ + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == + BCM2048_RDS_BLOCK_B) { + ps_id = bdev->rds_info.radio_text[i+1] & + BCM2048_RDS_BLOCK_MASK; + ps_group = bdev->rds_info.radio_text[i+1] & + BCM2048_RDS_GROUP_AB_MASK; + + /* + * Poor RSSI will lead to RDS data corruption + * So using 3 (same) sequential values to justify major changes + */ + if (ps_group != bdev->rds_info.rds_ps_group) { + if (crc == BCM2048_RDS_CRC_NONE) { + bdev->rds_info.rds_ps_group_cnt++; + if (bdev->rds_info.rds_ps_group_cnt > 2) { + bdev->rds_info.rds_ps_group = ps_group; + bdev->rds_info.rds_ps_group_cnt = 0; + dev_err(&bdev->client->dev, + "RDS PS Group change!\n"); + } else { + return -EIO; + } + } else { + bdev->rds_info.rds_ps_group_cnt = 0; + } + } + + if (ps_id == BCM2048_RDS_PS) { + index = bdev->rds_info.radio_text[i+2] & + BCM2048_RDS_PS_INDEX; + index <<= 1; + return index; + } + } + + return -EIO; +} + +static void bcm2048_parse_rds_ps(struct bcm2048_device *bdev) +{ + int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0; + + for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { + + if (match_b) { + match_b = 0; + index = bcm2048_parse_ps_match_b(bdev, i); + if (index >= 0 && index < (BCM2048_MAX_RDS_PS - 1)) + match_c = 1; + continue; + } else if (match_c) { + match_c = 0; + if (bcm2048_parse_ps_match_c(bdev, i, index)) + match_d = 1; + continue; + } else if (match_d) { + match_d = 0; + bcm2048_parse_ps_match_d(bdev, i, index); + continue; + } + + /* Skip erroneous blocks due to messed up A block altogether */ + if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) + == BCM2048_RDS_BLOCK_A) { + crc = bcm2048_rds_block_crc(bdev, i); + if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) + continue; + /* Syncronize to a good RDS PI */ + if (((bdev->rds_info.radio_text[i+1] << 8) + + bdev->rds_info.radio_text[i+2]) == + bdev->rds_info.rds_pi) + match_b = 1; + } + } +} + +static void bcm2048_rds_fifo_receive(struct bcm2048_device *bdev) +{ + int err; + + mutex_lock(&bdev->mutex); + + err = bcm2048_recv_duples(bdev, BCM2048_I2C_RDS_DATA, + bdev->rds_info.radio_text, bdev->fifo_size); + if (err != 2) { + dev_err(&bdev->client->dev, "RDS Read problem\n"); + return; + } + + bdev->rds_info.text_len = bdev->fifo_size; + + bcm2048_parse_rds_pi(bdev); + bcm2048_parse_rds_rt(bdev); + bcm2048_parse_rds_ps(bdev); + + mutex_unlock(&bdev->mutex); + + wake_up_interruptible(&bdev->read_queue); +} + +static int bcm2048_get_rds_data(struct bcm2048_device *bdev, char *data) +{ + int err = 0, i, p = 0; + char *data_buffer; + + mutex_lock(&bdev->mutex); + + if (!bdev->rds_info.text_len) { + err = -EINVAL; + goto unlock; + } + + data_buffer = kzalloc(BCM2048_MAX_RDS_RADIO_TEXT*5, GFP_KERNEL); + if (!data_buffer) { + err = -ENOMEM; + goto unlock; + } + + for (i = 0; i < bdev->rds_info.text_len; i++) { + p += sprintf(data_buffer+p, "%x ", + bdev->rds_info.radio_text[i]); + } + + memcpy(data, data_buffer, p); + kfree(data_buffer); + +unlock: + mutex_unlock(&bdev->mutex); + return err; +} + +/* + * BCM2048 default initialization sequence + */ +static int bcm2048_init(struct bcm2048_device *bdev) +{ + int err; + + err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON); + if (err < 0) + goto exit; + + err = bcm2048_set_audio_route(bdev, BCM2048_AUDIO_ROUTE_DAC); + if (err < 0) + goto exit; + + err = bcm2048_set_dac_output(bdev, BCM2048_DAC_OUTPUT_LEFT | + BCM2048_DAC_OUTPUT_RIGHT); + +exit: + return err; +} + +/* + * BCM2048 default deinitialization sequence + */ +static int bcm2048_deinit(struct bcm2048_device *bdev) +{ + int err; + + err = bcm2048_set_audio_route(bdev, 0); + if (err < 0) + goto exit; + + err = bcm2048_set_dac_output(bdev, 0); + if (err < 0) + goto exit; + + err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); + if (err < 0) + goto exit; + +exit: + return err; +} + +/* + * BCM2048 probe sequence + */ +static int bcm2048_probe(struct bcm2048_device *bdev) +{ + int err; + + err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON); + if (err < 0) + goto unlock; + + err = bcm2048_checkrev(bdev); + if (err < 0) + goto unlock; + + err = bcm2048_set_mute(bdev, BCM2048_DEFAULT_MUTE); + if (err < 0) + goto unlock; + + err = bcm2048_set_region(bdev, BCM2048_DEFAULT_REGION); + if (err < 0) + goto unlock; + + err = bcm2048_set_fm_search_rssi_threshold(bdev, + BCM2048_DEFAULT_RSSI_THRESHOLD); + if (err < 0) + goto unlock; + + err = bcm2048_set_fm_automatic_stereo_mono(bdev, BCM2048_ITEM_ENABLED); + if (err < 0) + goto unlock; + + err = bcm2048_get_rds_wline(bdev); + if (err < BCM2048_DEFAULT_RDS_WLINE) + err = bcm2048_set_rds_wline(bdev, BCM2048_DEFAULT_RDS_WLINE); + if (err < 0) + goto unlock; + + err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); + + init_waitqueue_head(&bdev->read_queue); + bdev->rds_data_available = 0; + bdev->rd_index = 0; + bdev->users = 0; + +unlock: + return err; +} + +/* + * BCM2048 workqueue handler + */ +static void bcm2048_work(struct work_struct *work) +{ + struct bcm2048_device *bdev; + u8 flag_lsb, flag_msb, flags; + + bdev = container_of(work, struct bcm2048_device, work); + bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &flag_lsb); + bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &flag_msb); + + if (flag_lsb & (BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED | + BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)) { + + if (flag_lsb & BCM2048_FM_FLAG_SEARCH_TUNE_FAIL) + bdev->scan_state = BCM2048_SCAN_FAIL; + else + bdev->scan_state = BCM2048_SCAN_OK; + + complete(&bdev->compl); + } + + if (flag_msb & BCM2048_RDS_FLAG_FIFO_WLINE) { + bcm2048_rds_fifo_receive(bdev); + if (bdev->rds_state) { + flags = BCM2048_RDS_FLAG_FIFO_WLINE; + bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, + flags); + } + bdev->rds_data_available = 1; + bdev->rd_index = 0; /* new data, new start */ + } +} + +/* + * BCM2048 interrupt handler + */ +static irqreturn_t bcm2048_handler(int irq, void *dev) +{ + struct bcm2048_device *bdev = dev; + + dev_dbg(&bdev->client->dev, "IRQ called, queuing work\n"); + if (bdev->power_state) + schedule_work(&bdev->work); + + return IRQ_HANDLED; +} + +/* + * BCM2048 sysfs interface definitions + */ +#define property_write(prop, type, mask, check) \ +static ssize_t bcm2048_##prop##_write(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) \ +{ \ + struct bcm2048_device *bdev = dev_get_drvdata(dev); \ + type value; \ + int err; \ + \ + if (!bdev) \ + return -ENODEV; \ + \ + sscanf(buf, mask, &value); \ + \ + if (check) \ + return -EDOM; \ + \ + err = bcm2048_set_##prop(bdev, value); \ + \ + return err < 0 ? err : count; \ +} + +#define property_read(prop, size, mask) \ +static ssize_t bcm2048_##prop##_read(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct bcm2048_device *bdev = dev_get_drvdata(dev); \ + size value; \ + \ + if (!bdev) \ + return -ENODEV; \ + \ + value = bcm2048_get_##prop(bdev); \ + \ + if (value >= 0) \ + value = sprintf(buf, mask "\n", value); \ + \ + return value; \ +} + +#define property_signed_read(prop, size, mask) \ +static ssize_t bcm2048_##prop##_read(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct bcm2048_device *bdev = dev_get_drvdata(dev); \ + size value; \ + \ + if (!bdev) \ + return -ENODEV; \ + \ + value = bcm2048_get_##prop(bdev); \ + \ + value = sprintf(buf, mask "\n", value); \ + \ + return value; \ +} + +#define DEFINE_SYSFS_PROPERTY(prop, signal, size, mask, check) \ +property_write(prop, signal size, mask, check) \ +property_read(prop, size, mask) + +#define property_str_read(prop, size) \ +static ssize_t bcm2048_##prop##_read(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct bcm2048_device *bdev = dev_get_drvdata(dev); \ + int count; \ + u8 *out; \ + \ + if (!bdev) \ + return -ENODEV; \ + \ + out = kzalloc(size + 1, GFP_KERNEL); \ + if (!out) \ + return -ENOMEM; \ + \ + bcm2048_get_##prop(bdev, out); \ + count = sprintf(buf, "%s\n", out); \ + \ + kfree(out); \ + \ + return count; \ +} + +DEFINE_SYSFS_PROPERTY(power_state, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(mute, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(audio_route, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(dac_output, unsigned, int, "%u", 0) + +DEFINE_SYSFS_PROPERTY(fm_hi_lo_injection, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_frequency, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_af_frequency, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_deemphasis, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_rds_mask, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_best_tune_mode, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_search_rssi_threshold, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_search_mode_direction, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(fm_search_tune_mode, unsigned, int, "%u", value > 3) + +DEFINE_SYSFS_PROPERTY(rds, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(rds_b_block_mask, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(rds_b_block_match, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(rds_pi_mask, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(rds_pi_match, unsigned, int, "%u", 0) +DEFINE_SYSFS_PROPERTY(rds_wline, unsigned, int, "%u", 0) +property_read(rds_pi, unsigned int, "%x") +property_str_read(rds_rt, (BCM2048_MAX_RDS_RT + 1)) +property_str_read(rds_ps, (BCM2048_MAX_RDS_PS + 1)) + +property_read(fm_rds_flags, unsigned int, "%u") +property_str_read(rds_data, BCM2048_MAX_RDS_RADIO_TEXT*5) + +property_read(region_bottom_frequency, unsigned int, "%u") +property_read(region_top_frequency, unsigned int, "%u") +property_signed_read(fm_carrier_error, int, "%d") +property_signed_read(fm_rssi, int, "%d") +DEFINE_SYSFS_PROPERTY(region, unsigned, int, "%u", 0) + +static struct device_attribute attrs[] = { + __ATTR(power_state, S_IRUGO | S_IWUSR, bcm2048_power_state_read, + bcm2048_power_state_write), + __ATTR(mute, S_IRUGO | S_IWUSR, bcm2048_mute_read, + bcm2048_mute_write), + __ATTR(audio_route, S_IRUGO | S_IWUSR, bcm2048_audio_route_read, + bcm2048_audio_route_write), + __ATTR(dac_output, S_IRUGO | S_IWUSR, bcm2048_dac_output_read, + bcm2048_dac_output_write), + __ATTR(fm_hi_lo_injection, S_IRUGO | S_IWUSR, + bcm2048_fm_hi_lo_injection_read, + bcm2048_fm_hi_lo_injection_write), + __ATTR(fm_frequency, S_IRUGO | S_IWUSR, bcm2048_fm_frequency_read, + bcm2048_fm_frequency_write), + __ATTR(fm_af_frequency, S_IRUGO | S_IWUSR, + bcm2048_fm_af_frequency_read, + bcm2048_fm_af_frequency_write), + __ATTR(fm_deemphasis, S_IRUGO | S_IWUSR, bcm2048_fm_deemphasis_read, + bcm2048_fm_deemphasis_write), + __ATTR(fm_rds_mask, S_IRUGO | S_IWUSR, bcm2048_fm_rds_mask_read, + bcm2048_fm_rds_mask_write), + __ATTR(fm_best_tune_mode, S_IRUGO | S_IWUSR, + bcm2048_fm_best_tune_mode_read, + bcm2048_fm_best_tune_mode_write), + __ATTR(fm_search_rssi_threshold, S_IRUGO | S_IWUSR, + bcm2048_fm_search_rssi_threshold_read, + bcm2048_fm_search_rssi_threshold_write), + __ATTR(fm_search_mode_direction, S_IRUGO | S_IWUSR, + bcm2048_fm_search_mode_direction_read, + bcm2048_fm_search_mode_direction_write), + __ATTR(fm_search_tune_mode, S_IRUGO | S_IWUSR, + bcm2048_fm_search_tune_mode_read, + bcm2048_fm_search_tune_mode_write), + __ATTR(rds, S_IRUGO | S_IWUSR, bcm2048_rds_read, + bcm2048_rds_write), + __ATTR(rds_b_block_mask, S_IRUGO | S_IWUSR, + bcm2048_rds_b_block_mask_read, + bcm2048_rds_b_block_mask_write), + __ATTR(rds_b_block_match, S_IRUGO | S_IWUSR, + bcm2048_rds_b_block_match_read, + bcm2048_rds_b_block_match_write), + __ATTR(rds_pi_mask, S_IRUGO | S_IWUSR, bcm2048_rds_pi_mask_read, + bcm2048_rds_pi_mask_write), + __ATTR(rds_pi_match, S_IRUGO | S_IWUSR, bcm2048_rds_pi_match_read, + bcm2048_rds_pi_match_write), + __ATTR(rds_wline, S_IRUGO | S_IWUSR, bcm2048_rds_wline_read, + bcm2048_rds_wline_write), + __ATTR(rds_pi, S_IRUGO, bcm2048_rds_pi_read, NULL), + __ATTR(rds_rt, S_IRUGO, bcm2048_rds_rt_read, NULL), + __ATTR(rds_ps, S_IRUGO, bcm2048_rds_ps_read, NULL), + __ATTR(fm_rds_flags, S_IRUGO, bcm2048_fm_rds_flags_read, NULL), + __ATTR(region_bottom_frequency, S_IRUGO, + bcm2048_region_bottom_frequency_read, NULL), + __ATTR(region_top_frequency, S_IRUGO, + bcm2048_region_top_frequency_read, NULL), + __ATTR(fm_carrier_error, S_IRUGO, + bcm2048_fm_carrier_error_read, NULL), + __ATTR(fm_rssi, S_IRUGO, + bcm2048_fm_rssi_read, NULL), + __ATTR(region, S_IRUGO | S_IWUSR, bcm2048_region_read, + bcm2048_region_write), + __ATTR(rds_data, S_IRUGO, bcm2048_rds_data_read, NULL), +}; + +static int bcm2048_sysfs_unregister_properties(struct bcm2048_device *bdev, + int size) +{ + int i; + + for (i = 0; i < size; i++) + device_remove_file(&bdev->client->dev, &attrs[i]); + + return 0; +} + +static int bcm2048_sysfs_register_properties(struct bcm2048_device *bdev) +{ + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(attrs); i++) { + if (device_create_file(&bdev->client->dev, &attrs[i]) != 0) { + dev_err(&bdev->client->dev, + "could not register sysfs entry\n"); + err = -EBUSY; + bcm2048_sysfs_unregister_properties(bdev, i); + break; + } + } + + return err; +} + + +static int bcm2048_fops_open(struct file *file) +{ + struct bcm2048_device *bdev = video_drvdata(file); + + bdev->users++; + bdev->rd_index = 0; + bdev->rds_data_available = 0; + + return 0; +} + +static int bcm2048_fops_release(struct file *file) +{ + struct bcm2048_device *bdev = video_drvdata(file); + + bdev->users--; + + return 0; +} + +static unsigned int bcm2048_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct bcm2048_device *bdev = video_drvdata(file); + int retval = 0; + + poll_wait(file, &bdev->read_queue, pts); + + if (bdev->rds_data_available) + retval = POLLIN | POLLRDNORM; + + return retval; +} + +static ssize_t bcm2048_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct bcm2048_device *bdev = video_drvdata(file); + int i; + int retval = 0; + + /* we return at least 3 bytes, one block */ + count = (count / 3) * 3; /* only multiples of 3 */ + if (count < 3) + return -ENOBUFS; + + while (!bdev->rds_data_available) { + if (file->f_flags & O_NONBLOCK) { + retval = -EWOULDBLOCK; + goto done; + } + /* interruptible_sleep_on(&bdev->read_queue); */ + if (wait_event_interruptible(bdev->read_queue, + bdev->rds_data_available) < 0) { + retval = -EINTR; + goto done; + } + } + + mutex_lock(&bdev->mutex); + /* copy data to userspace */ + i = bdev->fifo_size - bdev->rd_index; + if (count > i) + count = (i / 3) * 3; + + i = 0; + while (i < count) { + unsigned char tmpbuf[3]; + tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2]; + tmpbuf[i+1] = bdev->rds_info.radio_text[bdev->rd_index+i+1]; + tmpbuf[i+2] = ((bdev->rds_info.radio_text[bdev->rd_index+i] + & 0xf0) >> 4); + if ((bdev->rds_info.radio_text[bdev->rd_index+i] & + BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE) + tmpbuf[i+2] |= 0x80; + if (copy_to_user(buf+i, tmpbuf, 3)) { + retval = -EFAULT; + break; + }; + i += 3; + } + + bdev->rd_index += i; + if (bdev->rd_index >= bdev->fifo_size) + bdev->rds_data_available = 0; + + mutex_unlock(&bdev->mutex); + if (retval == 0) + retval = i; + +done: + return retval; +} + +/* + * bcm2048_fops - file operations interface + */ +static const struct v4l2_file_operations bcm2048_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + /* for RDS read support */ + .open = bcm2048_fops_open, + .release = bcm2048_fops_release, + .read = bcm2048_fops_read, + .poll = bcm2048_fops_poll +}; + +/* + * Video4Linux Interface + */ +static struct v4l2_queryctrl bcm2048_v4l2_queryctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BALANCE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BASS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_TREBLE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_AUDIO_LOUDNESS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, +}; + +static int bcm2048_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + + strlcpy(capability->driver, BCM2048_DRIVER_NAME, + sizeof(capability->driver)); + strlcpy(capability->card, BCM2048_DRIVER_CARD, + sizeof(capability->card)); + snprintf(capability->bus_info, 32, "I2C: 0x%X", bdev->client->addr); + capability->version = BCM2048_DRIVER_VERSION; + capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_HW_FREQ_SEEK; + + return 0; +} + +static int bcm2048_vidioc_g_input(struct file *filp, void *priv, + unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int bcm2048_vidioc_s_input(struct file *filp, void *priv, + unsigned int i) +{ + if (i) + return -EINVAL; + + return 0; +} + +static int bcm2048_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bcm2048_v4l2_queryctrl); i++) { + if (qc->id && qc->id == bcm2048_v4l2_queryctrl[i].id) { + memcpy(qc, &(bcm2048_v4l2_queryctrl[i]), sizeof(*qc)); + return 0; + } + } + + return -EINVAL; +} + +static int bcm2048_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + int err = 0; + + if (!bdev) + return -ENODEV; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + err = bcm2048_get_mute(bdev); + if (err >= 0) + ctrl->value = err; + break; + } + + return err; +} + +static int bcm2048_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + int err = 0; + + if (!bdev) + return -ENODEV; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + if (bdev->power_state) { + err = bcm2048_set_mute(bdev, ctrl->value); + err |= bcm2048_deinit(bdev); + } + } else { + if (!bdev->power_state) { + err = bcm2048_init(bdev); + err |= bcm2048_set_mute(bdev, ctrl->value); + } + } + break; + } + + return err; +} + +static int bcm2048_vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index > 1) + return -EINVAL; + + strncpy(audio->name, "Radio", 32); + audio->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int bcm2048_vidioc_s_audio(struct file *file, void *priv, + const struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + + return 0; +} + +static int bcm2048_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + s8 f_error; + s8 rssi; + + if (!bdev) + return -ENODEV; + + if (tuner->index > 0) + return -EINVAL; + + strncpy(tuner->name, "FM Receiver", 32); + tuner->type = V4L2_TUNER_RADIO; + tuner->rangelow = + dev_to_v4l2(bcm2048_get_region_bottom_frequency(bdev)); + tuner->rangehigh = + dev_to_v4l2(bcm2048_get_region_top_frequency(bdev)); + tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; + tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; + tuner->audmode = V4L2_TUNER_MODE_STEREO; + tuner->afc = 0; + if (bdev->power_state) { + /* + * Report frequencies with high carrier errors to have zero + * signal level + */ + f_error = bcm2048_get_fm_carrier_error(bdev); + if (f_error < BCM2048_FREQ_ERROR_FLOOR || + f_error > BCM2048_FREQ_ERROR_ROOF) { + tuner->signal = 0; + } else { + /* + * RSSI level -60 dB is defined to report full + * signal strenght + */ + rssi = bcm2048_get_fm_rssi(bdev); + if (rssi >= BCM2048_RSSI_LEVEL_BASE) { + tuner->signal = 0xFFFF; + } else if (rssi > BCM2048_RSSI_LEVEL_ROOF) { + tuner->signal = (rssi + + BCM2048_RSSI_LEVEL_ROOF_NEG) + * BCM2048_SIGNAL_MULTIPLIER; + } else { + tuner->signal = 0; + } + } + } else { + tuner->signal = 0; + } + + return 0; +} + +static int bcm2048_vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *tuner) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + + if (!bdev) + return -ENODEV; + + if (tuner->index > 0) + return -EINVAL; + + return 0; +} + +static int bcm2048_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + int err = 0; + int f; + + if (!bdev->power_state) + return -ENODEV; + + freq->type = V4L2_TUNER_RADIO; + f = bcm2048_get_fm_frequency(bdev); + + if (f < 0) + err = f; + else + freq->frequency = dev_to_v4l2(f); + + return err; +} + +static int bcm2048_vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *freq) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + int err; + + if (freq->type != V4L2_TUNER_RADIO) + return -EINVAL; + + if (!bdev->power_state) + return -ENODEV; + + err = bcm2048_set_fm_frequency(bdev, v4l2_to_dev(freq->frequency)); + err |= bcm2048_set_fm_search_tune_mode(bdev, BCM2048_FM_PRE_SET_MODE); + + return err; +} + +static int bcm2048_vidioc_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *seek) +{ + struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); + int err; + + if (!bdev->power_state) + return -ENODEV; + + if ((seek->tuner != 0) || (seek->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + err = bcm2048_set_fm_search_mode_direction(bdev, seek->seek_upward); + err |= bcm2048_set_fm_search_tune_mode(bdev, + BCM2048_FM_AUTO_SEARCH_MODE); + + return err; +} + +static struct v4l2_ioctl_ops bcm2048_ioctl_ops = { + .vidioc_querycap = bcm2048_vidioc_querycap, + .vidioc_g_input = bcm2048_vidioc_g_input, + .vidioc_s_input = bcm2048_vidioc_s_input, + .vidioc_queryctrl = bcm2048_vidioc_queryctrl, + .vidioc_g_ctrl = bcm2048_vidioc_g_ctrl, + .vidioc_s_ctrl = bcm2048_vidioc_s_ctrl, + .vidioc_g_audio = bcm2048_vidioc_g_audio, + .vidioc_s_audio = bcm2048_vidioc_s_audio, + .vidioc_g_tuner = bcm2048_vidioc_g_tuner, + .vidioc_s_tuner = bcm2048_vidioc_s_tuner, + .vidioc_g_frequency = bcm2048_vidioc_g_frequency, + .vidioc_s_frequency = bcm2048_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = bcm2048_vidioc_s_hw_freq_seek, +}; + +/* + * bcm2048_viddev_template - video device interface + */ +static struct video_device bcm2048_viddev_template = { + .fops = &bcm2048_fops, + .name = BCM2048_DRIVER_NAME, + .release = video_device_release, + .ioctl_ops = &bcm2048_ioctl_ops, +}; + +/* + * I2C driver interface + */ +static int bcm2048_i2c_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bcm2048_device *bdev; + int err, skip_release = 0; + + bdev = kzalloc(sizeof(*bdev), GFP_KERNEL); + if (!bdev) { + dev_dbg(&client->dev, "Failed to alloc video device.\n"); + err = -ENOMEM; + goto exit; + } + + bdev->videodev = video_device_alloc(); + if (!bdev->videodev) { + dev_dbg(&client->dev, "Failed to alloc video device.\n"); + err = -ENOMEM; + goto free_bdev; + } + + bdev->client = client; + i2c_set_clientdata(client, bdev); + mutex_init(&bdev->mutex); + init_completion(&bdev->compl); + INIT_WORK(&bdev->work, bcm2048_work); + + if (client->irq) { + err = request_irq(client->irq, + bcm2048_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->name, bdev); + if (err < 0) { + dev_err(&client->dev, "Could not request IRQ\n"); + goto free_vdev; + } + dev_dbg(&client->dev, "IRQ requested.\n"); + } else { + dev_dbg(&client->dev, "IRQ not configured. Using timeouts.\n"); + } + + memcpy(bdev->videodev, &bcm2048_viddev_template, + sizeof(bcm2048_viddev_template)); + video_set_drvdata(bdev->videodev, bdev); + if (video_register_device(bdev->videodev, VFL_TYPE_RADIO, radio_nr)) { + dev_dbg(&client->dev, "Could not register video device.\n"); + err = -EIO; + goto free_irq; + } + + err = bcm2048_sysfs_register_properties(bdev); + if (err < 0) { + dev_dbg(&client->dev, "Could not register sysfs interface.\n"); + goto free_registration; + } + + err = bcm2048_probe(bdev); + if (err < 0) { + dev_dbg(&client->dev, "Failed to probe device information.\n"); + goto free_sysfs; + } + + return 0; + +free_sysfs: + bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs)); +free_registration: + video_unregister_device(bdev->videodev); + /* video_unregister_device frees bdev->videodev */ + bdev->videodev = NULL; + skip_release = 1; +free_irq: + if (client->irq) + free_irq(client->irq, bdev); +free_vdev: + if (!skip_release) + video_device_release(bdev->videodev); + i2c_set_clientdata(client, NULL); +free_bdev: + kfree(bdev); +exit: + return err; +} + +static int __exit bcm2048_i2c_driver_remove(struct i2c_client *client) +{ + struct bcm2048_device *bdev = i2c_get_clientdata(client); + struct video_device *vd; + + if (!client->adapter) + return -ENODEV; + + if (bdev) { + vd = bdev->videodev; + + bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs)); + + if (vd) + video_unregister_device(vd); + + if (bdev->power_state) + bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); + + if (client->irq > 0) + free_irq(client->irq, bdev); + + cancel_work_sync(&bdev->work); + + kfree(bdev); + } + + i2c_set_clientdata(client, NULL); + + return 0; +} + +/* + * bcm2048_i2c_driver - i2c driver interface + */ +static const struct i2c_device_id bcm2048_id[] = { + { "bcm2048" , 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, bcm2048_id); + +static struct i2c_driver bcm2048_i2c_driver = { + .driver = { + .name = BCM2048_DRIVER_NAME, + }, + .probe = bcm2048_i2c_driver_probe, + .remove = __exit_p(bcm2048_i2c_driver_remove), + .id_table = bcm2048_id, +}; + +/* + * Module Interface + */ +static int __init bcm2048_module_init(void) +{ + pr_info(BCM2048_DRIVER_DESC "\n"); + + return i2c_add_driver(&bcm2048_i2c_driver); +} +module_init(bcm2048_module_init); + +static void __exit bcm2048_module_exit(void) +{ + i2c_del_driver(&bcm2048_i2c_driver); +} +module_exit(bcm2048_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(BCM2048_DRIVER_AUTHOR); +MODULE_DESCRIPTION(BCM2048_DRIVER_DESC); +MODULE_VERSION("0.0.2"); diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.h b/drivers/staging/media/bcm2048/radio-bcm2048.h new file mode 100644 index 0000000..4c90a32 --- /dev/null +++ b/drivers/staging/media/bcm2048/radio-bcm2048.h @@ -0,0 +1,30 @@ +/* + * drivers/staging/media/radio-bcm2048.h + * + * Property and command definitions for bcm2048 radio receiver chip. + * + * Copyright (C) Nokia Corporation + * Contact: Eero Nurkkala + * + * 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 BCM2048_H +#define BCM2048_H + +#define BCM2048_NAME "bcm2048" +#define BCM2048_I2C_ADDR 0x22 + +#endif /* ifndef BCM2048_H */ -- cgit v0.10.2 From d44250fd17de6dea2b16e23d2767ab0eff48f4d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Nov 2013 08:37:34 -0300 Subject: [media] bcm2048: add TODO file for this staging driver Describe the tasks to be done for this driver to be promoted to a non-staging one. [m.chehab@samsung.com: Add a patch description; add a note about the CodingStyle issues and make sure that the README lines are not bigger than 80 cols.] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/TODO b/drivers/staging/media/bcm2048/TODO new file mode 100644 index 0000000..051f85d --- /dev/null +++ b/drivers/staging/media/bcm2048/TODO @@ -0,0 +1,24 @@ +TODO: + +From the initial code review: + +The main thing you need to do is to implement all the controls using the +control framework (see Documentation/video4linux/v4l2-controls.txt). +Most drivers are by now converted to the control framework, so you will +find many examples of how to do this in drivers/media/radio. + +The sysfs stuff should be replaced by controls as well. A lot of the RDS +support is now available as controls (although there may well be some +missing features, but that is easy enough to add). Since the RDS data is +actually read() from the device I am not sure whether the RDS +properties/controls should be there at all. + +Correct Coding Style, as this driver also violates several Style +rules, and do evil tricks, like returning from a function inside a +macro. + +Finally this driver should probably be split up into two parts: one +v4l2_subdev-based core driver and one platform driver. See e.g. +radio-si4713/si4713-i2c.c as a good example. But I would wait with that +until the rest of the driver is cleaned up. Then I have a better idea of +whether this is necessary or not. -- cgit v0.10.2 From dc793175c5b532f343fe2224afd9189130da0004 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 6 Nov 2013 11:21:30 -0300 Subject: [media] smiapp: Fix BUG_ON() on an impossible condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit internal_csi_format_idx and csi_format_idx are unsigned integers, therefore they can never be nevative. CC drivers/media/i2c/smiapp/smiapp-core.o In file included from include/linux/err.h:4:0, from include/linux/clk.h:15, from drivers/media/i2c/smiapp/smiapp-core.c:29: drivers/media/i2c/smiapp/smiapp-core.c: In function ‘smiapp_update_mbus_formats’: include/linux/kernel.h:669:20: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] #define min(x, y) ({ \ ^ include/linux/compiler.h:153:42: note: in definition of macro ‘unlikely’ # define unlikely(x) __builtin_expect(!!(x), 0) ^ drivers/media/i2c/smiapp/smiapp-core.c:402:2: note: in expansion of macro ‘BUG_ON’ BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); ^ drivers/media/i2c/smiapp/smiapp-core.c:402:9: note: in expansion of macro ‘min’ BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); ^ Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index ae66d91..fbd48f0 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]); -- cgit v0.10.2 From e1c759bd3daf7a59f0d6a6fa06ca237307da7879 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Fri, 6 Dec 2013 02:43:27 -0300 Subject: [media] media: i2c: lm3560: fix missing unlock on error, fault handling Correct reference of reading values. (rval -> reg_val) Add the missing unlock before return from function lm3560_get_ctrl() to avoid deadlock. Thank you Dan Carpenter & Sakari. Signed-off-by: Daniel Jeong Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 3317a9a..ab5857d 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) -- cgit v0.10.2 From 202dfbdc2b88286463b9e05df4be1c0bce50af4a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 6 Nov 2013 15:48:38 -0300 Subject: [media] videobuf2-dma-sg: Fix typo on debug message num_pages_from_user and buf->num_pages were swapped. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 2f86054..c13bf95 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -200,7 +200,7 @@ 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); + buf->num_pages, num_pages_from_user); while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); kfree(buf->pages); -- cgit v0.10.2 From f956035ce70b8da9928aac1424485c3526fccb11 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 8 Nov 2013 07:08:45 -0300 Subject: [media] vb2: Return 0 when streamon and streamoff are already on/off According to the doc: If VIDIOC_STREAMON is called when streaming is already in progress, or if VIDIOC_STREAMOFF is called when streaming is already stopped, then the ioctl does nothing and 0 is returned. The current implementation was returning -EINVAL instead. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 57ba131..e949a0e 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1697,8 +1697,8 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } if (q->streaming) { - dprintk(1, "streamon: already streaming\n"); - return -EBUSY; + dprintk(3, "streamon successful: already streaming\n"); + return 0; } /* @@ -1754,8 +1754,8 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) } if (!q->streaming) { - dprintk(1, "streamoff: not streaming\n"); - return -EINVAL; + dprintk(3, "streamoff successful: not streaming\n"); + return 0; } /* -- cgit v0.10.2 From 77c0782e9729cee9d1d67cd1343ed7c3f947a36a Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 29 Nov 2013 04:50:29 -0300 Subject: [media] videobuf2: Add log for size checking error in __qbuf_dmabuf __qbuf_dmabuf checks whether size of provided dmabuf is large enough, and it returns error without any log. So this patch adds error log in the case. Signed-off-by: Seung-Woo Kim Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index e949a0e..144caf1 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1116,6 +1116,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; } -- cgit v0.10.2 From 1380f5754cb0cc4b765629b153fc0e7030b86da2 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 25 Nov 2013 05:49:02 -0300 Subject: [media] videobuf2: Add missing lock held on vb2_fop_release vb2_fop_release does not hold the lock although it is modifying the queue->owner field. This could lead to race conditions on the vb2_perform_io function when multiple applications are accessing the video device via read/write API: [ 308.297741] BUG: unable to handle kernel NULL pointer dereference at 0000000000000260 [ 308.297759] IP: [] vb2_perform_fileio+0x372/0x610 [videobuf2_core] [ 308.297794] PGD 159719067 PUD 158119067 PMD 0 [ 308.297812] Oops: 0000 #1 SMP [ 308.297826] Modules linked in: qt5023_video videobuf2_dma_sg qtec_xform videobuf2_vmalloc videobuf2_memops videobuf2_core qtec_white qtec_mem gpio_xilinx qtec_cmosis qtec_pcie fglrx(PO) spi_xilinx spi_bitbang qt5023 [ 308.297888] CPU: 1 PID: 2189 Comm: java Tainted: P O 3.11.0-qtec-standard #1 [ 308.297919] Hardware name: QTechnology QT5022/QT5022, BIOS PM_2.1.0.309 X64 05/23/2013 [ 308.297952] task: ffff8801564e1690 ti: ffff88014dc02000 task.ti: ffff88014dc02000 [ 308.297962] RIP: 0010:[] [] vb2_perform_fileio+0x372/0x610 [videobuf2_core] [ 308.297985] RSP: 0018:ffff88014dc03df8 EFLAGS: 00010202 [ 308.297995] RAX: 0000000000000000 RBX: ffff880158a23000 RCX: dead000000100100 [ 308.298003] RDX: 0000000000000000 RSI: dead000000200200 RDI: 0000000000000000 [ 308.298012] RBP: ffff88014dc03e58 R08: 0000000000000000 R09: 0000000000000001 [ 308.298020] R10: ffffea00051e8380 R11: ffff88014dc03fd8 R12: ffff880158a23070 [ 308.298029] R13: ffff8801549040b8 R14: 0000000000198000 R15: 0000000001887e60 [ 308.298040] FS: 00007f65130d5700(0000) GS:ffff88015ed00000(0000) knlGS:0000000000000000 [ 308.298049] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 308.298057] CR2: 0000000000000260 CR3: 0000000159630000 CR4: 00000000000007e0 [ 308.298064] Stack: [ 308.298071] ffff880156416c00 0000000000198000 0000000000000000 ffff880100000001 [ 308.298087] ffff88014dc03f50 00000000810a79ca 0002000000000001 ffff880154904718 [ 308.298101] ffff880156416c00 0000000000198000 ffff880154904338 ffff88014dc03f50 [ 308.298116] Call Trace: [ 308.298143] [] vb2_read+0x14/0x20 [videobuf2_core] [ 308.298198] [] vb2_fop_read+0xc4/0x120 [videobuf2_core] [ 308.298252] [] v4l2_read+0x7e/0xc0 [ 308.298296] [] vfs_read+0xa9/0x160 [ 308.298312] [] SyS_read+0x52/0xb0 [ 308.298328] [] tracesys+0xd0/0xd5 [ 308.298335] Code: e5 d6 ff ff 83 3d be 24 00 00 04 89 c2 4c 8b 45 b0 44 8b 4d b8 0f 8f 20 02 00 00 85 d2 75 32 83 83 78 03 00 00 01 4b 8b 44 c5 48 <8b> 88 60 02 00 00 85 c9 0f 84 b0 00 00 00 8b 40 58 89 c2 41 89 [ 308.298487] RIP [] vb2_perform_fileio+0x372/0x610 [videobuf2_core] [ 308.298507] RSP [ 308.298514] CR2: 0000000000000260 [ 308.298526] ---[ end trace e8f01717c96d1e41 ]--- Signed-off-by: Ricardo Ribalda Acked-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index fb27ff7..8a712ca 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-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e5798f7..d3b32b6 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); diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 144caf1..f61a79f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2632,16 +2632,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/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 941055e..0ae974e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -491,6 +491,7 @@ int vb2_ioctl_expbuf(struct file *file, void *priv, int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma); int vb2_fop_release(struct file *file); +int _vb2_fop_release(struct file *file, struct mutex *lock); ssize_t vb2_fop_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos); ssize_t vb2_fop_read(struct file *file, char __user *buf, -- cgit v0.10.2 From 50ac952d2263bd5d7812acf1839d57c34c6f8d9f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 26 Nov 2013 09:58:44 -0300 Subject: [media] videobuf2-dma-sg: Support io userptr operations on io memory Memory exported via remap_pfn_range cannot be remapped via get_user_pages. Other videobuf2 methods (like the dma-contig) supports io memory. This patch adds support for this kind of memory. v2: Comments by Marek Szyprowski -Use vb2_get_vma and vb2_put_vma Signed-off-by: Ricardo Ribalda Delgado Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index c13bf95..d86bb8cd 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) return NULL; - 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, @@ -201,8 +239,11 @@ userptr_fail_alloc_table_from_pages: userptr_fail_get_user_pages: dprintk(1, "get_user_pages requested/got: %d/%d]\n", buf->num_pages, num_pages_from_user); - while (--num_pages_from_user >= 0) - put_page(buf->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); kfree(buf); return NULL; @@ -225,9 +266,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); } -- cgit v0.10.2 From 356ba0213dca4dd68906bb315f8c7a46e81510ea Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 9 Dec 2013 14:02:51 -0200 Subject: [media] radio-bcm2048: fix signal of value As value can be initialized with a value lower than zero, change it to int, to avoid those warnings: drivers/staging/media/bcm2048/radio-bcm2048.c: In function 'bcm2048_rds_pi_read': drivers/staging/media/bcm2048/radio-bcm2048.c:1989:9: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] struct bcm2048_device *bdev = dev_get_drvdata(dev); \ ^ drivers/staging/media/bcm2048/radio-bcm2048.c:2070:1: note: in expansion of macro 'property_read' property_read(rds_pi, unsigned int, "%x") ^ drivers/staging/media/bcm2048/radio-bcm2048.c: In function 'bcm2048_fm_rds_flags_read': drivers/staging/media/bcm2048/radio-bcm2048.c:1989:9: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] struct bcm2048_device *bdev = dev_get_drvdata(dev); \ ^ drivers/staging/media/bcm2048/radio-bcm2048.c:2074:1: note: in expansion of macro 'property_read' property_read(fm_rds_flags, unsigned int, "%u") ^ drivers/staging/media/bcm2048/radio-bcm2048.c: In function 'bcm2048_region_bottom_frequency_read': drivers/staging/media/bcm2048/radio-bcm2048.c:1989:9: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] struct bcm2048_device *bdev = dev_get_drvdata(dev); \ ^ drivers/staging/media/bcm2048/radio-bcm2048.c:2077:1: note: in expansion of macro 'property_read' property_read(region_bottom_frequency, unsigned int, "%u") ^ drivers/staging/media/bcm2048/radio-bcm2048.c: In function 'bcm2048_region_top_frequency_read': drivers/staging/media/bcm2048/radio-bcm2048.c:1989:9: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] struct bcm2048_device *bdev = dev_get_drvdata(dev); \ ^ drivers/staging/media/bcm2048/radio-bcm2048.c:2078:1: note: in expansion of macro 'property_read' property_read(region_top_frequency, unsigned int, "%u") Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 782cc11..494ec39 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -1987,7 +1987,7 @@ static ssize_t bcm2048_##prop##_read(struct device *dev, \ char *buf) \ { \ struct bcm2048_device *bdev = dev_get_drvdata(dev); \ - size value; \ + int value; \ \ if (!bdev) \ return -ENODEV; \ -- cgit v0.10.2 From 903cbb83d22a4c22acb48c2ed2580cc0bffaa772 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 00:09:44 -0300 Subject: [media] v4l: ti-vpe: use module_platform_driver to simplify the code module_platform_driver() makes the code simpler by eliminating boilerplate code. Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 4e58069..89658a3 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2081,18 +2081,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, "); -- cgit v0.10.2 From 6676cafe6bb69898cdb94937651249d0999e4b59 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 00:10:45 -0300 Subject: [media] v4l: ti-vpe: fix error return code in vpe_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Reviewed-by: Archit Taneja Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 89658a3..1a31c85 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2007,8 +2007,10 @@ static int vpe_probe(struct platform_device *pdev) vpe_top_vpdma_reset(dev); 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; -- cgit v0.10.2 From b68231a1b5e95a532f4d5135cdbd0fe6b0bfcb3b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 00:15:13 -0300 Subject: [media] v4l: ti-vpe: fix return value check in vpe_probe() In case of error, the function devm_kzalloc() and devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Reviewed-by: Archit Taneja Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 1a31c85..f949ef5 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1942,8 +1942,8 @@ static int vpe_probe(struct platform_device *pdev) 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); @@ -1962,8 +1962,8 @@ static int vpe_probe(struct platform_device *pdev) * 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); + if (!dev->base) { + ret = -ENOMEM; goto v4l2_dev_unreg; } -- cgit v0.10.2 From 431cb350187c6bf1ed083622d633418a298a7216 Mon Sep 17 00:00:00 2001 From: Roland Scheidegger Date: Sat, 2 Nov 2013 16:49:32 -0300 Subject: [media] az6007: support Technisat Cablestar Combo HDCI (minus remote) This is similar to the Terratec H7. It works with the same az6007 firmware as the former, however the drx-k firmware of the H7 will NOT work. Hence use a different firmware name. The firmware does not need to exist as the one in the eeprom is just fine as long as the h7 one doesn't get loaded, but maybe some day someone wants to load it (the one from the h5 would work too). Also since the config entry is now different anyway disable support for rc. AFAIK the Technisat remote (TS35) is RC5 and the code (which a code comment claims doesn't work anyway) only would handle NEC hence it's pointless creating a device and polling it if we already know it can't work. CI is untested. Originally based on idea found on http://www.linuxtv.org/wiki/index.php/TechniSat_CableStar_Combo_HD_CI claiming only id needs to be added (but failed to mention it only worked because the driver couldn't find the h7 drx-k firmware...). Signed-off-by: Roland Scheidegger Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 419a2d6..4a53454 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -365,6 +365,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/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 44c64ef3..c1051c3 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}, }; -- cgit v0.10.2 From 215c65a067ccf5dc22e68f1f159ee83678e63ca9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 26 Nov 2013 22:18:28 -0300 Subject: [media] v4l: sh_vou: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. Cc: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index d7f0249..7f6ea65 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. -- cgit v0.10.2 From fe9a2bd881d96b1491b283483d7346abb52111f9 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 6 Nov 2013 16:25:06 -0300 Subject: [media] af9035: add [0413:6a05] Leadtek WinFast DTV Dongle Dual It is IT9135 dual design. Thanks to Michael Piko for reporting that! Reported-by: Michael Piko Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index c8fcd78..798565c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1534,6 +1534,8 @@ static const struct usb_device_id af9035_id_table[] = { /* XXX: that same ID [0ccd:0099] is used by af9015 driver too */ { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099, &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a05, + &af9035_props, "Leadtek WinFast DTV Dongle Dual", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); -- cgit v0.10.2 From eed8c3eebce72fe4fc766f9a23e4324b04bd86cf Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 7 Nov 2013 09:44:42 -0300 Subject: [media] media: i2c: lm3560: fix missing unlock on error in lm3560_set_ctrl() Add the missing unlock before return from function lm3560_set_ctrl() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index ab5857d..e728d9f 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -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; } -- cgit v0.10.2 From cc58f4db5173f5cd039de4681ec4778b5cd71182 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 8 Nov 2013 21:24:18 -0300 Subject: [media] media: i2c: lm3560: use correct clientdata in lm3560_remove() We had set the i2c clientdata to &flash->subdev_led[LM3560_LED1] after call lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"), but the container_of() in lm3560_remove() return the wrong pointer to flash.(should be container_of(subdev, struct lm3560_flash, subdev_led[LM3560_LED_MAX-1]) This patch fix to set i2c clientdata to flash so we can get flash from clientdata directly. Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index e728d9f..d98ca3a 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -448,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++) { -- cgit v0.10.2 From 51d882edb2fc691bdb0e5c768084c90b6435656f Mon Sep 17 00:00:00 2001 From: Evgeny Plehov Date: Wed, 13 Nov 2013 20:53:59 -0300 Subject: [media] dw2102: Geniatech T220 support Support for Geniatech T220 DVB-T/T2/C USB stick. Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index c1a63b2..241c737 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 @@ -1095,6 +1097,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 +1376,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 }; @@ -1630,6 +1685,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 +1708,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)}, { } }; @@ -2077,6 +2134,54 @@ 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.legacy = { + .rc_map_table = rc_map_su3000_table, + .rc_map_size = ARRAY_SIZE(rc_map_su3000_table), + .rc_interval = 150, + .rc_query = dw2102_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) { @@ -2149,7 +2254,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 +2276,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); -- cgit v0.10.2 From a49de26a0ef565cc466a80b0140c56256e7f3f7b Mon Sep 17 00:00:00 2001 From: Evgeny Plehov Date: Fri, 15 Nov 2013 16:43:33 -0300 Subject: [media] dw2102: Use RC Core instead of the legacy RC (second edition) Use RC Core instead of the legacy RC. DVBWorld, TBS, TeVii, Prof hardware decode only NEC remotes (one byte code). Geniatech hardware decode only RC5 (two bytes). + New keymap for Geniatech HDStar (SU3000). Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index b1cde8c..0b8c549 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 0000000..8dbd3e9 --- /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 + * + * 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 +#include + +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 "); diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 241c737..172a855 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -112,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; }; @@ -131,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); @@ -1459,174 +1448,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, @@ -1634,32 +1478,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; @@ -1768,9 +1614,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, @@ -1834,10 +1678,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, }, @@ -1888,10 +1733,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, }, @@ -1938,10 +1784,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, }, @@ -1986,10 +1833,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, }, @@ -2079,11 +1927,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, @@ -2143,11 +1992,12 @@ static struct dvb_usb_device_properties t220_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, @@ -2193,8 +2043,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, @@ -2219,8 +2069,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; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 6628f5d..a20ed97 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -193,6 +193,7 @@ void rc_map_init(void); #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" #define RC_MAP_WINFAST "rc-winfast" #define RC_MAP_WINFAST_USBII_DELUXE "rc-winfast-usbii-deluxe" +#define RC_MAP_SU3000 "rc-su3000" /* * Please, do not just append newer Remote Controller names at the end. -- cgit v0.10.2 From cd86e3b535eea2e2685f65f4318a8b13e62b11c3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 21 Nov 2013 00:38:44 -0300 Subject: [media] cx88: use correct pci drvdata type in cx88_audio_finidev() We had set the pci drvdata in cx88_audio_initdev() as a type of struct snd_card, so cx88_audio_finidev() should used it as the same type too. Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index 400eb1c..d014206e 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--; } -- cgit v0.10.2 From eda9dfd6752e58c5c075076654007311da6b5268 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Thu, 21 Nov 2013 17:26:42 -0300 Subject: [media] mceusb: Add Hauppauge WinTV-HVR-930C HD Add usb id of Hauppauge WinTV-HVR-930C HD to mceusb RC driver. This device has no IR transmitter (according to eeprom content decoded by tveeprom). Set the rc mapping to Hauppauge, every key of the deliviered remote control works correctly. [m.chehab@samsung.com: fix merge conflicts and unmangled whitespace] Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 3c76101..a25bb15 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 */ { } }; -- cgit v0.10.2 From e351bf25fa373a3de0be2141b962c5c3c27006a2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Nov 2013 04:51:47 -0300 Subject: [media] cx18: check for allocation failure in cx18_read_eeprom() It upsets static checkers when we don't check for allocation failure. I moved the memset() of "tv" earlier so we don't use uninitialized data on error. Fixes: 1d212cf0c2d8 ('[media] cx18: struct i2c_client is too big for stack') Signed-off-by: Dan Carpenter Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index c1f8cc6..716bdc5 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; -- cgit v0.10.2 From 1cdbcc5db4e6d51ce9bb1313195167cada9aa6e9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Nov 2013 04:55:43 -0300 Subject: [media] cxusb: unlock on error in cxusb_i2c_xfer() We recently introduced some new error paths which are missing their unlocks. Fixes: 64f7ef8afbf8 ('[media] cxusb: Don't use dynamic static allocation') Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 20e345d..a1c641e 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) -- cgit v0.10.2 From 324ed533bf0b23c309b805272c4ffcc5d51493a6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Nov 2013 04:56:33 -0300 Subject: [media] dw2102: some missing unlocks on error We recently introduced some new error paths but the unlocks are missing. Fixes: 0065a79a8698 ('[media] dw2102: Don't use dynamic static allocation') Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 172a855..ae0f56a 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -292,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; @@ -307,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; @@ -331,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; @@ -348,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; @@ -377,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; @@ -421,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, @@ -457,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; @@ -472,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) @@ -497,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; @@ -521,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; @@ -547,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[], @@ -557,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; @@ -609,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, @@ -643,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; @@ -662,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); @@ -676,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[], -- cgit v0.10.2 From 2d01237389dc64b37745ce87e64d6808b6eed582 Mon Sep 17 00:00:00 2001 From: Wade Farnsworth Date: Fri, 22 Nov 2013 16:48:28 -0300 Subject: [media] v4l2-dev: Add tracepoints for QBUF and DQBUF Add tracepoints to the QBUF and DQBUF ioctls to enable rudimentary performance measurements using standard kernel tracers. [m.chehab@samsung.com: CodingStyle fixes (whitespacing)] Signed-off-by: Wade Farnsworth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index b5aaaac..1cc1749 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -31,6 +31,10 @@ #include #include + +#define CREATE_TRACE_POINTS +#include + #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -391,6 +395,11 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } else ret = -ENOTTY; + if (cmd == VIDIOC_DQBUF) + trace_v4l2_dqbuf(vdev->minor, (struct v4l2_buffer *)arg); + else if (cmd == VIDIOC_QBUF) + trace_v4l2_qbuf(vdev->minor, (struct v4l2_buffer *)arg); + return ret; } diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h new file mode 100644 index 0000000..ef94eca --- /dev/null +++ b/include/trace/events/v4l2.h @@ -0,0 +1,157 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM v4l2 + +#if !defined(_TRACE_V4L2_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_V4L2_H + +#include + +#define show_type(type) \ + __print_symbolic(type, \ + { V4L2_BUF_TYPE_VIDEO_CAPTURE, "VIDEO_CAPTURE" }, \ + { V4L2_BUF_TYPE_VIDEO_OUTPUT, "VIDEO_OUTPUT" }, \ + { V4L2_BUF_TYPE_VIDEO_OVERLAY, "VIDEO_OVERLAY" }, \ + { V4L2_BUF_TYPE_VBI_CAPTURE, "VBI_CAPTURE" }, \ + { V4L2_BUF_TYPE_VBI_OUTPUT, "VBI_OUTPUT" }, \ + { V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, "SLICED_VBI_CAPTURE" }, \ + { V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, "SLICED_VBI_OUTPUT" }, \ + { V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" },\ + { V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" },\ + { V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, "VIDEO_OUTPUT_MPLANE" }, \ + { V4L2_BUF_TYPE_PRIVATE, "PRIVATE" }) + +#define show_field(field) \ + __print_symbolic(field, \ + { V4L2_FIELD_ANY, "ANY" }, \ + { V4L2_FIELD_NONE, "NONE" }, \ + { V4L2_FIELD_TOP, "TOP" }, \ + { V4L2_FIELD_BOTTOM, "BOTTOM" }, \ + { V4L2_FIELD_INTERLACED, "INTERLACED" }, \ + { V4L2_FIELD_SEQ_TB, "SEQ_TB" }, \ + { V4L2_FIELD_SEQ_BT, "SEQ_BT" }, \ + { V4L2_FIELD_ALTERNATE, "ALTERNATE" }, \ + { V4L2_FIELD_INTERLACED_TB, "INTERLACED_TB" }, \ + { V4L2_FIELD_INTERLACED_BT, "INTERLACED_BT" }) + +#define show_timecode_type(type) \ + __print_symbolic(type, \ + { V4L2_TC_TYPE_24FPS, "24FPS" }, \ + { V4L2_TC_TYPE_25FPS, "25FPS" }, \ + { V4L2_TC_TYPE_30FPS, "30FPS" }, \ + { V4L2_TC_TYPE_50FPS, "50FPS" }, \ + { V4L2_TC_TYPE_60FPS, "60FPS" }) + +#define show_flags(flags) \ + __print_flags(flags, "|", \ + { V4L2_BUF_FLAG_MAPPED, "MAPPED" }, \ + { V4L2_BUF_FLAG_QUEUED, "QUEUED" }, \ + { V4L2_BUF_FLAG_DONE, "DONE" }, \ + { V4L2_BUF_FLAG_KEYFRAME, "KEYFRAME" }, \ + { V4L2_BUF_FLAG_PFRAME, "PFRAME" }, \ + { V4L2_BUF_FLAG_BFRAME, "BFRAME" }, \ + { V4L2_BUF_FLAG_ERROR, "ERROR" }, \ + { V4L2_BUF_FLAG_TIMECODE, "TIMECODE" }, \ + { V4L2_BUF_FLAG_PREPARED, "PREPARED" }, \ + { V4L2_BUF_FLAG_NO_CACHE_INVALIDATE, "NO_CACHE_INVALIDATE" }, \ + { V4L2_BUF_FLAG_NO_CACHE_CLEAN, "NO_CACHE_CLEAN" }, \ + { V4L2_BUF_FLAG_TIMESTAMP_MASK, "TIMESTAMP_MASK" }, \ + { V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN, "TIMESTAMP_UNKNOWN" }, \ + { V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, "TIMESTAMP_MONOTONIC" }, \ + { V4L2_BUF_FLAG_TIMESTAMP_COPY, "TIMESTAMP_COPY" }) + +#define show_timecode_flags(flags) \ + __print_flags(flags, "|", \ + { V4L2_TC_FLAG_DROPFRAME, "DROPFRAME" }, \ + { V4L2_TC_FLAG_COLORFRAME, "COLORFRAME" }, \ + { V4L2_TC_USERBITS_USERDEFINED, "USERBITS_USERDEFINED" }, \ + { V4L2_TC_USERBITS_8BITCHARS, "USERBITS_8BITCHARS" }) + +#define V4L2_TRACE_EVENT(event_name) \ + TRACE_EVENT(event_name, \ + TP_PROTO(int minor, struct v4l2_buffer *buf), \ + \ + TP_ARGS(minor, buf), \ + \ + TP_STRUCT__entry( \ + __field(int, minor) \ + __field(u32, index) \ + __field(u32, type) \ + __field(u32, bytesused) \ + __field(u32, flags) \ + __field(u32, field) \ + __field(s64, timestamp) \ + __field(u32, timecode_type) \ + __field(u32, timecode_flags) \ + __field(u8, timecode_frames) \ + __field(u8, timecode_seconds) \ + __field(u8, timecode_minutes) \ + __field(u8, timecode_hours) \ + __field(u8, timecode_userbits0) \ + __field(u8, timecode_userbits1) \ + __field(u8, timecode_userbits2) \ + __field(u8, timecode_userbits3) \ + __field(u32, sequence) \ + ), \ + \ + TP_fast_assign( \ + __entry->minor = minor; \ + __entry->index = buf->index; \ + __entry->type = buf->type; \ + __entry->bytesused = buf->bytesused; \ + __entry->flags = buf->flags; \ + __entry->field = buf->field; \ + __entry->timestamp = \ + timeval_to_ns(&buf->timestamp); \ + __entry->timecode_type = buf->timecode.type; \ + __entry->timecode_flags = buf->timecode.flags; \ + __entry->timecode_frames = \ + buf->timecode.frames; \ + __entry->timecode_seconds = \ + buf->timecode.seconds; \ + __entry->timecode_minutes = \ + buf->timecode.minutes; \ + __entry->timecode_hours = buf->timecode.hours; \ + __entry->timecode_userbits0 = \ + buf->timecode.userbits[0]; \ + __entry->timecode_userbits1 = \ + buf->timecode.userbits[1]; \ + __entry->timecode_userbits2 = \ + buf->timecode.userbits[2]; \ + __entry->timecode_userbits3 = \ + buf->timecode.userbits[3]; \ + __entry->sequence = buf->sequence; \ + ), \ + \ + TP_printk("minor = %d, index = %u, type = %s, " \ + "bytesused = %u, flags = %s, " \ + "field = %s, timestamp = %llu, timecode = { " \ + "type = %s, flags = %s, frames = %u, " \ + "seconds = %u, minutes = %u, hours = %u, " \ + "userbits = { %u %u %u %u } }, " \ + "sequence = %u", __entry->minor, \ + __entry->index, show_type(__entry->type), \ + __entry->bytesused, \ + show_flags(__entry->flags), \ + show_field(__entry->field), \ + __entry->timestamp, \ + show_timecode_type(__entry->timecode_type), \ + show_timecode_flags(__entry->timecode_flags), \ + __entry->timecode_frames, \ + __entry->timecode_seconds, \ + __entry->timecode_minutes, \ + __entry->timecode_hours, \ + __entry->timecode_userbits0, \ + __entry->timecode_userbits1, \ + __entry->timecode_userbits2, \ + __entry->timecode_userbits3, \ + __entry->sequence \ + ) \ + ) + +V4L2_TRACE_EVENT(v4l2_dqbuf); +V4L2_TRACE_EVENT(v4l2_qbuf); + +#endif /* if !defined(_TRACE_V4L2_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include -- cgit v0.10.2 From f1ff7c49f494bdfa49ca58c3c0b70b844f1f7d08 Mon Sep 17 00:00:00 2001 From: Mauro Dreissig Date: Tue, 26 Nov 2013 11:15:20 -0300 Subject: [media] staging: as102: Declare local variables as static As pointed out by sparse: drivers/staging/media/as102/as102_fw.c:29:6: warning: symbol 'as102_st_fw1' was not declared. Should it be static? drivers/staging/media/as102/as102_fw.c:30:6: warning: symbol 'as102_st_fw2' was not declared. Should it be static? drivers/staging/media/as102/as102_fw.c:31:6: warning: symbol 'as102_dt_fw1' was not declared. Should it be static? drivers/staging/media/as102/as102_fw.c:32:6: warning: symbol 'as102_dt_fw2' was not declared. Should it be static? drivers/staging/media/as102/as102_usb_drv.c:194:25: warning: symbol 'as102_priv_ops' was not declared. Should it be static? Also use the const qualifier on the firmware name strings. Signed-off-by: Mauro Dreissig Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index b9670ee..9e7c6d7 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -26,10 +26,10 @@ #include "as102_drv.h" #include "as102_fw.h" -char as102_st_fw1[] = "as102_data1_st.hex"; -char as102_st_fw2[] = "as102_data2_st.hex"; -char as102_dt_fw1[] = "as102_data1_dt.hex"; -char as102_dt_fw2[] = "as102_data2_dt.hex"; +static const char as102_st_fw1[] = "as102_data1_st.hex"; +static const char as102_st_fw2[] = "as102_data2_st.hex"; +static const char as102_dt_fw1[] = "as102_data1_dt.hex"; +static const char as102_dt_fw2[] = "as102_data2_dt.hex"; static unsigned char atohx(unsigned char *dst, char *src) { @@ -167,7 +167,7 @@ int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) int errno = -EFAULT; const struct firmware *firmware = NULL; unsigned char *cmd_buf = NULL; - char *fw1, *fw2; + const char *fw1, *fw2; struct usb_device *dev = bus_adap->usb_dev; ENTER(); diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index 9f275f0..0eaced3 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -191,7 +191,7 @@ static int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap, return ret ? ret : actual_len; } -struct as102_priv_ops_t as102_priv_ops = { +static struct as102_priv_ops_t as102_priv_ops = { .upload_fw_pkt = as102_send_ep1, .xfer_cmd = as102_usb_xfer_cmd, .as102_read_ep2 = as102_read_ep2, -- cgit v0.10.2 From cdfe3e352b6d93d74a46453d28138e5a5bc95aee Mon Sep 17 00:00:00 2001 From: Mauro Dreissig Date: Tue, 26 Nov 2013 11:15:21 -0300 Subject: [media] staging: as102: Remove ENTER/LEAVE debugging macros Too much noise, also does not cover every possible code paths. Signed-off-by: Mauro Dreissig Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c index ac92eaf..62218db 100644 --- a/drivers/staging/media/as102/as102_drv.c +++ b/drivers/staging/media/as102/as102_drv.c @@ -112,8 +112,6 @@ static int as10x_pid_filter(struct as102_dev_t *dev, struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap; int ret = -EFAULT; - ENTER(); - if (mutex_lock_interruptible(&dev->bus_adap.lock)) { dprintk(debug, "mutex_lock_interruptible(lock) failed !\n"); return -EBUSY; @@ -141,8 +139,6 @@ static int as10x_pid_filter(struct as102_dev_t *dev, } mutex_unlock(&dev->bus_adap.lock); - - LEAVE(); return ret; } @@ -152,8 +148,6 @@ static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) struct dvb_demux *demux = dvbdmxfeed->demux; struct as102_dev_t *as102_dev = demux->priv; - ENTER(); - if (mutex_lock_interruptible(&as102_dev->sem)) return -ERESTARTSYS; @@ -165,7 +159,6 @@ static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) ret = as102_start_stream(as102_dev); mutex_unlock(&as102_dev->sem); - LEAVE(); return ret; } @@ -174,8 +167,6 @@ static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) struct dvb_demux *demux = dvbdmxfeed->demux; struct as102_dev_t *as102_dev = demux->priv; - ENTER(); - if (mutex_lock_interruptible(&as102_dev->sem)) return -ERESTARTSYS; @@ -187,7 +178,6 @@ static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) dvbdmxfeed->pid, 0); mutex_unlock(&as102_dev->sem); - LEAVE(); return 0; } diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h index b0e5a23..a06837d 100644 --- a/drivers/staging/media/as102/as102_drv.h +++ b/drivers/staging/media/as102/as102_drv.h @@ -38,14 +38,6 @@ extern int elna_enable; printk(args); \ } } while (0) -#ifdef TRACE -#define ENTER() pr_debug(">> enter %s\n", __func__) -#define LEAVE() pr_debug("<< leave %s\n", __func__) -#else -#define ENTER() -#define LEAVE() -#endif - #define AS102_DEVICE_MAJOR 192 #define AS102_USB_BUF_SIZE 512 diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c index 9ce8c9d..72b2c48 100644 --- a/drivers/staging/media/as102/as102_fe.c +++ b/drivers/staging/media/as102/as102_fe.c @@ -34,8 +34,6 @@ static int as102_fe_set_frontend(struct dvb_frontend *fe) struct as102_dev_t *dev; struct as10x_tune_args tune_args = { 0 }; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; @@ -52,7 +50,6 @@ static int as102_fe_set_frontend(struct dvb_frontend *fe) mutex_unlock(&dev->bus_adap.lock); - LEAVE(); return (ret < 0) ? -EINVAL : 0; } @@ -63,8 +60,6 @@ static int as102_fe_get_frontend(struct dvb_frontend *fe) struct as102_dev_t *dev; struct as10x_tps tps = { 0 }; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -EINVAL; @@ -80,13 +75,11 @@ static int as102_fe_get_frontend(struct dvb_frontend *fe) mutex_unlock(&dev->bus_adap.lock); - LEAVE(); return (ret < 0) ? -EINVAL : 0; } static int as102_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *settings) { - ENTER(); #if 0 dprintk(debug, "step_size = %d\n", settings->step_size); @@ -97,7 +90,6 @@ static int as102_fe_get_tune_settings(struct dvb_frontend *fe, settings->min_delay_ms = 1000; - LEAVE(); return 0; } @@ -108,8 +100,6 @@ static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) struct as102_dev_t *dev; struct as10x_tune_status tstate = { 0 }; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; @@ -168,7 +158,6 @@ static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) out: mutex_unlock(&dev->bus_adap.lock); - LEAVE(); return ret; } @@ -183,15 +172,12 @@ static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr) { struct as102_dev_t *dev; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; *snr = dev->demod_stats.mer; - LEAVE(); return 0; } @@ -199,15 +185,12 @@ static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber) { struct as102_dev_t *dev; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; *ber = dev->ber; - LEAVE(); return 0; } @@ -216,15 +199,12 @@ static int as102_fe_read_signal_strength(struct dvb_frontend *fe, { struct as102_dev_t *dev; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; *strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2); - LEAVE(); return 0; } @@ -232,8 +212,6 @@ static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct as102_dev_t *dev; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; @@ -243,7 +221,6 @@ static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) else *ucblocks = 0; - LEAVE(); return 0; } @@ -252,8 +229,6 @@ static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) struct as102_dev_t *dev; int ret; - ENTER(); - dev = (struct as102_dev_t *) fe->tuner_priv; if (dev == NULL) return -ENODEV; @@ -272,7 +247,6 @@ static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) mutex_unlock(&dev->bus_adap.lock); - LEAVE(); return ret; } diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index 9e7c6d7..f33f752 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -109,8 +109,6 @@ static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, int total_read_bytes = 0, errno = 0; unsigned char addr_has_changed = 0; - ENTER(); - for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { int read_bytes = 0, data_len = 0; @@ -158,7 +156,6 @@ static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, } } error: - LEAVE(); return (errno == 0) ? total_read_bytes : errno; } @@ -170,8 +167,6 @@ int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) const char *fw1, *fw2; struct usb_device *dev = bus_adap->usb_dev; - ENTER(); - /* select fw file to upload */ if (dual_tuner) { fw1 = as102_dt_fw1; @@ -233,6 +228,5 @@ error: kfree(cmd_buf); release_firmware(firmware); - LEAVE(); return errno; } diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index 0eaced3..8759553 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -92,7 +92,6 @@ static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap, unsigned char *recv_buf, int recv_buf_len) { int ret = 0; - ENTER(); if (send_buf != NULL) { ret = usb_control_msg(bus_adap->usb_dev, @@ -140,7 +139,6 @@ static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap, #endif } - LEAVE(); return ret; } @@ -240,8 +238,6 @@ static void as102_free_usb_stream_buffer(struct as102_dev_t *dev) { int i; - ENTER(); - for (i = 0; i < MAX_STREAM_URB; i++) usb_free_urb(dev->stream_urb[i]); @@ -249,15 +245,12 @@ static void as102_free_usb_stream_buffer(struct as102_dev_t *dev) MAX_STREAM_URB * AS102_USB_BUF_SIZE, dev->stream, dev->dma_addr); - LEAVE(); } static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev) { int i, ret = 0; - ENTER(); - dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev, MAX_STREAM_URB * AS102_USB_BUF_SIZE, GFP_KERNEL, @@ -287,7 +280,6 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev) dev->stream_urb[i] = urb; } - LEAVE(); return ret; } @@ -318,23 +310,17 @@ static void as102_usb_release(struct kref *kref) { struct as102_dev_t *as102_dev; - ENTER(); - as102_dev = container_of(kref, struct as102_dev_t, kref); if (as102_dev != NULL) { usb_put_dev(as102_dev->bus_adap.usb_dev); kfree(as102_dev); } - - LEAVE(); } static void as102_usb_disconnect(struct usb_interface *intf) { struct as102_dev_t *as102_dev; - ENTER(); - /* extract as102_dev_t from usb_device private data */ as102_dev = usb_get_intfdata(intf); @@ -353,8 +339,6 @@ static void as102_usb_disconnect(struct usb_interface *intf) kref_put(&as102_dev->kref, as102_usb_release); pr_info("%s: device has been disconnected\n", DRIVER_NAME); - - LEAVE(); } static int as102_usb_probe(struct usb_interface *intf, @@ -364,8 +348,6 @@ static int as102_usb_probe(struct usb_interface *intf, struct as102_dev_t *as102_dev; int i; - ENTER(); - /* This should never actually happen */ if (ARRAY_SIZE(as102_usb_id_table) != (sizeof(as102_device_names) / sizeof(const char *))) { @@ -424,7 +406,6 @@ static int as102_usb_probe(struct usb_interface *intf, /* register dvb layer */ ret = as102_dvb_register(as102_dev); - LEAVE(); return ret; failed: @@ -439,8 +420,6 @@ static int as102_open(struct inode *inode, struct file *file) struct usb_interface *intf = NULL; struct as102_dev_t *dev = NULL; - ENTER(); - /* read minor from inode */ minor = iminor(inode); @@ -467,7 +446,6 @@ static int as102_open(struct inode *inode, struct file *file) kref_get(&dev->kref); exit: - LEAVE(); return ret; } @@ -476,15 +454,12 @@ static int as102_release(struct inode *inode, struct file *file) int ret = 0; struct as102_dev_t *dev = NULL; - ENTER(); - dev = file->private_data; if (dev != NULL) { /* decrement the count on our device */ kref_put(&dev->kref, as102_usb_release); } - LEAVE(); return ret; } diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c index a73df10..9e49f15 100644 --- a/drivers/staging/media/as102/as10x_cmd.c +++ b/drivers/staging/media/as102/as10x_cmd.c @@ -34,8 +34,6 @@ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -63,7 +61,6 @@ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP); out: - LEAVE(); return error; } @@ -78,8 +75,6 @@ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -106,7 +101,6 @@ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP); out: - LEAVE(); return error; } @@ -123,8 +117,6 @@ int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; - ENTER(); - preq = adap->cmd; prsp = adap->rsp; @@ -164,7 +156,6 @@ int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP); out: - LEAVE(); return error; } @@ -181,8 +172,6 @@ int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; - ENTER(); - preq = adap->cmd; prsp = adap->rsp; @@ -220,7 +209,6 @@ int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, pstatus->BER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.BER); out: - LEAVE(); return error; } @@ -236,8 +224,6 @@ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -281,7 +267,6 @@ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) ptps->cell_ID = le16_to_cpu(prsp->body.get_tps.rsp.tps.cell_ID); out: - LEAVE(); return error; } @@ -298,8 +283,6 @@ int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -343,7 +326,6 @@ int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, prsp->body.get_demod_stats.rsp.stats.has_started; out: - LEAVE(); return error; } @@ -361,8 +343,6 @@ int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -397,7 +377,6 @@ int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, *is_ready = prsp->body.get_impulse_rsp.rsp.is_ready; out: - LEAVE(); return error; } diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c index 4a2bbd7..b1e300d 100644 --- a/drivers/staging/media/as102/as10x_cmd_cfg.c +++ b/drivers/staging/media/as102/as10x_cmd_cfg.c @@ -40,8 +40,6 @@ int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, uint16_t tag, int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -81,7 +79,6 @@ int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, uint16_t tag, } out: - LEAVE(); return error; } @@ -99,8 +96,6 @@ int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, uint16_t tag, int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -136,7 +131,6 @@ int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, uint16_t tag, error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP); out: - LEAVE(); return error; } @@ -156,8 +150,6 @@ int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode) int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -188,7 +180,6 @@ int as10x_cmd_eLNA_change_mode(struct as10x_bus_adapter_t *adap, uint8_t mode) error = as10x_rsp_parse(prsp, CONTROL_PROC_ELNA_CHANGE_MODE_RSP); out: - LEAVE(); return error; } diff --git a/drivers/staging/media/as102/as10x_cmd_stream.c b/drivers/staging/media/as102/as10x_cmd_stream.c index 6d000f6..1088ca1 100644 --- a/drivers/staging/media/as102/as10x_cmd_stream.c +++ b/drivers/staging/media/as102/as10x_cmd_stream.c @@ -34,8 +34,6 @@ int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap, int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -77,7 +75,6 @@ int as10x_cmd_add_PID_filter(struct as10x_bus_adapter_t *adap, } out: - LEAVE(); return error; } @@ -94,8 +91,6 @@ int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap, int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -126,7 +121,6 @@ int as10x_cmd_del_PID_filter(struct as10x_bus_adapter_t *adap, error = as10x_rsp_parse(prsp, CONTROL_PROC_REMOVEFILTER_RSP); out: - LEAVE(); return error; } @@ -141,8 +135,6 @@ int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap) int error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -172,7 +164,6 @@ int as10x_cmd_start_streaming(struct as10x_bus_adapter_t *adap) error = as10x_rsp_parse(prsp, CONTROL_PROC_START_STREAMING_RSP); out: - LEAVE(); return error; } @@ -187,8 +178,6 @@ int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap) int8_t error; struct as10x_cmd_t *pcmd, *prsp; - ENTER(); - pcmd = adap->cmd; prsp = adap->rsp; @@ -218,6 +207,5 @@ int as10x_cmd_stop_streaming(struct as10x_bus_adapter_t *adap) error = as10x_rsp_parse(prsp, CONTROL_PROC_STOP_STREAMING_RSP); out: - LEAVE(); return error; } -- cgit v0.10.2 From 7b2f25c0b073479fa1afea3ae65dd9d9f0da5586 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 29 Nov 2013 20:01:34 -0300 Subject: [media] v4l: vs6624: Fix warning due to unused function vs6624_read() is only called in the conditionally-compiled vs6624_g_register() function. Make the former conditionally-compiled as well to silence build warnings. Signed-off-by: Laurent Pinchart Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 25bdd93..23f4f65 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) -- cgit v0.10.2 From 07e4de3004ecedd67c72708d6c0fe69c51317f79 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:51 -0300 Subject: [media] em28xx: add support for GPO controlled analog capturing LEDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices are equipped with a capturing status LED that needs to be switched on/off explicitly via a GPO port. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index fc157af..31d6ab2 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -608,46 +608,51 @@ int em28xx_capture_start(struct em28xx *dev, int start) dev->chip_id == CHIP_ID_EM2884 || dev->chip_id == CHIP_ID_EM28174) { /* 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; - } - + } else { + /* 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; - /* 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) { + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); - if (!start) { - /* disable video capture */ - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); - return rc; - } + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); - if (dev->board.is_webcam) - rc = em28xx_write_reg(dev, 0x13, 0x0c); + 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); - /* enable video capture */ - rc = em28xx_write_reg(dev, 0x48, 0x00); + msleep(6); + } else { + /* disable video capture */ + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + } + } - 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); + if (rc < 0) + return rc; - msleep(6); + /* Switch (explicitly controlled) analog capturing LED on/off */ + if ((dev->mode == EM28XX_ANALOG_MODE) + && dev->board.analog_capturing_led) { + struct em28xx_led *led = dev->board.analog_capturing_led; + em28xx_write_reg_bits(dev, led->gpio_reg, + (!start ^ led->inverted) ? + ~led->gpio_mask : led->gpio_mask, + led->gpio_mask); + } return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f8726ad..8003c2f 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -374,6 +374,12 @@ enum em28xx_adecoder { EM28XX_TVAUDIO, }; +struct em28xx_led { + u8 gpio_reg; + u8 gpio_mask; + bool inverted; +}; + struct em28xx_board { char *name; int vchannels; @@ -410,6 +416,9 @@ 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 *analog_capturing_led; }; struct em28xx_eeprom { -- cgit v0.10.2 From f522260993822d82e2c480aa676aa4ca1230d235 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:52 -0300 Subject: [media] em28xx: extend the support for device buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code supports only a single snapshot button assigned to register 0x0c bit 5. But devices may be equipped with multiple buttons with different functionalities and they can also be assigned to the various GPI-ports. Extend the em28xx-input code to handle multiple buttons assigned to different GPI-ports / register addresses and bits. Also make easier to extend the code with further button types. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a519669..ebb112c 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -413,6 +413,20 @@ static struct em28xx_reg_seq pctv_520e[] = { }; /* + * 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}, +}; + +/* * Board definitions */ struct em28xx_board em28xx_boards[] = { @@ -1391,7 +1405,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 +1427,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 = { { @@ -2841,7 +2855,7 @@ static void request_module_async(struct work_struct *work) 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 */ diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ea181e4..20c6a8a 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -31,7 +31,7 @@ #include "em28xx.h" #define EM28XX_SNAPSHOT_KEY KEY_CAMERA -#define EM28XX_SBUTTON_QUERY_INTERVAL 500 +#define EM28XX_BUTTONS_QUERY_INTERVAL 500 static unsigned int ir_debug; module_param(ir_debug, int, 0644); @@ -470,38 +470,69 @@ 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 pressed; + + /* 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 pressed */ + pressed = regval & button->mask; + if (button->inverted) + pressed = !pressed; + /* Handle button state */ + if (!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; + default: + WARN_ONCE(1, "BUG: unhandled button role."); + } + /* Clear button state (if needed) */ + if (button->reg_clearing) + em28xx_write_reg(dev, button->reg_clearing, + (~regval & button->mask) + | (regval & ~button->mask)); + /* Next button */ + j++; + } } - /* 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(EM28XX_BUTTONS_QUERY_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; @@ -510,14 +541,13 @@ static void em28xx_register_snapshot_button(struct em28xx *dev) input_dev = input_allocate_device(); if (!input_dev) { em28xx_errdev("input_allocate_device failed\n"); - return; + 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 +565,71 @@ 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; + + 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."); + addr_new = 0; + } + /* Register input device (if needed) */ + if (button->role == EM28XX_BUTTON_SNAPSHOT) { + if (em28xx_register_snapshot_button(dev) < 0) + addr_new = 0; + } + /* 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++; + } + /* Next button */ + i++; + } + + /* Start polling */ + if (dev->num_button_polling_addresses) { + INIT_DELAYED_WORK(&dev->buttons_query_work, + em28xx_query_buttons); + schedule_delayed_work(&dev->buttons_query_work, + msecs_to_jiffies(EM28XX_BUTTONS_QUERY_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 +640,8 @@ 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->board.buttons) + em28xx_init_buttons(dev); if (dev->board.has_ir_i2c) { i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev); @@ -688,7 +764,7 @@ static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; - em28xx_deregister_snapshot_button(dev); + em28xx_shutdown_buttons(dev); /* skip detach on non attached boards */ if (!ir) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 8003c2f..e185d00 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -181,6 +181,9 @@ /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_XFER_TIMEOUT 20 +/* max. number of button state polling addresses */ +#define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5 + enum em28xx_mode { EM28XX_SUSPEND, EM28XX_ANALOG_MODE, @@ -380,6 +383,19 @@ struct em28xx_led { bool inverted; }; +enum em28xx_button_role { + EM28XX_BUTTON_SNAPSHOT = 0, + 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; @@ -401,7 +417,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; @@ -419,6 +434,9 @@ struct em28xx_board { /* LEDs that need to be controlled explicitly */ struct em28xx_led *analog_capturing_led; + + /* Buttons */ + struct em28xx_button *buttons; }; struct em28xx_eeprom { @@ -648,10 +666,13 @@ 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 num_button_polling_addresses; + /* 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; }; -- cgit v0.10.2 From 7763481a97488f201e7b6a42ddce1d163cd6297a Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:53 -0300 Subject: [media] em28xx: add debouncing mechanism for GPI-connected buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far, the driver only supports a snapshot button which is assigned to register 0x0c bit 5. This special port has a built-in debouncing mechanism. For buttons connected to ordinary GPI ports, this patch implements a software debouncing mechanism. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 20c6a8a..ebc5387 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -479,7 +479,7 @@ static void em28xx_query_buttons(struct work_struct *work) container_of(work, struct em28xx, buttons_query_work.work); u8 i, j; int regval; - bool pressed; + bool is_pressed, was_pressed; /* Poll and evaluate all addresses */ for (i = 0; i < dev->num_button_polling_addresses; i++) { @@ -497,12 +497,21 @@ static void em28xx_query_buttons(struct work_struct *work) j++; continue; } - /* Determine if button is pressed */ - pressed = regval & button->mask; - if (button->inverted) - pressed = !pressed; + /* 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 (!pressed) { + if (!is_pressed || was_pressed) { j++; continue; } @@ -518,14 +527,11 @@ static void em28xx_query_buttons(struct work_struct *work) default: WARN_ONCE(1, "BUG: unhandled button role."); } - /* Clear button state (if needed) */ - if (button->reg_clearing) - em28xx_write_reg(dev, button->reg_clearing, - (~regval & button->mask) - | (regval & ~button->mask)); /* 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->buttons_query_work, @@ -611,6 +617,8 @@ static void em28xx_init_buttons(struct em28xx *dev) /* 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, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e185d00..df828c6 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -669,6 +669,7 @@ struct em28xx { /* 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; /* Snapshot button input device */ char snapshot_button_path[30]; /* path of the input dev */ -- cgit v0.10.2 From 6b8a3170c9984be58e50e23c78e2eb7833f33c4c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:55 -0300 Subject: [media] em28xx: prepare for supporting multiple LEDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a LED role and store all LEDs in an array. Also provide a helper function to retrieve a specific LED. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 31d6ab2..4a8179a 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -600,6 +600,22 @@ int em28xx_colorlevels_set_default(struct em28xx *dev) return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); } +const struct em28xx_led *em28xx_find_led(struct em28xx *dev, + enum em28xx_led_role role) +{ + 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) { int rc; @@ -645,13 +661,14 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; /* Switch (explicitly controlled) analog capturing LED on/off */ - if ((dev->mode == EM28XX_ANALOG_MODE) - && dev->board.analog_capturing_led) { - struct em28xx_led *led = dev->board.analog_capturing_led; - em28xx_write_reg_bits(dev, led->gpio_reg, - (!start ^ led->inverted) ? - ~led->gpio_mask : led->gpio_mask, - led->gpio_mask); + 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); } return rc; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index df828c6..f60f236 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -377,7 +377,13 @@ enum em28xx_adecoder { EM28XX_TVAUDIO, }; +enum em28xx_led_role { + EM28XX_LED_ANALOG_CAPTURING = 0, + EM28XX_NUM_LED_ROLES, /* must be the last */ +}; + struct em28xx_led { + enum em28xx_led_role role; u8 gpio_reg; u8 gpio_mask; bool inverted; @@ -433,7 +439,7 @@ struct em28xx_board { char *ir_codes; /* LEDs that need to be controlled explicitly */ - struct em28xx_led *analog_capturing_led; + struct em28xx_led *leds; /* Buttons */ struct em28xx_button *buttons; @@ -711,6 +717,8 @@ 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); -- cgit v0.10.2 From 6063d07785b7059d276cb1a39c76186804f5938e Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:56 -0300 Subject: [media] em28xx: add support for illumination button and LED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SpeedLink VAD Laplace webcam is equipped with an illumination button and an illumination LED. When the button is pressed, the driver must toggle the LED state via the corresponding GPO port. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 4a8179a..fc82540 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -226,6 +226,25 @@ 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 */ diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ebc5387..e0acc92 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -480,6 +480,7 @@ static void em28xx_query_buttons(struct work_struct *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++) { @@ -524,6 +525,15 @@ static void em28xx_query_buttons(struct work_struct *work) 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."); } @@ -600,10 +610,17 @@ static void em28xx_init_buttons(struct em28xx *dev) WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded."); addr_new = 0; } - /* Register input device (if needed) */ + /* Button role specific checks and actions */ if (button->role == EM28XX_BUTTON_SNAPSHOT) { + /* Register input device */ if (em28xx_register_snapshot_button(dev) < 0) addr_new = 0; + } 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"); + addr_new = 0; + } } /* Add read address to list of polling addresses */ if (addr_new) { diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f60f236..43e2968 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -379,6 +379,7 @@ enum em28xx_adecoder { enum em28xx_led_role { EM28XX_LED_ANALOG_CAPTURING = 0, + EM28XX_LED_ILLUMINATION, EM28XX_NUM_LED_ROLES, /* must be the last */ }; @@ -391,6 +392,7 @@ struct em28xx_led { enum em28xx_button_role { EM28XX_BUTTON_SNAPSHOT = 0, + EM28XX_BUTTON_ILLUMINATION, EM28XX_NUM_BUTTON_ROLES, /* must be the last */ }; @@ -709,6 +711,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); -- cgit v0.10.2 From 0c37e736cb869a3a7cc95a2486be73ffc1e1ee7d Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 1 Dec 2013 18:06:57 -0300 Subject: [media] em28xx: add support for the SpeedLink Vicious And Devine Laplace webcams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SpeedLink Vicious And Devine Laplace webcam is using an EM2765 bridge and an OV2640 sensor. It has a built-in microphone (USB standard device class) and provides 3 buttons (snapshot, illumination, mute) and 2 LEDs (capturing/mute and illumination/flash). It is also equipped with an eeprom. The device is available in two colors: white (1ae7:9003) and black (1ae7:9004). For further details see http://linuxtv.org/wiki/index.php/VAD_Laplace. Please note the following limitations that need to be addressed later: - resolution limited to 640x480 (sensor supports 1600x1200) - picture quality needs to be improved - AV-mute button doesn't work yet Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ebb112c..6454309 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -412,6 +412,21 @@ 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 */ @@ -426,6 +441,41 @@ static struct em28xx_button std_snapshot_button[] = { {-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 */ @@ -2057,6 +2107,24 @@ 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, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -2222,6 +2290,10 @@ 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 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 43e2968..6e26fba 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -132,6 +132,7 @@ #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 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v0.10.2 From f1b84d365af6d5ce1898fb7956e2053db14cc96e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 2 Dec 2013 20:15:04 -0300 Subject: [media] media: pci: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is not preferred. Signed-off-by: Jingoo Han Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 6e91e84..b1e08c3 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 b762c5b..e81173c 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/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 77edc11..e5cfb6c 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,} }; -- cgit v0.10.2 From fe104a9b61ac8856e7973058b71f33224a7d5ed7 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 3 Dec 2013 08:51:12 -0300 Subject: [media] v4l: ti-vpe: Fix the data_type value for UYVY VPDMA format The data_type value to be programmed in the data descriptors to fetch/write a UYVY buffer was not mentioned correctly in the older DRA7x documentation. This caused VPE to fail with UYVY color formats. Update the data_type value to fix functionality when UYVY format is used. Signed-off-by: Archit Taneja Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h index f0e9a80..c1a6ce1 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 -- cgit v0.10.2 From a51cd8f5d0a21ccc8d313a9992293ab2541b40a8 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 3 Dec 2013 08:51:13 -0300 Subject: [media] v4l: ti-vpe: make sure VPDMA line stride constraints are met When VPDMA fetches or writes to an image buffer, the line stride must be a multiple of 16 bytes. If it isn't, VPDMA HW will write/fetch until the next 16 byte boundry. This causes VPE to work incorrectly for source or destination widths which don't satisfy the above alignment requirement. In order to prevent this, we now make sure that when we set pix format for the input and output buffers, the VPE source and destination image line strides are 16 byte aligned. Also, the motion vector buffers for the de-interlacer are allocated in such a way that it ensures the same alignment. Signed-off-by: Archit Taneja Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index af0a5ff..f97253f 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -602,7 +602,7 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, if (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; @@ -655,7 +655,7 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width, 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 eaa2a71..62dd143 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -45,7 +45,10 @@ struct vpdma_data_format { }; #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/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index f949ef5..6697770 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -54,10 +55,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) @@ -780,12 +777,21 @@ 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]; ctx->deinterlacing = 1; - mv_buf_size = - (s_q_data->width * s_q_data->height * mv->depth) >> 3; + /* + * 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; } else { ctx->deinterlacing = 0; mv_buf_size = 0; @@ -1352,7 +1358,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,7 +1370,31 @@ 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); @@ -1383,15 +1414,11 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, } 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; -- cgit v0.10.2 From 8a38db133358f9370e6bb453371e630495c59070 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 5 Dec 2013 15:38:19 -0300 Subject: [media] doc: no singing Stop that, stop that! You're not going to do a song while I'm here. Signed-off-by: Kees Cook Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/cgroups/resource_counter.txt b/Documentation/cgroups/resource_counter.txt index c4d99ed..caa6d66 100644 --- a/Documentation/cgroups/resource_counter.txt +++ b/Documentation/cgroups/resource_counter.txt @@ -95,7 +95,7 @@ to work with it. f. u64 res_counter_uncharge_until (struct res_counter *rc, struct res_counter *top, - unsinged long val) + unsigned long val) Almost same as res_cunter_uncharge() but propagation of uncharge stops when rc == top. This is useful when kill a res_coutner in diff --git a/Documentation/video4linux/si476x.txt b/Documentation/video4linux/si476x.txt index 2f9b487..6166079 100644 --- a/Documentation/video4linux/si476x.txt +++ b/Documentation/video4linux/si476x.txt @@ -147,7 +147,7 @@ The drivers exposes following files: -------------------------------------------------------------------- 0x12 | readfreq | Current tuned frequency -------------------------------------------------------------------- - 0x14 | freqoff | Singed frequency offset in units of + 0x14 | freqoff | Signed frequency offset in units of | | 2ppm -------------------------------------------------------------------- 0x15 | rssi | Signed value of RSSI in dBuV diff --git a/arch/score/lib/checksum.S b/arch/score/lib/checksum.S index 706157e..1141f2b 100644 --- a/arch/score/lib/checksum.S +++ b/arch/score/lib/checksum.S @@ -137,7 +137,7 @@ ENTRY(csum_partial) ldi r25, 0 mv r10, r5 cmpi.c r5, 0x8 - blt small_csumcpy /* < 8(singed) bytes to copy */ + blt small_csumcpy /* < 8(signed) bytes to copy */ cmpi.c r5, 0x0 beq out andri.c r25, src, 0x1 /* odd buffer? */ -- cgit v0.10.2 From 4cd89e91bb8dbefe54743df6a5c4437812c96e3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Dec 2013 21:25:13 -0300 Subject: [media] v4l: omap4iss: Replace printk by dev_err dev_err is preferred over printk(KERN_ERR) when a device pointer is available. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index b7c8a6b..3ac986e 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1108,7 +1108,7 @@ static int iss_register_entities(struct iss_device *iss) iss->media_dev.link_notify = iss_pipeline_link_notify; ret = media_device_register(&iss->media_dev); if (ret < 0) { - printk(KERN_ERR "%s: Media device registration failed (%d)\n", + dev_err(iss->dev, "%s: Media device registration failed (%d)\n", __func__, ret); return ret; } @@ -1116,7 +1116,7 @@ static int iss_register_entities(struct iss_device *iss) iss->v4l2_dev.mdev = &iss->media_dev; ret = v4l2_device_register(iss->dev, &iss->v4l2_dev); if (ret < 0) { - printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", + dev_err(iss->dev, "%s: V4L2 device registration failed (%d)\n", __func__, ret); goto done; } @@ -1175,8 +1175,8 @@ static int iss_register_entities(struct iss_device *iss) break; default: - printk(KERN_ERR "%s: invalid interface type %u\n", - __func__, subdevs->interface); + dev_err(iss->dev, "%s: invalid interface type %u\n", + __func__, subdevs->interface); ret = -EINVAL; goto done; } diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 0ee8381..9ced9ce 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -517,7 +517,8 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) } while (soft_reset_retries < 5); if (soft_reset_retries == 5) { - printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); + dev_err(csi2->iss->dev, + "CSI2: Soft reset try count exceeded!\n"); return -EBUSY; } @@ -535,8 +536,8 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) } while (--i > 0); if (i == 0) { - printk(KERN_ERR - "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); + dev_err(csi2->iss->dev, + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); return -EBUSY; } diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index 2afea98..e0d0247 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -78,7 +78,7 @@ static int csiphy_set_power(struct iss_csiphy *phy, u32 power) } while ((reg != power >> 2) && (retry_count < 250)); if (retry_count == 250) { - printk(KERN_ERR "CSI2 CIO set power failed!\n"); + dev_err(phy->iss->dev, "CSI2 CIO set power failed!\n"); return -EBUSY; } diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 766491e..5a92bac 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1116,7 +1116,8 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); if (ret < 0) - printk(KERN_ERR "%s: could not register video device (%d)\n", + dev_err(video->iss->dev, + "%s: could not register video device (%d)\n", __func__, ret); return ret; -- cgit v0.10.2 From 499226fb196fef838fa38700b96448a2ec41b704 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Dec 2013 21:26:37 -0300 Subject: [media] v4l: omap4iss: Don't split log strings on multiple lines Non-split strings help grepping for messages. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 3ac986e..53dcb54 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1073,9 +1073,9 @@ iss_register_subdev_group(struct iss_device *iss, adapter = i2c_get_adapter(board_info->i2c_adapter_id); if (adapter == NULL) { - dev_err(iss->dev, "%s: Unable to get I2C adapter %d for " - "device %s\n", __func__, - board_info->i2c_adapter_id, + dev_err(iss->dev, + "%s: Unable to get I2C adapter %d for device %s\n", + __func__, board_info->i2c_adapter_id, board_info->board_info->type); continue; } diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 9ced9ce..c3a5fca 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -754,8 +754,8 @@ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) CSI2_COMPLEXIO_IRQSTATUS); writel(cpxio1_irqstatus, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS); - dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ " - "%x\n", cpxio1_irqstatus); + dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ %x\n", + cpxio1_irqstatus); pipe->error = true; } @@ -764,13 +764,8 @@ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) CSI2_IRQ_ECC_NO_CORRECTION | CSI2_IRQ_COMPLEXIO_ERR | CSI2_IRQ_FIFO_OVF)) { - dev_dbg(iss->dev, "CSI2 Err:" - " OCP:%d," - " Short_pack:%d," - " ECC:%d," - " CPXIO:%d," - " FIFO_OVF:%d," - "\n", + dev_dbg(iss->dev, + "CSI2 Err: OCP:%d SHORT:%d ECC:%d CPXIO:%d OVF:%d\n", (csi2_irqstatus & CSI2_IRQ_OCP_ERR) ? 1 : 0, (csi2_irqstatus & diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index cb5df52..e5a3a8cf 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -321,10 +321,7 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK | ISP5_IRQ_RSZ_FIFO_OVF)) { - dev_dbg(iss->dev, "RSZ Err:" - " FIFO_IN_BLK:%d," - " FIFO_OVF:%d," - "\n", + dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n", (events & ISP5_IRQ_RSZ_FIFO_IN_BLK) ? 1 : 0, (events & diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 5a92bac..3e543d9 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -324,8 +324,8 @@ static int iss_video_buf_prepare(struct vb2_buffer *vb) addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (!IS_ALIGNED(addr, 32)) { - dev_dbg(video->iss->dev, "Buffer address must be " - "aligned to 32 bytes boundary.\n"); + dev_dbg(video->iss->dev, + "Buffer address must be aligned to 32 bytes boundary.\n"); return -EINVAL; } -- cgit v0.10.2 From a0fe029ccc648fb7f5dfcba5d4345040db574a53 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Dec 2013 21:28:37 -0300 Subject: [media] v4l: omap4iss: Restrict line lengths to 80 characters where possible Pure CodingStyle fixes. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index e0d0247..25e6f89 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -178,7 +178,8 @@ int omap4iss_csiphy_config(struct iss_device *iss, if (lanes->data[i].pos == 0) continue; - if (lanes->data[i].pol > 1 || lanes->data[i].pos > (csi2->phy->max_data_lanes + 1)) + if (lanes->data[i].pol > 1 || + lanes->data[i].pos > (csi2->phy->max_data_lanes + 1)) return -EINVAL; if (used_lanes & (1 << lanes->data[i].pos)) @@ -188,7 +189,8 @@ int omap4iss_csiphy_config(struct iss_device *iss, csi2->phy->used_data_lanes++; } - if (lanes->clk.pol > 1 || lanes->clk.pos > (csi2->phy->max_data_lanes + 1)) + if (lanes->clk.pol > 1 || + lanes->clk.pos > (csi2->phy->max_data_lanes + 1)) return -EINVAL; if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index acc6005..eee6891 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -23,10 +23,6 @@ #include "iss_regs.h" #include "iss_ipipeif.h" -static struct v4l2_mbus_framefmt * -__ipipeif_get_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which); - static const unsigned int ipipeif_fmts[] = { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, @@ -274,7 +270,8 @@ static void ipipeif_isif0_isr(struct iss_ipipeif_device *ipipeif) */ void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) { - if (omap4iss_module_sync_is_stopping(&ipipeif->wait, &ipipeif->stopping)) + if (omap4iss_module_sync_is_stopping(&ipipeif->wait, + &ipipeif->stopping)) return; if (events & ISP5_IRQ_ISIF0) @@ -285,7 +282,8 @@ void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) * ISP video operations */ -static int ipipeif_video_queue(struct iss_video *video, struct iss_buffer *buffer) +static int ipipeif_video_queue(struct iss_video *video, + struct iss_buffer *buffer) { struct iss_ipipeif_device *ipipeif = container_of(video, struct iss_ipipeif_device, video_out); @@ -385,8 +383,9 @@ static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__ipipeif_get_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) +__ipipeif_get_format(struct iss_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_format(fh, pad); @@ -402,9 +401,10 @@ __ipipeif_get_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh * * @fmt: Format */ static void -ipipeif_try_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh, - unsigned int pad, struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) +ipipeif_try_format(struct iss_ipipeif_device *ipipeif, + 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 width = fmt->width; @@ -413,8 +413,8 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh switch (pad) { case IPIPEIF_PAD_SINK: - /* TODO: If the IPIPEIF output formatter pad is connected directly - * to the resizer, only YUV formats can be used. + /* TODO: If the IPIPEIF output formatter pad is connected + * directly to the resizer, only YUV formats can be used. */ for (i = 0; i < ARRAY_SIZE(ipipeif_fmts); i++) { if (fmt->code == ipipeif_fmts[i]) @@ -431,7 +431,8 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh break; case IPIPEIF_PAD_SOURCE_ISIF_SF: - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, which); + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); /* The data formatter truncates the number of horizontal output @@ -445,7 +446,8 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif, struct v4l2_subdev_fh *fh break; case IPIPEIF_PAD_SOURCE_VP: - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, which); + format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); fmt->width = clamp_t(u32, width, 32, fmt->width); @@ -514,7 +516,8 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -524,7 +527,8 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ipipeif_try_format(ipipeif, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -578,14 +582,16 @@ static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == IPIPEIF_PAD_SINK) { - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, - fmt->which); + format = __ipipeif_get_format(ipipeif, fh, + IPIPEIF_PAD_SOURCE_ISIF_SF, + fmt->which); *format = fmt->format; - ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, format, - fmt->which); + ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_ISIF_SF, + format, fmt->which); - format = __ipipeif_get_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_VP, - fmt->which); + format = __ipipeif_get_format(ipipeif, fh, + IPIPEIF_PAD_SOURCE_VP, + fmt->which); *format = fmt->format; ipipeif_try_format(ipipeif, fh, IPIPEIF_PAD_SOURCE_VP, format, fmt->which); @@ -594,7 +600,8 @@ static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int ipipeif_link_validate(struct v4l2_subdev *sd, struct media_link *link, +static int ipipeif_link_validate(struct v4l2_subdev *sd, + struct media_link *link, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { @@ -618,7 +625,8 @@ static int ipipeif_link_validate(struct v4l2_subdev *sd, struct media_link *link * formats are initialized on the file handle. Otherwise active formats are * initialized on the device. */ -static int ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int ipipeif_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { struct v4l2_subdev_format format; @@ -778,8 +786,9 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) return ret; /* Connect the IPIPEIF subdev to the video node. */ - ret = media_entity_create_link(&ipipeif->subdev.entity, IPIPEIF_PAD_SOURCE_ISIF_SF, - &ipipeif->video_out.video.entity, 0, 0); + ret = media_entity_create_link(&ipipeif->subdev.entity, + IPIPEIF_PAD_SOURCE_ISIF_SF, + &ipipeif->video_out.video.entity, 0, 0); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index e5a3a8cf..08b2505 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -23,10 +23,6 @@ #include "iss_regs.h" #include "iss_resizer.h" -static struct v4l2_mbus_framefmt * -__resizer_get_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which); - static const unsigned int resizer_fmts[] = { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, @@ -329,7 +325,8 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) pipe->error = true; } - if (omap4iss_module_sync_is_stopping(&resizer->wait, &resizer->stopping)) + if (omap4iss_module_sync_is_stopping(&resizer->wait, + &resizer->stopping)) return; if (events & ISP5_IRQ_RSZ_INT_DMA) @@ -340,7 +337,8 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) * ISS video operations */ -static int resizer_video_queue(struct iss_video *video, struct iss_buffer *buffer) +static int resizer_video_queue(struct iss_video *video, + struct iss_buffer *buffer) { struct iss_resizer_device *resizer = container_of(video, struct iss_resizer_device, video_out); @@ -453,8 +451,9 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__resizer_get_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) +__resizer_get_format(struct iss_resizer_device *resizer, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_format(fh, pad); @@ -470,9 +469,10 @@ __resizer_get_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh * * @fmt: Format */ static void -resizer_try_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh, - unsigned int pad, struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) +resizer_try_format(struct iss_resizer_device *resizer, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) { enum v4l2_mbus_pixelcode pixelcode; struct v4l2_mbus_framefmt *format; @@ -498,7 +498,8 @@ resizer_try_format(struct iss_resizer_device *resizer, struct v4l2_subdev_fh *fh case RESIZER_PAD_SOURCE_MEM: pixelcode = fmt->code; - format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, which); + format = __resizer_get_format(resizer, fh, RESIZER_PAD_SINK, + which); memcpy(fmt, format, sizeof(*fmt)); if ((pixelcode == V4L2_MBUS_FMT_YUYV8_1_5X8) && @@ -586,7 +587,8 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(resizer, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(resizer, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); fse->min_width = format.width; fse->min_height = format.height; @@ -596,7 +598,8 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(resizer, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(resizer, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); fse->max_width = format.width; fse->max_height = format.height; @@ -650,8 +653,9 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == RESIZER_PAD_SINK) { - format = __resizer_get_format(resizer, fh, RESIZER_PAD_SOURCE_MEM, - fmt->which); + format = __resizer_get_format(resizer, fh, + RESIZER_PAD_SOURCE_MEM, + fmt->which); *format = fmt->format; resizer_try_format(resizer, fh, RESIZER_PAD_SOURCE_MEM, format, fmt->which); @@ -660,7 +664,8 @@ 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, +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) { @@ -684,7 +689,8 @@ static int resizer_link_validate(struct v4l2_subdev *sd, struct media_link *link * formats are initialized on the file handle. Otherwise active formats are * initialized on the device. */ -static int resizer_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { struct v4l2_subdev_format format; @@ -833,8 +839,9 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) return ret; /* Connect the RESIZER subdev to the video node. */ - ret = media_entity_create_link(&resizer->subdev.entity, RESIZER_PAD_SOURCE_MEM, - &resizer->video_out.video.entity, 0, 0); + ret = media_entity_create_link(&resizer->subdev.entity, + RESIZER_PAD_SOURCE_MEM, + &resizer->video_out.video.entity, 0, 0); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 3e543d9..0a7137b 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -282,7 +282,8 @@ iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh) * Video queue operations */ -static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int iss_video_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -298,7 +299,7 @@ static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format alloc_ctxs[0] = video->alloc_ctx; - *count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0]))); + *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); return 0; } @@ -425,11 +426,13 @@ struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) * first, so the input number might lag behind by 1 in some cases. */ if (video == pipe->output && !pipe->do_propagation) - buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number); + buf->vb.v4l2_buf.sequence = + atomic_inc_return(&pipe->frame_number); else buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); - vb2_buffer_done(&buf->vb, pipe->error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb, pipe->error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); pipe->error = false; spin_lock_irqsave(&video->qlock, flags); -- cgit v0.10.2 From 29ee62616348d7c7680728ad7df8d52fa5e67c0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Dec 2013 21:29:06 -0300 Subject: [media] v4l: omap4iss: Remove double semicolon at end of line Pure CodingStyle fixes. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 0a7137b..7763b8d 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1039,7 +1039,7 @@ static int iss_video_mmap(struct file *file, struct vm_area_struct *vma) { struct iss_video_fh *vfh = to_iss_video_fh(file->private_data); - return vb2_mmap(&vfh->queue, vma);; + return vb2_mmap(&vfh->queue, vma); } static struct v4l2_file_operations iss_video_fops = { -- cgit v0.10.2 From ade1ec3736c432981fefaa07b20e818c8501a44e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Aug 2013 12:03:50 -0300 Subject: [media] v4l: omap4iss: Define more ISS and ISP IRQ register bits Add more register definitions at iss_regs.h and improve some register names. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 53dcb54..815b09b 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -40,9 +40,9 @@ static void iss_print_status(struct iss_device *iss) ISS_PRINT_REGISTER(iss, HL_REVISION); ISS_PRINT_REGISTER(iss, HL_SYSCONFIG); - ISS_PRINT_REGISTER(iss, HL_IRQSTATUS_5); - ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_SET); - ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_CLR); + ISS_PRINT_REGISTER(iss, HL_IRQSTATUS(5)); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_SET(5)); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_CLR(5)); ISS_PRINT_REGISTER(iss, CTRL); ISS_PRINT_REGISTER(iss, CLKCTRL); ISS_PRINT_REGISTER(iss, CLKSTAT); @@ -75,8 +75,8 @@ static void iss_enable_interrupts(struct iss_device *iss) static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB | ISS_HL_IRQ_ISP(0); /* Enable HL interrupts */ - writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); - writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET); + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_SET(5)); } @@ -86,7 +86,7 @@ static void iss_enable_interrupts(struct iss_device *iss) */ static void iss_disable_interrupts(struct iss_device *iss) { - writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR); + writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_CLR(5)); } /* @@ -96,10 +96,10 @@ static void iss_disable_interrupts(struct iss_device *iss) void omap4iss_isp_enable_interrupts(struct iss_device *iss) { static const u32 isp_irq = ISP5_IRQ_OCP_ERR | - ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | ISP5_IRQ_RSZ_FIFO_OVF | ISP5_IRQ_RSZ_INT_DMA | - ISP5_IRQ_ISIF0; + ISP5_IRQ_ISIF_INT(0); /* Enable ISP interrupts */ writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQSTATUS(0)); @@ -256,16 +256,16 @@ static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) */ static irqreturn_t iss_isr(int irq, void *_iss) { - static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF | - ISP5_IRQ_ISIF0; - static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK | + static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF_IRQ | + ISP5_IRQ_ISIF_INT(0); + static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | ISP5_IRQ_RSZ_FIFO_OVF | ISP5_IRQ_RSZ_INT_DMA; struct iss_device *iss = _iss; u32 irqstatus; - irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); - writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); + writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); if (irqstatus & ISS_HL_IRQ_CSIA) omap4iss_csi2_isr(&iss->csi2a); diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index eee6891..e96040f 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -274,7 +274,7 @@ void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) &ipipeif->stopping)) return; - if (events & ISP5_IRQ_ISIF0) + if (events & ISP5_IRQ_ISIF_INT(0)) ipipeif_isif0_isr(ipipeif); } diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index 7327d0c..16975ca 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -24,12 +24,16 @@ #define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE 0x2 #define ISS_HL_SYSCONFIG_SOFTRESET (1 << 0) -#define ISS_HL_IRQSTATUS_5 (0x24 + (0x10 * 5)) -#define ISS_HL_IRQENABLE_5_SET (0x28 + (0x10 * 5)) -#define ISS_HL_IRQENABLE_5_CLR (0x2C + (0x10 * 5)) +#define ISS_HL_IRQSTATUS_RAW(i) (0x20 + (0x10 * (i))) +#define ISS_HL_IRQSTATUS(i) (0x24 + (0x10 * (i))) +#define ISS_HL_IRQENABLE_SET(i) (0x28 + (0x10 * (i))) +#define ISS_HL_IRQENABLE_CLR(i) (0x2c + (0x10 * (i))) +#define ISS_HL_IRQ_HS_VS (1 << 17) +#define ISS_HL_IRQ_SIMCOP(i) (1 << (12 + (i))) #define ISS_HL_IRQ_BTE (1 << 11) #define ISS_HL_IRQ_CBUFF (1 << 10) +#define ISS_HL_IRQ_CCP2(i) (1 << ((i) > 3 ? 16 : 14 + (i))) #define ISS_HL_IRQ_CSIB (1 << 5) #define ISS_HL_IRQ_CSIA (1 << 4) #define ISS_HL_IRQ_ISP(i) (1 << (i)) @@ -267,16 +271,30 @@ /* Bits shared for ISP5_IRQ* registers */ #define ISP5_IRQ_OCP_ERR (1 << 31) +#define ISP5_IRQ_IPIPE_INT_DPC_RNEW1 (1 << 29) +#define ISP5_IRQ_IPIPE_INT_DPC_RNEW0 (1 << 28) +#define ISP5_IRQ_IPIPE_INT_DPC_INIT (1 << 27) +#define ISP5_IRQ_IPIPE_INT_EOF (1 << 25) +#define ISP5_IRQ_H3A_INT_EOF (1 << 24) +#define ISP5_IRQ_RSZ_INT_EOF1 (1 << 23) #define ISP5_IRQ_RSZ_INT_EOF0 (1 << 22) -#define ISP5_IRQ_RSZ_FIFO_IN_BLK (1 << 19) +#define ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR (1 << 19) #define ISP5_IRQ_RSZ_FIFO_OVF (1 << 18) +#define ISP5_IRQ_RSZ_INT_CYC_RSZB (1 << 17) #define ISP5_IRQ_RSZ_INT_CYC_RSZA (1 << 16) #define ISP5_IRQ_RSZ_INT_DMA (1 << 15) -#define ISP5_IRQ_IPIPEIF (1 << 9) -#define ISP5_IRQ_ISIF3 (1 << 3) -#define ISP5_IRQ_ISIF2 (1 << 2) -#define ISP5_IRQ_ISIF1 (1 << 1) -#define ISP5_IRQ_ISIF0 (1 << 0) +#define ISP5_IRQ_RSZ_INT_LAST_PIX (1 << 14) +#define ISP5_IRQ_RSZ_INT_REG (1 << 13) +#define ISP5_IRQ_H3A_INT (1 << 12) +#define ISP5_IRQ_AF_INT (1 << 11) +#define ISP5_IRQ_AEW_INT (1 << 10) +#define ISP5_IRQ_IPIPEIF_IRQ (1 << 9) +#define ISP5_IRQ_IPIPE_INT_HST (1 << 8) +#define ISP5_IRQ_IPIPE_INT_BSC (1 << 7) +#define ISP5_IRQ_IPIPE_INT_DMA (1 << 6) +#define ISP5_IRQ_IPIPE_INT_LAST_PIX (1 << 5) +#define ISP5_IRQ_IPIPE_INT_REG (1 << 4) +#define ISP5_IRQ_ISIF_INT(i) (1 << (i)) #define ISP5_CTRL (0x006C) #define ISP5_CTRL_MSTANDBY (1 << 24) diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 08b2505..272b92a 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -315,11 +315,11 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) struct iss_pipeline *pipe = to_iss_pipeline(&resizer->subdev.entity); - if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK | + if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | ISP5_IRQ_RSZ_FIFO_OVF)) { dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n", (events & - ISP5_IRQ_RSZ_FIFO_IN_BLK) ? 1 : 0, + ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR) ? 1 : 0, (events & ISP5_IRQ_RSZ_FIFO_OVF) ? 1 : 0); pipe->error = true; -- cgit v0.10.2 From 54d0059c7bbc26b10d11148f507c3a3a56d2bdd5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Aug 2013 12:03:50 -0300 Subject: [media] v4l: omap4iss: isif: Define more VDINT registers Use a macro to get the VDINT indexed registers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index e96040f..5464742 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -67,7 +67,7 @@ static void ipipeif_print_status(struct iss_ipipeif_device *ipipeif) ISIF_PRINT_REGISTER(iss, SPH); ISIF_PRINT_REGISTER(iss, LNH); ISIF_PRINT_REGISTER(iss, LNV); - ISIF_PRINT_REGISTER(iss, VDINT0); + ISIF_PRINT_REGISTER(iss, VDINT(0)); ISIF_PRINT_REGISTER(iss, HSIZE); ISP5_PRINT_REGISTER(iss, SYSCONFIG); @@ -213,7 +213,7 @@ cont_raw: /* Generate ISIF0 on the last line of the image */ writel(format->height - 1, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_VDINT0); + iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_VDINT(0)); /* IPIPEIF_PAD_SOURCE_ISIF_SF */ format = &ipipeif->formats[IPIPEIF_PAD_SOURCE_ISIF_SF]; diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index 16975ca..d969351 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -363,8 +363,8 @@ #define ISIF_CCOLP_CP3_F0_B (3 << 0) #define ISIF_CCOLP_CP3_F0_GB (2 << 0) -#define ISIF_VDINT0 (0x0070) -#define ISIF_VDINT0_MASK (0x7FFF) +#define ISIF_VDINT(i) (0x0070 + (i) * 4) +#define ISIF_VDINT_MASK (0x7fff) #define ISIF_CGAMMAWD (0x0080) #define ISIF_CGAMMAWD_GWDI_MASK (0xF << 1) -- cgit v0.10.2 From 5122f6a26d4e706653e1677708594b82b1ec7cd3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Aug 2013 12:48:36 -0300 Subject: [media] v4l: omap4iss: Enhance IRQ debugging Add a pretty print function for ISP IRQs and remove the _INT suffix from interrupt names to enhance readability. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 815b09b..65a1680 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -197,43 +197,44 @@ void omap4iss_configure_bridge(struct iss_device *iss, writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); } +#if defined(DEBUG) && defined(ISS_ISR_DEBUG) static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) { - static const char *name[] = { - "ISP_IRQ0", - "ISP_IRQ1", - "ISP_IRQ2", - "ISP_IRQ3", - "CSIA_IRQ", - "CSIB_IRQ", - "CCP2_IRQ0", - "CCP2_IRQ1", - "CCP2_IRQ2", - "CCP2_IRQ3", - "CBUFF_IRQ", - "BTE_IRQ", - "SIMCOP_IRQ0", - "SIMCOP_IRQ1", - "SIMCOP_IRQ2", - "SIMCOP_IRQ3", - "CCP2_IRQ8", - "HS_VS_IRQ", - "res18", - "res19", - "res20", - "res21", - "res22", - "res23", - "res24", - "res25", - "res26", - "res27", - "res28", - "res29", - "res30", - "res31", + static const char * const name[] = { + "ISP_0", + "ISP_1", + "ISP_2", + "ISP_3", + "CSIA", + "CSIB", + "CCP2_0", + "CCP2_1", + "CCP2_2", + "CCP2_3", + "CBUFF", + "BTE", + "SIMCOP_0", + "SIMCOP_1", + "SIMCOP_2", + "SIMCOP_3", + "CCP2_8", + "HS_VS", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", }; - int i; + unsigned int i; dev_dbg(iss->dev, "ISS IRQ: "); @@ -244,6 +245,54 @@ static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) pr_cont("\n"); } +static inline void iss_isp_isr_dbg(struct iss_device *iss, u32 irqstatus) +{ + static const char * const name[] = { + "ISIF_0", + "ISIF_1", + "ISIF_2", + "ISIF_3", + "IPIPEREQ", + "IPIPELAST_PIX", + "IPIPEDMA", + "IPIPEBSC", + "IPIPEHST", + "IPIPEIF", + "AEW", + "AF", + "H3A", + "RSZ_REG", + "RSZ_LAST_PIX", + "RSZ_DMA", + "RSZ_CYC_RZA", + "RSZ_CYC_RZB", + "RSZ_FIFO_OVF", + "RSZ_FIFO_IN_BLK_ERR", + "20", + "21", + "RSZ_EOF0", + "RSZ_EOF1", + "H3A_EOF", + "IPIPE_EOF", + "26", + "IPIPE_DPC_INI", + "IPIPE_DPC_RNEW0", + "IPIPE_DPC_RNEW1", + "30", + "OCP_ERR", + }; + unsigned int i; + + dev_dbg(iss->dev, "ISP IRQ: "); + + for (i = 0; i < ARRAY_SIZE(name); i++) { + if ((1 << i) & irqstatus) + pr_cont("%s ", name[i]); + } + pr_cont("\n"); +} +#endif + /* * iss_isr - Interrupt Service Routine for ISS module. * @irq: Not used currently. @@ -290,6 +339,10 @@ static irqreturn_t iss_isr(int irq, void *_iss) if (isp_irqstatus & resizer_events) omap4iss_resizer_isr(&iss->resizer, isp_irqstatus & resizer_events); + +#if defined(DEBUG) && defined(ISS_ISR_DEBUG) + iss_isp_isr_dbg(iss, isp_irqstatus); +#endif } omap4iss_flush(iss); -- cgit v0.10.2 From 380df42b5730541a740735052d61e9d2ee09d0ce Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Aug 2013 12:51:03 -0300 Subject: [media] v4l: omap4iss: Don't make IRQ debugging functions inline Let the compiler decide. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 65a1680..e6528fa 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -198,7 +198,7 @@ void omap4iss_configure_bridge(struct iss_device *iss, } #if defined(DEBUG) && defined(ISS_ISR_DEBUG) -static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) +static void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) { static const char * const name[] = { "ISP_0", @@ -245,7 +245,7 @@ static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) pr_cont("\n"); } -static inline void iss_isp_isr_dbg(struct iss_device *iss, u32 irqstatus) +static void iss_isp_isr_dbg(struct iss_device *iss, u32 irqstatus) { static const char * const name[] = { "ISIF_0", -- cgit v0.10.2 From cd782f9d6d6c4a713b5cc5ccc0bb65f86e294b2f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Aug 2013 13:40:57 -0300 Subject: [media] v4l: omap4iss: Fix operators precedence in ternary operators The ternary operator ? : has a low precedence. Use parenthesis where needed. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index c3a5fca..ac5868ac 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -274,7 +274,7 @@ static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr) */ static inline int is_usr_def_mapping(u32 format_id) { - return ((format_id & 0xF0) == 0x40) ? 1 : 0; + return (format_id & 0xF0) == 0x40 ? 1 : 0; } /* @@ -766,16 +766,11 @@ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) CSI2_IRQ_FIFO_OVF)) { dev_dbg(iss->dev, "CSI2 Err: OCP:%d SHORT:%d ECC:%d CPXIO:%d OVF:%d\n", - (csi2_irqstatus & - CSI2_IRQ_OCP_ERR) ? 1 : 0, - (csi2_irqstatus & - CSI2_IRQ_SHORT_PACKET) ? 1 : 0, - (csi2_irqstatus & - CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0, - (csi2_irqstatus & - CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0, - (csi2_irqstatus & - CSI2_IRQ_FIFO_OVF) ? 1 : 0); + csi2_irqstatus & CSI2_IRQ_OCP_ERR ? 1 : 0, + csi2_irqstatus & CSI2_IRQ_SHORT_PACKET ? 1 : 0, + csi2_irqstatus & CSI2_IRQ_ECC_NO_CORRECTION ? 1 : 0, + csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR ? 1 : 0, + csi2_irqstatus & CSI2_IRQ_FIFO_OVF ? 1 : 0); pipe->error = true; } @@ -1209,8 +1204,7 @@ static int csi2_link_setup(struct media_entity *entity, return -EINVAL; } - ctrl->vp_only_enable = - (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; + ctrl->vp_only_enable = csi2->output & CSI2_OUTPUT_MEMORY ? false : true; ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF); return 0; diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index fc38a5c5..bdafd78 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -75,7 +75,7 @@ static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable) writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN) & ~IPIPE_SRC_EN_EN) | - enable ? IPIPE_SRC_EN_EN : 0, + (enable ? IPIPE_SRC_EN_EN : 0), iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN); } diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 5464742..3d6cc88 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -85,7 +85,7 @@ static void ipipeif_write_enable(struct iss_ipipeif_device *ipipeif, u8 enable) writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & ~ISIF_SYNCEN_DWEN) | - enable ? ISIF_SYNCEN_DWEN : 0, + (enable ? ISIF_SYNCEN_DWEN : 0), iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); } @@ -100,7 +100,7 @@ static void ipipeif_enable(struct iss_ipipeif_device *ipipeif, u8 enable) writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & ~ISIF_SYNCEN_SYEN) | - enable ? ISIF_SYNCEN_SYEN : 0, + (enable ? ISIF_SYNCEN_SYEN : 0), iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); } diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 272b92a..68eb2a7 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -118,13 +118,13 @@ static void resizer_enable(struct iss_resizer_device *resizer, u8 enable) writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN) & ~RSZ_SRC_EN_SRC_EN) | - enable ? RSZ_SRC_EN_SRC_EN : 0, + (enable ? RSZ_SRC_EN_SRC_EN : 0), iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN); /* TODO: Enable RSZB */ writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) & ~RSZ_EN_EN) | - enable ? RSZ_EN_EN : 0, + (enable ? RSZ_EN_EN : 0), iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); } @@ -202,7 +202,7 @@ static void resizer_configure(struct iss_resizer_device *resizer) /* Select RSZ input */ writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0) & ~RSZ_SRC_FMT0_SEL) | - (resizer->input == RESIZER_INPUT_IPIPEIF) ? RSZ_SRC_FMT0_SEL : 0, + (resizer->input == RESIZER_INPUT_IPIPEIF ? RSZ_SRC_FMT0_SEL : 0), iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0); /* RSZ ignores WEN signal from IPIPE/IPIPEIF */ @@ -318,10 +318,8 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | ISP5_IRQ_RSZ_FIFO_OVF)) { dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n", - (events & - ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR) ? 1 : 0, - (events & - ISP5_IRQ_RSZ_FIFO_OVF) ? 1 : 0); + events & ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR ? 1 : 0, + events & ISP5_IRQ_RSZ_FIFO_OVF ? 1 : 0); pipe->error = true; } -- cgit v0.10.2 From 74536b2ff073ed53d9c449d838938d6e500819f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 29 Aug 2013 07:40:37 -0300 Subject: [media] v4l: omap4iss: isif: Ignore VD0 interrupts when no buffer is available The ISIF generates VD0 interrupts even when writes are disabled. Disabling the ISIF when no buffer is available is thus not be enough, we need to handle the situation explicitly. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 3d6cc88..47fb1d6 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -235,6 +235,13 @@ static void ipipeif_isr_buffer(struct iss_ipipeif_device *ipipeif) { struct iss_buffer *buffer; + /* The ISIF generates VD0 interrupts even when writes are disabled. + * deal with it anyway). Disabling the ISIF when no buffer is available + * is thus not be enough, we need to handle the situation explicitly. + */ + if (list_empty(&ipipeif->video_out.dmaqueue)) + return; + ipipeif_write_enable(ipipeif, 0); buffer = omap4iss_video_buffer_next(&ipipeif->video_out); -- cgit v0.10.2 From 86efc5043269df417d6145d912c87aafbed0c5c9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 Aug 2013 19:23:31 -0300 Subject: [media] v4l: omap4iss: ipipeif: Shift input data according to the input format Input samples must be left-aligned on the ISIF 16-bit data bus. Configure the 16-to-16-bit selector to shift data according to the input format. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 47fb1d6..2853851 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -129,6 +129,7 @@ static void ipipeif_set_outaddr(struct iss_ipipeif_device *ipipeif, u32 addr) static void ipipeif_configure(struct iss_ipipeif_device *ipipeif) { struct iss_device *iss = to_iss_device(ipipeif); + const struct iss_format_info *info; struct v4l2_mbus_framefmt *format; u32 isif_ccolp = 0; @@ -194,9 +195,10 @@ cont_raw: ISIF_MODESET_INPMOD_RAW | ISIF_MODESET_CCDW_2BIT, iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET); + info = omap4iss_video_format_info(format->code); writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD) & ~ISIF_CGAMMAWD_GWDI_MASK) | - ISIF_CGAMMAWD_GWDI_BIT11, + ISIF_CGAMMAWD_GWDI(info->bpp), iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD); /* Set RAW Bayer pattern */ diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index d969351..5995e62 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -368,7 +368,7 @@ #define ISIF_CGAMMAWD (0x0080) #define ISIF_CGAMMAWD_GWDI_MASK (0xF << 1) -#define ISIF_CGAMMAWD_GWDI_BIT11 (0x4 << 1) +#define ISIF_CGAMMAWD_GWDI(bpp) ((16 - (bpp)) << 1) #define ISIF_CCDCFG (0x0088) #define ISIF_CCDCFG_Y8POS (1 << 11) -- cgit v0.10.2 From 1be9ba20e1b3dccc7fb972f0f370ae27c0187718 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 29 Aug 2013 08:36:22 -0300 Subject: [media] v4l: omap4iss: csi2: Enable automatic ULP mode transition Automatically switch between ULP and ON states based on ULPM signal from complex I/O. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index 25e6f89..d5c7cec 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -63,8 +63,8 @@ static int csiphy_set_power(struct iss_csiphy *phy, u32 power) writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) & ~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) | - power, - phy->cfg_regs + CSI2_COMPLEXIO_CFG); + power | CSI2_COMPLEXIO_CFG_PWR_AUTO, + phy->cfg_regs + CSI2_COMPLEXIO_CFG); retry_count = 0; do { -- cgit v0.10.2 From 11abbfd30f74f79fe78d9ff79cc3fcfa86a975c5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 Aug 2013 22:23:17 -0300 Subject: [media] v4l: omap4iss: Create and use register access functions Replace the direct readl/writel calls with helper functions that take an ISS pointer and compute the register memory address. Also add bit clear, set and update helpers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index e6528fa..ba8460d 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -32,7 +32,7 @@ #define ISS_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###ISS " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_##name)) static void iss_print_status(struct iss_device *iss) { @@ -62,8 +62,8 @@ static void iss_print_status(struct iss_device *iss) */ void omap4iss_flush(struct iss_device *iss) { - writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); - readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION, 0); + iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); } /* @@ -75,8 +75,8 @@ static void iss_enable_interrupts(struct iss_device *iss) static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB | ISS_HL_IRQ_ISP(0); /* Enable HL interrupts */ - writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); - writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_SET(5)); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), hl_irq); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_SET(5), hl_irq); } @@ -86,7 +86,7 @@ static void iss_enable_interrupts(struct iss_device *iss) */ static void iss_disable_interrupts(struct iss_device *iss) { - writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_CLR(5)); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_CLR(5), -1); } /* @@ -102,8 +102,9 @@ void omap4iss_isp_enable_interrupts(struct iss_device *iss) ISP5_IRQ_ISIF_INT(0); /* Enable ISP interrupts */ - writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQSTATUS(0)); - writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_SET(0)); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), isp_irq); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_SET(0), + isp_irq); } /* @@ -112,7 +113,7 @@ void omap4iss_isp_enable_interrupts(struct iss_device *iss) */ void omap4iss_isp_disable_interrupts(struct iss_device *iss) { - writel(-1, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_CLR(0)); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_CLR(0), -1); } int omap4iss_get_external_info(struct iss_pipeline *pipe, @@ -169,11 +170,11 @@ void omap4iss_configure_bridge(struct iss_device *iss, u32 issctrl_val; u32 isp5ctrl_val; - issctrl_val = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); + issctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL); issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK; issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK; - isp5ctrl_val = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + isp5ctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL); switch (input) { case IPIPEIF_INPUT_CSI2A: @@ -193,8 +194,8 @@ void omap4iss_configure_bridge(struct iss_device *iss, isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT | ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE; - writel(issctrl_val, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); - writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL, issctrl_val); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, isp5ctrl_val); } #if defined(DEBUG) && defined(ISS_ISR_DEBUG) @@ -313,8 +314,8 @@ static irqreturn_t iss_isr(int irq, void *_iss) struct iss_device *iss = _iss; u32 irqstatus; - irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); - writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS(5)); + irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5)); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), irqstatus); if (irqstatus & ISS_HL_IRQ_CSIA) omap4iss_csi2_isr(&iss->csi2a); @@ -323,10 +324,10 @@ static irqreturn_t iss_isr(int irq, void *_iss) omap4iss_csi2_isr(&iss->csi2b); if (irqstatus & ISS_HL_IRQ_ISP(0)) { - u32 isp_irqstatus = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + - ISP5_IRQSTATUS(0)); - writel(isp_irqstatus, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + - ISP5_IRQSTATUS(0)); + u32 isp_irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, + ISP5_IRQSTATUS(0)); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), + isp_irqstatus); if (isp_irqstatus & ISP5_IRQ_OCP_ERR) dev_dbg(iss->dev, "ISP5 OCP Error!\n"); @@ -689,12 +690,11 @@ static int iss_reset(struct iss_device *iss) { unsigned long timeout = 0; - writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) | - ISS_HL_SYSCONFIG_SOFTRESET, - iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG); + iss_reg_set(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, + ISS_HL_SYSCONFIG_SOFTRESET); - while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) & - ISS_HL_SYSCONFIG_SOFTRESET) { + while (iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG) & + ISS_HL_SYSCONFIG_SOFTRESET) { if (timeout++ > 100) { dev_alert(iss->dev, "cannot reset ISS\n"); return -ETIMEDOUT; @@ -710,18 +710,15 @@ static int iss_isp_reset(struct iss_device *iss) unsigned long timeout = 0; /* Fist, ensure that the ISP is IDLE (no transactions happening) */ - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & - ~ISP5_SYSCONFIG_STANDBYMODE_MASK) | - ISP5_SYSCONFIG_STANDBYMODE_SMART, - iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG, + ISP5_SYSCONFIG_STANDBYMODE_MASK, + ISP5_SYSCONFIG_STANDBYMODE_SMART); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) | - ISP5_CTRL_MSTANDBY, - iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, ISP5_CTRL_MSTANDBY); for (;;) { - if (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & - ISP5_CTRL_MSTANDBY_WAIT) + if (iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL) & + ISP5_CTRL_MSTANDBY_WAIT) break; if (timeout++ > 1000) { dev_alert(iss->dev, "cannot set ISP5 to standby\n"); @@ -731,13 +728,12 @@ static int iss_isp_reset(struct iss_device *iss) } /* Now finally, do the reset */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) | - ISP5_SYSCONFIG_SOFTRESET, - iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG, + ISP5_SYSCONFIG_SOFTRESET); timeout = 0; - while (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & - ISP5_SYSCONFIG_SOFTRESET) { + while (iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG) & + ISP5_SYSCONFIG_SOFTRESET) { if (timeout++ > 1000) { dev_alert(iss->dev, "cannot reset ISP5\n"); return -ETIMEDOUT; @@ -848,15 +844,14 @@ static int __iss_subclk_update(struct iss_device *iss) if (iss->subclk_resources & OMAP4_ISS_SUBCLK_ISP) clk |= ISS_CLKCTRL_ISP; - writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) & - ~ISS_CLKCTRL_MASK) | clk, - iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL); + iss_reg_update(iss, OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, + ISS_CLKCTRL_MASK, clk); /* Wait for HW assertion */ while (--timeout > 0) { udelay(1); - if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) & - ISS_CLKCTRL_MASK) == clk) + if ((iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CLKSTAT) & + ISS_CLKCTRL_MASK) == clk) break; } @@ -911,9 +906,8 @@ static void __iss_isp_subclk_update(struct iss_device *iss) if (clk) clk |= ISP5_CTRL_BL_CLK_ENABLE; - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & - ~ISS_ISP5_CLKCTRL_MASK) | clk, - iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, + ISS_ISP5_CLKCTRL_MASK, clk); } void omap4iss_isp_subclk_enable(struct iss_device *iss, @@ -1380,7 +1374,7 @@ static int iss_probe(struct platform_device *pdev) if (ret < 0) goto error_iss; - iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + iss->revision = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); dev_info(iss->dev, "Revision %08x found\n", iss->revision); for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) { @@ -1390,9 +1384,9 @@ static int iss_probe(struct platform_device *pdev) } /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */ - writel((readl(iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL) & ~BTE_CTRL_BW_LIMITER_MASK) | - (18 << BTE_CTRL_BW_LIMITER_SHIFT), - iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL); + iss_reg_update(iss, OMAP4_ISS_MEM_BTE, BTE_CTRL, + BTE_CTRL_BW_LIMITER_MASK, + 18 << BTE_CTRL_BW_LIMITER_SHIFT); /* Perform ISP reset */ ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP); @@ -1404,7 +1398,7 @@ static int iss_probe(struct platform_device *pdev) goto error_iss; dev_info(iss->dev, "ISP Revision %08x found\n", - readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_REVISION)); + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_REVISION)); /* Interrupt */ iss->irq_num = platform_get_irq(pdev, 0); diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index f33664d..660809e 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -150,4 +150,84 @@ int omap4iss_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); void omap4iss_unregister_entities(struct platform_device *pdev); +/* + * iss_reg_read - Read the value of an OMAP4 ISS register + * @iss: the ISS device + * @res: memory resource in which the register is located + * @offset: register offset in the memory resource + * + * Return the register value. + */ +static inline +u32 iss_reg_read(struct iss_device *iss, enum iss_mem_resources res, + u32 offset) +{ + return readl(iss->regs[res] + offset); +} + +/* + * iss_reg_write - Write a value to an OMAP4 ISS register + * @iss: the ISS device + * @res: memory resource in which the register is located + * @offset: register offset in the memory resource + * @value: value to be written + */ +static inline +void iss_reg_write(struct iss_device *iss, enum iss_mem_resources res, + u32 offset, u32 value) +{ + writel(value, iss->regs[res] + offset); +} + +/* + * iss_reg_clr - Clear bits in an OMAP4 ISS register + * @iss: the ISS device + * @res: memory resource in which the register is located + * @offset: register offset in the memory resource + * @clr: bit mask to be cleared + */ +static inline +void iss_reg_clr(struct iss_device *iss, enum iss_mem_resources res, + u32 offset, u32 clr) +{ + u32 v = iss_reg_read(iss, res, offset); + + iss_reg_write(iss, res, offset, v & ~clr); +} + +/* + * iss_reg_set - Set bits in an OMAP4 ISS register + * @iss: the ISS device + * @res: memory resource in which the register is located + * @offset: register offset in the memory resource + * @set: bit mask to be set + */ +static inline +void iss_reg_set(struct iss_device *iss, enum iss_mem_resources res, + u32 offset, u32 set) +{ + u32 v = iss_reg_read(iss, res, offset); + + iss_reg_write(iss, res, offset, v | set); +} + +/* + * iss_reg_update - Clear and set bits in an OMAP4 ISS register + * @iss: the ISS device + * @res: memory resource in which the register is located + * @offset: register offset in the memory resource + * @clr: bit mask to be cleared + * @set: bit mask to be set + * + * Clear the clr mask first and then set the set mask. + */ +static inline +void iss_reg_update(struct iss_device *iss, enum iss_mem_resources res, + u32 offset, u32 clr, u32 set) +{ + u32 v = iss_reg_read(iss, res, offset); + + iss_reg_write(iss, res, offset, (v & ~clr) | set); +} + #endif /* _OMAP4_ISS_H_ */ diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index bdafd78..d0b9f8c 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -42,7 +42,7 @@ static const unsigned int ipipe_fmts[] = { */ #define IPIPE_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###IPIPE " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_##name)) static void ipipe_print_status(struct iss_ipipe_device *ipipe) { @@ -73,10 +73,8 @@ static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable) { struct iss_device *iss = to_iss_device(ipipe); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN) & - ~IPIPE_SRC_EN_EN) | - (enable ? IPIPE_SRC_EN_EN : 0), - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_EN); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_EN, + IPIPE_SRC_EN_EN, enable ? IPIPE_SRC_EN_EN : 0); } /* ----------------------------------------------------------------------------- @@ -92,31 +90,28 @@ static void ipipe_configure(struct iss_ipipe_device *ipipe) format = &ipipe->formats[IPIPE_PAD_SINK]; /* NOTE: Currently just supporting pipeline IN: RGB, OUT: YUV422 */ - writel(IPIPE_SRC_FMT_RAW2YUV, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_FMT); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_FMT, + IPIPE_SRC_FMT_RAW2YUV); /* Enable YUV444 -> YUV422 conversion */ - writel(IPIPE_YUV_PHS_LPF, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_YUV_PHS); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_YUV_PHS, + IPIPE_YUV_PHS_LPF); - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_VPS); - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_HPS); - writel((format->height - 2) & IPIPE_SRC_VSZ_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_VSZ); - writel((format->width - 1) & IPIPE_SRC_HSZ_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_HSZ); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VPS, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HPS, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VSZ, + (format->height - 2) & IPIPE_SRC_VSZ_MASK); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HSZ, + (format->width - 1) & IPIPE_SRC_HSZ_MASK); /* Ignore ipipeif_wrt signal, and operate on-the-fly. */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_MODE) & - ~(IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST), - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_MODE); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_MODE, + IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST); /* HACK: Values tuned for Ducati SW (OV) */ - writel(IPIPE_SRC_COL_EE_B | - IPIPE_SRC_COL_EO_GB | - IPIPE_SRC_COL_OE_GR | - IPIPE_SRC_COL_OO_R, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_SRC_COL); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_COL, + IPIPE_SRC_COL_EE_B | IPIPE_SRC_COL_EO_GB | + IPIPE_SRC_COL_OE_GR | IPIPE_SRC_COL_OO_R); /* IPIPE_PAD_SOURCE_VP */ format = &ipipe->formats[IPIPE_PAD_SOURCE_VP]; @@ -147,15 +142,13 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE); /* Enable clk_arm_g0 */ - writel(IPIPE_GCK_MMR_REG, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_GCK_MMR); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_MMR, + IPIPE_GCK_MMR_REG); /* Enable clk_pix_g[3:0] */ - writel(IPIPE_GCK_PIX_G3 | - IPIPE_GCK_PIX_G2 | - IPIPE_GCK_PIX_G1 | - IPIPE_GCK_PIX_G0, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPE] + IPIPE_GCK_PIX); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_PIX, + IPIPE_GCK_PIX_G3 | IPIPE_GCK_PIX_G2 | + IPIPE_GCK_PIX_G1 | IPIPE_GCK_PIX_G0); } switch (enable) { diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 2853851..2d11f62 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -40,15 +40,15 @@ static const unsigned int ipipeif_fmts[] = { */ #define IPIPEIF_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###IPIPEIF " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_##name)) #define ISIF_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###ISIF " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_##name)) #define ISP5_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###ISP5 " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_##name)) static void ipipeif_print_status(struct iss_ipipeif_device *ipipeif) { @@ -83,10 +83,8 @@ static void ipipeif_write_enable(struct iss_ipipeif_device *ipipeif, u8 enable) { struct iss_device *iss = to_iss_device(ipipeif); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & - ~ISIF_SYNCEN_DWEN) | - (enable ? ISIF_SYNCEN_DWEN : 0), - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SYNCEN, + ISIF_SYNCEN_DWEN, enable ? ISIF_SYNCEN_DWEN : 0); } /* @@ -98,10 +96,8 @@ static void ipipeif_enable(struct iss_ipipeif_device *ipipeif, u8 enable) { struct iss_device *iss = to_iss_device(ipipeif); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN) & - ~ISIF_SYNCEN_SYEN) | - (enable ? ISIF_SYNCEN_SYEN : 0), - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SYNCEN); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SYNCEN, + ISIF_SYNCEN_SYEN, enable ? ISIF_SYNCEN_SYEN : 0); } /* ----------------------------------------------------------------------------- @@ -120,10 +116,10 @@ static void ipipeif_set_outaddr(struct iss_ipipeif_device *ipipeif, u32 addr) struct iss_device *iss = to_iss_device(ipipeif); /* Save address splitted in Base Address H & L */ - writel((addr >> (16 + 5)) & ISIF_CADU_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CADU); - writel((addr >> 5) & ISIF_CADL_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CADL); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CADU, + (addr >> (16 + 5)) & ISIF_CADU_MASK); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CADL, + (addr >> 5) & ISIF_CADL_MASK); } static void ipipeif_configure(struct iss_ipipeif_device *ipipeif) @@ -139,25 +135,20 @@ static void ipipeif_configure(struct iss_ipipeif_device *ipipeif) format = &ipipeif->formats[IPIPEIF_PAD_SINK]; /* IPIPEIF with YUV422 input from ISIF */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG1) & - ~(IPIPEIF_CFG1_INPSRC1_MASK | IPIPEIF_CFG1_INPSRC2_MASK), - iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG1); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG1, + IPIPEIF_CFG1_INPSRC1_MASK | IPIPEIF_CFG1_INPSRC2_MASK); /* Select ISIF/IPIPEIF input format */ switch (format->code) { case V4L2_MBUS_FMT_UYVY8_1X16: case V4L2_MBUS_FMT_YUYV8_1X16: - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET) & - ~(ISIF_MODESET_CCDMD | - ISIF_MODESET_INPMOD_MASK | - ISIF_MODESET_CCDW_MASK)) | - ISIF_MODESET_INPMOD_YCBCR16, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET); - - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2) & - ~IPIPEIF_CFG2_YUV8) | - IPIPEIF_CFG2_YUV16, - iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_MODESET, + ISIF_MODESET_CCDMD | ISIF_MODESET_INPMOD_MASK | + ISIF_MODESET_CCDW_MASK, + ISIF_MODESET_INPMOD_YCBCR16); + + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG2, + IPIPEIF_CFG2_YUV8, IPIPEIF_CFG2_YUV16); break; case V4L2_MBUS_FMT_SGRBG10_1X10: @@ -184,44 +175,41 @@ static void ipipeif_configure(struct iss_ipipeif_device *ipipeif) ISIF_CCOLP_CP2_F0_R | ISIF_CCOLP_CP3_F0_GR; cont_raw: - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2) & - ~IPIPEIF_CFG2_YUV16), - iss->regs[OMAP4_ISS_MEM_ISP_IPIPEIF] + IPIPEIF_CFG2); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG2, + IPIPEIF_CFG2_YUV16); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET) & - ~(ISIF_MODESET_CCDMD | - ISIF_MODESET_INPMOD_MASK | - ISIF_MODESET_CCDW_MASK)) | - ISIF_MODESET_INPMOD_RAW | ISIF_MODESET_CCDW_2BIT, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_MODESET); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_MODESET, + ISIF_MODESET_CCDMD | ISIF_MODESET_INPMOD_MASK | + ISIF_MODESET_CCDW_MASK, ISIF_MODESET_INPMOD_RAW | + ISIF_MODESET_CCDW_2BIT); info = omap4iss_video_format_info(format->code); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD) & - ~ISIF_CGAMMAWD_GWDI_MASK) | - ISIF_CGAMMAWD_GWDI(info->bpp), - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CGAMMAWD); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CGAMMAWD, + ISIF_CGAMMAWD_GWDI_MASK, + ISIF_CGAMMAWD_GWDI(info->bpp)); /* Set RAW Bayer pattern */ - writel(isif_ccolp, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_CCOLP); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CCOLP, + isif_ccolp); break; } - writel(0 & ISIF_SPH_MASK, iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_SPH); - writel((format->width - 1) & ISIF_LNH_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_LNH); - writel((format->height - 1) & ISIF_LNV_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_LNV); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SPH, 0 & ISIF_SPH_MASK); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_LNH, + (format->width - 1) & ISIF_LNH_MASK); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_LNV, + (format->height - 1) & ISIF_LNV_MASK); /* Generate ISIF0 on the last line of the image */ - writel(format->height - 1, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_VDINT(0)); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_VDINT(0), + format->height - 1); /* IPIPEIF_PAD_SOURCE_ISIF_SF */ format = &ipipeif->formats[IPIPEIF_PAD_SOURCE_ISIF_SF]; - writel((ipipeif->video_out.bpl_value >> 5) & ISIF_HSIZE_HSIZE_MASK, - iss->regs[OMAP4_ISS_MEM_ISP_ISIF] + ISIF_HSIZE); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_HSIZE, + (ipipeif->video_out.bpl_value >> 5) & + ISIF_HSIZE_HSIZE_MASK); /* IPIPEIF_PAD_SOURCE_VP */ /* Do nothing? */ diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 68eb2a7..793325c 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -36,11 +36,11 @@ static const unsigned int resizer_fmts[] = { */ #define RSZ_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###RSZ " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_##name)) #define RZA_PRINT_REGISTER(iss, name)\ dev_dbg(iss->dev, "###RZA " #name "=0x%08x\n", \ - readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_##name)) + iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_##name)) static void resizer_print_status(struct iss_resizer_device *resizer) { @@ -116,16 +116,12 @@ static void resizer_enable(struct iss_resizer_device *resizer, u8 enable) { struct iss_device *iss = to_iss_device(resizer); - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN) & - ~RSZ_SRC_EN_SRC_EN) | - (enable ? RSZ_SRC_EN_SRC_EN : 0), - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_EN); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_EN, + RSZ_SRC_EN_SRC_EN, enable ? RSZ_SRC_EN_SRC_EN : 0); /* TODO: Enable RSZB */ - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) & - ~RSZ_EN_EN) | - (enable ? RSZ_EN_EN : 0), - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN, + enable ? RSZ_EN_EN : 0); } /* ----------------------------------------------------------------------------- @@ -148,16 +144,16 @@ static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr) outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM]; /* Save address splitted in Base Address H & L */ - writel((addr >> 16) & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_BAD_H); - writel(addr & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_BAD_L); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_H, + (addr >> 16) & 0xffff); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_L, + addr & 0xffff); /* SAD = BAD */ - writel((addr >> 16) & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_SAD_H); - writel(addr & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_SAD_L); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_H, + (addr >> 16) & 0xffff); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_L, + addr & 0xffff); /* Program UV buffer address... Hardcoded to be contiguous! */ if ((informat->code == V4L2_MBUS_FMT_UYVY8_1X16) && @@ -173,16 +169,16 @@ static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr) } /* Save address splitted in Base Address H & L */ - writel((c_addr >> 16) & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_BAD_H); - writel(c_addr & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_BAD_L); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_H, + (c_addr >> 16) & 0xffff); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_L, + c_addr & 0xffff); /* SAD = BAD */ - writel((c_addr >> 16) & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_SAD_H); - writel(c_addr & 0xffff, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_SAD_L); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_H, + (c_addr >> 16) & 0xffff); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_L, + c_addr & 0xffff); } } @@ -195,70 +191,70 @@ static void resizer_configure(struct iss_resizer_device *resizer) outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM]; /* Make sure we don't bypass the resizer */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0) & - ~RSZ_SRC_FMT0_BYPASS, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0, + RSZ_SRC_FMT0_BYPASS); /* Select RSZ input */ - writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0) & - ~RSZ_SRC_FMT0_SEL) | - (resizer->input == RESIZER_INPUT_IPIPEIF ? RSZ_SRC_FMT0_SEL : 0), - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_FMT0); + iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0, + RSZ_SRC_FMT0_SEL, + resizer->input == RESIZER_INPUT_IPIPEIF ? + RSZ_SRC_FMT0_SEL : 0); /* RSZ ignores WEN signal from IPIPE/IPIPEIF */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE) & - ~RSZ_SRC_MODE_WRT, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE, + RSZ_SRC_MODE_WRT); /* Set Resizer in free-running mode */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE) & - ~RSZ_SRC_MODE_OST, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_MODE); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE, + RSZ_SRC_MODE_OST); /* Init Resizer A */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_MODE) & - ~RZA_MODE_ONE_SHOT, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_MODE); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_MODE, + RZA_MODE_ONE_SHOT); /* Set size related things now */ - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_VPS); - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_HPS); - writel(informat->height - 2, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_VSZ); - writel(informat->width - 1, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SRC_HSZ); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VPS, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HPS, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VSZ, + informat->height - 2); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HSZ, + informat->width - 1); - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_I_VPS); - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_I_HPS); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_VPS, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_HPS, 0); - writel(outformat->height - 2, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_O_VSZ); - writel(outformat->width - 1, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_O_HSZ); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_VSZ, + outformat->height - 2); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_HSZ, + outformat->width - 1); - writel(0x100, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_V_DIF); - writel(0x100, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_H_DIF); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_V_DIF, 0x100); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_H_DIF, 0x100); /* Buffer output settings */ - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_PTR_S); - writel(outformat->height - 1, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_PTR_E); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_S, 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_E, + outformat->height - 1); - writel(resizer->video_out.bpl_value, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_Y_OFT); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_OFT, + resizer->video_out.bpl_value); /* UYVY -> NV12 conversion */ if ((informat->code == V4L2_MBUS_FMT_UYVY8_1X16) && (outformat->code == V4L2_MBUS_FMT_YUYV8_1_5X8)) { - writel(RSZ_420_CEN | RSZ_420_YEN, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_420); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, + RSZ_420_CEN | RSZ_420_YEN); /* UV Buffer output settings */ - writel(0, iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_PTR_S); - writel(outformat->height - 1, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_PTR_E); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_S, + 0); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_E, + outformat->height - 1); - writel(resizer->video_out.bpl_value, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_SDR_C_OFT); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_OFT, + resizer->video_out.bpl_value); } else { - writel(0, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_420); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, 0); } omap4iss_isp_enable_interrupts(iss); @@ -273,9 +269,7 @@ static void resizer_isr_buffer(struct iss_resizer_device *resizer) struct iss_device *iss = to_iss_device(resizer); struct iss_buffer *buffer; - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) & - ~RSZ_EN_EN, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN); buffer = omap4iss_video_buffer_next(&resizer->video_out); if (buffer == NULL) @@ -283,9 +277,7 @@ static void resizer_isr_buffer(struct iss_resizer_device *resizer) resizer_set_outaddr(resizer, buffer->iss_addr); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN) | - RSZ_EN_EN, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RZA_EN); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN); } /* @@ -386,17 +378,14 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR) | - RSZ_GCK_MMR_MMR, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR) | - RSZ_GCK_SDR_CORE, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR, + RSZ_GCK_MMR_MMR); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR, + RSZ_GCK_SDR_CORE); /* FIXME: Enable RSZB also */ - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG) | - RSZ_SYSCONFIG_RSZA_CLK_EN, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG); + iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG, + RSZ_SYSCONFIG_RSZA_CLK_EN); } switch (enable) { @@ -430,15 +419,12 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) resizer_enable(resizer, 0); omap4iss_isp_disable_interrupts(iss); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG) & - ~RSZ_SYSCONFIG_RSZA_CLK_EN, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_SYSCONFIG); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR) & - ~RSZ_GCK_SDR_CORE, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_SDR); - writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR) & - ~RSZ_GCK_MMR_MMR, - iss->regs[OMAP4_ISS_MEM_ISP_RESIZER] + RSZ_GCK_MMR); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG, + RSZ_SYSCONFIG_RSZA_CLK_EN); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR, + RSZ_GCK_SDR_CORE); + iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR, + RSZ_GCK_MMR_MMR); omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ); iss_video_dmaqueue_flags_clr(video_out); break; -- cgit v0.10.2 From 97059524ba6fd6c7dc77fa97e6957501b85af3be Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 Aug 2013 22:23:17 -0300 Subject: [media] v4l: omap4iss: csi: Create and use register access functions Replace the direct readl/writel calls with helper functions. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index ac5868ac..f8d6472 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -29,9 +29,8 @@ static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable) { struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl; - writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) | - (enable ? CSI2_CTRL_IF_EN : 0), - csi2->regs1 + CSI2_CTRL); + iss_reg_update(csi2->iss, csi2->regs1, CSI2_CTRL, CSI2_CTRL_IF_EN, + enable ? CSI2_CTRL_IF_EN : 0); currctrl->if_enable = enable; } @@ -90,7 +89,7 @@ static void csi2_recv_config(struct iss_csi2_device *csi2, */ reg |= CSI2_CTRL_ENDIANNESS; - writel(reg, csi2->regs1 + CSI2_CTRL); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTRL, reg); } static const unsigned int csi2_input_fmts[] = { @@ -260,10 +259,10 @@ static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr) ctx->ping_addr = addr; ctx->pong_addr = addr; - writel(ctx->ping_addr, - csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum)); - writel(ctx->pong_addr, - csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PING_ADDR(ctx->ctxnum), + ctx->ping_addr); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PONG_ADDR(ctx->ctxnum), + ctx->pong_addr); } /* @@ -288,7 +287,7 @@ static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable) struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; u32 reg; - reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum)); + reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum)); if (enable) { unsigned int skip = 0; @@ -306,7 +305,7 @@ static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable) reg &= ~CSI2_CTX_CTRL1_CTX_EN; } - writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum), reg); ctx->enabled = enable; } @@ -330,7 +329,7 @@ static void csi2_ctx_config(struct iss_csi2_device *csi2, if (ctx->checksum_enabled) reg |= CSI2_CTX_CTRL1_CS_EN; - writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctx->ctxnum), reg); /* Set up CSI2_CTx_CTRL2 */ reg = ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; @@ -342,23 +341,20 @@ static void csi2_ctx_config(struct iss_csi2_device *csi2, if (is_usr_def_mapping(ctx->format_id)) reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; - writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL2(ctx->ctxnum), reg); /* Set up CSI2_CTx_CTRL3 */ - writel(ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT, - csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL3(ctx->ctxnum), + ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT); /* Set up CSI2_CTx_DAT_OFST */ - reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum)); - reg &= ~CSI2_CTX_DAT_OFST_MASK; - reg |= ctx->data_offset; - writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum)); + iss_reg_update(csi2->iss, csi2->regs1, CSI2_CTX_DAT_OFST(ctx->ctxnum), + CSI2_CTX_DAT_OFST_MASK, ctx->data_offset); - writel(ctx->ping_addr, - csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum)); - - writel(ctx->pong_addr, - csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PING_ADDR(ctx->ctxnum), + ctx->ping_addr); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PONG_ADDR(ctx->ctxnum), + ctx->pong_addr); } /* @@ -370,7 +366,7 @@ static void csi2_timing_config(struct iss_csi2_device *csi2, { u32 reg; - reg = readl(csi2->regs1 + CSI2_TIMING); + reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_TIMING); if (timing->force_rx_mode) reg |= CSI2_TIMING_FORCE_RX_MODE_IO1; @@ -391,7 +387,7 @@ static void csi2_timing_config(struct iss_csi2_device *csi2, reg |= timing->stop_state_counter << CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT; - writel(reg, csi2->regs1 + CSI2_TIMING); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_TIMING, reg); } /* @@ -407,14 +403,14 @@ static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable) reg |= CSI2_CTX_IRQ_FS; for (i = 0; i < 8; i++) { - writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(i), + reg); if (enable) - writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg, - csi2->regs1 + CSI2_CTX_IRQENABLE(i)); + iss_reg_set(csi2->iss, csi2->regs1, + CSI2_CTX_IRQENABLE(i), reg); else - writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) & - ~reg, - csi2->regs1 + CSI2_CTX_IRQENABLE(i)); + iss_reg_clr(csi2->iss, csi2->regs1, + CSI2_CTX_IRQENABLE(i), reg); } } @@ -452,12 +448,13 @@ static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable) CSI2_COMPLEXIO_IRQ_ERRESC1 | CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 | CSI2_COMPLEXIO_IRQ_ERRSOTHS1; - writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS, reg); if (enable) - reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE); + iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQENABLE, + reg); else - reg = 0; - writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQENABLE, + 0); } /* @@ -474,13 +471,11 @@ static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable) CSI2_IRQ_COMPLEXIO_ERR | CSI2_IRQ_FIFO_OVF | CSI2_IRQ_CONTEXT0; - writel(reg, csi2->regs1 + CSI2_IRQSTATUS); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, reg); if (enable) - reg |= readl(csi2->regs1 + CSI2_IRQENABLE); + iss_reg_set(csi2->iss, csi2->regs1, CSI2_IRQENABLE, reg); else - reg = 0; - - writel(reg, csi2->regs1 + CSI2_IRQENABLE); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQENABLE, 0); } /* @@ -502,13 +497,12 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) if (csi2->phy->phy_in_use) return -EBUSY; - writel(readl(csi2->regs1 + CSI2_SYSCONFIG) | - CSI2_SYSCONFIG_SOFT_RESET, - csi2->regs1 + CSI2_SYSCONFIG); + iss_reg_set(csi2->iss, csi2->regs1, CSI2_SYSCONFIG, + CSI2_SYSCONFIG_SOFT_RESET); do { - reg = readl(csi2->regs1 + CSI2_SYSSTATUS) & - CSI2_SYSSTATUS_RESET_DONE; + reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_SYSSTATUS) + & CSI2_SYSSTATUS_RESET_DONE; if (reg == CSI2_SYSSTATUS_RESET_DONE) break; soft_reset_retries++; @@ -522,13 +516,12 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) return -EBUSY; } - writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) | - CSI2_COMPLEXIO_CFG_RESET_CTRL, - csi2->regs1 + CSI2_COMPLEXIO_CFG); + iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_CFG, + CSI2_COMPLEXIO_CFG_RESET_CTRL); i = 100; do { - reg = readl(csi2->phy->phy_regs + REGISTER1) + reg = iss_reg_read(csi2->iss, csi2->phy->phy_regs, REGISTER1) & REGISTER1_RESET_DONE_CTRLCLK; if (reg == REGISTER1_RESET_DONE_CTRLCLK) break; @@ -541,11 +534,10 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) return -EBUSY; } - writel((readl(csi2->regs1 + CSI2_SYSCONFIG) & - ~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK | - CSI2_SYSCONFIG_AUTO_IDLE)) | - CSI2_SYSCONFIG_MSTANDBY_MODE_NO, - csi2->regs1 + CSI2_SYSCONFIG); + iss_reg_update(csi2->iss, csi2->regs1, CSI2_SYSCONFIG, + CSI2_SYSCONFIG_MSTANDBY_MODE_MASK | + CSI2_SYSCONFIG_AUTO_IDLE, + CSI2_SYSCONFIG_MSTANDBY_MODE_NO); return 0; } @@ -627,7 +619,7 @@ static int csi2_configure(struct iss_csi2_device *csi2) */ #define CSI2_PRINT_REGISTER(iss, regs, name)\ dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \ - readl(regs + CSI2_##name)) + iss_reg_read(iss, regs, CSI2_##name)) static void csi2_print_status(struct iss_csi2_device *csi2) { @@ -695,8 +687,8 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2, unsigned int n = ctx->ctxnum; u32 status; - status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n)); - writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n)); + status = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n)); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n), status); /* Propagate frame number */ if (status & CSI2_CTX_IRQ_FS) { @@ -745,15 +737,15 @@ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) if (!csi2->available) return; - csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS); - writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS); + csi2_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_IRQSTATUS); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, csi2_irqstatus); /* Failure Cases */ if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) { - cpxio1_irqstatus = readl(csi2->regs1 + - CSI2_COMPLEXIO_IRQSTATUS); - writel(cpxio1_irqstatus, - csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS); + cpxio1_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, + CSI2_COMPLEXIO_IRQSTATUS); + iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS, + cpxio1_irqstatus); dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ %x\n", cpxio1_irqstatus); pipe->error = true; @@ -1319,7 +1311,7 @@ int omap4iss_csi2_init(struct iss_device *iss) csi2a->iss = iss; csi2a->available = 1; - csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1]; + csi2a->regs1 = OMAP4_ISS_MEM_CSI2_A_REGS1; csi2a->phy = &iss->csiphy1; csi2a->state = ISS_PIPELINE_STREAM_STOPPED; init_waitqueue_head(&csi2a->wait); @@ -1330,7 +1322,7 @@ int omap4iss_csi2_init(struct iss_device *iss) csi2b->iss = iss; csi2b->available = 1; - csi2b->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_B_REGS1]; + csi2b->regs1 = OMAP4_ISS_MEM_CSI2_B_REGS1; csi2b->phy = &iss->csiphy2; csi2b->state = ISS_PIPELINE_STREAM_STOPPED; init_waitqueue_head(&csi2b->wait); diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index d1d077b..69a6263 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -128,9 +128,9 @@ struct iss_csi2_device { u8 available; /* Is the IP present on the silicon? */ - /* Pointer to register remaps into kernel space */ - void __iomem *regs1; - void __iomem *regs2; + /* memory resources, as defined in enum iss_mem_resources */ + unsigned int regs1; + unsigned int regs2; u32 output; /* output to IPIPEIF, memory or both? */ bool dpcm_decompress; diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index d5c7cec..902391a 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -31,7 +31,7 @@ static void csiphy_lanes_config(struct iss_csiphy *phy) unsigned int i; u32 reg; - reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG); + reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG); for (i = 0; i < phy->max_data_lanes; i++) { reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) | @@ -47,7 +47,7 @@ static void csiphy_lanes_config(struct iss_csiphy *phy) reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0; reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT; - writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG); + iss_reg_write(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, reg); } /* @@ -61,16 +61,15 @@ static int csiphy_set_power(struct iss_csiphy *phy, u32 power) u32 reg; u8 retry_count; - writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) & - ~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) | - power | CSI2_COMPLEXIO_CFG_PWR_AUTO, - phy->cfg_regs + CSI2_COMPLEXIO_CFG); + iss_reg_update(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, + CSI2_COMPLEXIO_CFG_PWD_CMD_MASK, + power | CSI2_COMPLEXIO_CFG_PWR_AUTO); retry_count = 0; do { udelay(1); - reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) & - CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK; + reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG) + & CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK; if (reg != power >> 2) retry_count++; @@ -98,7 +97,7 @@ static void csiphy_dphy_config(struct iss_csiphy *phy) reg = phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT; reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT; - writel(reg, phy->phy_regs + REGISTER0); + iss_reg_write(phy->iss, phy->phy_regs, REGISTER0, reg); /* Set up REGISTER1 */ reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT; @@ -106,7 +105,7 @@ static void csiphy_dphy_config(struct iss_csiphy *phy) reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT; reg |= 0xB8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT; - writel(reg, phy->phy_regs + REGISTER1); + iss_reg_write(phy->iss, phy->phy_regs, REGISTER1, reg); } /* @@ -264,16 +263,16 @@ int omap4iss_csiphy_init(struct iss_device *iss) phy1->csi2 = &iss->csi2a; phy1->max_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES; phy1->used_data_lanes = 0; - phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1]; - phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1]; + phy1->cfg_regs = OMAP4_ISS_MEM_CSI2_A_REGS1; + phy1->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE1; mutex_init(&phy1->mutex); phy2->iss = iss; phy2->csi2 = &iss->csi2b; phy2->max_data_lanes = ISS_CSIPHY2_NUM_DATA_LANES; phy2->used_data_lanes = 0; - phy2->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_B_REGS1]; - phy2->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE2]; + phy2->cfg_regs = OMAP4_ISS_MEM_CSI2_B_REGS1; + phy2->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE2; mutex_init(&phy2->mutex); return 0; diff --git a/drivers/staging/media/omap4iss/iss_csiphy.h b/drivers/staging/media/omap4iss/iss_csiphy.h index df63eda..e9ca439 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.h +++ b/drivers/staging/media/omap4iss/iss_csiphy.h @@ -32,9 +32,9 @@ struct iss_csiphy { u8 phy_in_use; struct iss_csi2_device *csi2; - /* Pointer to register remaps into kernel space */ - void __iomem *cfg_regs; - void __iomem *phy_regs; + /* memory resources, as defined in enum iss_mem_resources */ + unsigned int cfg_regs; + unsigned int phy_regs; u8 max_data_lanes; /* number of CSI2 Data Lanes supported */ u8 used_data_lanes; /* number of CSI2 Data Lanes used */ -- cgit v0.10.2 From 82043ff6af1300ba6cdc868adbafbc5ee6965a89 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 4 Sep 2013 09:48:20 -0300 Subject: [media] v4l: omap4iss: resizer: Stop the whole resizer to avoid FIFO overflows When stopping the resizer due to a buffer underrun, disabling RZA only produces input FIFO overflows, most probably when the next frame is received. Disable the whole resizer to work around the problem. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 793325c..5bf5080 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -266,10 +266,12 @@ static void resizer_configure(struct iss_resizer_device *resizer) static void resizer_isr_buffer(struct iss_resizer_device *resizer) { - struct iss_device *iss = to_iss_device(resizer); struct iss_buffer *buffer; - iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN); + /* The whole resizer needs to be stopped. Disabling RZA only produces + * input FIFO overflows, most probably when the next frame is received. + */ + resizer_enable(resizer, 0); buffer = omap4iss_video_buffer_next(&resizer->video_out); if (buffer == NULL) @@ -277,7 +279,7 @@ static void resizer_isr_buffer(struct iss_resizer_device *resizer) resizer_set_outaddr(resizer, buffer->iss_addr); - iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN); + resizer_enable(resizer, 1); } /* -- cgit v0.10.2 From 3c4ee96b5fd5bb0223965fda97918fdd353d240c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 4 Sep 2013 12:32:12 -0300 Subject: [media] v4l: omap4iss: Convert hexadecimal constants to lower case The Linux kernel recommends lower case for hexadecimal constants. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index f8d6472..077545f 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -273,7 +273,7 @@ static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr) */ static inline int is_usr_def_mapping(u32 format_id) { - return (format_id & 0xF0) == 0x40 ? 1 : 0; + return (format_id & 0xf0) == 0x40 ? 1 : 0; } /* @@ -572,7 +572,7 @@ static int csi2_configure(struct iss_csi2_device *csi2) timing->force_rx_mode = 1; timing->stop_state_16x = 1; timing->stop_state_4x = 1; - timing->stop_state_counter = 0x1FF; + timing->stop_state_counter = 0x1ff; /* * The CSI2 receiver can't do any format conversion except DPCM diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index 902391a..7c3d55d 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -103,7 +103,7 @@ static void csiphy_dphy_config(struct iss_csiphy *phy) reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT; reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT; reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT; - reg |= 0xB8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT; + reg |= 0xb8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT; iss_reg_write(phy->iss, phy->phy_regs, REGISTER1, reg); } @@ -150,7 +150,7 @@ int omap4iss_csiphy_config(struct iss_device *iss, /* NOTE: Leave CSIPHY1 config to 0x0: D-PHY mode */ /* Enable all lanes for now */ cam_rx_ctrl |= - 0x1F << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT; + 0x1f << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT; /* Enable CTRLCLK */ cam_rx_ctrl |= OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK; } diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index 5995e62..efd0291 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -65,7 +65,7 @@ #define ISS_CLKSTAT_ISP (1 << 1) #define ISS_CLKSTAT_SIMCOP (1 << 0) -#define ISS_PM_STATUS 0x8C +#define ISS_PM_STATUS 0x8c #define ISS_PM_STATUS_CBUFF_PM_MASK (3 << 12) #define ISS_PM_STATUS_BTE_PM_MASK (3 << 10) #define ISS_PM_STATUS_SIMCOP_PM_MASK (3 << 8) @@ -76,20 +76,20 @@ #define REGISTER0 0x0 #define REGISTER0_HSCLOCKCONFIG (1 << 24) -#define REGISTER0_THS_TERM_MASK (0xFF << 8) +#define REGISTER0_THS_TERM_MASK (0xff << 8) #define REGISTER0_THS_TERM_SHIFT 8 -#define REGISTER0_THS_SETTLE_MASK (0xFF << 0) +#define REGISTER0_THS_SETTLE_MASK (0xff << 0) #define REGISTER0_THS_SETTLE_SHIFT 0 #define REGISTER1 0x4 #define REGISTER1_RESET_DONE_CTRLCLK (1 << 29) #define REGISTER1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) -#define REGISTER1_TCLK_TERM_MASK (0x3F << 18) +#define REGISTER1_TCLK_TERM_MASK (0x3f << 18) #define REGISTER1_TCLK_TERM_SHIFT 18 #define REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT 10 #define REGISTER1_CTRLCLK_DIV_FACTOR_MASK (0x3 << 8) #define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT 8 -#define REGISTER1_TCLK_SETTLE_MASK (0xFF << 0) +#define REGISTER1_TCLK_SETTLE_MASK (0xff << 0) #define REGISTER1_TCLK_SETTLE_SHIFT 0 #define REGISTER2 0x8 @@ -106,7 +106,7 @@ #define CSI2_SYSSTATUS_RESET_DONE (1 << 0) #define CSI2_IRQSTATUS 0x18 -#define CSI2_IRQENABLE 0x1C +#define CSI2_IRQENABLE 0x1c /* Shared bits across CSI2_IRQENABLE and IRQSTATUS */ @@ -159,7 +159,7 @@ #define CSI2_COMPLEXIO_IRQSTATUS 0x54 -#define CSI2_SHORT_PACKET 0x5C +#define CSI2_SHORT_PACKET 0x5c #define CSI2_COMPLEXIO_IRQENABLE 0x60 @@ -194,18 +194,18 @@ #define CSI2_DBG_P 0x68 -#define CSI2_TIMING 0x6C +#define CSI2_TIMING 0x6c #define CSI2_TIMING_FORCE_RX_MODE_IO1 (1 << 15) #define CSI2_TIMING_STOP_STATE_X16_IO1 (1 << 14) #define CSI2_TIMING_STOP_STATE_X4_IO1 (1 << 13) -#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK (0x1FFF << 0) +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK (0x1fff << 0) #define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT 0 #define CSI2_CTX_CTRL1(i) (0x70 + (0x20 * i)) #define CSI2_CTX_CTRL1_GENERIC (1 << 30) -#define CSI2_CTX_CTRL1_TRANSCODE (0xF << 24) -#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK (0xFF << 16) -#define CSI2_CTX_CTRL1_COUNT_MASK (0xFF << 8) +#define CSI2_CTX_CTRL1_TRANSCODE (0xf << 24) +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK (0xff << 16) +#define CSI2_CTX_CTRL1_COUNT_MASK (0xff << 8) #define CSI2_CTX_CTRL1_COUNT_SHIFT 8 #define CSI2_CTX_CTRL1_EOF_EN (1 << 7) #define CSI2_CTX_CTRL1_EOL_EN (1 << 6) @@ -221,14 +221,14 @@ #define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK (3 << 11) #define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 #define CSI2_CTX_CTRL2_DPCM_PRED (1 << 10) -#define CSI2_CTX_CTRL2_FORMAT_MASK (0x3FF << 0) +#define CSI2_CTX_CTRL2_FORMAT_MASK (0x3ff << 0) #define CSI2_CTX_CTRL2_FORMAT_SHIFT 0 #define CSI2_CTX_DAT_OFST(i) (0x78 + (0x20 * i)) -#define CSI2_CTX_DAT_OFST_MASK (0xFFF << 5) +#define CSI2_CTX_DAT_OFST_MASK (0xfff << 5) -#define CSI2_CTX_PING_ADDR(i) (0x7C + (0x20 * i)) -#define CSI2_CTX_PING_ADDR_MASK 0xFFFFFFE0 +#define CSI2_CTX_PING_ADDR(i) (0x7c + (0x20 * i)) +#define CSI2_CTX_PING_ADDR_MASK 0xffffffe0 #define CSI2_CTX_PONG_ADDR(i) (0x80 + (0x20 * i)) #define CSI2_CTX_PONG_ADDR_MASK CSI2_CTX_PING_ADDR_MASK @@ -236,7 +236,7 @@ #define CSI2_CTX_IRQENABLE(i) (0x84 + (0x20 * i)) #define CSI2_CTX_IRQSTATUS(i) (0x88 + (0x20 * i)) -#define CSI2_CTX_CTRL3(i) (0x8C + (0x20 * i)) +#define CSI2_CTX_CTRL3(i) (0x8c + (0x20 * i)) #define CSI2_CTX_CTRL3_ALPHA_SHIFT 5 #define CSI2_CTX_CTRL3_ALPHA_MASK \ (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT) @@ -253,7 +253,7 @@ /* ISS BTE */ #define BTE_CTRL (0x0030) -#define BTE_CTRL_BW_LIMITER_MASK (0x3FF << 22) +#define BTE_CTRL_BW_LIMITER_MASK (0x3ff << 22) #define BTE_CTRL_BW_LIMITER_SHIFT 22 /* ISS ISP_SYS1 */ @@ -266,7 +266,7 @@ #define ISP5_SYSCONFIG_SOFTRESET (1 << 1) #define ISP5_IRQSTATUS(i) (0x0028 + (0x10 * (i))) -#define ISP5_IRQENABLE_SET(i) (0x002C + (0x10 * (i))) +#define ISP5_IRQENABLE_SET(i) (0x002c + (0x10 * (i))) #define ISP5_IRQENABLE_CLR(i) (0x0030 + (0x10 * (i))) /* Bits shared for ISP5_IRQ* registers */ @@ -296,7 +296,7 @@ #define ISP5_IRQ_IPIPE_INT_REG (1 << 4) #define ISP5_IRQ_ISIF_INT(i) (1 << (i)) -#define ISP5_CTRL (0x006C) +#define ISP5_CTRL (0x006c) #define ISP5_CTRL_MSTANDBY (1 << 24) #define ISP5_CTRL_VD_PULSE_EXT (1 << 23) #define ISP5_CTRL_MSTANDBY_WAIT (1 << 20) @@ -327,25 +327,25 @@ #define ISIF_MODESET_VDPOL (1 << 2) #define ISIF_SPH (0x0018) -#define ISIF_SPH_MASK (0x7FFF) +#define ISIF_SPH_MASK (0x7fff) -#define ISIF_LNH (0x001C) -#define ISIF_LNH_MASK (0x7FFF) +#define ISIF_LNH (0x001c) +#define ISIF_LNH_MASK (0x7fff) #define ISIF_LNV (0x0028) -#define ISIF_LNV_MASK (0x7FFF) +#define ISIF_LNV_MASK (0x7fff) #define ISIF_HSIZE (0x0034) #define ISIF_HSIZE_ADCR (1 << 12) -#define ISIF_HSIZE_HSIZE_MASK (0xFFF) +#define ISIF_HSIZE_HSIZE_MASK (0xfff) -#define ISIF_CADU (0x003C) -#define ISIF_CADU_MASK (0x7FF) +#define ISIF_CADU (0x003c) +#define ISIF_CADU_MASK (0x7ff) #define ISIF_CADL (0x0040) -#define ISIF_CADL_MASK (0xFFFF) +#define ISIF_CADL_MASK (0xffff) -#define ISIF_CCOLP (0x004C) +#define ISIF_CCOLP (0x004c) #define ISIF_CCOLP_CP0_F0_R (0 << 6) #define ISIF_CCOLP_CP0_F0_GR (1 << 6) #define ISIF_CCOLP_CP0_F0_B (3 << 6) @@ -367,7 +367,7 @@ #define ISIF_VDINT_MASK (0x7fff) #define ISIF_CGAMMAWD (0x0080) -#define ISIF_CGAMMAWD_GWDI_MASK (0xF << 1) +#define ISIF_CGAMMAWD_GWDI_MASK (0xf << 1) #define ISIF_CGAMMAWD_GWDI(bpp) ((16 - (bpp)) << 1) #define ISIF_CCDCFG (0x0088) @@ -412,7 +412,7 @@ #define IPIPE_SRC_FMT_RAW2STATS (2 << 0) #define IPIPE_SRC_FMT_YUV2YUV (3 << 0) -#define IPIPE_SRC_COL (0x000C) +#define IPIPE_SRC_COL (0x000c) #define IPIPE_SRC_COL_OO_R (0 << 6) #define IPIPE_SRC_COL_OO_GR (1 << 6) #define IPIPE_SRC_COL_OO_B (3 << 6) @@ -431,16 +431,16 @@ #define IPIPE_SRC_COL_EE_GB (2 << 0) #define IPIPE_SRC_VPS (0x0010) -#define IPIPE_SRC_VPS_MASK (0xFFFF) +#define IPIPE_SRC_VPS_MASK (0xffff) #define IPIPE_SRC_VSZ (0x0014) -#define IPIPE_SRC_VSZ_MASK (0x1FFF) +#define IPIPE_SRC_VSZ_MASK (0x1fff) #define IPIPE_SRC_HPS (0x0018) -#define IPIPE_SRC_HPS_MASK (0xFFFF) +#define IPIPE_SRC_HPS_MASK (0xffff) -#define IPIPE_SRC_HSZ (0x001C) -#define IPIPE_SRC_HSZ_MASK (0x1FFE) +#define IPIPE_SRC_HSZ (0x001c) +#define IPIPE_SRC_HSZ_MASK (0x1ffe) #define IPIPE_SEL_SBU (0x0020) @@ -449,7 +449,7 @@ #define IPIPE_GCK_MMR (0x0028) #define IPIPE_GCK_MMR_REG (1 << 0) -#define IPIPE_GCK_PIX (0x002C) +#define IPIPE_GCK_PIX (0x002c) #define IPIPE_GCK_PIX_G3 (1 << 3) #define IPIPE_GCK_PIX_G2 (1 << 2) #define IPIPE_GCK_PIX_G1 (1 << 1) @@ -457,277 +457,277 @@ #define IPIPE_DPC_LUT_EN (0x0034) #define IPIPE_DPC_LUT_SEL (0x0038) -#define IPIPE_DPC_LUT_ADR (0x003C) +#define IPIPE_DPC_LUT_ADR (0x003c) #define IPIPE_DPC_LUT_SIZ (0x0040) #define IPIPE_DPC_OTF_EN (0x0044) #define IPIPE_DPC_OTF_TYP (0x0048) -#define IPIPE_DPC_OTF_2_D_THR_R (0x004C) +#define IPIPE_DPC_OTF_2_D_THR_R (0x004c) #define IPIPE_DPC_OTF_2_D_THR_GR (0x0050) #define IPIPE_DPC_OTF_2_D_THR_GB (0x0054) #define IPIPE_DPC_OTF_2_D_THR_B (0x0058) -#define IPIPE_DPC_OTF_2_C_THR_R (0x005C) +#define IPIPE_DPC_OTF_2_C_THR_R (0x005c) #define IPIPE_DPC_OTF_2_C_THR_GR (0x0060) #define IPIPE_DPC_OTF_2_C_THR_GB (0x0064) #define IPIPE_DPC_OTF_2_C_THR_B (0x0068) -#define IPIPE_DPC_OTF_3_SHF (0x006C) +#define IPIPE_DPC_OTF_3_SHF (0x006c) #define IPIPE_DPC_OTF_3_D_THR (0x0070) #define IPIPE_DPC_OTF_3_D_SPL (0x0074) #define IPIPE_DPC_OTF_3_D_MIN (0x0078) -#define IPIPE_DPC_OTF_3_D_MAX (0x007C) +#define IPIPE_DPC_OTF_3_D_MAX (0x007c) #define IPIPE_DPC_OTF_3_C_THR (0x0080) #define IPIPE_DPC_OTF_3_C_SLP (0x0084) #define IPIPE_DPC_OTF_3_C_MIN (0x0088) -#define IPIPE_DPC_OTF_3_C_MAX (0x008C) +#define IPIPE_DPC_OTF_3_C_MAX (0x008c) #define IPIPE_LSC_VOFT (0x0090) #define IPIPE_LSC_VA2 (0x0094) #define IPIPE_LSC_VA1 (0x0098) -#define IPIPE_LSC_VS (0x009C) -#define IPIPE_LSC_HOFT (0x00A0) -#define IPIPE_LSC_HA2 (0x00A4) -#define IPIPE_LSC_HA1 (0x00A8) -#define IPIPE_LSC_HS (0x00AC) -#define IPIPE_LSC_GAN_R (0x00B0) -#define IPIPE_LSC_GAN_GR (0x00B4) -#define IPIPE_LSC_GAN_GB (0x00B8) -#define IPIPE_LSC_GAN_B (0x00BC) -#define IPIPE_LSC_OFT_R (0x00C0) -#define IPIPE_LSC_OFT_GR (0x00C4) -#define IPIPE_LSC_OFT_GB (0x00C8) -#define IPIPE_LSC_OFT_B (0x00CC) -#define IPIPE_LSC_SHF (0x00D0) -#define IPIPE_LSC_MAX (0x00D4) - -#define IPIPE_D2F_1ST_EN (0x00D8) -#define IPIPE_D2F_1ST_TYP (0x00DC) -#define IPIPE_D2F_1ST_THR_00 (0x00E0) -#define IPIPE_D2F_1ST_THR_01 (0x00E4) -#define IPIPE_D2F_1ST_THR_02 (0x00E8) -#define IPIPE_D2F_1ST_THR_03 (0x00EC) -#define IPIPE_D2F_1ST_THR_04 (0x00F0) -#define IPIPE_D2F_1ST_THR_05 (0x00F4) -#define IPIPE_D2F_1ST_THR_06 (0x00F8) -#define IPIPE_D2F_1ST_THR_07 (0x00FC) +#define IPIPE_LSC_VS (0x009c) +#define IPIPE_LSC_HOFT (0x00a0) +#define IPIPE_LSC_HA2 (0x00a4) +#define IPIPE_LSC_HA1 (0x00a8) +#define IPIPE_LSC_HS (0x00ac) +#define IPIPE_LSC_GAN_R (0x00b0) +#define IPIPE_LSC_GAN_GR (0x00b4) +#define IPIPE_LSC_GAN_GB (0x00b8) +#define IPIPE_LSC_GAN_B (0x00bc) +#define IPIPE_LSC_OFT_R (0x00c0) +#define IPIPE_LSC_OFT_GR (0x00c4) +#define IPIPE_LSC_OFT_GB (0x00c8) +#define IPIPE_LSC_OFT_B (0x00cc) +#define IPIPE_LSC_SHF (0x00d0) +#define IPIPE_LSC_MAX (0x00d4) + +#define IPIPE_D2F_1ST_EN (0x00d8) +#define IPIPE_D2F_1ST_TYP (0x00dc) +#define IPIPE_D2F_1ST_THR_00 (0x00e0) +#define IPIPE_D2F_1ST_THR_01 (0x00e4) +#define IPIPE_D2F_1ST_THR_02 (0x00e8) +#define IPIPE_D2F_1ST_THR_03 (0x00ec) +#define IPIPE_D2F_1ST_THR_04 (0x00f0) +#define IPIPE_D2F_1ST_THR_05 (0x00f4) +#define IPIPE_D2F_1ST_THR_06 (0x00f8) +#define IPIPE_D2F_1ST_THR_07 (0x00fc) #define IPIPE_D2F_1ST_STR_00 (0x0100) #define IPIPE_D2F_1ST_STR_01 (0x0104) #define IPIPE_D2F_1ST_STR_02 (0x0108) -#define IPIPE_D2F_1ST_STR_03 (0x010C) +#define IPIPE_D2F_1ST_STR_03 (0x010c) #define IPIPE_D2F_1ST_STR_04 (0x0110) #define IPIPE_D2F_1ST_STR_05 (0x0114) #define IPIPE_D2F_1ST_STR_06 (0x0118) -#define IPIPE_D2F_1ST_STR_07 (0x011C) +#define IPIPE_D2F_1ST_STR_07 (0x011c) #define IPIPE_D2F_1ST_SPR_00 (0x0120) #define IPIPE_D2F_1ST_SPR_01 (0x0124) #define IPIPE_D2F_1ST_SPR_02 (0x0128) -#define IPIPE_D2F_1ST_SPR_03 (0x012C) +#define IPIPE_D2F_1ST_SPR_03 (0x012c) #define IPIPE_D2F_1ST_SPR_04 (0x0130) #define IPIPE_D2F_1ST_SPR_05 (0x0134) #define IPIPE_D2F_1ST_SPR_06 (0x0138) -#define IPIPE_D2F_1ST_SPR_07 (0x013C) +#define IPIPE_D2F_1ST_SPR_07 (0x013c) #define IPIPE_D2F_1ST_EDG_MIN (0x0140) #define IPIPE_D2F_1ST_EDG_MAX (0x0144) #define IPIPE_D2F_2ND_EN (0x0148) -#define IPIPE_D2F_2ND_TYP (0x014C) +#define IPIPE_D2F_2ND_TYP (0x014c) #define IPIPE_D2F_2ND_THR00 (0x0150) #define IPIPE_D2F_2ND_THR01 (0x0154) #define IPIPE_D2F_2ND_THR02 (0x0158) -#define IPIPE_D2F_2ND_THR03 (0x015C) +#define IPIPE_D2F_2ND_THR03 (0x015c) #define IPIPE_D2F_2ND_THR04 (0x0160) #define IPIPE_D2F_2ND_THR05 (0x0164) #define IPIPE_D2F_2ND_THR06 (0x0168) -#define IPIPE_D2F_2ND_THR07 (0x016C) +#define IPIPE_D2F_2ND_THR07 (0x016c) #define IPIPE_D2F_2ND_STR_00 (0x0170) #define IPIPE_D2F_2ND_STR_01 (0x0174) #define IPIPE_D2F_2ND_STR_02 (0x0178) -#define IPIPE_D2F_2ND_STR_03 (0x017C) +#define IPIPE_D2F_2ND_STR_03 (0x017c) #define IPIPE_D2F_2ND_STR_04 (0x0180) #define IPIPE_D2F_2ND_STR_05 (0x0184) #define IPIPE_D2F_2ND_STR_06 (0x0188) -#define IPIPE_D2F_2ND_STR_07 (0x018C) +#define IPIPE_D2F_2ND_STR_07 (0x018c) #define IPIPE_D2F_2ND_SPR_00 (0x0190) #define IPIPE_D2F_2ND_SPR_01 (0x0194) #define IPIPE_D2F_2ND_SPR_02 (0x0198) -#define IPIPE_D2F_2ND_SPR_03 (0x019C) -#define IPIPE_D2F_2ND_SPR_04 (0x01A0) -#define IPIPE_D2F_2ND_SPR_05 (0x01A4) -#define IPIPE_D2F_2ND_SPR_06 (0x01A8) -#define IPIPE_D2F_2ND_SPR_07 (0x01AC) -#define IPIPE_D2F_2ND_EDG_MIN (0x01B0) -#define IPIPE_D2F_2ND_EDG_MAX (0x01B4) - -#define IPIPE_GIC_EN (0x01B8) -#define IPIPE_GIC_TYP (0x01BC) -#define IPIPE_GIC_GAN (0x01C0) -#define IPIPE_GIC_NFGAIN (0x01C4) -#define IPIPE_GIC_THR (0x01C8) -#define IPIPE_GIC_SLP (0x01CC) - -#define IPIPE_WB2_OFT_R (0x01D0) -#define IPIPE_WB2_OFT_GR (0x01D4) -#define IPIPE_WB2_OFT_GB (0x01D8) -#define IPIPE_WB2_OFT_B (0x01DC) - -#define IPIPE_WB2_WGN_R (0x01E0) -#define IPIPE_WB2_WGN_GR (0x01E4) -#define IPIPE_WB2_WGN_GB (0x01E8) -#define IPIPE_WB2_WGN_B (0x01EC) - -#define IPIPE_CFA_MODE (0x01F0) -#define IPIPE_CFA_2DIR_HPF_THR (0x01F4) -#define IPIPE_CFA_2DIR_HPF_SLP (0x01F8) -#define IPIPE_CFA_2DIR_MIX_THR (0x01FC) +#define IPIPE_D2F_2ND_SPR_03 (0x019c) +#define IPIPE_D2F_2ND_SPR_04 (0x01a0) +#define IPIPE_D2F_2ND_SPR_05 (0x01a4) +#define IPIPE_D2F_2ND_SPR_06 (0x01a8) +#define IPIPE_D2F_2ND_SPR_07 (0x01ac) +#define IPIPE_D2F_2ND_EDG_MIN (0x01b0) +#define IPIPE_D2F_2ND_EDG_MAX (0x01b4) + +#define IPIPE_GIC_EN (0x01b8) +#define IPIPE_GIC_TYP (0x01bc) +#define IPIPE_GIC_GAN (0x01c0) +#define IPIPE_GIC_NFGAIN (0x01c4) +#define IPIPE_GIC_THR (0x01c8) +#define IPIPE_GIC_SLP (0x01cc) + +#define IPIPE_WB2_OFT_R (0x01d0) +#define IPIPE_WB2_OFT_GR (0x01d4) +#define IPIPE_WB2_OFT_GB (0x01d8) +#define IPIPE_WB2_OFT_B (0x01dc) + +#define IPIPE_WB2_WGN_R (0x01e0) +#define IPIPE_WB2_WGN_GR (0x01e4) +#define IPIPE_WB2_WGN_GB (0x01e8) +#define IPIPE_WB2_WGN_B (0x01ec) + +#define IPIPE_CFA_MODE (0x01f0) +#define IPIPE_CFA_2DIR_HPF_THR (0x01f4) +#define IPIPE_CFA_2DIR_HPF_SLP (0x01f8) +#define IPIPE_CFA_2DIR_MIX_THR (0x01fc) #define IPIPE_CFA_2DIR_MIX_SLP (0x0200) #define IPIPE_CFA_2DIR_DIR_TRH (0x0204) #define IPIPE_CFA_2DIR_DIR_SLP (0x0208) -#define IPIPE_CFA_2DIR_NDWT (0x020C) +#define IPIPE_CFA_2DIR_NDWT (0x020c) #define IPIPE_CFA_MONO_HUE_FRA (0x0210) #define IPIPE_CFA_MONO_EDG_THR (0x0214) #define IPIPE_CFA_MONO_THR_MIN (0x0218) -#define IPIPE_CFA_MONO_THR_SLP (0x021C) +#define IPIPE_CFA_MONO_THR_SLP (0x021c) #define IPIPE_CFA_MONO_SLP_MIN (0x0220) #define IPIPE_CFA_MONO_SLP_SLP (0x0224) #define IPIPE_CFA_MONO_LPWT (0x0228) -#define IPIPE_RGB1_MUL_RR (0x022C) +#define IPIPE_RGB1_MUL_RR (0x022c) #define IPIPE_RGB1_MUL_GR (0x0230) #define IPIPE_RGB1_MUL_BR (0x0234) #define IPIPE_RGB1_MUL_RG (0x0238) -#define IPIPE_RGB1_MUL_GG (0x023C) +#define IPIPE_RGB1_MUL_GG (0x023c) #define IPIPE_RGB1_MUL_BG (0x0240) #define IPIPE_RGB1_MUL_RB (0x0244) #define IPIPE_RGB1_MUL_GB (0x0248) -#define IPIPE_RGB1_MUL_BB (0x024C) +#define IPIPE_RGB1_MUL_BB (0x024c) #define IPIPE_RGB1_OFT_OR (0x0250) #define IPIPE_RGB1_OFT_OG (0x0254) #define IPIPE_RGB1_OFT_OB (0x0258) -#define IPIPE_GMM_CFG (0x025C) +#define IPIPE_GMM_CFG (0x025c) #define IPIPE_RGB2_MUL_RR (0x0260) #define IPIPE_RGB2_MUL_GR (0x0264) #define IPIPE_RGB2_MUL_BR (0x0268) -#define IPIPE_RGB2_MUL_RG (0x026C) +#define IPIPE_RGB2_MUL_RG (0x026c) #define IPIPE_RGB2_MUL_GG (0x0270) #define IPIPE_RGB2_MUL_BG (0x0274) #define IPIPE_RGB2_MUL_RB (0x0278) -#define IPIPE_RGB2_MUL_GB (0x027C) +#define IPIPE_RGB2_MUL_GB (0x027c) #define IPIPE_RGB2_MUL_BB (0x0280) #define IPIPE_RGB2_OFT_OR (0x0284) #define IPIPE_RGB2_OFT_OG (0x0288) -#define IPIPE_RGB2_OFT_OB (0x028C) +#define IPIPE_RGB2_OFT_OB (0x028c) #define IPIPE_YUV_ADJ (0x0294) #define IPIPE_YUV_MUL_RY (0x0298) -#define IPIPE_YUV_MUL_GY (0x029C) -#define IPIPE_YUV_MUL_BY (0x02A0) -#define IPIPE_YUV_MUL_RCB (0x02A4) -#define IPIPE_YUV_MUL_GCB (0x02A8) -#define IPIPE_YUV_MUL_BCB (0x02AC) -#define IPIPE_YUV_MUL_RCR (0x02B0) -#define IPIPE_YUV_MUL_GCR (0x02B4) -#define IPIPE_YUV_MUL_BCR (0x02B8) -#define IPIPE_YUV_OFT_Y (0x02BC) -#define IPIPE_YUV_OFT_CB (0x02C0) -#define IPIPE_YUV_OFT_CR (0x02C4) - -#define IPIPE_YUV_PHS (0x02C8) +#define IPIPE_YUV_MUL_GY (0x029c) +#define IPIPE_YUV_MUL_BY (0x02a0) +#define IPIPE_YUV_MUL_RCB (0x02a4) +#define IPIPE_YUV_MUL_GCB (0x02a8) +#define IPIPE_YUV_MUL_BCB (0x02ac) +#define IPIPE_YUV_MUL_RCR (0x02b0) +#define IPIPE_YUV_MUL_GCR (0x02b4) +#define IPIPE_YUV_MUL_BCR (0x02b8) +#define IPIPE_YUV_OFT_Y (0x02bc) +#define IPIPE_YUV_OFT_CB (0x02c0) +#define IPIPE_YUV_OFT_CR (0x02c4) + +#define IPIPE_YUV_PHS (0x02c8) #define IPIPE_YUV_PHS_LPF (1 << 1) #define IPIPE_YUV_PHS_POS (1 << 0) -#define IPIPE_YEE_EN (0x02D4) -#define IPIPE_YEE_TYP (0x02D8) -#define IPIPE_YEE_SHF (0x02DC) -#define IPIPE_YEE_MUL_00 (0x02E0) -#define IPIPE_YEE_MUL_01 (0x02E4) -#define IPIPE_YEE_MUL_02 (0x02E8) -#define IPIPE_YEE_MUL_10 (0x02EC) -#define IPIPE_YEE_MUL_11 (0x02F0) -#define IPIPE_YEE_MUL_12 (0x02F4) -#define IPIPE_YEE_MUL_20 (0x02F8) -#define IPIPE_YEE_MUL_21 (0x02FC) +#define IPIPE_YEE_EN (0x02d4) +#define IPIPE_YEE_TYP (0x02d8) +#define IPIPE_YEE_SHF (0x02dc) +#define IPIPE_YEE_MUL_00 (0x02e0) +#define IPIPE_YEE_MUL_01 (0x02e4) +#define IPIPE_YEE_MUL_02 (0x02e8) +#define IPIPE_YEE_MUL_10 (0x02ec) +#define IPIPE_YEE_MUL_11 (0x02f0) +#define IPIPE_YEE_MUL_12 (0x02f4) +#define IPIPE_YEE_MUL_20 (0x02f8) +#define IPIPE_YEE_MUL_21 (0x02fc) #define IPIPE_YEE_MUL_22 (0x0300) #define IPIPE_YEE_THR (0x0304) #define IPIPE_YEE_E_GAN (0x0308) -#define IPIPE_YEE_E_THR_1 (0x030C) +#define IPIPE_YEE_E_THR_1 (0x030c) #define IPIPE_YEE_E_THR_2 (0x0310) #define IPIPE_YEE_G_GAN (0x0314) #define IPIPE_YEE_G_OFT (0x0318) -#define IPIPE_CAR_EN (0x031C) +#define IPIPE_CAR_EN (0x031c) #define IPIPE_CAR_TYP (0x0320) #define IPIPE_CAR_SW (0x0324) #define IPIPE_CAR_HPF_TYP (0x0328) -#define IPIPE_CAR_HPF_SHF (0x032C) +#define IPIPE_CAR_HPF_SHF (0x032c) #define IPIPE_CAR_HPF_THR (0x0330) #define IPIPE_CAR_GN1_GAN (0x0334) #define IPIPE_CAR_GN1_SHF (0x0338) -#define IPIPE_CAR_GN1_MIN (0x033C) +#define IPIPE_CAR_GN1_MIN (0x033c) #define IPIPE_CAR_GN2_GAN (0x0340) #define IPIPE_CAR_GN2_SHF (0x0344) #define IPIPE_CAR_GN2_MIN (0x0348) -#define IPIPE_CGS_EN (0x034C) +#define IPIPE_CGS_EN (0x034c) #define IPIPE_CGS_GN1_L_THR (0x0350) #define IPIPE_CGS_GN1_L_GAIN (0x0354) #define IPIPE_CGS_GN1_L_SHF (0x0358) -#define IPIPE_CGS_GN1_L_MIN (0x035C) +#define IPIPE_CGS_GN1_L_MIN (0x035c) #define IPIPE_CGS_GN1_H_THR (0x0360) #define IPIPE_CGS_GN1_H_GAIN (0x0364) #define IPIPE_CGS_GN1_H_SHF (0x0368) -#define IPIPE_CGS_GN1_H_MIN (0x036C) +#define IPIPE_CGS_GN1_H_MIN (0x036c) #define IPIPE_CGS_GN2_L_THR (0x0370) #define IPIPE_CGS_GN2_L_GAIN (0x0374) #define IPIPE_CGS_GN2_L_SHF (0x0378) -#define IPIPE_CGS_GN2_L_MIN (0x037C) +#define IPIPE_CGS_GN2_L_MIN (0x037c) #define IPIPE_BOX_EN (0x0380) #define IPIPE_BOX_MODE (0x0384) #define IPIPE_BOX_TYP (0x0388) -#define IPIPE_BOX_SHF (0x038C) +#define IPIPE_BOX_SHF (0x038c) #define IPIPE_BOX_SDR_SAD_H (0x0390) #define IPIPE_BOX_SDR_SAD_L (0x0394) -#define IPIPE_HST_EN (0x039C) -#define IPIPE_HST_MODE (0x03A0) -#define IPIPE_HST_SEL (0x03A4) -#define IPIPE_HST_PARA (0x03A8) -#define IPIPE_HST_0_VPS (0x03AC) -#define IPIPE_HST_0_VSZ (0x03B0) -#define IPIPE_HST_0_HPS (0x03B4) -#define IPIPE_HST_0_HSZ (0x03B8) -#define IPIPE_HST_1_VPS (0x03BC) -#define IPIPE_HST_1_VSZ (0x03C0) -#define IPIPE_HST_1_HPS (0x03C4) -#define IPIPE_HST_1_HSZ (0x03C8) -#define IPIPE_HST_2_VPS (0x03CC) -#define IPIPE_HST_2_VSZ (0x03D0) -#define IPIPE_HST_2_HPS (0x03D4) -#define IPIPE_HST_2_HSZ (0x03D8) -#define IPIPE_HST_3_VPS (0x03DC) -#define IPIPE_HST_3_VSZ (0x03E0) -#define IPIPE_HST_3_HPS (0x03E4) -#define IPIPE_HST_3_HSZ (0x03E8) -#define IPIPE_HST_TBL (0x03EC) -#define IPIPE_HST_MUL_R (0x03F0) -#define IPIPE_HST_MUL_GR (0x03F4) -#define IPIPE_HST_MUL_GB (0x03F8) -#define IPIPE_HST_MUL_B (0x03FC) +#define IPIPE_HST_EN (0x039c) +#define IPIPE_HST_MODE (0x03a0) +#define IPIPE_HST_SEL (0x03a4) +#define IPIPE_HST_PARA (0x03a8) +#define IPIPE_HST_0_VPS (0x03ac) +#define IPIPE_HST_0_VSZ (0x03b0) +#define IPIPE_HST_0_HPS (0x03b4) +#define IPIPE_HST_0_HSZ (0x03b8) +#define IPIPE_HST_1_VPS (0x03bc) +#define IPIPE_HST_1_VSZ (0x03c0) +#define IPIPE_HST_1_HPS (0x03c4) +#define IPIPE_HST_1_HSZ (0x03c8) +#define IPIPE_HST_2_VPS (0x03cc) +#define IPIPE_HST_2_VSZ (0x03d0) +#define IPIPE_HST_2_HPS (0x03d4) +#define IPIPE_HST_2_HSZ (0x03d8) +#define IPIPE_HST_3_VPS (0x03dc) +#define IPIPE_HST_3_VSZ (0x03e0) +#define IPIPE_HST_3_HPS (0x03e4) +#define IPIPE_HST_3_HSZ (0x03e8) +#define IPIPE_HST_TBL (0x03ec) +#define IPIPE_HST_MUL_R (0x03f0) +#define IPIPE_HST_MUL_GR (0x03f4) +#define IPIPE_HST_MUL_GB (0x03f8) +#define IPIPE_HST_MUL_B (0x03fc) #define IPIPE_BSC_EN (0x0400) #define IPIPE_BSC_MODE (0x0404) #define IPIPE_BSC_TYP (0x0408) -#define IPIPE_BSC_ROW_VCT (0x040C) +#define IPIPE_BSC_ROW_VCT (0x040c) #define IPIPE_BSC_ROW_SHF (0x0410) #define IPIPE_BSC_ROW_VPO (0x0414) #define IPIPE_BSC_ROW_VNU (0x0418) -#define IPIPE_BSC_ROW_VSKIP (0x041C) +#define IPIPE_BSC_ROW_VSKIP (0x041c) #define IPIPE_BSC_ROW_HPO (0x0420) #define IPIPE_BSC_ROW_HNU (0x0424) #define IPIPE_BSC_ROW_HSKIP (0x0428) -#define IPIPE_BSC_COL_VCT (0x042C) +#define IPIPE_BSC_COL_VCT (0x042c) #define IPIPE_BSC_COL_SHF (0x0430) #define IPIPE_BSC_COL_VPO (0x0434) #define IPIPE_BSC_COL_VNU (0x0438) -#define IPIPE_BSC_COL_VSKIP (0x043C) +#define IPIPE_BSC_COL_VSKIP (0x043c) #define IPIPE_BSC_COL_HPO (0x0440) #define IPIPE_BSC_COL_HNU (0x0444) #define IPIPE_BSC_COL_HSKIP (0x0448) @@ -740,14 +740,14 @@ #define RSZ_SYSCONFIG_RSZB_CLK_EN (1 << 9) #define RSZ_SYSCONFIG_RSZA_CLK_EN (1 << 8) -#define RSZ_IN_FIFO_CTRL (0x000C) -#define RSZ_IN_FIFO_CTRL_THRLD_LOW_MASK (0x1FF << 16) +#define RSZ_IN_FIFO_CTRL (0x000c) +#define RSZ_IN_FIFO_CTRL_THRLD_LOW_MASK (0x1ff << 16) #define RSZ_IN_FIFO_CTRL_THRLD_LOW_SHIFT 16 -#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_MASK (0x1FF << 0) +#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_MASK (0x1ff << 0) #define RSZ_IN_FIFO_CTRL_THRLD_HIGH_SHIFT 0 #define RSZ_FRACDIV (0x0008) -#define RSZ_FRACDIV_MASK (0xFFFF) +#define RSZ_FRACDIV_MASK (0xffff) #define RSZ_SRC_EN (0x0020) #define RSZ_SRC_EN_SRC_EN (1 << 0) @@ -760,80 +760,80 @@ #define RSZ_SRC_FMT0_BYPASS (1 << 1) #define RSZ_SRC_FMT0_SEL (1 << 0) -#define RSZ_SRC_FMT1 (0x002C) +#define RSZ_SRC_FMT1 (0x002c) #define RSZ_SRC_FMT1_IN420 (1 << 1) #define RSZ_SRC_VPS (0x0030) #define RSZ_SRC_VSZ (0x0034) #define RSZ_SRC_HPS (0x0038) -#define RSZ_SRC_HSZ (0x003C) +#define RSZ_SRC_HSZ (0x003c) #define RSZ_DMA_RZA (0x0040) #define RSZ_DMA_RZB (0x0044) #define RSZ_DMA_STA (0x0048) -#define RSZ_GCK_MMR (0x004C) +#define RSZ_GCK_MMR (0x004c) #define RSZ_GCK_MMR_MMR (1 << 0) #define RSZ_GCK_SDR (0x0054) #define RSZ_GCK_SDR_CORE (1 << 0) #define RSZ_IRQ_RZA (0x0058) -#define RSZ_IRQ_RZA_MASK (0x1FFF) +#define RSZ_IRQ_RZA_MASK (0x1fff) -#define RSZ_IRQ_RZB (0x005C) -#define RSZ_IRQ_RZB_MASK (0x1FFF) +#define RSZ_IRQ_RZB (0x005c) +#define RSZ_IRQ_RZB_MASK (0x1fff) #define RSZ_YUV_Y_MIN (0x0060) #define RSZ_YUV_Y_MAX (0x0064) #define RSZ_YUV_C_MIN (0x0068) -#define RSZ_YUV_C_MAX (0x006C) +#define RSZ_YUV_C_MAX (0x006c) #define RSZ_SEQ (0x0074) #define RSZ_SEQ_HRVB (1 << 2) #define RSZ_SEQ_HRVA (1 << 0) #define RZA_EN (0x0078) -#define RZA_MODE (0x007C) +#define RZA_MODE (0x007c) #define RZA_MODE_ONE_SHOT (1 << 0) #define RZA_420 (0x0080) #define RZA_I_VPS (0x0084) #define RZA_I_HPS (0x0088) -#define RZA_O_VSZ (0x008C) +#define RZA_O_VSZ (0x008c) #define RZA_O_HSZ (0x0090) #define RZA_V_PHS_Y (0x0094) #define RZA_V_PHS_C (0x0098) -#define RZA_V_DIF (0x009C) -#define RZA_V_TYP (0x00A0) -#define RZA_V_LPF (0x00A4) -#define RZA_H_PHS (0x00A8) -#define RZA_H_DIF (0x00B0) -#define RZA_H_TYP (0x00B4) -#define RZA_H_LPF (0x00B8) -#define RZA_DWN_EN (0x00BC) -#define RZA_SDR_Y_BAD_H (0x00D0) -#define RZA_SDR_Y_BAD_L (0x00D4) -#define RZA_SDR_Y_SAD_H (0x00D8) -#define RZA_SDR_Y_SAD_L (0x00DC) -#define RZA_SDR_Y_OFT (0x00E0) -#define RZA_SDR_Y_PTR_S (0x00E4) -#define RZA_SDR_Y_PTR_E (0x00E8) -#define RZA_SDR_C_BAD_H (0x00EC) -#define RZA_SDR_C_BAD_L (0x00F0) -#define RZA_SDR_C_SAD_H (0x00F4) -#define RZA_SDR_C_SAD_L (0x00F8) -#define RZA_SDR_C_OFT (0x00FC) +#define RZA_V_DIF (0x009c) +#define RZA_V_TYP (0x00a0) +#define RZA_V_LPF (0x00a4) +#define RZA_H_PHS (0x00a8) +#define RZA_H_DIF (0x00b0) +#define RZA_H_TYP (0x00b4) +#define RZA_H_LPF (0x00b8) +#define RZA_DWN_EN (0x00bc) +#define RZA_SDR_Y_BAD_H (0x00d0) +#define RZA_SDR_Y_BAD_L (0x00d4) +#define RZA_SDR_Y_SAD_H (0x00d8) +#define RZA_SDR_Y_SAD_L (0x00dc) +#define RZA_SDR_Y_OFT (0x00e0) +#define RZA_SDR_Y_PTR_S (0x00e4) +#define RZA_SDR_Y_PTR_E (0x00e8) +#define RZA_SDR_C_BAD_H (0x00ec) +#define RZA_SDR_C_BAD_L (0x00f0) +#define RZA_SDR_C_SAD_H (0x00f4) +#define RZA_SDR_C_SAD_L (0x00f8) +#define RZA_SDR_C_OFT (0x00fc) #define RZA_SDR_C_PTR_S (0x0100) #define RZA_SDR_C_PTR_E (0x0104) #define RZB_EN (0x0108) -#define RZB_MODE (0x010C) +#define RZB_MODE (0x010c) #define RZB_420 (0x0110) #define RZB_I_VPS (0x0114) #define RZB_I_HPS (0x0118) -#define RZB_O_VSZ (0x011C) +#define RZB_O_VSZ (0x011c) #define RZB_O_HSZ (0x0120) -#define RZB_V_DIF (0x012C) +#define RZB_V_DIF (0x012c) #define RZB_V_TYP (0x0130) #define RZB_V_LPF (0x0134) @@ -844,11 +844,11 @@ #define RZB_SDR_Y_BAD_H (0x0160) #define RZB_SDR_Y_BAD_L (0x0164) #define RZB_SDR_Y_SAD_H (0x0168) -#define RZB_SDR_Y_SAD_L (0x016C) +#define RZB_SDR_Y_SAD_L (0x016c) #define RZB_SDR_Y_OFT (0x0170) #define RZB_SDR_Y_PTR_S (0x0174) #define RZB_SDR_Y_PTR_E (0x0178) -#define RZB_SDR_C_BAD_H (0x017C) +#define RZB_SDR_C_BAD_H (0x017c) #define RZB_SDR_C_BAD_L (0x0180) #define RZB_SDR_C_SAD_H (0x0184) #define RZB_SDR_C_SAD_L (0x0188) @@ -862,38 +862,38 @@ #define RSZ_420_CEN (1 << 1) #define RSZ_420_YEN (1 << 0) -#define RSZ_I_VPS_MASK (0x1FFF) +#define RSZ_I_VPS_MASK (0x1fff) -#define RSZ_I_HPS_MASK (0x1FFF) +#define RSZ_I_HPS_MASK (0x1fff) -#define RSZ_O_VSZ_MASK (0x1FFF) +#define RSZ_O_VSZ_MASK (0x1fff) -#define RSZ_O_HSZ_MASK (0x1FFE) +#define RSZ_O_HSZ_MASK (0x1ffe) -#define RSZ_V_PHS_Y_MASK (0x3FFF) +#define RSZ_V_PHS_Y_MASK (0x3fff) -#define RSZ_V_PHS_C_MASK (0x3FFF) +#define RSZ_V_PHS_C_MASK (0x3fff) -#define RSZ_V_DIF_MASK (0x3FFF) +#define RSZ_V_DIF_MASK (0x3fff) #define RSZ_V_TYP_C (1 << 1) #define RSZ_V_TYP_Y (1 << 0) -#define RSZ_V_LPF_C_MASK (0x3F << 6) +#define RSZ_V_LPF_C_MASK (0x3f << 6) #define RSZ_V_LPF_C_SHIFT 6 -#define RSZ_V_LPF_Y_MASK (0x3F << 0) +#define RSZ_V_LPF_Y_MASK (0x3f << 0) #define RSZ_V_LPF_Y_SHIFT 0 -#define RSZ_H_PHS_MASK (0x3FFF) +#define RSZ_H_PHS_MASK (0x3fff) -#define RSZ_H_DIF_MASK (0x3FFF) +#define RSZ_H_DIF_MASK (0x3fff) #define RSZ_H_TYP_C (1 << 1) #define RSZ_H_TYP_Y (1 << 0) -#define RSZ_H_LPF_C_MASK (0x3F << 6) +#define RSZ_H_LPF_C_MASK (0x3f << 6) #define RSZ_H_LPF_C_SHIFT 6 -#define RSZ_H_LPF_Y_MASK (0x3F << 0) +#define RSZ_H_LPF_Y_MASK (0x3f << 0) #define RSZ_H_LPF_Y_SHIFT 0 #define RSZ_DWN_EN_DWN_EN (1 << 0) -- cgit v0.10.2 From a1d4eab06d6777c8bbd46f83e8a495722bb37a84 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Sep 2013 08:19:21 -0300 Subject: [media] v4l: omap4iss: Add description field to iss_format_info structure The field stores the format description in a human-readable form. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 7763b8d..b4ffde8 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -33,61 +33,61 @@ static struct iss_format_info formats[] = { { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, - V4L2_PIX_FMT_GREY, 8, }, + V4L2_PIX_FMT_GREY, 8, "Greyscale 8 bpp", }, { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8, - V4L2_PIX_FMT_Y10, 10, }, + V4L2_PIX_FMT_Y10, 10, "Greyscale 10 bpp", }, { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8, - V4L2_PIX_FMT_Y12, 12, }, + V4L2_PIX_FMT_Y12, 12, "Greyscale 12 bpp", }, { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_PIX_FMT_SBGGR8, 8, }, + V4L2_PIX_FMT_SBGGR8, 8, "BGGR Bayer 8 bpp", }, { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, - V4L2_PIX_FMT_SGBRG8, 8, }, + V4L2_PIX_FMT_SGBRG8, 8, "GBRG Bayer 8 bpp", }, { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_PIX_FMT_SGRBG8, 8, }, + V4L2_PIX_FMT_SGRBG8, 8, "GRBG Bayer 8 bpp", }, { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, - V4L2_PIX_FMT_SRGGB8, 8, }, + V4L2_PIX_FMT_SRGGB8, 8, "RGGB Bayer 8 bpp", }, { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_1X10, 0, - V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + V4L2_PIX_FMT_SGRBG10DPCM8, 8, "GRBG Bayer 10 bpp DPCM8", }, { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_PIX_FMT_SBGGR10, 10, }, + V4L2_PIX_FMT_SBGGR10, 10, "BGGR Bayer 10 bpp", }, { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8, - V4L2_PIX_FMT_SGBRG10, 10, }, + V4L2_PIX_FMT_SGBRG10, 10, "GBRG Bayer 10 bpp", }, { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_PIX_FMT_SGRBG10, 10, }, + V4L2_PIX_FMT_SGRBG10, 10, "GRBG Bayer 10 bpp", }, { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8, - V4L2_PIX_FMT_SRGGB10, 10, }, + V4L2_PIX_FMT_SRGGB10, 10, "RGGB Bayer 10 bpp", }, { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_PIX_FMT_SBGGR12, 12, }, + V4L2_PIX_FMT_SBGGR12, 12, "BGGR Bayer 12 bpp", }, { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8, - V4L2_PIX_FMT_SGBRG12, 12, }, + V4L2_PIX_FMT_SGBRG12, 12, "GBRG Bayer 12 bpp", }, { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_PIX_FMT_SGRBG12, 12, }, + V4L2_PIX_FMT_SGRBG12, 12, "GRBG Bayer 12 bpp", }, { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8, - V4L2_PIX_FMT_SRGGB12, 12, }, + V4L2_PIX_FMT_SRGGB12, 12, "RGGB Bayer 12 bpp", }, { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, 0, - V4L2_PIX_FMT_UYVY, 16, }, + V4L2_PIX_FMT_UYVY, 16, "YUV 4:2:2 (UYVY)", }, { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, 0, - V4L2_PIX_FMT_YUYV, 16, }, + V4L2_PIX_FMT_YUYV, 16, "YUV 4:2:2 (YUYV)", }, { V4L2_MBUS_FMT_YUYV8_1_5X8, V4L2_MBUS_FMT_YUYV8_1_5X8, V4L2_MBUS_FMT_YUYV8_1_5X8, 0, - V4L2_PIX_FMT_NV12, 8, }, + V4L2_PIX_FMT_NV12, 8, "YUV 4:2:0 (NV12)", }, }; const struct iss_format_info * diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 35f14d8..2c8d256 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -40,6 +40,7 @@ struct v4l2_pix_format; * shifted to be 8 bits per pixel. =0 if format is not shiftable. * @pixelformat: V4L2 pixel format FCC identifier * @bpp: Bits per pixel + * @description: Human-readable format description */ struct iss_format_info { enum v4l2_mbus_pixelcode code; @@ -48,6 +49,7 @@ struct iss_format_info { enum v4l2_mbus_pixelcode flavor; u32 pixelformat; unsigned int bpp; + const char *description; }; enum iss_pipeline_stream_state { -- cgit v0.10.2 From cc3c2ac29152b07ce58c8c7b34a8a8e8f321335a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Sep 2013 08:20:16 -0300 Subject: [media] v4l: omap4iss: Make __iss_video_get_format() return a v4l2_mbus_framefmt The function will be used by a caller that needs the media bus format instead of the pixel format currently returned. Move the media bus format to pixel format conversion to the existing caller. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index b4ffde8..5dbd774 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -232,7 +232,8 @@ iss_video_far_end(struct iss_video *video) } static int -__iss_video_get_format(struct iss_video *video, struct v4l2_format *format) +__iss_video_get_format(struct iss_video *video, + struct v4l2_mbus_framefmt *format) { struct v4l2_subdev_format fmt; struct v4l2_subdev *subdev; @@ -243,6 +244,7 @@ __iss_video_get_format(struct iss_video *video, struct v4l2_format *format) if (subdev == NULL) return -EINVAL; + memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -253,26 +255,29 @@ __iss_video_get_format(struct iss_video *video, struct v4l2_format *format) if (ret) return ret; - format->type = video->type; - return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); + *format = fmt.format; + return 0; } static int iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh) { - struct v4l2_format format; + struct v4l2_mbus_framefmt format; + struct v4l2_pix_format pixfmt; int ret; - memcpy(&format, &vfh->format, sizeof(format)); ret = __iss_video_get_format(video, &format); if (ret < 0) return ret; - if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || - vfh->format.fmt.pix.height != format.fmt.pix.height || - vfh->format.fmt.pix.width != format.fmt.pix.width || - vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || - vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) + pixfmt.bytesperline = 0; + ret = iss_video_mbus_to_pix(video, &format, &pixfmt); + + if (vfh->format.fmt.pix.pixelformat != pixfmt.pixelformat || + vfh->format.fmt.pix.height != pixfmt.height || + vfh->format.fmt.pix.width != pixfmt.width || + vfh->format.fmt.pix.bytesperline != pixfmt.bytesperline || + vfh->format.fmt.pix.sizeimage != pixfmt.sizeimage) return -EINVAL; return ret; -- cgit v0.10.2 From 0b1d4249660fbb0c558a096ce72914b7f5fa82a8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 12 Jul 2013 11:25:36 -0300 Subject: [media] v4l: omap4iss: Add enum_fmt_vid_cap ioctl support List the pixel formats compatible with the active format currently configured on the connected pad. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 5dbd774..68eab6e 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -496,6 +496,41 @@ iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) } static int +iss_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_mbus_framefmt format; + unsigned int index = f->index; + unsigned int i; + int ret; + + if (f->type != video->type) + return -EINVAL; + + ret = __iss_video_get_format(video, &format); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + const struct iss_format_info *info = &formats[i]; + + if (format.code != info->code) + continue; + + if (index == 0) { + f->pixelformat = info->pixelformat; + strlcpy(f->description, info->description, + sizeof(f->description)); + return 0; + } + + index--; + } + + return -EINVAL; +} + +static int iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format) { struct iss_video_fh *vfh = to_iss_video_fh(fh); @@ -918,6 +953,7 @@ iss_video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_querycap = iss_video_querycap, + .vidioc_enum_fmt_vid_cap = iss_video_enum_format, .vidioc_g_fmt_vid_cap = iss_video_get_format, .vidioc_s_fmt_vid_cap = iss_video_set_format, .vidioc_try_fmt_vid_cap = iss_video_try_format, -- cgit v0.10.2 From 6016498f2b9d72b4f813d7349f0621ccc92c4f5a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Oct 2013 11:52:45 -0300 Subject: [media] v4l: omap4iss: Propagate stop timeouts from submodules to the driver core Return an error from the s_stream handlers when stopping the stream failed instead of just logging the error and ignoring it. While we're at it, move the logging code from submodules to the driver code. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index ba8460d..a0bf2f3 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -614,6 +614,8 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; + int failure = 0; + int ret; entity = &pipe->output->video.entity; while (1) { @@ -629,10 +631,15 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); - v4l2_subdev_call(subdev, video, s_stream, 0); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0) { + dev_dbg(iss->dev, "%s: module stop timeout.\n", + subdev->name); + failure = -ETIMEDOUT; + } } - return 0; + return failure; } /* diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 077545f..7e7e955 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1056,6 +1056,7 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) struct iss_device *iss = csi2->iss; struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); struct iss_video *video_out = &csi2->video_out; + int ret = 0; if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) { if (enable == ISS_PIPELINE_STREAM_STOPPED) @@ -1069,8 +1070,6 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) switch (enable) { case ISS_PIPELINE_STREAM_CONTINUOUS: { - int ret; - ret = omap4iss_csiphy_config(iss, sd); if (ret < 0) return ret; @@ -1102,8 +1101,7 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) return 0; if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait, &csi2->stopping)) - dev_dbg(iss->dev, "%s: module stop timeout.\n", - sd->name); + ret = -ETIMEDOUT; csi2_ctx_enable(csi2, 0, 0); csi2_if_enable(csi2, 0); csi2_irq_ctx_set(csi2, 0); @@ -1117,7 +1115,7 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) } csi2->state = enable; - return 0; + return ret; } /* subdev video operations */ diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index d0b9f8c..c013f83 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -166,8 +166,7 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) return 0; if (omap4iss_module_sync_idle(&sd->entity, &ipipe->wait, &ipipe->stopping)) - dev_dbg(iss->dev, "%s: module stop timeout.\n", - sd->name); + ret = -ETIMEDOUT; ipipe_enable(ipipe, 0); omap4iss_isp_disable_interrupts(iss); diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 2d11f62..00bc937 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -363,8 +363,7 @@ static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) return 0; if (omap4iss_module_sync_idle(&sd->entity, &ipipeif->wait, &ipipeif->stopping)) - dev_dbg(iss->dev, "%s: module stop timeout.\n", - sd->name); + ret = -ETIMEDOUT; if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) ipipeif_write_enable(ipipeif, 0); diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 5bf5080..9dbf018 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -416,8 +416,7 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) return 0; if (omap4iss_module_sync_idle(&sd->entity, &resizer->wait, &resizer->stopping)) - dev_dbg(iss->dev, "%s: module stop timeout.\n", - sd->name); + ret = -ETIMEDOUT; resizer_enable(resizer, 0); omap4iss_isp_disable_interrupts(iss); -- cgit v0.10.2 From af15d025ecdf35ad1eb438595727d80155d8d28e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 10 Oct 2013 10:40:02 -0300 Subject: [media] v4l: omap4iss: Enable/disabling the ISP interrupts globally ISP interrupts are enabled/disabled when starting/stopping the IPIPEIF or resizer. This doesn't permit using the two modules in separate pipelines. Fix it by enabling/disabling the ISP interrupts at the same time as the ISS interrupts, in the ISS device get/put operations. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index a0bf2f3..dffa31e 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -67,53 +67,59 @@ void omap4iss_flush(struct iss_device *iss) } /* - * iss_enable_interrupts - Enable ISS interrupts. + * iss_isp_enable_interrupts - Enable ISS ISP interrupts. * @iss: OMAP4 ISS device */ -static void iss_enable_interrupts(struct iss_device *iss) +static void omap4iss_isp_enable_interrupts(struct iss_device *iss) { - static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB | ISS_HL_IRQ_ISP(0); - - /* Enable HL interrupts */ - iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), hl_irq); - iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_SET(5), hl_irq); + static const u32 isp_irq = ISP5_IRQ_OCP_ERR | + ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | + ISP5_IRQ_RSZ_FIFO_OVF | + ISP5_IRQ_RSZ_INT_DMA | + ISP5_IRQ_ISIF_INT(0); + /* Enable ISP interrupts */ + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), isp_irq); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_SET(0), + isp_irq); } /* - * iss_disable_interrupts - Disable ISS interrupts. + * iss_isp_disable_interrupts - Disable ISS interrupts. * @iss: OMAP4 ISS device */ -static void iss_disable_interrupts(struct iss_device *iss) +static void omap4iss_isp_disable_interrupts(struct iss_device *iss) { - iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_CLR(5), -1); + iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_CLR(0), ~0); } /* - * iss_isp_enable_interrupts - Enable ISS ISP interrupts. + * iss_enable_interrupts - Enable ISS interrupts. * @iss: OMAP4 ISS device */ -void omap4iss_isp_enable_interrupts(struct iss_device *iss) +static void iss_enable_interrupts(struct iss_device *iss) { - static const u32 isp_irq = ISP5_IRQ_OCP_ERR | - ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | - ISP5_IRQ_RSZ_FIFO_OVF | - ISP5_IRQ_RSZ_INT_DMA | - ISP5_IRQ_ISIF_INT(0); + static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB + | ISS_HL_IRQ_ISP(0); - /* Enable ISP interrupts */ - iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), isp_irq); - iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_SET(0), - isp_irq); + /* Enable HL interrupts */ + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), hl_irq); + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_SET(5), hl_irq); + + if (iss->regs[OMAP4_ISS_MEM_ISP_SYS1]) + omap4iss_isp_enable_interrupts(iss); } /* - * iss_isp_disable_interrupts - Disable ISS interrupts. + * iss_disable_interrupts - Disable ISS interrupts. * @iss: OMAP4 ISS device */ -void omap4iss_isp_disable_interrupts(struct iss_device *iss) +static void iss_disable_interrupts(struct iss_device *iss) { - iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_CLR(0), -1); + if (iss->regs[OMAP4_ISS_MEM_ISP_SYS1]) + omap4iss_isp_disable_interrupts(iss); + + iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_CLR(5), ~0); } int omap4iss_get_external_info(struct iss_pipeline *pipe, diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 660809e..f63caaf 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -141,9 +141,6 @@ void omap4iss_isp_subclk_enable(struct iss_device *iss, void omap4iss_isp_subclk_disable(struct iss_device *iss, enum iss_isp_subclk_resource res); -void omap4iss_isp_enable_interrupts(struct iss_device *iss); -void omap4iss_isp_disable_interrupts(struct iss_device *iss); - int omap4iss_pipeline_pm_use(struct media_entity *entity, int use); int omap4iss_register_entities(struct platform_device *pdev, diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index c013f83..6eaafc5 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -116,8 +116,6 @@ static void ipipe_configure(struct iss_ipipe_device *ipipe) /* IPIPE_PAD_SOURCE_VP */ format = &ipipe->formats[IPIPE_PAD_SOURCE_VP]; /* Do nothing? */ - - omap4iss_isp_enable_interrupts(iss); } /* ----------------------------------------------------------------------------- @@ -169,7 +167,6 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) ret = -ETIMEDOUT; ipipe_enable(ipipe, 0); - omap4iss_isp_disable_interrupts(iss); omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE); break; } diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 00bc937..7bc1457 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -213,8 +213,6 @@ cont_raw: /* IPIPEIF_PAD_SOURCE_VP */ /* Do nothing? */ - - omap4iss_isp_enable_interrupts(iss); } /* ----------------------------------------------------------------------------- @@ -368,7 +366,6 @@ static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) ipipeif_write_enable(ipipeif, 0); ipipeif_enable(ipipeif, 0); - omap4iss_isp_disable_interrupts(iss); omap4iss_isp_subclk_disable(iss, IPIPEIF_DRV_SUBCLK_MASK); iss_video_dmaqueue_flags_clr(video_out); break; diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 9dbf018..4673c05 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -256,8 +256,6 @@ static void resizer_configure(struct iss_resizer_device *resizer) } else { iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, 0); } - - omap4iss_isp_enable_interrupts(iss); } /* ----------------------------------------------------------------------------- @@ -419,7 +417,6 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) ret = -ETIMEDOUT; resizer_enable(resizer, 0); - omap4iss_isp_disable_interrupts(iss); iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG, RSZ_SYSCONFIG_RSZA_CLK_EN); iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR, -- cgit v0.10.2 From f3632ba850c70bf24a80295621857166e0c0b14c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Oct 2013 11:52:45 -0300 Subject: [media] v4l: omap4iss: Reset the ISS when the pipeline can't be stopped When a failure to stop a module in the pipeline is detected, the only way to recover is to reset the ISS. However, as other users can be using a different pipeline with other modules, the ISS can't be reset synchronously with the error detection. Keep track of modules that have failed to stop, and reset the ISS accordingly when the last user releases the last reference to the ISS. Refuse to start streaming on a pipeline that contains a crashed module, as the hardware wouldn't work anyway. Modify the omap4iss_pipeline_set_stream() function to record the new ISS pipeline state only when no error occurs, except when stopping the pipeline in which case the pipeline is still marked as stopped. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index dffa31e..5ad604d 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -573,12 +573,22 @@ static int iss_pipeline_link_notify(struct media_link *link, u32 flags, static int iss_pipeline_enable(struct iss_pipeline *pipe, enum iss_pipeline_stream_state mode) { + struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; unsigned long flags; int ret; + /* If one of the entities in the pipeline has crashed it will not work + * properly. 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). + */ + if (pipe->entities & iss->crashed) + return -EIO; + spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); @@ -617,6 +627,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, */ static int iss_pipeline_disable(struct iss_pipeline *pipe) { + struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; @@ -641,6 +652,11 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) if (ret < 0) { dev_dbg(iss->dev, "%s: module stop timeout.\n", subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISS will be reset when + * applications will release it. + */ + iss->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } @@ -715,6 +731,7 @@ static int iss_reset(struct iss_device *iss) usleep_range(10, 10); } + iss->crashed = 0; return 0; } @@ -1058,6 +1075,13 @@ void omap4iss_put(struct iss_device *iss) BUG_ON(iss->ref_count == 0); if (--iss->ref_count == 0) { iss_disable_interrupts(iss); + /* Reset the ISS if an entity has failed to stop. This is the + * only way to recover from such conditions, although it would + * be worth investigating whether resetting the ISP only can't + * fix the problem in some cases. + */ + if (iss->crashed) + iss_reset(iss); iss_disable_clocks(iss); } mutex_unlock(&iss->iss_mutex); diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index f63caaf..3c1e920 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -77,6 +77,10 @@ struct iss_reg { u32 val; }; +/* + * struct iss_device - ISS device structure. + * @crashed: Bitmask of crashed entities (indexed by entity ID) + */ struct iss_device { struct v4l2_device v4l2_dev; struct media_device media_dev; @@ -93,6 +97,7 @@ struct iss_device { u64 raw_dmamask; struct mutex iss_mutex; /* For handling ref_count field */ + bool crashed; int has_context; int ref_count; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 68eab6e..9106487 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -772,6 +772,8 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); + struct media_entity_graph graph; + struct media_entity *entity; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; @@ -791,6 +793,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; + pipe->entities = 0; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); @@ -799,6 +802,11 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_media_entity_pipeline_start; + entity = &video->video.entity; + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) + pipe->entities |= 1 << entity->id; + /* Verify that the currently configured format matches the output of * the connected subdev. */ diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 2c8d256..73e1a34 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -77,6 +77,7 @@ enum iss_pipeline_state { /* * struct iss_pipeline - An OMAP4 ISS hardware pipeline + * @entities: Bitmask of entities in the pipeline (indexed by entity ID) * @error: A hardware error occurred during capture */ struct iss_pipeline { @@ -86,6 +87,7 @@ struct iss_pipeline { enum iss_pipeline_stream_state stream_state; struct iss_video *input; struct iss_video *output; + unsigned int entities; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; -- cgit v0.10.2 From 216814fb0167673c6417b5db83ade84e58031e2c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 10 Oct 2013 09:06:27 -0300 Subject: [media] v4l: omap4iss: csi2: Replace manual if statement with a subclk field Instead of manually checking whether the CSI2 module is CSI2a or CSI2b in order to select the right subclock to enable/disable, add a subclk field to the iss_csi2 structure, initialize it with the corresponding subclock value and use it at runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 7e7e955..e4fc4a0 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1062,10 +1062,7 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) if (enable == ISS_PIPELINE_STREAM_STOPPED) return 0; - if (csi2 == &iss->csi2a) - omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A); - else if (csi2 == &iss->csi2b) - omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_B); + omap4iss_subclk_enable(iss, csi2->subclk); } switch (enable) { @@ -1106,10 +1103,7 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) csi2_if_enable(csi2, 0); csi2_irq_ctx_set(csi2, 0); omap4iss_csiphy_release(csi2->phy); - if (csi2 == &iss->csi2a) - omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A); - else if (csi2 == &iss->csi2b) - omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_B); + omap4iss_subclk_disable(iss, csi2->subclk); iss_video_dmaqueue_flags_clr(video_out); break; } @@ -1311,6 +1305,7 @@ int omap4iss_csi2_init(struct iss_device *iss) csi2a->available = 1; csi2a->regs1 = OMAP4_ISS_MEM_CSI2_A_REGS1; csi2a->phy = &iss->csiphy1; + csi2a->subclk = OMAP4_ISS_SUBCLK_CSI2_A; csi2a->state = ISS_PIPELINE_STREAM_STOPPED; init_waitqueue_head(&csi2a->wait); @@ -1322,6 +1317,7 @@ int omap4iss_csi2_init(struct iss_device *iss) csi2b->available = 1; csi2b->regs1 = OMAP4_ISS_MEM_CSI2_B_REGS1; csi2b->phy = &iss->csiphy2; + csi2b->subclk = OMAP4_ISS_SUBCLK_CSI2_B; csi2b->state = ISS_PIPELINE_STREAM_STOPPED; init_waitqueue_head(&csi2b->wait); diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index 69a6263..971aa7b 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -131,6 +131,8 @@ struct iss_csi2_device { /* memory resources, as defined in enum iss_mem_resources */ unsigned int regs1; unsigned int regs2; + /* ISP subclock, as defined in enum iss_isp_subclk_resource */ + unsigned int subclk; u32 output; /* output to IPIPEIF, memory or both? */ bool dpcm_decompress; -- cgit v0.10.2 From 112da08512bb0c58c169ec8bda0166f627250a2c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 5 Nov 2013 12:32:05 -0300 Subject: [media] v4l: omap4iss: Cancel streaming when a fatal error occurs When a fatal error that prevents any further video streaming occurs in a pipeline, all queued buffers must be marked as erroneous and new buffers must be prevented from being queued. Implement this behaviour with a new omap4iss_pipeline_cancel_stream() function that can be used by submodules to cancel streaming. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 5ad604d..61fbfcd 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -693,6 +693,23 @@ int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, } /* + * omap4iss_pipeline_cancel_stream - Cancel stream on a pipeline + * @pipe: ISS 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 omap4iss_pipeline_cancel_stream(struct iss_pipeline *pipe) +{ + if (pipe->input) + omap4iss_video_cancel_stream(pipe->input); + if (pipe->output) + omap4iss_video_cancel_stream(pipe->output); +} + +/* * iss_pipeline_is_last - Verify if entity has an enabled link to the output * video node * @me: ISS module's media entity diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 3c1e920..346db92 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -131,6 +131,7 @@ int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait, int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, enum iss_pipeline_stream_state state); +void omap4iss_pipeline_cancel_stream(struct iss_pipeline *pipe); void omap4iss_configure_bridge(struct iss_device *iss, enum ipipeif_input_entity input); diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 4673c05..c6225d8 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -312,7 +312,7 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n", events & ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR ? 1 : 0, events & ISP5_IRQ_RSZ_FIFO_OVF ? 1 : 0); - pipe->error = true; + omap4iss_pipeline_cancel_stream(pipe); } if (omap4iss_module_sync_is_stopping(&resizer->wait, diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 9106487..482b72f 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -328,6 +328,15 @@ static int iss_video_buf_prepare(struct vb2_buffer *vb) if (vb2_plane_size(vb, 0) < size) return -ENOBUFS; + /* 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 = vb2_dma_contig_plane_dma_addr(vb, 0); if (!IS_ALIGNED(addr, 32)) { dev_dbg(video->iss->dev, @@ -346,12 +355,20 @@ static void iss_video_buf_queue(struct vb2_buffer *vb) struct iss_video *video = vfh->video; struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); - unsigned int empty; unsigned long flags; + bool empty; spin_lock_irqsave(&video->qlock, flags); + + if (unlikely(video->error)) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&video->qlock, flags); + return; + } + empty = list_empty(&video->dmaqueue); list_add_tail(&buffer->list, &video->dmaqueue); + spin_unlock_irqrestore(&video->qlock, flags); if (empty) { @@ -471,6 +488,33 @@ struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) return buf; } +/* + * omap4iss_video_cancel_stream - Cancel stream on a video node + * @video: ISS video object + * + * Cancelling a stream mark all buffers on the video node as erroneous and makes + * sure no new buffer can be queued. + */ +void omap4iss_video_cancel_stream(struct iss_video *video) +{ + unsigned long flags; + + spin_lock_irqsave(&video->qlock, flags); + + while (!list_empty(&video->dmaqueue)) { + struct iss_buffer *buf; + + buf = list_first_entry(&video->dmaqueue, struct iss_buffer, + list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + video->error = true; + + spin_unlock_irqrestore(&video->qlock, flags); +} + /* ----------------------------------------------------------------------------- * V4L2 ioctls */ @@ -852,6 +896,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = &vfh->queue; INIT_LIST_HEAD(&video->dmaqueue); spin_lock_init(&video->qlock); + video->error = false; atomic_set(&pipe->frame_number, -1); ret = vb2_streamon(&vfh->queue, type); diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 73e1a34..878e4a3 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -163,10 +163,11 @@ struct iss_video { /* Pipeline state */ struct iss_pipeline pipe; struct mutex stream_lock; /* pipeline and stream states */ + bool error; /* Video buffers queue */ struct vb2_queue *queue; - spinlock_t qlock; /* Spinlock for dmaqueue */ + spinlock_t qlock; /* protects dmaqueue and error */ struct list_head dmaqueue; enum iss_video_dmaqueue_flags dmaqueue_flags; struct vb2_alloc_ctx *alloc_ctx; @@ -194,6 +195,7 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev); void omap4iss_video_unregister(struct iss_video *video); struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video); +void omap4iss_video_cancel_stream(struct iss_video *video); struct media_pad *omap4iss_video_remote_pad(struct iss_video *video); const struct iss_format_info * -- cgit v0.10.2 From bea7791529375230261c24a2c249e58b7111e885 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 13 Nov 2013 19:54:32 -0300 Subject: [media] v4l: omap4iss: resizer: Fix comment regarding bypass mode The comment explaining the usage of the bypass bit is wrong, fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index c6225d8..ae831b8 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -190,7 +190,9 @@ static void resizer_configure(struct iss_resizer_device *resizer) informat = &resizer->formats[RESIZER_PAD_SINK]; outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM]; - /* Make sure we don't bypass the resizer */ + /* Disable pass-through more. Despite its name, the BYPASS bit controls + * pass-through mode, not bypass mode. + */ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0, RSZ_SRC_FMT0_BYPASS); -- cgit v0.10.2 From 4d88550a9a370e6fe6733d5c9424c10f20c0580f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Dec 2013 17:16:20 -0300 Subject: [media] mt9v032: Remove unused macro The EXT_CLK macro is unused, remove it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 2c50eff..2055820 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -215,8 +215,6 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) max_t(s32, mt9v032->hblank, 660 - crop->width)); } -#define EXT_CLK 25000000 - static int mt9v032_power_on(struct mt9v032 *mt9v032) { struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); -- cgit v0.10.2 From 2b9e9f779cb29f25102d4e7f14df557ead58e659 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Dec 2013 11:52:44 -0300 Subject: [media] mt9v032: Fix pixel array size The active pixel array size is 753x481 with 4 additional black rows at the top. Fix the driver accordingly. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 2055820..12360e4 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -27,8 +27,9 @@ #include #include -#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 -- cgit v0.10.2 From 637f005e455739c8128dadb73dfbe67a4db26f45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Dec 2013 16:39:18 -0300 Subject: [media] mt9v032: Fix binning configuration The sensor can scale the image down using binning by 1, 2 or 4 in both directions. Update size enumeration and ratio and binning factor computation accordingly. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 12360e4..38bf664 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -127,6 +127,8 @@ struct mt9v032 { struct v4l2_mbus_framefmt format; struct v4l2_rect crop; + unsigned int hratio; + unsigned int vratio; struct v4l2_ctrl_handler ctrls; struct { @@ -311,22 +313,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; @@ -369,12 +369,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; @@ -391,18 +391,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,21 +432,25 @@ static int mt9v032_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, MT9V032_WINDOW_WIDTH_MIN), + max(__crop->width / 4, MT9V032_WINDOW_WIDTH_MIN), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), + max(__crop->height / 4, MT9V032_WINDOW_HEIGHT_MIN), __crop->height); - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, 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; @@ -490,8 +506,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; @@ -665,7 +684,7 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", client->addr); - mt9v032_configure_pixel_rate(mt9v032, 1); + mt9v032_configure_pixel_rate(mt9v032); return ret; } @@ -824,6 +843,9 @@ static int mt9v032_probe(struct i2c_client *client, 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; -- cgit v0.10.2 From 220ddc7f1d19de71cbe1fe29dbf97548efbb5fa9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 4 Dec 2013 15:31:13 -0300 Subject: [media] mt9v032: Add support for monochrome models Identify the model based on the I2C device name and configure formats accordingly. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 38bf664..a31d6d1 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -121,6 +121,24 @@ #define MT9V032_AGC_ENABLE (1 << 1) #define MT9V032_THERMAL_INFO 0xc1 +enum mt9v032_model { + MT9V032_MODEL_COLOR, + MT9V032_MODEL_MONO, +}; + +struct mt9v032_model_info { + bool color; +}; + +static const struct mt9v032_model_info mt9v032_models[] = { + [MT9V032_MODEL_COLOR] = { + .color = true, + }, + [MT9V032_MODEL_MONO] = { + .color = false, + }, +}; + struct mt9v032 { struct v4l2_subdev subdev; struct media_pad pad; @@ -142,6 +160,7 @@ struct mt9v032 { struct clk *clk; struct mt9v032_platform_data *pdata; + const struct mt9v032_model_info *model; u32 sysclk; u16 chip_control; @@ -691,6 +710,7 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) 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; @@ -701,7 +721,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; @@ -773,6 +798,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); @@ -837,7 +863,11 @@ 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; @@ -876,7 +906,8 @@ 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_COLOR] }, + { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_MONO] }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v032_id); -- cgit v0.10.2 From 0a466b600d03e8c4a90ac8aa9e9e0aa382976003 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Dec 2013 11:59:52 -0300 Subject: [media] mt9v032: Add support for model-specific parameters To prepare support of the MT9V034, add the necessary infrastructure to support model-specific parameters. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index a31d6d1..62db26b 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -126,15 +126,51 @@ enum mt9v032_model { MT9V032_MODEL_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" }, +}; + +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, + }, +}; + static const struct mt9v032_model_info mt9v032_models[] = { [MT9V032_MODEL_COLOR] = { + .data = &mt9v032_model_data[0], .color = true, }, [MT9V032_MODEL_MONO] = { + .data = &mt9v032_model_data[0], .color = false, }, }; @@ -161,6 +197,7 @@ struct mt9v032 { struct mt9v032_platform_data *pdata; const struct mt9v032_model_info *model; + const struct mt9v032_model_version *version; u32 sysclk; u16 chip_control; @@ -232,9 +269,11 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) { struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); struct v4l2_rect *crop = &mt9v032->crop; + unsigned int hblank; - return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, - max_t(s32, mt9v032->hblank, 660 - crop->width)); + hblank = max_t(s32, mt9v032->hblank, + mt9v032->model->data->min_row_time - crop->width); + return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); } static int mt9v032_power_on(struct mt9v032 *mt9v032) @@ -279,7 +318,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; @@ -678,7 +717,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", @@ -691,17 +731,29 @@ 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); @@ -811,16 +863,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, -- cgit v0.10.2 From daecfebcb8847ae759efdd1637857b2108000844 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Dec 2013 12:01:03 -0300 Subject: [media] mt9v032: Add support for the MT9V034 The MT9V034 sensor is very similar to the MT9V032, with a couple of different registers and parameters. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 62db26b..0d2b4a8 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -36,6 +36,7 @@ #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 @@ -54,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) @@ -69,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) @@ -82,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 @@ -97,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) @@ -122,8 +133,10 @@ #define MT9V032_THERMAL_INFO 0xc1 enum mt9v032_model { - MT9V032_MODEL_COLOR, - MT9V032_MODEL_MONO, + MT9V032_MODEL_V032_COLOR, + MT9V032_MODEL_V032_MONO, + MT9V032_MODEL_V034_COLOR, + MT9V032_MODEL_V034_MONO, }; struct mt9v032_model_version { @@ -149,6 +162,7 @@ struct mt9v032_model_info { 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[] = { @@ -161,18 +175,35 @@ static const struct mt9v032_model_data mt9v032_model_data[] = { .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_COLOR] = { + [MT9V032_MODEL_V032_COLOR] = { .data = &mt9v032_model_data[0], .color = true, }, - [MT9V032_MODEL_MONO] = { + [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 { @@ -269,10 +300,15 @@ 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; - hblank = max_t(s32, mt9v032->hblank, - mt9v032->model->data->min_row_time - crop->width); + if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) + min_hblank += (mt9v032->hratio - 1) * 10; + min_hblank = max((int)mt9v032->model->data->min_row_time - crop->width, + (int)min_hblank); + hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); + return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); } @@ -958,8 +994,10 @@ static int mt9v032_remove(struct i2c_client *client) } static const struct i2c_device_id mt9v032_id[] = { - { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_COLOR] }, - { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_MONO] }, + { "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); -- cgit v0.10.2 From 3299ba5c0b213be5d911752d40251c1abc1004f7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 15 Oct 2013 18:58:43 -0300 Subject: [media] v4l: vsp1: Supply frames to the DU continuously When operating in DU output mode (deep pipeline to the DU through the LIF), the VSP1 needs to constantly supply frames to the display. To ensure reuse the last queued buffer instead of returning it to the user. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 714c53e..eb1225d 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)) -- cgit v0.10.2 From e5ad37b64de975463c51f9ed4e4c55dc6e442ba5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 24 Aug 2013 20:49:58 -0300 Subject: [media] v4l: vsp1: Add cropping support Implement the get and set selection operations on the RPF and WPF entities. Only the crop targets are currently available. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 254871d..bce2be5 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 9752d55..782f770 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 c182d85..6cbdb54 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_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index db4b85e..7baed81 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 = { -- cgit v0.10.2 From 7e941a7999fe5ece856c066ba94c929870e30fe0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Jul 2013 12:05:06 -0300 Subject: [media] v4l: Add media format codes for AHSV8888 on 32-bit busses Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index f72c1cc..bfaef50 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2492,6 +2492,163 @@
+ HSV/HSL Formats + + Those formats transfer pixel data as RGB values in a cylindrical-coordinate + system using Hue-Saturation-Value or Hue-Saturation-Lightness components. The + format code is made of the following information. + + The hue, saturation, value or lightness and optional alpha + components order code, as encoded in a pixel sample. The only currently + supported value is AHSV. + + The number of bits per component, for each component. The values + can be different for all components. The only currently supported value is 8888. + + The number of bus samples per pixel. Pixels that are wider than + the bus width must be transferred in multiple samples. The only currently + supported value is 1. + The bus width. + For formats where the total number of bits per pixel is smaller + than the number of bus samples per pixel times the bus width, a padding + value stating if the bytes are padded in their most high order bits + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger + than 1, an endianness value stating if the pixel is transferred MSB first + (BE) or LSB first (LE). + + + + The following table lists existing HSV/HSL formats. + + + HSV/HSL formats + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier + Code + + Data organization + + + + + Bit + 31 + 30 + 29 + 28 + 27 + 26 + 25 + 24 + 23 + 22 + 21 + 20 + 19 + 18 + 17 + 16 + 15 + 14 + 13 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_MBUS_FMT_AHSV8888_1X32 + 0x6001 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + h7 + h6 + h5 + h4 + h3 + h2 + h1 + h0 + s7 + s6 + s5 + s4 + s3 + s2 + s1 + s0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + +
+
+ +
JPEG Compressed Formats Those data formats consist of an ordered sequence of 8-bit bytes diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index a960125..b5c3aab 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -110,6 +110,9 @@ enum v4l2_mbus_pixelcode { /* S5C73M3 sensor specific interleaved UYVY and JPEG */ V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 = 0x5001, + + /* HSV - next is 0x6002 */ + V4L2_MBUS_FMT_AHSV8888_1X32 = 0x6001, }; /** -- cgit v0.10.2 From 5cdf5741d6529f3c04dcd117b4c9b9039d101602 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Jul 2013 17:30:14 -0300 Subject: [media] v4l: vsp1: Add HST and HSI support The Hue Saturation value Transform and Hue Saturation value Inverse transform entities convert from RGB to HSV and back. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 4da2261..587b800 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,5 +1,5 @@ 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_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 d6c6ecd..275286e 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -28,6 +28,7 @@ struct clk; struct device; struct vsp1_platform_data; +struct vsp1_hsit; struct vsp1_lif; struct vsp1_rwpf; struct vsp1_uds; @@ -47,6 +48,8 @@ struct vsp1_device { struct mutex lock; int ref_count; + struct vsp1_hsit *hsi; + struct vsp1_hsit *hst; struct vsp1_lif *lif; struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; struct vsp1_uds *uds[VPS1_MAX_UDS]; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index d16bf0f..bcede46 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -20,6 +20,7 @@ #include #include "vsp1.h" +#include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_rwpf.h" #include "vsp1_uds.h" @@ -152,6 +153,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)) { diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 9028f9d..3798ef4 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -122,6 +122,8 @@ 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_RPF(0), VI6_DPR_RPF_ROUTE(0) }, { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index c4feab2c..0d61746 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -20,6 +20,8 @@ struct vsp1_device; enum vsp1_entity_type { + VSP1_ENTITY_HSI, + VSP1_ENTITY_HST, VSP1_ENTITY_LIF, VSP1_ENTITY_RPF, VSP1_ENTITY_UDS, diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c new file mode 100644 index 0000000..2854853 --- /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 +#include + +#include + +#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 0000000..82f1c8426 --- /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 +#include + +#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_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 1d3304f..0b93f88 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -424,12 +424,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 -- cgit v0.10.2 From a626e64e0bee4fb26848dbed92223dde488f3d93 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Jul 2013 12:03:30 -0300 Subject: [media] v4l: vsp1: Add SRU support The Super Resolution Unit performs super resolution processing with optional upscaling by a factor of two. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 587b800..45621cb 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_hsit.o vsp1_lif.o vsp1_uds.o +vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_sru.o +vsp1-y += 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 275286e..2ab13f5 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -31,6 +31,7 @@ struct vsp1_platform_data; struct vsp1_hsit; struct vsp1_lif; struct vsp1_rwpf; +struct vsp1_sru; struct vsp1_uds; #define VPS1_MAX_RPF 5 @@ -52,6 +53,7 @@ struct vsp1_device { struct vsp1_hsit *hst; struct vsp1_lif *lif; 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 bcede46..6f82951 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -23,6 +23,7 @@ #include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_rwpf.h" +#include "vsp1_sru.h" #include "vsp1_uds.h" /* ----------------------------------------------------------------------------- @@ -192,6 +193,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 3798ef4..b11e5a6 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -15,6 +15,7 @@ #include #include +#include #include #include "vsp1.h" @@ -130,6 +131,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, { 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) }, @@ -179,5 +181,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 0d61746..08e4480 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -24,6 +24,7 @@ enum vsp1_entity_type { VSP1_ENTITY_HST, VSP1_ENTITY_LIF, VSP1_ENTITY_RPF, + VSP1_ENTITY_SRU, VSP1_ENTITY_UDS, VSP1_ENTITY_WPF, }; diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 0b93f88..530cc61 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 diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c new file mode 100644 index 0000000..7ab1a0b --- /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 +#include + +#include + +#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 0000000..381870b --- /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 +#include +#include + +#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/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h index a73a456..27c0ede 100644 --- a/include/linux/platform_data/vsp1.h +++ b/include/linux/platform_data/vsp1.h @@ -14,6 +14,7 @@ #define __PLATFORM_VSP1_H__ #define VSP1_HAS_LIF (1 << 0) +#define VSP1_HAS_SRU (1 << 1) struct vsp1_platform_data { unsigned int features; -- cgit v0.10.2 From 989af88339db26345e23271dae1089d949c4a0f1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Jul 2013 12:03:30 -0300 Subject: [media] v4l: vsp1: Add LUT support The Look-Up Table looks up values in 8-bit indexed tables separately for each color component. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 45621cb..151cecd 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,6 +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_hsit.o vsp1_lif.o vsp1_sru.o -vsp1-y += 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 2ab13f5..94d1b02 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -30,6 +30,7 @@ struct device; struct vsp1_platform_data; struct vsp1_hsit; struct vsp1_lif; +struct vsp1_lut; struct vsp1_rwpf; struct vsp1_sru; struct vsp1_uds; @@ -52,6 +53,7 @@ struct vsp1_device { 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]; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 6f82951..0df0a99 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -22,6 +22,7 @@ #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" @@ -180,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; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index b11e5a6..0226e47 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -126,6 +126,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, { 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) }, diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 08e4480..e152798 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -23,6 +23,7 @@ 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, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c new file mode 100644 index 0000000..4e9dc7c --- /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 +#include +#include + +#include + +#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 0000000..f92ffb8 --- /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 +#include + +#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 530cc61..2865080 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -425,6 +425,7 @@ */ #define VI6_LUT_CTRL 0x2800 +#define VI6_LUT_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * CLU Control Registers diff --git a/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h index 27c0ede..63170e26 100644 --- a/include/linux/platform_data/vsp1.h +++ b/include/linux/platform_data/vsp1.h @@ -14,7 +14,8 @@ #define __PLATFORM_VSP1_H__ #define VSP1_HAS_LIF (1 << 0) -#define VSP1_HAS_SRU (1 << 1) +#define VSP1_HAS_LUT (1 << 1) +#define VSP1_HAS_SRU (1 << 2) struct vsp1_platform_data { unsigned int features; diff --git a/include/uapi/linux/vsp1.h b/include/uapi/linux/vsp1.h new file mode 100644 index 0000000..e18858f --- /dev/null +++ b/include/uapi/linux/vsp1.h @@ -0,0 +1,34 @@ +/* + * vsp1.h + * + * Renesas R-Car VSP1 - User-space API + * + * Copyright (C) 2013 Renesas Corporation + * + * Contacts: Laurent Pinchart + * + * 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 __VSP1_USER_H__ +#define __VSP1_USER_H__ + +#include +#include + +/* + * Private IOCTLs + * + * VIDIOC_VSP1_LUT_CONFIG - Configure the lookup table + */ + +#define VIDIOC_VSP1_LUT_CONFIG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config) + +struct vsp1_lut_config { + u32 lut[256]; +}; + +#endif /* __VSP1_USER_H__ */ -- cgit v0.10.2 From baaf046d8297cb1c777374341953833750a73131 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 10 Oct 2013 04:45:56 -0300 Subject: [media] s5p-jpeg: Fix encoder and decoder video dev names It is hard to distinguish between decoder and encoder video device because their names are same. So this patch fixes the names. Signed-off-by: Seung-Woo Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index c818cbe..091b06a 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1301,8 +1301,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; @@ -1329,8 +1329,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; -- cgit v0.10.2 From f7074ab348e29c8068a2daaaf89920aebc903a98 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 18 Aug 2013 16:14:27 -0300 Subject: [media] s5p-jpeg: Add initial device tree support for S5PV210/Exynos4210 SoCs This patch enables the JPEG codec on S5PV210 and Exynos4210 SoCs. There are some differences in newer versions of the JPEG codec IP on SoCs like Exynos4x12 and Exynos5 series and support for them will be added in subsequent patches. Signed-off-by: Sylwester Nawrocki Acked-by: Stephen Warren Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt new file mode 100644 index 0000000..937b755 --- /dev/null +++ b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt @@ -0,0 +1,11 @@ +Samsung S5P/EXYNOS SoC series JPEG codec + +Required properties: + +- compatible : should be one of: + "samsung,s5pv210-jpeg", "samsung,exynos4210-jpeg"; +- reg : address and length of the JPEG codec IP register set; +- interrupts : specifies the JPEG codec IP interrupt; +- clocks : should contain the JPEG codec IP gate clock specifier, from the + common clock bindings; +- clock-names : should contain "jpeg" entry. diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 091b06a..a4bff8e 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1428,10 +1429,20 @@ static const struct dev_pm_ops s5p_jpeg_pm_ops = { .runtime_resume = s5p_jpeg_runtime_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id s5p_jpeg_of_match[] = { + { .compatible = "samsung,s5pv210-jpeg" }, + { .compatible = "samsung,exynos4210-jpeg" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s5p_jpeg_of_match); +#endif + static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, .remove = s5p_jpeg_remove, .driver = { + .of_match_table = of_match_ptr(s5p_jpeg_of_match), .owner = THIS_MODULE, .name = S5P_JPEG_M2M_NAME, .pm = &s5p_jpeg_pm_ops, @@ -1443,4 +1454,3 @@ module_platform_driver(s5p_jpeg_driver); MODULE_AUTHOR("Andrzej Pietrasiewicz "); MODULE_DESCRIPTION("Samsung JPEG codec driver"); MODULE_LICENSE("GPL"); - -- cgit v0.10.2 From 78e5a3ce9cca4fa87d28d4d8d4a5fb74bf606fde Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:08 -0300 Subject: [media] s5p-jpeg: Reorder quantization tables Reorder quantization tables so that their elements are arranged in the manner reflecting compression quality level that is in accordance with V4L2 documentation, i.e. the larger value of the V4L2_CID_JPEG_COMPRESSION_QUALITY control the better image quality, and thus lower compression quality. The modification allows also to get rid of reverse logic in the s_ctrl callback while assigning user space value to the ctx->compr_quality variable. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index a4bff8e..6dae39f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -84,15 +84,25 @@ static struct s5p_jpeg_fmt formats_dec[] = { #define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec) 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, @@ -104,38 +114,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, @@ -147,25 +157,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 } }; @@ -834,7 +834,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; @@ -863,7 +863,7 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) 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, -- cgit v0.10.2 From c97ba28b0f17d88ef072e3536aa160df6d48431d Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:09 -0300 Subject: [media] s5p-jpeg: Fix output YUV 4:2:0 fourcc for decoder Output samples during decoding phase for the YUV 4:2:0 format are arranged in the manner compatible with 2-planar NV12, not 3-planar YUV420 fourcc. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 6dae39f..eb06af1 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -58,7 +58,7 @@ static struct s5p_jpeg_fmt formats_enc[] = { static struct s5p_jpeg_fmt formats_dec[] = { { .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, + .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, .colplanes = 3, .h_align = 4, -- cgit v0.10.2 From cc690904247fdbb169e2544cbbbd4ebd2c05f8cb Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:10 -0300 Subject: [media] s5p-jpeg: Fix erroneous condition while validating bytesperline value The aim of the condition is ensuring that the bytesperline value set by the user space application is proper for the given format and adjusting it if isn't. As the depth value of the format description entry is expressed in bits then the bytesperline value needs to be divided, not multiplied, by that value to get the number of bytes required to store single line of image samples. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index eb06af1..c5b7d92 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -670,7 +670,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; -- cgit v0.10.2 From 170f55bd85d19b06d825c18462fcb5985d6e5508 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:11 -0300 Subject: [media] s5p-jpeg: Remove superfluous call to the jpeg_bound_align_image function Aligning capture queue image dimensions while enqueuing output queue doesn't make a sense as the S_FMT ioctl might have not been called for the capture queue until that moment, whereas it is required to know capture format as the type of alignment heavily depends on it. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index c5b7d92..c5b6873 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1089,13 +1089,6 @@ 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; } v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); -- cgit v0.10.2 From 31dc0ac0d0686a5e9246d83f98105d0aa21eb2b8 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:12 -0300 Subject: [media] s5p-jpeg: Rename functions specific to the S5PC210 SoC accordingly Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index c5b6873..e119239 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -213,8 +213,9 @@ 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 inline void s5p_jpeg_set_qtbl(void __iomem *regs, + const unsigned char *qtbl, + unsigned long tab, int len) { int i; @@ -222,22 +223,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; @@ -245,28 +249,32 @@ 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)); } /* @@ -962,8 +970,8 @@ static void s5p_jpeg_device_run(void *priv) * 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); /* use table 1 for Cb and Cr*/ @@ -1406,14 +1414,16 @@ static int s5p_jpeg_runtime_suspend(struct device *dev) static int s5p_jpeg_runtime_resume(struct device *dev) { struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + /* * JPEG IP allows storing two Huffman tables for each component * We fill table 0 for each component */ - jpeg_set_hdctbl(jpeg->regs); - jpeg_set_hdctblg(jpeg->regs); - jpeg_set_hactbl(jpeg->regs); - jpeg_set_hactblg(jpeg->regs); + s5p_jpeg_set_hdctbl(jpeg->regs); + s5p_jpeg_set_hdctblg(jpeg->regs); + s5p_jpeg_set_hactbl(jpeg->regs); + s5p_jpeg_set_hactblg(jpeg->regs); + return 0; } -- cgit v0.10.2 From f1347132a655f40397d9ceb703950bc5a097416d Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:13 -0300 Subject: [media] s5p-jpeg: Fix clock resource management Standard suspend/resume path is called after runtime resume of the given device, so suspend/resume callbacks must do all clock management done also by runtime pm to allow for proper power domain shutdown. Moreover, JPEG clock is enabled from probe function but is is not necessary. This patch also moves control of jpeg clock to runtime_pm callbacks. Signed-off-by: Marek Szyprowski Signed-off-by: Seung-Woo Kim Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index e119239..84e7429 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1272,7 +1272,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); @@ -1380,7 +1379,6 @@ device_register_rollback: v4l2_device_unregister(&jpeg->v4l2_dev); clk_get_rollback: - clk_disable_unprepare(jpeg->clk); clk_put(jpeg->clk); return ret; @@ -1400,7 +1398,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; @@ -1408,12 +1408,21 @@ 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); + int ret; + + ret = clk_prepare_enable(jpeg->clk); + if (ret < 0) + return ret; /* * JPEG IP allows storing two Huffman tables for each component @@ -1427,9 +1436,25 @@ static int s5p_jpeg_runtime_resume(struct device *dev) 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 -- cgit v0.10.2 From b3c932a9940349cb3f00226e9713a2b25dcedc71 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:14 -0300 Subject: [media] s5p-jpeg: Fix lack of spin_lock protection s5p_jpeg_device_run and s5p_jpeg_runtime_resume callbacks should have spin_lock protection as they alter device registers. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 84e7429..57ee2655 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -930,7 +930,9 @@ 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->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -998,6 +1000,8 @@ static void s5p_jpeg_device_run(void *priv) } jpeg_start(jpeg->regs); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); } static int s5p_jpeg_job_ready(void *priv) @@ -1418,12 +1422,15 @@ static int s5p_jpeg_runtime_suspend(struct device *dev) 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 @@ -1433,6 +1440,8 @@ static int s5p_jpeg_runtime_resume(struct device *dev) s5p_jpeg_set_hactbl(jpeg->regs); s5p_jpeg_set_hactblg(jpeg->regs); + spin_unlock_irqrestore(&jpeg->slock, flags); + return 0; } -- cgit v0.10.2 From 088f8300c99e798637012195682d40dd1070bb82 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Mon, 25 Nov 2013 06:58:15 -0300 Subject: [media] s5p-jpeg: Synchronize cached controls with V4L2 core This patch adds proper initialization of the in-driver cached state of JPEG controls with V4L2 core. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 57ee2655..447022f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -865,6 +865,7 @@ 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); @@ -884,13 +885,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 = { -- cgit v0.10.2 From f8e2ff262142ba79d1d9f601684a5b9e2f75f397 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 4 Dec 2013 14:12:03 -0300 Subject: [media] omap3isp: Modify clocks registration to avoid circular references The clock core code is going to be modified so clk_get() takes reference on the clock provider module. Until the potential circular reference issue is properly addressed, we pass NULL as the first argument to clk_register(), in order to disallow sub-devices taking a reference on the ISP module back trough clk_get(). This should prevent locking the modules in memory. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 561bce8..fdbdeae 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -290,9 +290,11 @@ static int isp_xclk_init(struct isp_device *isp) struct clk_init_data init; unsigned int i; + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) + isp->xclks[i].clk = ERR_PTR(-EINVAL); + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { struct isp_xclk *xclk = &isp->xclks[i]; - struct clk *clk; xclk->isp = isp; xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; @@ -305,10 +307,15 @@ static int isp_xclk_init(struct isp_device *isp) init.num_parents = 1; xclk->hw.init = &init; - - clk = devm_clk_register(isp->dev, &xclk->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + /* + * The first argument is NULL in order to avoid circular + * reference, as this driver takes reference on the + * sensor subdevice modules and the sensors would take + * reference on this module through clk_get(). + */ + xclk->clk = clk_register(NULL, &xclk->hw); + if (IS_ERR(xclk->clk)) + return PTR_ERR(xclk->clk); if (pdata->xclks[i].con_id == NULL && pdata->xclks[i].dev_id == NULL) @@ -320,7 +327,7 @@ static int isp_xclk_init(struct isp_device *isp) xclk->lookup->con_id = pdata->xclks[i].con_id; xclk->lookup->dev_id = pdata->xclks[i].dev_id; - xclk->lookup->clk = clk; + xclk->lookup->clk = xclk->clk; clkdev_add(xclk->lookup); } @@ -335,6 +342,9 @@ static void isp_xclk_cleanup(struct isp_device *isp) for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { struct isp_xclk *xclk = &isp->xclks[i]; + if (!IS_ERR(xclk->clk)) + clk_unregister(xclk->clk); + if (xclk->lookup) clkdev_drop(xclk->lookup); } diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index ce65d3a..d1e857e 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -135,6 +135,7 @@ struct isp_xclk { struct isp_device *isp; struct clk_hw hw; struct clk_lookup *lookup; + struct clk *clk; enum isp_xclk_id id; spinlock_t lock; /* Protects enabled and divider */ -- cgit v0.10.2 From fd8308b42c11ff21cedfb08a73f1d7ff73d72e4c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 4 Dec 2013 13:49:02 -0300 Subject: [media] omap3isp: Use devm_ioremap_resource() Replace devm_request_mem_region() and devm_ioremap_nocache() with devm_ioremap_resource(). The behaviour remains the same and the code is simplified. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index fdbdeae..bb4e0a7 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2130,28 +2130,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 d1e857e..72685ad 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -152,7 +152,6 @@ 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. * @crashed: Bitmask of crashed entities (indexed by entity ID) @@ -188,7 +187,6 @@ 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 */ -- cgit v0.10.2 From 792e8eca55c8aebb369f7fdf4c7e3c01703cc8c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Dec 2013 22:46:17 -0300 Subject: [media] omap3isp: Fix buffer flags handling when querying buffer A missing break resulted in all done buffers being flagged with V4L2_BUF_FLAG_QUEUED. Fix it. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index e15f013..5f0f8fa 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; -- cgit v0.10.2 From 9ff889b612ae3c36a537b8f023dd0201412de87f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 07:31:04 -0300 Subject: [media] v4l: of: Return an int in v4l2_of_parse_endpoint() When CONFIG_OF is not defined the v4l2_of_parse_endpoint() function is defined as a stub that returns -ENOSYS. Make the real function return an integer as well to be able to differentiate between the two cases. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index a6478dc..66a0e23 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); diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h index 3a8a841..3480cd0 100644 --- a/include/media/v4l2-of.h +++ b/include/media/v4l2-of.h @@ -72,8 +72,8 @@ struct v4l2_of_endpoint { }; #ifdef CONFIG_OF -void v4l2_of_parse_endpoint(const struct device_node *node, - struct v4l2_of_endpoint *link); +int v4l2_of_parse_endpoint(const struct device_node *node, + struct v4l2_of_endpoint *endpoint); struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, struct device_node *previous); struct device_node *v4l2_of_get_remote_port_parent( -- cgit v0.10.2 From 189c028f4771d69559762cb8035cc6927b3647fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 07:31:04 -0300 Subject: [media] v4l: of: Remove struct v4l2_of_endpoint remote field The field isn't set when parsing the endpoint. Remove it. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h index 3480cd0..541cea4 100644 --- a/include/media/v4l2-of.h +++ b/include/media/v4l2-of.h @@ -53,7 +53,6 @@ struct v4l2_of_bus_parallel { * @port: identifier (value of reg property) of a port this endpoint belongs to * @id: identifier (value of reg property) of this endpoint * @local_node: pointer to device_node of this endpoint - * @remote: phandle to remote endpoint node * @bus_type: bus type * @bus: bus configuration data structure * @head: list head for this structure @@ -62,7 +61,6 @@ struct v4l2_of_endpoint { unsigned int port; unsigned int id; const struct device_node *local_node; - const __be32 *remote; enum v4l2_mbus_type bus_type; union { struct v4l2_of_bus_parallel parallel; -- cgit v0.10.2 From 027eb8806564216be18b0d3f50f998600398b65f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 07:31:04 -0300 Subject: [media] v4l: of: Drop endpoint node reference in v4l2_of_get_remote_port() The v4l2_of_get_remote_port() function acquires a reference to an endpoint node through a phandle and then returns the node's parent, without dropping the reference to the endpoint node. Fix it. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index 66a0e23..42e3e8a 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -266,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); -- cgit v0.10.2 From c324981d3b5915fd2e386890ef7df3746954941a Mon Sep 17 00:00:00 2001 From: Lisa Nguyen Date: Mon, 28 Oct 2013 18:23:34 -0300 Subject: [media] staging: media: davinci_vpfe: Remove spaces before semicolons Remove unnecessary spaces before semicolons to meet kernel coding style. Signed-off-by: Lisa Nguyen Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index 766a071..b7044a3 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1009,7 +1009,7 @@ static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee) yee->es_ofst_grad > YEE_THR_MASK) return -EINVAL; - for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++) + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT; i++) if (yee->table[i] > YEE_ENTRY_MASK) return -EINVAL; diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c index e027b92..2d36b60 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -791,7 +791,7 @@ ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, /* valied table */ tbl = lut_3d->table; - for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { /* Each entry has 0-9 (B), 10-19 (G) and 20-29 R values */ val = tbl[i].b & D3_LUT_ENTRY_MASK; @@ -899,7 +899,7 @@ ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, if (!gbce->table) return; - for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2) + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; count += 2) w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) << GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask), ((count/2) << 2) + GBCE_TB_START_ADDR); diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index ff48fce..4171cfd 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -918,7 +918,7 @@ isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) (0 << ISIF_VDFC_EN_SHIFT), DFCCTL); isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL); - for (i = 0 ; i < vdfc->num_vdefects; i++) { + for (i = 0; i < vdfc->num_vdefects; i++) { count = DFC_WRITE_WAIT_COUNT; while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2)) -- cgit v0.10.2 From 3e6e3b3d3d6877e5b9fe0c2cd9788127a4974a3d Mon Sep 17 00:00:00 2001 From: Lisa Nguyen Date: Wed, 11 Dec 2013 03:09:22 -0300 Subject: [media] staging: media: davinci_vpfe: Rewrite return statement in vpfe_video.c Rewrite the return statement in vpfe_video.c. This will prevent the checkpatch.pl script from generating a warning saying to remove () from this particular return statement. Signed-off-by: Lisa Nguyen Acked-by: Laurent Pinchart Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 24d98a6..3b036be 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -346,7 +346,7 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) } mutex_unlock(&mdev->graph_mutex); - return (ret == 0) ? ret : -ETIMEDOUT ; + return ret ? -ETIMEDOUT : 0; } /* -- cgit v0.10.2 From 7391232e1215b3583de86af6e942aaa5c5c9fa97 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:37 -0300 Subject: [media] si4713: Reorganized drivers/media/radio directory Added a new si4713 directory which will contain all si4713 related files. Also updated Makefile and Kconfig Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 6ecdc39..e92cec6 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 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 3b64560..eb1a3a0 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 diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c deleted file mode 100644 index ba4cfc9..0000000 --- a/drivers/media/radio/radio-si4713.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * drivers/media/radio/radio-si4713.c - * - * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter: - * - * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT - * Contact: Eduardo Valentin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* module parameters */ -static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */ -module_param(radio_nr, int, 0); -MODULE_PARM_DESC(radio_nr, - "Minor number for radio device (-1 ==> auto assign)"); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Eduardo Valentin "); -MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter"); -MODULE_VERSION("0.0.1"); -MODULE_ALIAS("platform:radio-si4713"); - -/* Driver state struct */ -struct radio_si4713_device { - struct v4l2_device v4l2_dev; - struct video_device radio_dev; - struct mutex lock; -}; - -/* radio_si4713_fops - file operations interface */ -static const struct v4l2_file_operations radio_si4713_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = v4l2_ctrl_poll, - /* Note: locking is done at the subdev level in the i2c driver. */ - .unlocked_ioctl = video_ioctl2, -}; - -/* Video4Linux Interface */ - -/* radio_si4713_querycap - query device capabilities */ -static int radio_si4713_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) -{ - strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver)); - strlcpy(capability->card, "Silicon Labs Si4713 Modulator", - sizeof(capability->card)); - strlcpy(capability->bus_info, "platform:radio-si4713", - sizeof(capability->bus_info)); - capability->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; - capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -/* - * v4l2 ioctl call backs. - * we are just a wrapper for v4l2_sub_devs. - */ -static inline struct v4l2_device *get_v4l2_dev(struct file *file) -{ - return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev; -} - -static int radio_si4713_g_modulator(struct file *file, void *p, - struct v4l2_modulator *vm) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - g_modulator, vm); -} - -static int radio_si4713_s_modulator(struct file *file, void *p, - const struct v4l2_modulator *vm) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - s_modulator, vm); -} - -static int radio_si4713_g_frequency(struct file *file, void *p, - struct v4l2_frequency *vf) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - g_frequency, vf); -} - -static int radio_si4713_s_frequency(struct file *file, void *p, - const struct v4l2_frequency *vf) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, - s_frequency, vf); -} - -static long radio_si4713_default(struct file *file, void *p, - bool valid_prio, unsigned int cmd, void *arg) -{ - return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, - ioctl, cmd, arg); -} - -static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = { - .vidioc_querycap = radio_si4713_querycap, - .vidioc_g_modulator = radio_si4713_g_modulator, - .vidioc_s_modulator = radio_si4713_s_modulator, - .vidioc_g_frequency = radio_si4713_g_frequency, - .vidioc_s_frequency = radio_si4713_s_frequency, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_default = radio_si4713_default, -}; - -/* radio_si4713_vdev_template - video device interface */ -static struct video_device radio_si4713_vdev_template = { - .fops = &radio_si4713_fops, - .name = "radio-si4713", - .release = video_device_release_empty, - .ioctl_ops = &radio_si4713_ioctl_ops, - .vfl_dir = VFL_DIR_TX, -}; - -/* Platform driver interface */ -/* radio_si4713_pdriver_probe - probe for the device */ -static int radio_si4713_pdriver_probe(struct platform_device *pdev) -{ - struct radio_si4713_platform_data *pdata = pdev->dev.platform_data; - struct radio_si4713_device *rsdev; - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - int rval = 0; - - if (!pdata) { - dev_err(&pdev->dev, "Cannot proceed without platform data.\n"); - rval = -EINVAL; - goto exit; - } - - rsdev = devm_kzalloc(&pdev->dev, sizeof(*rsdev), GFP_KERNEL); - if (!rsdev) { - dev_err(&pdev->dev, "Failed to alloc video device.\n"); - rval = -ENOMEM; - goto exit; - } - mutex_init(&rsdev->lock); - - rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev); - if (rval) { - dev_err(&pdev->dev, "Failed to register v4l2 device.\n"); - goto exit; - } - - adapter = i2c_get_adapter(pdata->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "Cannot get i2c adapter %d\n", - pdata->i2c_bus); - rval = -ENODEV; - goto unregister_v4l2_dev; - } - - sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, - pdata->subdev_board_info, NULL); - if (!sd) { - dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); - rval = -ENODEV; - goto put_adapter; - } - - rsdev->radio_dev = radio_si4713_vdev_template; - rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev; - rsdev->radio_dev.ctrl_handler = sd->ctrl_handler; - set_bit(V4L2_FL_USE_FH_PRIO, &rsdev->radio_dev.flags); - /* Serialize all access to the si4713 */ - rsdev->radio_dev.lock = &rsdev->lock; - video_set_drvdata(&rsdev->radio_dev, rsdev); - if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { - dev_err(&pdev->dev, "Could not register video device.\n"); - rval = -EIO; - goto put_adapter; - } - dev_info(&pdev->dev, "New device successfully probed\n"); - - goto exit; - -put_adapter: - i2c_put_adapter(adapter); -unregister_v4l2_dev: - v4l2_device_unregister(&rsdev->v4l2_dev); -exit: - return rval; -} - -/* radio_si4713_pdriver_remove - remove the device */ -static int radio_si4713_pdriver_remove(struct platform_device *pdev) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); - struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, - struct v4l2_subdev, list); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct radio_si4713_device *rsdev; - - rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev); - video_unregister_device(&rsdev->radio_dev); - i2c_put_adapter(client->adapter); - v4l2_device_unregister(&rsdev->v4l2_dev); - - return 0; -} - -static struct platform_driver radio_si4713_pdriver = { - .driver = { - .name = "radio-si4713", - .owner = THIS_MODULE, - }, - .probe = radio_si4713_pdriver_probe, - .remove = radio_si4713_pdriver_remove, -}; - -module_platform_driver(radio_si4713_pdriver); diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c deleted file mode 100644 index 9ec48cc..0000000 --- a/drivers/media/radio/si4713-i2c.c +++ /dev/null @@ -1,1532 +0,0 @@ -/* - * drivers/media/radio/si4713-i2c.c - * - * Silicon Labs Si4713 FM Radio Transmitter I2C commands. - * - * Copyright (c) 2009 Nokia Corporation - * Contact: Eduardo Valentin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "si4713-i2c.h" - -/* module parameters */ -static int debug; -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Eduardo Valentin "); -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_DEVIATION 0x1A5E -#define DEFAULT_ACOMP_ATIME 0x0000 -#define DEFAULT_ACOMP_RTIME 0xF4240L -#define DEFAULT_ACOMP_GAIN 0x0F -#define DEFAULT_ACOMP_THRESHOLD (-0x28) -#define DEFAULT_MUTE 0x01 -#define DEFAULT_POWER_LEVEL 88 -#define DEFAULT_FREQUENCY 8800 -#define DEFAULT_PREEMPHASIS FMPE_EU -#define DEFAULT_TUNE_RNL 0xFF - -#define to_si4713_device(sd) container_of(sd, struct si4713_device, sd) - -/* frequency domain transformation (using times 10 to avoid floats) */ -#define FREQDEV_UNIT 100000 -#define FREQV4L2_MULTI 625 -#define si4713_to_v4l2(f) ((f * FREQDEV_UNIT) / FREQV4L2_MULTI) -#define v4l2_to_si4713(f) ((f * FREQV4L2_MULTI) / FREQDEV_UNIT) -#define FREQ_RANGE_LOW 7600 -#define FREQ_RANGE_HIGH 10800 - -#define MAX_ARGS 7 - -#define RDS_BLOCK 8 -#define RDS_BLOCK_CLEAR 0x03 -#define RDS_BLOCK_LOAD 0x04 -#define RDS_RADIOTEXT_2A 0x20 -#define RDS_RADIOTEXT_BLK_SIZE 4 -#define RDS_RADIOTEXT_INDEX_MAX 0x0F -#define RDS_CARRIAGE_RETURN 0x0D - -#define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) - -#define get_status_bit(p, b, m) (((p) & (m)) >> (b)) -#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) - -#define ATTACK_TIME_UNIT 500 - -#define POWER_OFF 0x00 -#define POWER_ON 0x01 - -#define msb(x) ((u8)((u16) x >> 8)) -#define lsb(x) ((u8)((u16) x & 0x00FF)) -#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) -#define check_command_failed(status) (!(status & SI4713_CTS) || \ - (status & SI4713_ERR)) -/* mute definition */ -#define set_mute(p) ((p & 1) | ((p & 1) << 1)); - -#ifdef DEBUG -#define DBG_BUFFER(device, message, buffer, size) \ - { \ - int i; \ - char str[(size)*5]; \ - for (i = 0; i < size; i++) \ - sprintf(str + i * 5, " 0x%02x", buffer[i]); \ - v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ - } -#else -#define DBG_BUFFER(device, message, buffer, size) -#endif - -/* - * Values for limiter release time (sorted by second column) - * device release - * value time (us) - */ -static long limiter_times[] = { - 2000, 250, - 1000, 500, - 510, 1000, - 255, 2000, - 170, 3000, - 127, 4020, - 102, 5010, - 85, 6020, - 73, 7010, - 64, 7990, - 57, 8970, - 51, 10030, - 25, 20470, - 17, 30110, - 13, 39380, - 10, 51190, - 8, 63690, - 7, 73140, - 6, 85330, - 5, 102390, -}; - -/* - * Values for audio compression release time (sorted by second column) - * device release - * value time (us) - */ -static unsigned long acomp_rtimes[] = { - 0, 100000, - 1, 200000, - 2, 350000, - 3, 525000, - 4, 1000000, -}; - -/* - * Values for preemphasis (sorted by second column) - * device preemphasis - * value value (v4l2) - */ -static unsigned long preemphasis_values[] = { - FMPE_DISABLED, V4L2_PREEMPHASIS_DISABLED, - FMPE_EU, V4L2_PREEMPHASIS_50_uS, - FMPE_USA, V4L2_PREEMPHASIS_75_uS, -}; - -static int usecs_to_dev(unsigned long usecs, unsigned long const array[], - int size) -{ - int i; - int rval = -EINVAL; - - for (i = 0; i < size / 2; i++) - if (array[(i * 2) + 1] >= usecs) { - rval = array[i * 2]; - break; - } - - return rval; -} - -/* si4713_handler: IRQ handler, just complete work */ -static irqreturn_t si4713_handler(int irq, void *dev) -{ - struct si4713_device *sdev = dev; - - v4l2_dbg(2, debug, &sdev->sd, - "%s: sending signal to completion work.\n", __func__); - complete(&sdev->work); - - return IRQ_HANDLED; -} - -/* - * si4713_send_command - sends a command to si4713 and waits its response - * @sdev: si4713_device structure for the device we are communicating - * @command: command id - * @args: command arguments we are sending (up to 7) - * @argn: actual size of @args - * @response: buffer to place the expected response from the device (up to 15) - * @respn: actual size of @response - * @usecs: amount of time to wait before reading the response (in usecs) - */ -static int si4713_send_command(struct si4713_device *sdev, const u8 command, - const u8 args[], const int argn, - u8 response[], const int respn, const int usecs) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); - u8 data1[MAX_ARGS + 1]; - int err; - - if (!client->adapter) - return -ENODEV; - - /* First send the command and its arguments */ - data1[0] = command; - memcpy(data1 + 1, args, argn); - DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); - - err = i2c_master_send(client, data1, argn + 1); - if (err != argn + 1) { - v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", - command); - return (err > 0) ? -EIO : err; - } - - /* Wait response from interrupt */ - if (!wait_for_completion_timeout(&sdev->work, - usecs_to_jiffies(usecs) + 1)) - 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; - - return 0; -} - -/* - * si4713_read_property - reads a si4713 property - * @sdev: si4713_device structure for the device we are communicating - * @prop: property identification number - * @pv: property value to be returned on success - */ -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 - */ - const u8 args[SI4713_GET_PROP_NARGS] = { - 0x00, - msb(prop), - lsb(prop), - }; - - err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (err < 0) - return err; - - *pv = compose_u16(val[2], val[3]); - - v4l2_dbg(1, debug, &sdev->sd, - "%s: property=0x%02x value=0x%02x status=0x%02x\n", - __func__, prop, *pv, val[0]); - - return err; -} - -/* - * si4713_write_property - modifies a si4713 property - * @sdev: si4713_device structure for the device we are communicating - * @prop: property identification number - * @val: new value for that property - */ -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 - */ - const u8 args[SI4713_SET_PROP_NARGS] = { - 0x00, - msb(prop), - lsb(prop), - msb(val), - lsb(val), - }; - - rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - DEFAULT_TIMEOUT); - - if (rval < 0) - return rval; - - v4l2_dbg(1, debug, &sdev->sd, - "%s: property=0x%02x value=0x%02x status=0x%02x\n", - __func__, prop, val, resp[0]); - - /* - * As there is no command response for SET_PROPERTY, - * wait Tcomp time to finish before proceed, in order - * to have property properly set. - */ - msleep(TIMEOUT_SET_PROPERTY); - - return rval; -} - -/* - * si4713_powerup - Powers the device up - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_powerup(struct si4713_device *sdev) -{ - int err; - u8 resp[SI4713_PWUP_NRESP]; - /* - * .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, - 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 (gpio_is_valid(sdev->gpio_reset)) { - udelay(50); - gpio_set_value(sdev->gpio_reset, 1); - } - - err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - TIMEOUT_POWER_UP); - - if (!err) { - v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", - resp[0]); - 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, - 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); - if (err) - v4l2_err(&sdev->sd, - "Failed to disable supplies: %d\n", err); - } - - return err; -} - -/* - * si4713_powerdown - Powers the device down - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_powerdown(struct si4713_device *sdev) -{ - int err; - u8 resp[SI4713_PWDN_NRESP]; - - if (!sdev->power_state) - return 0; - - err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, - NULL, 0, - resp, ARRAY_SIZE(resp), - DEFAULT_TIMEOUT); - - if (!err) { - v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", - resp[0]); - 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); - sdev->power_state = POWER_OFF; - } - - return err; -} - -/* - * si4713_checkrev - Checks if we are treating a device with the correct rev. - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_checkrev(struct si4713_device *sdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); - int rval; - u8 resp[SI4713_GETREV_NRESP]; - - rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, - NULL, 0, - resp, ARRAY_SIZE(resp), - DEFAULT_TIMEOUT); - - if (rval < 0) - return rval; - - if (resp[1] == SI4713_PRODUCT_NUMBER) { - 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"); - rval = -EINVAL; - } - return rval; -} - -/* - * si4713_wait_stc - Waits STC interrupt and clears status bits. Useful - * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS - * @sdev: si4713_device structure for the device we are communicating - * @usecs: timeout to wait for STC interrupt signal - */ -static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) -{ - int err; - u8 resp[SI4713_GET_STATUS_NRESP]; - - /* Wait response from STC interrupt */ - if (!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; -} - -/* - * 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. - * @sdev: si4713_device structure for the device we are communicating - * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) - */ -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 - */ - const u8 args[SI4713_TXFREQ_NARGS] = { - 0x00, - msb(frequency), - lsb(frequency), - }; - - err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (err < 0) - return err; - - v4l2_dbg(1, debug, &sdev->sd, - "%s: frequency=0x%02x status=0x%02x\n", __func__, - frequency, val[0]); - - err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); - if (err < 0) - return err; - - return compose_u16(args[1], args[2]); -} - -/* - * 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. - * @sdev: si4713_device structure for the device we are communicating - * @power: tuning power (88 - 115 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, - u8 antcap) -{ - int err; - u8 val[SI4713_TXPWR_NRESP]; - /* - * .First byte = 0 - * .Second byte = 0 - * .Third byte = power - * .Fourth byte = antcap - */ - const 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; - - err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (err < 0) - return err; - - v4l2_dbg(1, debug, &sdev->sd, - "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", - __func__, power, antcap, val[0]); - - return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_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. - * @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) - */ -static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, - u8 antcap) -{ - int err; - u8 val[SI4713_TXMEA_NRESP]; - /* - * .First byte = 0 - * .Second byte = frequency's MSB - * .Third byte = frequency's LSB - * .Fourth byte = antcap - */ - const u8 args[SI4713_TXMEA_NARGS] = { - 0x00, - msb(frequency), - lsb(frequency), - antcap, - }; - - sdev->tune_rnl = DEFAULT_TUNE_RNL; - - if (antcap > SI4713_MAX_ANTCAP) - return -EDOM; - - err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (err < 0) - return err; - - v4l2_dbg(1, debug, &sdev->sd, - "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", - __func__, frequency, antcap, val[0]); - - return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); -} - -/* - * 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. - * @sdev: si4713_device structure for the device we are communicating - * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. - * @frequency: returned frequency - * @power: returned power - * @antcap: returned antenna capacitance - * @noise: returned noise level - */ -static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, - u16 *frequency, u8 *power, - u8 *antcap, u8 *noise) -{ - int err; - u8 val[SI4713_TXSTATUS_NRESP]; - /* - * .First byte = intack bit - */ - const u8 args[SI4713_TXSTATUS_NARGS] = { - intack & SI4713_INTACK_MASK, - }; - - err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (!err) { - v4l2_dbg(1, debug, &sdev->sd, - "%s: status=0x%02x\n", __func__, val[0]); - *frequency = compose_u16(val[2], val[3]); - sdev->frequency = *frequency; - *power = val[5]; - *antcap = val[6]; - *noise = val[7]; - v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz " - "(power %d, antcap %d, rnl %d)\n", __func__, - *frequency, *power, *antcap, *noise); - } - - return err; -} - -/* - * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer. - * @sdev: si4713_device structure for the device we are communicating - * @mode: the buffer operation mode. - * @rdsb: RDS Block B - * @rdsc: RDS Block C - * @rdsd: RDS Block D - * @cbleft: returns the number of available circular buffer blocks minus the - * number of used circular buffer blocks. - */ -static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb, - u16 rdsc, u16 rdsd, s8 *cbleft) -{ - int err; - u8 val[SI4713_RDSBUFF_NRESP]; - - const u8 args[SI4713_RDSBUFF_NARGS] = { - mode & SI4713_RDSBUFF_MODE_MASK, - msb(rdsb), - lsb(rdsb), - msb(rdsc), - lsb(rdsc), - msb(rdsd), - lsb(rdsd), - }; - - err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (!err) { - v4l2_dbg(1, debug, &sdev->sd, - "%s: status=0x%02x\n", __func__, val[0]); - *cbleft = (s8)val[2] - val[3]; - v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts" - " 0x%02x cb avail: %d cb used %d fifo avail" - " %d fifo used %d\n", __func__, val[1], - val[2], val[3], val[4], val[5]); - } - - return err; -} - -/* - * si4713_tx_rds_ps - Loads the program service buffer. - * @sdev: si4713_device structure for the device we are communicating - * @psid: program service id to be loaded. - * @pschar: assumed 4 size char array to be loaded into the program service - */ -static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, - unsigned char *pschar) -{ - int err; - u8 val[SI4713_RDSPS_NRESP]; - - const u8 args[SI4713_RDSPS_NARGS] = { - psid & SI4713_RDSPS_PSID_MASK, - pschar[0], - pschar[1], - pschar[2], - pschar[3], - }; - - err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS, - args, ARRAY_SIZE(args), val, - ARRAY_SIZE(val), DEFAULT_TIMEOUT); - - if (err < 0) - return err; - - v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); - - return err; -} - -static int si4713_set_power_state(struct si4713_device *sdev, u8 value) -{ - if (value) - return si4713_powerup(sdev); - return si4713_powerdown(sdev); -} - -static int si4713_set_mute(struct si4713_device *sdev, u16 mute) -{ - int rval = 0; - - mute = set_mute(mute); - - if (sdev->power_state) - rval = si4713_write_property(sdev, - SI4713_TX_LINE_INPUT_MUTE, mute); - - return rval; -} - -static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) -{ - int rval = 0, i; - u8 len = 0; - - /* We want to clear the whole thing */ - if (!strlen(ps_name)) - memset(ps_name, 0, MAX_RDS_PS_NAME + 1); - - if (sdev->power_state) { - /* Write the new ps name and clear the padding */ - for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) { - rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)), - ps_name + i); - if (rval < 0) - return rval; - } - - /* Setup the size to be sent */ - if (strlen(ps_name)) - len = strlen(ps_name) - 1; - else - len = 1; - - rval = si4713_write_property(sdev, - SI4713_TX_RDS_PS_MESSAGE_COUNT, - rds_ps_nblocks(len)); - if (rval < 0) - return rval; - - rval = si4713_write_property(sdev, - SI4713_TX_RDS_PS_REPEAT_COUNT, - DEFAULT_RDS_PS_REPEAT_COUNT * 2); - if (rval < 0) - return rval; - } - - return rval; -} - -static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) -{ - int rval = 0, i; - u16 t_index = 0; - u8 b_index = 0, cr_inserted = 0; - s8 left; - - if (!sdev->power_state) - return rval; - - rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left); - if (rval < 0) - return rval; - - if (!strlen(rt)) - return rval; - - do { - /* RDS spec says that if the last block isn't used, - * then apply a carriage return - */ - if (t_index < (RDS_RADIOTEXT_INDEX_MAX * RDS_RADIOTEXT_BLK_SIZE)) { - 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; - cr_inserted = 1; - break; - } - } - } - - rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_LOAD, - compose_u16(RDS_RADIOTEXT_2A, b_index++), - compose_u16(rt[t_index], rt[t_index + 1]), - compose_u16(rt[t_index + 2], rt[t_index + 3]), - &left); - if (rval < 0) - return rval; - - t_index += RDS_RADIOTEXT_BLK_SIZE; - - if (cr_inserted) - break; - } while (left > 0); - - return rval; -} - -/* - * si4713_update_tune_status - update properties from tx_tune_status - * command. Must be called with sdev->mutex held. - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_update_tune_status(struct si4713_device *sdev) -{ - int rval; - u16 f = 0; - u8 p = 0, a = 0, n = 0; - - rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); - - if (rval < 0) - goto exit; - -/* TODO: check that power_level and antenna_capacitor really are not - changed by the hardware. If they are, then these controls should become - volatiles. - sdev->power_level = p; - sdev->antenna_capacitor = a;*/ - sdev->tune_rnl = n; - -exit: - return rval; -} - -static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, - s32 *bit, s32 *mask, u16 *property, int *mul, - unsigned long **table, int *size) -{ - s32 rval = 0; - - switch (id) { - /* FM_TX class controls */ - case V4L2_CID_RDS_TX_PI: - *property = SI4713_TX_RDS_PI; - *mul = 1; - break; - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: - *property = SI4713_TX_ACOMP_THRESHOLD; - *mul = 1; - break; - case V4L2_CID_AUDIO_COMPRESSION_GAIN: - *property = SI4713_TX_ACOMP_GAIN; - *mul = 1; - break; - case V4L2_CID_PILOT_TONE_FREQUENCY: - *property = SI4713_TX_PILOT_FREQUENCY; - *mul = 1; - break; - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: - *property = SI4713_TX_ACOMP_ATTACK_TIME; - *mul = ATTACK_TIME_UNIT; - break; - case V4L2_CID_PILOT_TONE_DEVIATION: - *property = SI4713_TX_PILOT_DEVIATION; - *mul = 10; - break; - case V4L2_CID_AUDIO_LIMITER_DEVIATION: - *property = SI4713_TX_AUDIO_DEVIATION; - *mul = 10; - break; - case V4L2_CID_RDS_TX_DEVIATION: - *property = SI4713_TX_RDS_DEVIATION; - *mul = 1; - break; - - case V4L2_CID_RDS_TX_PTY: - *property = SI4713_TX_RDS_PS_MISC; - *bit = 5; - *mask = 0x1F << 5; - break; - case V4L2_CID_AUDIO_LIMITER_ENABLED: - *property = SI4713_TX_ACOMP_ENABLE; - *bit = 1; - *mask = 1 << 1; - break; - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: - *property = SI4713_TX_ACOMP_ENABLE; - *bit = 0; - *mask = 1 << 0; - break; - case V4L2_CID_PILOT_TONE_ENABLED: - *property = SI4713_TX_COMPONENT_ENABLE; - *bit = 0; - *mask = 1 << 0; - break; - - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: - *property = SI4713_TX_LIMITER_RELEASE_TIME; - *table = limiter_times; - *size = ARRAY_SIZE(limiter_times); - break; - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: - *property = SI4713_TX_ACOMP_RELEASE_TIME; - *table = acomp_rtimes; - *size = ARRAY_SIZE(acomp_rtimes); - break; - case V4L2_CID_TUNE_PREEMPHASIS: - *property = SI4713_TX_PREEMPHASIS; - *table = preemphasis_values; - *size = ARRAY_SIZE(preemphasis_values); - break; - - default: - rval = -EINVAL; - break; - } - - return rval; -} - -static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f); -static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *); -/* - * si4713_setup - Sets the device up with current configuration. - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_setup(struct si4713_device *sdev) -{ - struct v4l2_frequency f; - struct v4l2_modulator vm; - int rval; - - /* Device procedure needs to set frequency first */ - f.tuner = 0; - f.frequency = sdev->frequency ? sdev->frequency : DEFAULT_FREQUENCY; - f.frequency = si4713_to_v4l2(f.frequency); - rval = si4713_s_frequency(&sdev->sd, &f); - - vm.index = 0; - if (sdev->stereo) - vm.txsubchans = V4L2_TUNER_SUB_STEREO; - else - vm.txsubchans = V4L2_TUNER_SUB_MONO; - if (sdev->rds_enabled) - vm.txsubchans |= V4L2_TUNER_SUB_RDS; - si4713_s_modulator(&sdev->sd, &vm); - - return rval; -} - -/* - * si4713_initialize - Sets the device up with default configuration. - * @sdev: si4713_device structure for the device we are communicating - */ -static int si4713_initialize(struct si4713_device *sdev) -{ - int rval; - - rval = si4713_set_power_state(sdev, POWER_ON); - if (rval < 0) - return rval; - - rval = si4713_checkrev(sdev); - if (rval < 0) - return rval; - - rval = si4713_set_power_state(sdev, POWER_OFF); - if (rval < 0) - return rval; - - - sdev->frequency = DEFAULT_FREQUENCY; - sdev->stereo = 1; - sdev->tune_rnl = DEFAULT_TUNE_RNL; - return 0; -} - -/* si4713_s_ctrl - set the value of a control */ -static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct si4713_device *sdev = - container_of(ctrl->handler, struct si4713_device, ctrl_handler); - u32 val = 0; - s32 bit = 0, mask = 0; - u16 property = 0; - int mul = 0; - unsigned long *table = NULL; - int size = 0; - bool force = false; - int c; - int ret = 0; - - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - if (ctrl->is_new) { - if (ctrl->val) { - ret = si4713_set_mute(sdev, ctrl->val); - if (!ret) - ret = si4713_set_power_state(sdev, POWER_DOWN); - return ret; - } - ret = si4713_set_power_state(sdev, POWER_UP); - if (!ret) - ret = si4713_set_mute(sdev, ctrl->val); - if (!ret) - ret = si4713_setup(sdev); - if (ret) - return ret; - force = true; - } - - if (!sdev->power_state) - return 0; - - for (c = 1; !ret && c < ctrl->ncontrols; c++) { - ctrl = ctrl->cluster[c]; - - if (!force && !ctrl->is_new) - continue; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_PS_NAME: - ret = si4713_set_rds_ps_name(sdev, ctrl->string); - break; - - case V4L2_CID_RDS_TX_RADIO_TEXT: - ret = si4713_set_rds_radio_text(sdev, ctrl->string); - break; - - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - /* don't handle this control if we force setting all - * controls since in that case it will be handled by - * V4L2_CID_TUNE_POWER_LEVEL. */ - if (force) - break; - /* fall through */ - case V4L2_CID_TUNE_POWER_LEVEL: - ret = si4713_tx_tune_power(sdev, - sdev->tune_pwr_level->val, sdev->tune_ant_cap->val); - if (!ret) { - /* Make sure we don't set this twice */ - sdev->tune_ant_cap->is_new = false; - sdev->tune_pwr_level->is_new = false; - } - break; - - default: - ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit, - &mask, &property, &mul, &table, &size); - if (ret < 0) - break; - - val = ctrl->val; - if (mul) { - val = val / mul; - } else if (table) { - ret = usecs_to_dev(val, table, size); - if (ret < 0) - break; - val = ret; - ret = 0; - } - - if (mask) { - ret = si4713_read_property(sdev, property, &val); - if (ret < 0) - break; - val = set_bits(val, ctrl->val, bit, mask); - } - - ret = si4713_write_property(sdev, property, val); - if (ret < 0) - break; - if (mask) - val = ctrl->val; - break; - } - } - - return ret; -} - -/* si4713_ioctl - deal with private ioctls (only rnl for now) */ -static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct si4713_device *sdev = to_si4713_device(sd); - struct si4713_rnl *rnl = arg; - u16 frequency; - int rval = 0; - - if (!arg) - return -EINVAL; - - switch (cmd) { - case SI4713_IOC_MEASURE_RNL: - frequency = v4l2_to_si4713(rnl->frequency); - - if (sdev->power_state) { - /* Set desired measurement frequency */ - rval = si4713_tx_tune_measure(sdev, frequency, 0); - if (rval < 0) - return rval; - /* get results from tune status */ - rval = si4713_update_tune_status(sdev); - if (rval < 0) - return rval; - } - rnl->rnl = sdev->tune_rnl; - break; - - default: - /* nothing */ - rval = -ENOIOCTLCMD; - } - - return rval; -} - -/* si4713_g_modulator - get modulator attributes */ -static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - - if (!sdev) - return -ENODEV; - - if (vm->index > 0) - return -EINVAL; - - strncpy(vm->name, "FM Modulator", 32); - vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | - V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS; - - /* Report current frequency range limits */ - vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); - vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); - - if (sdev->power_state) { - u32 comp_en = 0; - - rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, - &comp_en); - if (rval < 0) - return rval; - - sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); - } - - /* Report current audio mode: mono or stereo */ - if (sdev->stereo) - vm->txsubchans = V4L2_TUNER_SUB_STEREO; - else - vm->txsubchans = V4L2_TUNER_SUB_MONO; - - /* Report rds feature status */ - if (sdev->rds_enabled) - vm->txsubchans |= V4L2_TUNER_SUB_RDS; - else - vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; - - return rval; -} - -/* si4713_s_modulator - set modulator attributes */ -static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *vm) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - u16 stereo, rds; - u32 p; - - if (!sdev) - return -ENODEV; - - if (vm->index > 0) - return -EINVAL; - - /* Set audio mode: mono or stereo */ - if (vm->txsubchans & V4L2_TUNER_SUB_STEREO) - stereo = 1; - else if (vm->txsubchans & V4L2_TUNER_SUB_MONO) - stereo = 0; - else - return -EINVAL; - - rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); - - if (sdev->power_state) { - rval = si4713_read_property(sdev, - SI4713_TX_COMPONENT_ENABLE, &p); - if (rval < 0) - return rval; - - p = set_bits(p, stereo, 1, 1 << 1); - p = set_bits(p, rds, 2, 1 << 2); - - rval = si4713_write_property(sdev, - SI4713_TX_COMPONENT_ENABLE, p); - if (rval < 0) - return rval; - } - - sdev->stereo = stereo; - sdev->rds_enabled = rds; - - return rval; -} - -/* si4713_g_frequency - get tuner or modulator radio frequency */ -static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - - if (f->tuner) - return -EINVAL; - - if (sdev->power_state) { - u16 freq; - u8 p, a, n; - - rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); - if (rval < 0) - return rval; - - sdev->frequency = freq; - } - - f->frequency = si4713_to_v4l2(sdev->frequency); - - return rval; -} - -/* si4713_s_frequency - set tuner or modulator radio frequency */ -static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) -{ - struct si4713_device *sdev = to_si4713_device(sd); - int rval = 0; - u16 frequency = v4l2_to_si4713(f->frequency); - - if (f->tuner) - return -EINVAL; - - /* Check frequency range */ - frequency = clamp_t(u16, frequency, FREQ_RANGE_LOW, FREQ_RANGE_HIGH); - - if (sdev->power_state) { - rval = si4713_tx_tune_freq(sdev, frequency); - if (rval < 0) - return rval; - frequency = rval; - rval = 0; - } - sdev->frequency = frequency; - - return rval; -} - -static const struct v4l2_ctrl_ops si4713_ctrl_ops = { - .s_ctrl = si4713_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { - .ioctl = si4713_ioctl, -}; - -static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = { - .g_frequency = si4713_g_frequency, - .s_frequency = si4713_s_frequency, - .g_modulator = si4713_g_modulator, - .s_modulator = si4713_s_modulator, -}; - -static const struct v4l2_subdev_ops si4713_subdev_ops = { - .core = &si4713_subdev_core_ops, - .tuner = &si4713_subdev_tuner_ops, -}; - -/* - * I2C driver interface - */ -/* si4713_probe - probe for the device */ -static int si4713_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct si4713_device *sdev; - struct si4713_platform_data *pdata = client->dev.platform_data; - struct v4l2_ctrl_handler *hdl; - int rval, i; - - sdev = kzalloc(sizeof *sdev, GFP_KERNEL); - if (!sdev) { - dev_err(&client->dev, "Failed to alloc video device.\n"); - rval = -ENOMEM; - goto exit; - } - - sdev->gpio_reset = -1; - if (pdata && gpio_is_valid(pdata->gpio_reset)) { - rval = gpio_request(pdata->gpio_reset, "si4713 reset"); - if (rval) { - dev_err(&client->dev, - "Failed to request gpio: %d\n", rval); - goto free_sdev; - } - sdev->gpio_reset = pdata->gpio_reset; - gpio_direction_output(sdev->gpio_reset, 0); - } - - for (i = 0; i < ARRAY_SIZE(sdev->supplies); i++) - sdev->supplies[i].supply = si4713_supply_names[i]; - - rval = regulator_bulk_get(&client->dev, ARRAY_SIZE(sdev->supplies), - sdev->supplies); - if (rval) { - dev_err(&client->dev, "Cannot get regulators: %d\n", rval); - goto free_gpio; - } - - v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops); - - init_completion(&sdev->work); - - hdl = &sdev->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 20); - sdev->mute = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, DEFAULT_MUTE); - - sdev->rds_pi = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, DEFAULT_RDS_PI); - sdev->rds_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_RDS_TX_PTY, 0, 31, 1, DEFAULT_RDS_PTY); - sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION, - 10, DEFAULT_RDS_DEVIATION); - /* - * Report step as 8. From RDS spec, psname - * should be 8. But there are receivers which scroll strings - * sized as 8xN. - */ - sdev->rds_ps_name = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_RDS_TX_PS_NAME, 0, MAX_RDS_PS_NAME, 8, 0); - /* - * Report step as 32 (2A block). From RDS spec, - * radio text should be 32 for 2A block. But there are receivers - * which scroll strings sized as 32xN. Setting default to 32. - */ - sdev->rds_radio_text = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_RDS_TX_RADIO_TEXT, 0, MAX_RDS_RADIO_TEXT, 32, 0); - - sdev->limiter_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_LIMITER_ENABLED, 0, 1, 1, 1); - sdev->limiter_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, 250, - MAX_LIMITER_RELEASE_TIME, 10, DEFAULT_LIMITER_RTIME); - sdev->limiter_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_LIMITER_DEVIATION, 0, - MAX_LIMITER_DEVIATION, 10, DEFAULT_LIMITER_DEV); - - sdev->compression_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_COMPRESSION_ENABLED, 0, 1, 1, 1); - sdev->compression_gain = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_COMPRESSION_GAIN, 0, MAX_ACOMP_GAIN, 1, - DEFAULT_ACOMP_GAIN); - sdev->compression_threshold = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, MIN_ACOMP_THRESHOLD, - MAX_ACOMP_THRESHOLD, 1, - DEFAULT_ACOMP_THRESHOLD); - sdev->compression_attack_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, 0, - MAX_ACOMP_ATTACK_TIME, 500, DEFAULT_ACOMP_ATIME); - sdev->compression_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, 100000, - MAX_ACOMP_RELEASE_TIME, 100000, DEFAULT_ACOMP_RTIME); - - sdev->pilot_tone_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_PILOT_TONE_ENABLED, 0, 1, 1, 1); - sdev->pilot_tone_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_PILOT_TONE_DEVIATION, 0, MAX_PILOT_DEVIATION, - 10, DEFAULT_PILOT_DEVIATION); - sdev->pilot_tone_freq = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_PILOT_TONE_FREQUENCY, 0, MAX_PILOT_FREQUENCY, - 1, DEFAULT_PILOT_FREQUENCY); - - sdev->tune_preemphasis = v4l2_ctrl_new_std_menu(hdl, &si4713_ctrl_ops, - V4L2_CID_TUNE_PREEMPHASIS, - V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS); - sdev->tune_pwr_level = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_TUNE_POWER_LEVEL, 0, 120, 1, DEFAULT_POWER_LEVEL); - sdev->tune_ant_cap = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, - V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, 191, 1, 0); - - if (hdl->error) { - rval = hdl->error; - goto free_ctrls; - } - v4l2_ctrl_cluster(20, &sdev->mute); - sdev->sd.ctrl_handler = hdl; - - if (client->irq) { - rval = request_irq(client->irq, - si4713_handler, IRQF_TRIGGER_FALLING, - client->name, sdev); - if (rval < 0) { - v4l2_err(&sdev->sd, "Could not request IRQ\n"); - goto put_reg; - } - v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); - } else { - v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); - } - - rval = si4713_initialize(sdev); - if (rval < 0) { - v4l2_err(&sdev->sd, "Failed to probe device information.\n"); - goto free_irq; - } - - return 0; - -free_irq: - if (client->irq) - free_irq(client->irq, sdev); -free_ctrls: - v4l2_ctrl_handler_free(hdl); -put_reg: - regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); -free_gpio: - if (gpio_is_valid(sdev->gpio_reset)) - gpio_free(sdev->gpio_reset); -free_sdev: - kfree(sdev); -exit: - return rval; -} - -/* si4713_remove - remove the device */ -static int si4713_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct si4713_device *sdev = to_si4713_device(sd); - - if (sdev->power_state) - si4713_set_power_state(sdev, POWER_DOWN); - - if (client->irq > 0) - free_irq(client->irq, sdev); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); - if (gpio_is_valid(sdev->gpio_reset)) - gpio_free(sdev->gpio_reset); - kfree(sdev); - - return 0; -} - -/* si4713_i2c_driver - i2c driver interface */ -static const struct i2c_device_id si4713_id[] = { - { "si4713" , 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, si4713_id); - -static struct i2c_driver si4713_i2c_driver = { - .driver = { - .name = "si4713", - }, - .probe = si4713_probe, - .remove = si4713_remove, - .id_table = si4713_id, -}; - -module_i2c_driver(si4713_i2c_driver); diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713-i2c.h deleted file mode 100644 index 25cdea2..0000000 --- a/drivers/media/radio/si4713-i2c.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * drivers/media/radio/si4713-i2c.h - * - * Property and commands definitions for Si4713 radio transmitter chip. - * - * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT - * Contact: Eduardo Valentin - * - * 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. - * - */ - -#ifndef SI4713_I2C_H -#define SI4713_I2C_H - -#include -#include -#include - -#define SI4713_PRODUCT_NUMBER 0x0D - -/* Command Timeouts */ -#define DEFAULT_TIMEOUT 500 -#define TIMEOUT_SET_PROPERTY 20 -#define TIMEOUT_TX_TUNE_POWER 30000 -#define TIMEOUT_TX_TUNE 110000 -#define TIMEOUT_POWER_UP 200000 - -/* - * Command and its arguments definitions - */ -#define SI4713_PWUP_CTSIEN (1<<7) -#define SI4713_PWUP_GPO2OEN (1<<6) -#define SI4713_PWUP_PATCH (1<<5) -#define SI4713_PWUP_XOSCEN (1<<4) -#define SI4713_PWUP_FUNC_TX 0x02 -#define SI4713_PWUP_FUNC_PATCH 0x0F -#define SI4713_PWUP_OPMOD_ANALOG 0x50 -#define SI4713_PWUP_OPMOD_DIGITAL 0x0F -#define SI4713_PWUP_NARGS 2 -#define SI4713_PWUP_NRESP 1 -#define SI4713_CMD_POWER_UP 0x01 - -#define SI4713_GETREV_NRESP 9 -#define SI4713_CMD_GET_REV 0x10 - -#define SI4713_PWDN_NRESP 1 -#define SI4713_CMD_POWER_DOWN 0x11 - -#define SI4713_SET_PROP_NARGS 5 -#define SI4713_SET_PROP_NRESP 1 -#define SI4713_CMD_SET_PROPERTY 0x12 - -#define SI4713_GET_PROP_NARGS 3 -#define SI4713_GET_PROP_NRESP 4 -#define SI4713_CMD_GET_PROPERTY 0x13 - -#define SI4713_GET_STATUS_NRESP 1 -#define SI4713_CMD_GET_INT_STATUS 0x14 - -#define SI4713_CMD_PATCH_ARGS 0x15 -#define SI4713_CMD_PATCH_DATA 0x16 - -#define SI4713_MAX_FREQ 10800 -#define SI4713_MIN_FREQ 7600 -#define SI4713_TXFREQ_NARGS 3 -#define SI4713_TXFREQ_NRESP 1 -#define SI4713_CMD_TX_TUNE_FREQ 0x30 - -#define SI4713_MAX_POWER 120 -#define SI4713_MIN_POWER 88 -#define SI4713_MAX_ANTCAP 191 -#define SI4713_MIN_ANTCAP 0 -#define SI4713_TXPWR_NARGS 4 -#define SI4713_TXPWR_NRESP 1 -#define SI4713_CMD_TX_TUNE_POWER 0x31 - -#define SI4713_TXMEA_NARGS 4 -#define SI4713_TXMEA_NRESP 1 -#define SI4713_CMD_TX_TUNE_MEASURE 0x32 - -#define SI4713_INTACK_MASK 0x01 -#define SI4713_TXSTATUS_NARGS 1 -#define SI4713_TXSTATUS_NRESP 8 -#define SI4713_CMD_TX_TUNE_STATUS 0x33 - -#define SI4713_OVERMOD_BIT (1 << 2) -#define SI4713_IALH_BIT (1 << 1) -#define SI4713_IALL_BIT (1 << 0) -#define SI4713_ASQSTATUS_NARGS 1 -#define SI4713_ASQSTATUS_NRESP 5 -#define SI4713_CMD_TX_ASQ_STATUS 0x34 - -#define SI4713_RDSBUFF_MODE_MASK 0x87 -#define SI4713_RDSBUFF_NARGS 7 -#define SI4713_RDSBUFF_NRESP 6 -#define SI4713_CMD_TX_RDS_BUFF 0x35 - -#define SI4713_RDSPS_PSID_MASK 0x1F -#define SI4713_RDSPS_NARGS 5 -#define SI4713_RDSPS_NRESP 1 -#define SI4713_CMD_TX_RDS_PS 0x36 - -#define SI4713_CMD_GPO_CTL 0x80 -#define SI4713_CMD_GPO_SET 0x81 - -/* - * Bits from status response - */ -#define SI4713_CTS (1<<7) -#define SI4713_ERR (1<<6) -#define SI4713_RDS_INT (1<<2) -#define SI4713_ASQ_INT (1<<1) -#define SI4713_STC_INT (1<<0) - -/* - * Property definitions - */ -#define SI4713_GPO_IEN 0x0001 -#define SI4713_DIG_INPUT_FORMAT 0x0101 -#define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 -#define SI4713_REFCLK_FREQ 0x0201 -#define SI4713_REFCLK_PRESCALE 0x0202 -#define SI4713_TX_COMPONENT_ENABLE 0x2100 -#define SI4713_TX_AUDIO_DEVIATION 0x2101 -#define SI4713_TX_PILOT_DEVIATION 0x2102 -#define SI4713_TX_RDS_DEVIATION 0x2103 -#define SI4713_TX_LINE_INPUT_LEVEL 0x2104 -#define SI4713_TX_LINE_INPUT_MUTE 0x2105 -#define SI4713_TX_PREEMPHASIS 0x2106 -#define SI4713_TX_PILOT_FREQUENCY 0x2107 -#define SI4713_TX_ACOMP_ENABLE 0x2200 -#define SI4713_TX_ACOMP_THRESHOLD 0x2201 -#define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 -#define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 -#define SI4713_TX_ACOMP_GAIN 0x2204 -#define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 -#define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 -#define SI4713_TX_ASQ_LEVEL_LOW 0x2301 -#define SI4713_TX_ASQ_DURATION_LOW 0x2302 -#define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 -#define SI4713_TX_ASQ_DURATION_HIGH 0x2304 -#define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 -#define SI4713_TX_RDS_PI 0x2C01 -#define SI4713_TX_RDS_PS_MIX 0x2C02 -#define SI4713_TX_RDS_PS_MISC 0x2C03 -#define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 -#define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 -#define SI4713_TX_RDS_PS_AF 0x2C06 -#define SI4713_TX_RDS_FIFO_SIZE 0x2C07 - -#define PREEMPHASIS_USA 75 -#define PREEMPHASIS_EU 50 -#define PREEMPHASIS_DISABLED 0 -#define FMPE_USA 0x00 -#define FMPE_EU 0x01 -#define FMPE_DISABLED 0x02 - -#define POWER_UP 0x01 -#define POWER_DOWN 0x00 - -#define MAX_RDS_PTY 31 -#define MAX_RDS_DEVIATION 90000 - -/* - * PSNAME is known to be defined as 8 character sized (RDS Spec). - * However, there is receivers which scroll PSNAME 8xN sized. - */ -#define MAX_RDS_PS_NAME 96 - -/* - * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) - * character sized (RDS Spec). - * However, there is receivers which scroll them as well. - */ -#define MAX_RDS_RADIO_TEXT 384 - -#define MAX_LIMITER_RELEASE_TIME 102390 -#define MAX_LIMITER_DEVIATION 90000 - -#define MAX_PILOT_DEVIATION 90000 -#define MAX_PILOT_FREQUENCY 19000 - -#define MAX_ACOMP_RELEASE_TIME 1000000 -#define MAX_ACOMP_ATTACK_TIME 5000 -#define MAX_ACOMP_THRESHOLD 0 -#define MIN_ACOMP_THRESHOLD (-40) -#define MAX_ACOMP_GAIN 20 - -#define SI4713_NUM_SUPPLIES 2 - -/* - * si4713_device - private data - */ -struct si4713_device { - /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ - struct v4l2_subdev sd; - struct v4l2_ctrl_handler ctrl_handler; - /* private data structures */ - struct { /* si4713 control cluster */ - /* This is one big cluster since the mute control - * powers off the device and after unmuting again all - * controls need to be set at once. The only way of doing - * that is by making it one big cluster. */ - struct v4l2_ctrl *mute; - struct v4l2_ctrl *rds_ps_name; - struct v4l2_ctrl *rds_radio_text; - struct v4l2_ctrl *rds_pi; - struct v4l2_ctrl *rds_deviation; - struct v4l2_ctrl *rds_pty; - struct v4l2_ctrl *compression_enabled; - struct v4l2_ctrl *compression_threshold; - struct v4l2_ctrl *compression_gain; - struct v4l2_ctrl *compression_attack_time; - struct v4l2_ctrl *compression_release_time; - struct v4l2_ctrl *pilot_tone_enabled; - struct v4l2_ctrl *pilot_tone_freq; - struct v4l2_ctrl *pilot_tone_deviation; - struct v4l2_ctrl *limiter_enabled; - struct v4l2_ctrl *limiter_deviation; - struct v4l2_ctrl *limiter_release_time; - struct v4l2_ctrl *tune_preemphasis; - struct v4l2_ctrl *tune_pwr_level; - struct v4l2_ctrl *tune_ant_cap; - }; - struct completion work; - struct regulator_bulk_data supplies[SI4713_NUM_SUPPLIES]; - int gpio_reset; - u32 power_state; - u32 rds_enabled; - u32 frequency; - u32 preemphasis; - u32 stereo; - u32 tune_rnl; -}; -#endif /* ifndef SI4713_I2C_H */ diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig new file mode 100644 index 0000000..ec640b8 --- /dev/null +++ b/drivers/media/radio/si4713/Kconfig @@ -0,0 +1,25 @@ +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 0000000..a8c1194 --- /dev/null +++ b/drivers/media/radio/si4713/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for radios with Silicon Labs Si4713 FM Radio Transmitters +# + +obj-$(CONFIG_I2C_SI4713) += si4713.o +obj-$(CONFIG_PLATFORM_SI4713) += radio-platform-si4713.o diff --git a/drivers/media/radio/si4713/radio-platform-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c new file mode 100644 index 0000000..ba4cfc9 --- /dev/null +++ b/drivers/media/radio/si4713/radio-platform-si4713.c @@ -0,0 +1,246 @@ +/* + * drivers/media/radio/radio-si4713.c + * + * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter: + * + * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT + * Contact: Eduardo Valentin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* module parameters */ +static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */ +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, + "Minor number for radio device (-1 ==> auto assign)"); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Eduardo Valentin "); +MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter"); +MODULE_VERSION("0.0.1"); +MODULE_ALIAS("platform:radio-si4713"); + +/* Driver state struct */ +struct radio_si4713_device { + struct v4l2_device v4l2_dev; + struct video_device radio_dev; + struct mutex lock; +}; + +/* radio_si4713_fops - file operations interface */ +static const struct v4l2_file_operations radio_si4713_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + /* Note: locking is done at the subdev level in the i2c driver. */ + .unlocked_ioctl = video_ioctl2, +}; + +/* Video4Linux Interface */ + +/* radio_si4713_querycap - query device capabilities */ +static int radio_si4713_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver)); + strlcpy(capability->card, "Silicon Labs Si4713 Modulator", + sizeof(capability->card)); + strlcpy(capability->bus_info, "platform:radio-si4713", + sizeof(capability->bus_info)); + capability->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; + capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +/* + * v4l2 ioctl call backs. + * we are just a wrapper for v4l2_sub_devs. + */ +static inline struct v4l2_device *get_v4l2_dev(struct file *file) +{ + return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev; +} + +static int radio_si4713_g_modulator(struct file *file, void *p, + struct v4l2_modulator *vm) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + g_modulator, vm); +} + +static int radio_si4713_s_modulator(struct file *file, void *p, + const struct v4l2_modulator *vm) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + s_modulator, vm); +} + +static int radio_si4713_g_frequency(struct file *file, void *p, + struct v4l2_frequency *vf) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + g_frequency, vf); +} + +static int radio_si4713_s_frequency(struct file *file, void *p, + const struct v4l2_frequency *vf) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + s_frequency, vf); +} + +static long radio_si4713_default(struct file *file, void *p, + bool valid_prio, unsigned int cmd, void *arg) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + ioctl, cmd, arg); +} + +static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = { + .vidioc_querycap = radio_si4713_querycap, + .vidioc_g_modulator = radio_si4713_g_modulator, + .vidioc_s_modulator = radio_si4713_s_modulator, + .vidioc_g_frequency = radio_si4713_g_frequency, + .vidioc_s_frequency = radio_si4713_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_default = radio_si4713_default, +}; + +/* radio_si4713_vdev_template - video device interface */ +static struct video_device radio_si4713_vdev_template = { + .fops = &radio_si4713_fops, + .name = "radio-si4713", + .release = video_device_release_empty, + .ioctl_ops = &radio_si4713_ioctl_ops, + .vfl_dir = VFL_DIR_TX, +}; + +/* Platform driver interface */ +/* radio_si4713_pdriver_probe - probe for the device */ +static int radio_si4713_pdriver_probe(struct platform_device *pdev) +{ + struct radio_si4713_platform_data *pdata = pdev->dev.platform_data; + struct radio_si4713_device *rsdev; + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; + int rval = 0; + + if (!pdata) { + dev_err(&pdev->dev, "Cannot proceed without platform data.\n"); + rval = -EINVAL; + goto exit; + } + + rsdev = devm_kzalloc(&pdev->dev, sizeof(*rsdev), GFP_KERNEL); + if (!rsdev) { + dev_err(&pdev->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + mutex_init(&rsdev->lock); + + rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev); + if (rval) { + dev_err(&pdev->dev, "Failed to register v4l2 device.\n"); + goto exit; + } + + adapter = i2c_get_adapter(pdata->i2c_bus); + if (!adapter) { + dev_err(&pdev->dev, "Cannot get i2c adapter %d\n", + pdata->i2c_bus); + rval = -ENODEV; + goto unregister_v4l2_dev; + } + + sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, + pdata->subdev_board_info, NULL); + if (!sd) { + dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); + rval = -ENODEV; + goto put_adapter; + } + + rsdev->radio_dev = radio_si4713_vdev_template; + rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev; + rsdev->radio_dev.ctrl_handler = sd->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &rsdev->radio_dev.flags); + /* Serialize all access to the si4713 */ + rsdev->radio_dev.lock = &rsdev->lock; + video_set_drvdata(&rsdev->radio_dev, rsdev); + if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { + dev_err(&pdev->dev, "Could not register video device.\n"); + rval = -EIO; + goto put_adapter; + } + dev_info(&pdev->dev, "New device successfully probed\n"); + + goto exit; + +put_adapter: + i2c_put_adapter(adapter); +unregister_v4l2_dev: + v4l2_device_unregister(&rsdev->v4l2_dev); +exit: + return rval; +} + +/* radio_si4713_pdriver_remove - remove the device */ +static int radio_si4713_pdriver_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct radio_si4713_device *rsdev; + + rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev); + video_unregister_device(&rsdev->radio_dev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&rsdev->v4l2_dev); + + return 0; +} + +static struct platform_driver radio_si4713_pdriver = { + .driver = { + .name = "radio-si4713", + .owner = THIS_MODULE, + }, + .probe = radio_si4713_pdriver_probe, + .remove = radio_si4713_pdriver_remove, +}; + +module_platform_driver(radio_si4713_pdriver); diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c new file mode 100644 index 0000000..4f3308f --- /dev/null +++ b/drivers/media/radio/si4713/si4713.c @@ -0,0 +1,1532 @@ +/* + * drivers/media/radio/si4713-i2c.c + * + * Silicon Labs Si4713 FM Radio Transmitter I2C commands. + * + * Copyright (c) 2009 Nokia Corporation + * Contact: Eduardo Valentin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "si4713.h" + +/* module parameters */ +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eduardo Valentin "); +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_DEVIATION 0x1A5E +#define DEFAULT_ACOMP_ATIME 0x0000 +#define DEFAULT_ACOMP_RTIME 0xF4240L +#define DEFAULT_ACOMP_GAIN 0x0F +#define DEFAULT_ACOMP_THRESHOLD (-0x28) +#define DEFAULT_MUTE 0x01 +#define DEFAULT_POWER_LEVEL 88 +#define DEFAULT_FREQUENCY 8800 +#define DEFAULT_PREEMPHASIS FMPE_EU +#define DEFAULT_TUNE_RNL 0xFF + +#define to_si4713_device(sd) container_of(sd, struct si4713_device, sd) + +/* frequency domain transformation (using times 10 to avoid floats) */ +#define FREQDEV_UNIT 100000 +#define FREQV4L2_MULTI 625 +#define si4713_to_v4l2(f) ((f * FREQDEV_UNIT) / FREQV4L2_MULTI) +#define v4l2_to_si4713(f) ((f * FREQV4L2_MULTI) / FREQDEV_UNIT) +#define FREQ_RANGE_LOW 7600 +#define FREQ_RANGE_HIGH 10800 + +#define MAX_ARGS 7 + +#define RDS_BLOCK 8 +#define RDS_BLOCK_CLEAR 0x03 +#define RDS_BLOCK_LOAD 0x04 +#define RDS_RADIOTEXT_2A 0x20 +#define RDS_RADIOTEXT_BLK_SIZE 4 +#define RDS_RADIOTEXT_INDEX_MAX 0x0F +#define RDS_CARRIAGE_RETURN 0x0D + +#define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) + +#define get_status_bit(p, b, m) (((p) & (m)) >> (b)) +#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) + +#define ATTACK_TIME_UNIT 500 + +#define POWER_OFF 0x00 +#define POWER_ON 0x01 + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) +#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) +#define check_command_failed(status) (!(status & SI4713_CTS) || \ + (status & SI4713_ERR)) +/* mute definition */ +#define set_mute(p) ((p & 1) | ((p & 1) << 1)); + +#ifdef DEBUG +#define DBG_BUFFER(device, message, buffer, size) \ + { \ + int i; \ + char str[(size)*5]; \ + for (i = 0; i < size; i++) \ + sprintf(str + i * 5, " 0x%02x", buffer[i]); \ + v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ + } +#else +#define DBG_BUFFER(device, message, buffer, size) +#endif + +/* + * Values for limiter release time (sorted by second column) + * device release + * value time (us) + */ +static long limiter_times[] = { + 2000, 250, + 1000, 500, + 510, 1000, + 255, 2000, + 170, 3000, + 127, 4020, + 102, 5010, + 85, 6020, + 73, 7010, + 64, 7990, + 57, 8970, + 51, 10030, + 25, 20470, + 17, 30110, + 13, 39380, + 10, 51190, + 8, 63690, + 7, 73140, + 6, 85330, + 5, 102390, +}; + +/* + * Values for audio compression release time (sorted by second column) + * device release + * value time (us) + */ +static unsigned long acomp_rtimes[] = { + 0, 100000, + 1, 200000, + 2, 350000, + 3, 525000, + 4, 1000000, +}; + +/* + * Values for preemphasis (sorted by second column) + * device preemphasis + * value value (v4l2) + */ +static unsigned long preemphasis_values[] = { + FMPE_DISABLED, V4L2_PREEMPHASIS_DISABLED, + FMPE_EU, V4L2_PREEMPHASIS_50_uS, + FMPE_USA, V4L2_PREEMPHASIS_75_uS, +}; + +static int usecs_to_dev(unsigned long usecs, unsigned long const array[], + int size) +{ + int i; + int rval = -EINVAL; + + for (i = 0; i < size / 2; i++) + if (array[(i * 2) + 1] >= usecs) { + rval = array[i * 2]; + break; + } + + return rval; +} + +/* si4713_handler: IRQ handler, just complete work */ +static irqreturn_t si4713_handler(int irq, void *dev) +{ + struct si4713_device *sdev = dev; + + v4l2_dbg(2, debug, &sdev->sd, + "%s: sending signal to completion work.\n", __func__); + complete(&sdev->work); + + return IRQ_HANDLED; +} + +/* + * si4713_send_command - sends a command to si4713 and waits its response + * @sdev: si4713_device structure for the device we are communicating + * @command: command id + * @args: command arguments we are sending (up to 7) + * @argn: actual size of @args + * @response: buffer to place the expected response from the device (up to 15) + * @respn: actual size of @response + * @usecs: amount of time to wait before reading the response (in usecs) + */ +static int si4713_send_command(struct si4713_device *sdev, const u8 command, + const u8 args[], const int argn, + u8 response[], const int respn, const int usecs) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); + u8 data1[MAX_ARGS + 1]; + int err; + + if (!client->adapter) + return -ENODEV; + + /* First send the command and its arguments */ + data1[0] = command; + memcpy(data1 + 1, args, argn); + DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); + + err = i2c_master_send(client, data1, argn + 1); + if (err != argn + 1) { + v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + /* Wait response from interrupt */ + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + 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; + + return 0; +} + +/* + * si4713_read_property - reads a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @pv: property value to be returned on success + */ +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 + */ + const u8 args[SI4713_GET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + }; + + err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + *pv = compose_u16(val[2], val[3]); + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, *pv, val[0]); + + return err; +} + +/* + * si4713_write_property - modifies a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @val: new value for that property + */ +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 + */ + const u8 args[SI4713_SET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + msb(val), + lsb(val), + }; + + rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, val, resp[0]); + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +/* + * si4713_powerup - Powers the device up + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerup(struct si4713_device *sdev) +{ + int err; + u8 resp[SI4713_PWUP_NRESP]; + /* + * .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, + 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 (gpio_is_valid(sdev->gpio_reset)) { + udelay(50); + gpio_set_value(sdev->gpio_reset, 1); + } + + err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + TIMEOUT_POWER_UP); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", + resp[0]); + 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, + 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); + if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + } + + return err; +} + +/* + * si4713_powerdown - Powers the device down + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerdown(struct si4713_device *sdev) +{ + int err; + u8 resp[SI4713_PWDN_NRESP]; + + if (!sdev->power_state) + return 0; + + err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", + resp[0]); + 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); + sdev->power_state = POWER_OFF; + } + + return err; +} + +/* + * si4713_checkrev - Checks if we are treating a device with the correct rev. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_checkrev(struct si4713_device *sdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); + int rval; + u8 resp[SI4713_GETREV_NRESP]; + + rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + if (resp[1] == SI4713_PRODUCT_NUMBER) { + 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"); + rval = -EINVAL; + } + return rval; +} + +/* + * si4713_wait_stc - Waits STC interrupt and clears status bits. Useful + * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS + * @sdev: si4713_device structure for the device we are communicating + * @usecs: timeout to wait for STC interrupt signal + */ +static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) +{ + int err; + u8 resp[SI4713_GET_STATUS_NRESP]; + + /* Wait response from STC interrupt */ + if (!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; +} + +/* + * 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. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + */ +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 + */ + const u8 args[SI4713_TXFREQ_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x status=0x%02x\n", __func__, + frequency, val[0]); + + err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); + if (err < 0) + return err; + + return compose_u16(args[1], args[2]); +} + +/* + * 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. + * @sdev: si4713_device structure for the device we are communicating + * @power: tuning power (88 - 115 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, + u8 antcap) +{ + int err; + u8 val[SI4713_TXPWR_NRESP]; + /* + * .First byte = 0 + * .Second byte = 0 + * .Third byte = power + * .Fourth byte = antcap + */ + const 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; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, power, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_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. + * @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) + */ +static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, + u8 antcap) +{ + int err; + u8 val[SI4713_TXMEA_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + * .Fourth byte = antcap + */ + const u8 args[SI4713_TXMEA_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + antcap, + }; + + sdev->tune_rnl = DEFAULT_TUNE_RNL; + + if (antcap > SI4713_MAX_ANTCAP) + return -EDOM; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, frequency, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); +} + +/* + * 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. + * @sdev: si4713_device structure for the device we are communicating + * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. + * @frequency: returned frequency + * @power: returned power + * @antcap: returned antenna capacitance + * @noise: returned noise level + */ +static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, + u16 *frequency, u8 *power, + u8 *antcap, u8 *noise) +{ + int err; + u8 val[SI4713_TXSTATUS_NRESP]; + /* + * .First byte = intack bit + */ + const u8 args[SI4713_TXSTATUS_NARGS] = { + intack & SI4713_INTACK_MASK, + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, + "%s: status=0x%02x\n", __func__, val[0]); + *frequency = compose_u16(val[2], val[3]); + sdev->frequency = *frequency; + *power = val[5]; + *antcap = val[6]; + *noise = val[7]; + v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz " + "(power %d, antcap %d, rnl %d)\n", __func__, + *frequency, *power, *antcap, *noise); + } + + return err; +} + +/* + * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer. + * @sdev: si4713_device structure for the device we are communicating + * @mode: the buffer operation mode. + * @rdsb: RDS Block B + * @rdsc: RDS Block C + * @rdsd: RDS Block D + * @cbleft: returns the number of available circular buffer blocks minus the + * number of used circular buffer blocks. + */ +static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb, + u16 rdsc, u16 rdsd, s8 *cbleft) +{ + int err; + u8 val[SI4713_RDSBUFF_NRESP]; + + const u8 args[SI4713_RDSBUFF_NARGS] = { + mode & SI4713_RDSBUFF_MODE_MASK, + msb(rdsb), + lsb(rdsb), + msb(rdsc), + lsb(rdsc), + msb(rdsd), + lsb(rdsd), + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, + "%s: status=0x%02x\n", __func__, val[0]); + *cbleft = (s8)val[2] - val[3]; + v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts" + " 0x%02x cb avail: %d cb used %d fifo avail" + " %d fifo used %d\n", __func__, val[1], + val[2], val[3], val[4], val[5]); + } + + return err; +} + +/* + * si4713_tx_rds_ps - Loads the program service buffer. + * @sdev: si4713_device structure for the device we are communicating + * @psid: program service id to be loaded. + * @pschar: assumed 4 size char array to be loaded into the program service + */ +static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, + unsigned char *pschar) +{ + int err; + u8 val[SI4713_RDSPS_NRESP]; + + const u8 args[SI4713_RDSPS_NARGS] = { + psid & SI4713_RDSPS_PSID_MASK, + pschar[0], + pschar[1], + pschar[2], + pschar[3], + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); + + return err; +} + +static int si4713_set_power_state(struct si4713_device *sdev, u8 value) +{ + if (value) + return si4713_powerup(sdev); + return si4713_powerdown(sdev); +} + +static int si4713_set_mute(struct si4713_device *sdev, u16 mute) +{ + int rval = 0; + + mute = set_mute(mute); + + if (sdev->power_state) + rval = si4713_write_property(sdev, + SI4713_TX_LINE_INPUT_MUTE, mute); + + return rval; +} + +static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) +{ + int rval = 0, i; + u8 len = 0; + + /* We want to clear the whole thing */ + if (!strlen(ps_name)) + memset(ps_name, 0, MAX_RDS_PS_NAME + 1); + + if (sdev->power_state) { + /* Write the new ps name and clear the padding */ + for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) { + rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)), + ps_name + i); + if (rval < 0) + return rval; + } + + /* Setup the size to be sent */ + if (strlen(ps_name)) + len = strlen(ps_name) - 1; + else + len = 1; + + rval = si4713_write_property(sdev, + SI4713_TX_RDS_PS_MESSAGE_COUNT, + rds_ps_nblocks(len)); + if (rval < 0) + return rval; + + rval = si4713_write_property(sdev, + SI4713_TX_RDS_PS_REPEAT_COUNT, + DEFAULT_RDS_PS_REPEAT_COUNT * 2); + if (rval < 0) + return rval; + } + + return rval; +} + +static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) +{ + int rval = 0, i; + u16 t_index = 0; + u8 b_index = 0, cr_inserted = 0; + s8 left; + + if (!sdev->power_state) + return rval; + + rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left); + if (rval < 0) + return rval; + + if (!strlen(rt)) + return rval; + + do { + /* RDS spec says that if the last block isn't used, + * then apply a carriage return + */ + if (t_index < (RDS_RADIOTEXT_INDEX_MAX * RDS_RADIOTEXT_BLK_SIZE)) { + 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; + cr_inserted = 1; + break; + } + } + } + + rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_LOAD, + compose_u16(RDS_RADIOTEXT_2A, b_index++), + compose_u16(rt[t_index], rt[t_index + 1]), + compose_u16(rt[t_index + 2], rt[t_index + 3]), + &left); + if (rval < 0) + return rval; + + t_index += RDS_RADIOTEXT_BLK_SIZE; + + if (cr_inserted) + break; + } while (left > 0); + + return rval; +} + +/* + * si4713_update_tune_status - update properties from tx_tune_status + * command. Must be called with sdev->mutex held. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_update_tune_status(struct si4713_device *sdev) +{ + int rval; + u16 f = 0; + u8 p = 0, a = 0, n = 0; + + rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); + + if (rval < 0) + goto exit; + +/* TODO: check that power_level and antenna_capacitor really are not + changed by the hardware. If they are, then these controls should become + volatiles. + sdev->power_level = p; + sdev->antenna_capacitor = a;*/ + sdev->tune_rnl = n; + +exit: + return rval; +} + +static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, + s32 *bit, s32 *mask, u16 *property, int *mul, + unsigned long **table, int *size) +{ + s32 rval = 0; + + switch (id) { + /* FM_TX class controls */ + case V4L2_CID_RDS_TX_PI: + *property = SI4713_TX_RDS_PI; + *mul = 1; + break; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + *property = SI4713_TX_ACOMP_THRESHOLD; + *mul = 1; + break; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + *property = SI4713_TX_ACOMP_GAIN; + *mul = 1; + break; + case V4L2_CID_PILOT_TONE_FREQUENCY: + *property = SI4713_TX_PILOT_FREQUENCY; + *mul = 1; + break; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + *property = SI4713_TX_ACOMP_ATTACK_TIME; + *mul = ATTACK_TIME_UNIT; + break; + case V4L2_CID_PILOT_TONE_DEVIATION: + *property = SI4713_TX_PILOT_DEVIATION; + *mul = 10; + break; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + *property = SI4713_TX_AUDIO_DEVIATION; + *mul = 10; + break; + case V4L2_CID_RDS_TX_DEVIATION: + *property = SI4713_TX_RDS_DEVIATION; + *mul = 1; + break; + + case V4L2_CID_RDS_TX_PTY: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 5; + *mask = 0x1F << 5; + break; + case V4L2_CID_AUDIO_LIMITER_ENABLED: + *property = SI4713_TX_ACOMP_ENABLE; + *bit = 1; + *mask = 1 << 1; + break; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + *property = SI4713_TX_ACOMP_ENABLE; + *bit = 0; + *mask = 1 << 0; + break; + case V4L2_CID_PILOT_TONE_ENABLED: + *property = SI4713_TX_COMPONENT_ENABLE; + *bit = 0; + *mask = 1 << 0; + break; + + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + *property = SI4713_TX_LIMITER_RELEASE_TIME; + *table = limiter_times; + *size = ARRAY_SIZE(limiter_times); + break; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + *property = SI4713_TX_ACOMP_RELEASE_TIME; + *table = acomp_rtimes; + *size = ARRAY_SIZE(acomp_rtimes); + break; + case V4L2_CID_TUNE_PREEMPHASIS: + *property = SI4713_TX_PREEMPHASIS; + *table = preemphasis_values; + *size = ARRAY_SIZE(preemphasis_values); + break; + + default: + rval = -EINVAL; + break; + } + + return rval; +} + +static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f); +static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *); +/* + * si4713_setup - Sets the device up with current configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_setup(struct si4713_device *sdev) +{ + struct v4l2_frequency f; + struct v4l2_modulator vm; + int rval; + + /* Device procedure needs to set frequency first */ + f.tuner = 0; + f.frequency = sdev->frequency ? sdev->frequency : DEFAULT_FREQUENCY; + f.frequency = si4713_to_v4l2(f.frequency); + rval = si4713_s_frequency(&sdev->sd, &f); + + vm.index = 0; + if (sdev->stereo) + vm.txsubchans = V4L2_TUNER_SUB_STEREO; + else + vm.txsubchans = V4L2_TUNER_SUB_MONO; + if (sdev->rds_enabled) + vm.txsubchans |= V4L2_TUNER_SUB_RDS; + si4713_s_modulator(&sdev->sd, &vm); + + return rval; +} + +/* + * si4713_initialize - Sets the device up with default configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_initialize(struct si4713_device *sdev) +{ + int rval; + + rval = si4713_set_power_state(sdev, POWER_ON); + if (rval < 0) + return rval; + + rval = si4713_checkrev(sdev); + if (rval < 0) + return rval; + + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + return rval; + + + sdev->frequency = DEFAULT_FREQUENCY; + sdev->stereo = 1; + sdev->tune_rnl = DEFAULT_TUNE_RNL; + return 0; +} + +/* si4713_s_ctrl - set the value of a control */ +static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct si4713_device *sdev = + container_of(ctrl->handler, struct si4713_device, ctrl_handler); + u32 val = 0; + s32 bit = 0, mask = 0; + u16 property = 0; + int mul = 0; + unsigned long *table = NULL; + int size = 0; + bool force = false; + int c; + int ret = 0; + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + if (ctrl->is_new) { + if (ctrl->val) { + ret = si4713_set_mute(sdev, ctrl->val); + if (!ret) + ret = si4713_set_power_state(sdev, POWER_DOWN); + return ret; + } + ret = si4713_set_power_state(sdev, POWER_UP); + if (!ret) + ret = si4713_set_mute(sdev, ctrl->val); + if (!ret) + ret = si4713_setup(sdev); + if (ret) + return ret; + force = true; + } + + if (!sdev->power_state) + return 0; + + for (c = 1; !ret && c < ctrl->ncontrols; c++) { + ctrl = ctrl->cluster[c]; + + if (!force && !ctrl->is_new) + continue; + + switch (ctrl->id) { + case V4L2_CID_RDS_TX_PS_NAME: + ret = si4713_set_rds_ps_name(sdev, ctrl->string); + break; + + case V4L2_CID_RDS_TX_RADIO_TEXT: + ret = si4713_set_rds_radio_text(sdev, ctrl->string); + break; + + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + /* don't handle this control if we force setting all + * controls since in that case it will be handled by + * V4L2_CID_TUNE_POWER_LEVEL. */ + if (force) + break; + /* fall through */ + case V4L2_CID_TUNE_POWER_LEVEL: + ret = si4713_tx_tune_power(sdev, + sdev->tune_pwr_level->val, sdev->tune_ant_cap->val); + if (!ret) { + /* Make sure we don't set this twice */ + sdev->tune_ant_cap->is_new = false; + sdev->tune_pwr_level->is_new = false; + } + break; + + default: + ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit, + &mask, &property, &mul, &table, &size); + if (ret < 0) + break; + + val = ctrl->val; + if (mul) { + val = val / mul; + } else if (table) { + ret = usecs_to_dev(val, table, size); + if (ret < 0) + break; + val = ret; + ret = 0; + } + + if (mask) { + ret = si4713_read_property(sdev, property, &val); + if (ret < 0) + break; + val = set_bits(val, ctrl->val, bit, mask); + } + + ret = si4713_write_property(sdev, property, val); + if (ret < 0) + break; + if (mask) + val = ctrl->val; + break; + } + } + + return ret; +} + +/* si4713_ioctl - deal with private ioctls (only rnl for now) */ +static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct si4713_device *sdev = to_si4713_device(sd); + struct si4713_rnl *rnl = arg; + u16 frequency; + int rval = 0; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case SI4713_IOC_MEASURE_RNL: + frequency = v4l2_to_si4713(rnl->frequency); + + if (sdev->power_state) { + /* Set desired measurement frequency */ + rval = si4713_tx_tune_measure(sdev, frequency, 0); + if (rval < 0) + return rval; + /* get results from tune status */ + rval = si4713_update_tune_status(sdev); + if (rval < 0) + return rval; + } + rnl->rnl = sdev->tune_rnl; + break; + + default: + /* nothing */ + rval = -ENOIOCTLCMD; + } + + return rval; +} + +/* si4713_g_modulator - get modulator attributes */ +static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + if (!sdev) + return -ENODEV; + + if (vm->index > 0) + return -EINVAL; + + strncpy(vm->name, "FM Modulator", 32); + vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS; + + /* Report current frequency range limits */ + vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); + vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); + + if (sdev->power_state) { + u32 comp_en = 0; + + rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, + &comp_en); + if (rval < 0) + return rval; + + sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); + } + + /* Report current audio mode: mono or stereo */ + if (sdev->stereo) + vm->txsubchans = V4L2_TUNER_SUB_STEREO; + else + vm->txsubchans = V4L2_TUNER_SUB_MONO; + + /* Report rds feature status */ + if (sdev->rds_enabled) + vm->txsubchans |= V4L2_TUNER_SUB_RDS; + else + vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; + + return rval; +} + +/* si4713_s_modulator - set modulator attributes */ +static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *vm) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + u16 stereo, rds; + u32 p; + + if (!sdev) + return -ENODEV; + + if (vm->index > 0) + return -EINVAL; + + /* Set audio mode: mono or stereo */ + if (vm->txsubchans & V4L2_TUNER_SUB_STEREO) + stereo = 1; + else if (vm->txsubchans & V4L2_TUNER_SUB_MONO) + stereo = 0; + else + return -EINVAL; + + rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); + + if (sdev->power_state) { + rval = si4713_read_property(sdev, + SI4713_TX_COMPONENT_ENABLE, &p); + if (rval < 0) + return rval; + + p = set_bits(p, stereo, 1, 1 << 1); + p = set_bits(p, rds, 2, 1 << 2); + + rval = si4713_write_property(sdev, + SI4713_TX_COMPONENT_ENABLE, p); + if (rval < 0) + return rval; + } + + sdev->stereo = stereo; + sdev->rds_enabled = rds; + + return rval; +} + +/* si4713_g_frequency - get tuner or modulator radio frequency */ +static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + if (f->tuner) + return -EINVAL; + + if (sdev->power_state) { + u16 freq; + u8 p, a, n; + + rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); + if (rval < 0) + return rval; + + sdev->frequency = freq; + } + + f->frequency = si4713_to_v4l2(sdev->frequency); + + return rval; +} + +/* si4713_s_frequency - set tuner or modulator radio frequency */ +static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + u16 frequency = v4l2_to_si4713(f->frequency); + + if (f->tuner) + return -EINVAL; + + /* Check frequency range */ + frequency = clamp_t(u16, frequency, FREQ_RANGE_LOW, FREQ_RANGE_HIGH); + + if (sdev->power_state) { + rval = si4713_tx_tune_freq(sdev, frequency); + if (rval < 0) + return rval; + frequency = rval; + rval = 0; + } + sdev->frequency = frequency; + + return rval; +} + +static const struct v4l2_ctrl_ops si4713_ctrl_ops = { + .s_ctrl = si4713_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { + .ioctl = si4713_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = { + .g_frequency = si4713_g_frequency, + .s_frequency = si4713_s_frequency, + .g_modulator = si4713_g_modulator, + .s_modulator = si4713_s_modulator, +}; + +static const struct v4l2_subdev_ops si4713_subdev_ops = { + .core = &si4713_subdev_core_ops, + .tuner = &si4713_subdev_tuner_ops, +}; + +/* + * I2C driver interface + */ +/* si4713_probe - probe for the device */ +static int si4713_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si4713_device *sdev; + struct si4713_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; + int rval, i; + + sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + if (!sdev) { + dev_err(&client->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + + sdev->gpio_reset = -1; + if (pdata && gpio_is_valid(pdata->gpio_reset)) { + rval = gpio_request(pdata->gpio_reset, "si4713 reset"); + if (rval) { + dev_err(&client->dev, + "Failed to request gpio: %d\n", rval); + goto free_sdev; + } + sdev->gpio_reset = pdata->gpio_reset; + gpio_direction_output(sdev->gpio_reset, 0); + } + + for (i = 0; i < ARRAY_SIZE(sdev->supplies); i++) + sdev->supplies[i].supply = si4713_supply_names[i]; + + rval = regulator_bulk_get(&client->dev, ARRAY_SIZE(sdev->supplies), + sdev->supplies); + if (rval) { + dev_err(&client->dev, "Cannot get regulators: %d\n", rval); + goto free_gpio; + } + + v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops); + + init_completion(&sdev->work); + + hdl = &sdev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 20); + sdev->mute = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, DEFAULT_MUTE); + + sdev->rds_pi = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, DEFAULT_RDS_PI); + sdev->rds_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PTY, 0, 31, 1, DEFAULT_RDS_PTY); + sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION, + 10, DEFAULT_RDS_DEVIATION); + /* + * Report step as 8. From RDS spec, psname + * should be 8. But there are receivers which scroll strings + * sized as 8xN. + */ + sdev->rds_ps_name = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_PS_NAME, 0, MAX_RDS_PS_NAME, 8, 0); + /* + * Report step as 32 (2A block). From RDS spec, + * radio text should be 32 for 2A block. But there are receivers + * which scroll strings sized as 32xN. Setting default to 32. + */ + sdev->rds_radio_text = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_RADIO_TEXT, 0, MAX_RDS_RADIO_TEXT, 32, 0); + + sdev->limiter_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_ENABLED, 0, 1, 1, 1); + sdev->limiter_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, 250, + MAX_LIMITER_RELEASE_TIME, 10, DEFAULT_LIMITER_RTIME); + sdev->limiter_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_LIMITER_DEVIATION, 0, + MAX_LIMITER_DEVIATION, 10, DEFAULT_LIMITER_DEV); + + sdev->compression_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_ENABLED, 0, 1, 1, 1); + sdev->compression_gain = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_GAIN, 0, MAX_ACOMP_GAIN, 1, + DEFAULT_ACOMP_GAIN); + sdev->compression_threshold = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, MIN_ACOMP_THRESHOLD, + MAX_ACOMP_THRESHOLD, 1, + DEFAULT_ACOMP_THRESHOLD); + sdev->compression_attack_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, 0, + MAX_ACOMP_ATTACK_TIME, 500, DEFAULT_ACOMP_ATIME); + sdev->compression_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, 100000, + MAX_ACOMP_RELEASE_TIME, 100000, DEFAULT_ACOMP_RTIME); + + sdev->pilot_tone_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_ENABLED, 0, 1, 1, 1); + sdev->pilot_tone_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_DEVIATION, 0, MAX_PILOT_DEVIATION, + 10, DEFAULT_PILOT_DEVIATION); + sdev->pilot_tone_freq = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_PILOT_TONE_FREQUENCY, 0, MAX_PILOT_FREQUENCY, + 1, DEFAULT_PILOT_FREQUENCY); + + sdev->tune_preemphasis = v4l2_ctrl_new_std_menu(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_PREEMPHASIS, + V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS); + sdev->tune_pwr_level = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_POWER_LEVEL, 0, 120, 1, DEFAULT_POWER_LEVEL); + sdev->tune_ant_cap = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, 191, 1, 0); + + if (hdl->error) { + rval = hdl->error; + goto free_ctrls; + } + v4l2_ctrl_cluster(20, &sdev->mute); + sdev->sd.ctrl_handler = hdl; + + if (client->irq) { + rval = request_irq(client->irq, + si4713_handler, IRQF_TRIGGER_FALLING, + client->name, sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Could not request IRQ\n"); + goto put_reg; + } + v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); + } else { + v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); + } + + rval = si4713_initialize(sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Failed to probe device information.\n"); + goto free_irq; + } + + return 0; + +free_irq: + if (client->irq) + free_irq(client->irq, sdev); +free_ctrls: + v4l2_ctrl_handler_free(hdl); +put_reg: + regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); +free_gpio: + if (gpio_is_valid(sdev->gpio_reset)) + gpio_free(sdev->gpio_reset); +free_sdev: + kfree(sdev); +exit: + return rval; +} + +/* si4713_remove - remove the device */ +static int si4713_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct si4713_device *sdev = to_si4713_device(sd); + + if (sdev->power_state) + si4713_set_power_state(sdev, POWER_DOWN); + + if (client->irq > 0) + free_irq(client->irq, sdev); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); + if (gpio_is_valid(sdev->gpio_reset)) + gpio_free(sdev->gpio_reset); + kfree(sdev); + + return 0; +} + +/* si4713_i2c_driver - i2c driver interface */ +static const struct i2c_device_id si4713_id[] = { + { "si4713" , 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, si4713_id); + +static struct i2c_driver si4713_i2c_driver = { + .driver = { + .name = "si4713", + }, + .probe = si4713_probe, + .remove = si4713_remove, + .id_table = si4713_id, +}; + +module_i2c_driver(si4713_i2c_driver); diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h new file mode 100644 index 0000000..25cdea2 --- /dev/null +++ b/drivers/media/radio/si4713/si4713.h @@ -0,0 +1,238 @@ +/* + * drivers/media/radio/si4713-i2c.h + * + * Property and commands definitions for Si4713 radio transmitter chip. + * + * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT + * Contact: Eduardo Valentin + * + * 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. + * + */ + +#ifndef SI4713_I2C_H +#define SI4713_I2C_H + +#include +#include +#include + +#define SI4713_PRODUCT_NUMBER 0x0D + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT 500 +#define TIMEOUT_SET_PROPERTY 20 +#define TIMEOUT_TX_TUNE_POWER 30000 +#define TIMEOUT_TX_TUNE 110000 +#define TIMEOUT_POWER_UP 200000 + +/* + * Command and its arguments definitions + */ +#define SI4713_PWUP_CTSIEN (1<<7) +#define SI4713_PWUP_GPO2OEN (1<<6) +#define SI4713_PWUP_PATCH (1<<5) +#define SI4713_PWUP_XOSCEN (1<<4) +#define SI4713_PWUP_FUNC_TX 0x02 +#define SI4713_PWUP_FUNC_PATCH 0x0F +#define SI4713_PWUP_OPMOD_ANALOG 0x50 +#define SI4713_PWUP_OPMOD_DIGITAL 0x0F +#define SI4713_PWUP_NARGS 2 +#define SI4713_PWUP_NRESP 1 +#define SI4713_CMD_POWER_UP 0x01 + +#define SI4713_GETREV_NRESP 9 +#define SI4713_CMD_GET_REV 0x10 + +#define SI4713_PWDN_NRESP 1 +#define SI4713_CMD_POWER_DOWN 0x11 + +#define SI4713_SET_PROP_NARGS 5 +#define SI4713_SET_PROP_NRESP 1 +#define SI4713_CMD_SET_PROPERTY 0x12 + +#define SI4713_GET_PROP_NARGS 3 +#define SI4713_GET_PROP_NRESP 4 +#define SI4713_CMD_GET_PROPERTY 0x13 + +#define SI4713_GET_STATUS_NRESP 1 +#define SI4713_CMD_GET_INT_STATUS 0x14 + +#define SI4713_CMD_PATCH_ARGS 0x15 +#define SI4713_CMD_PATCH_DATA 0x16 + +#define SI4713_MAX_FREQ 10800 +#define SI4713_MIN_FREQ 7600 +#define SI4713_TXFREQ_NARGS 3 +#define SI4713_TXFREQ_NRESP 1 +#define SI4713_CMD_TX_TUNE_FREQ 0x30 + +#define SI4713_MAX_POWER 120 +#define SI4713_MIN_POWER 88 +#define SI4713_MAX_ANTCAP 191 +#define SI4713_MIN_ANTCAP 0 +#define SI4713_TXPWR_NARGS 4 +#define SI4713_TXPWR_NRESP 1 +#define SI4713_CMD_TX_TUNE_POWER 0x31 + +#define SI4713_TXMEA_NARGS 4 +#define SI4713_TXMEA_NRESP 1 +#define SI4713_CMD_TX_TUNE_MEASURE 0x32 + +#define SI4713_INTACK_MASK 0x01 +#define SI4713_TXSTATUS_NARGS 1 +#define SI4713_TXSTATUS_NRESP 8 +#define SI4713_CMD_TX_TUNE_STATUS 0x33 + +#define SI4713_OVERMOD_BIT (1 << 2) +#define SI4713_IALH_BIT (1 << 1) +#define SI4713_IALL_BIT (1 << 0) +#define SI4713_ASQSTATUS_NARGS 1 +#define SI4713_ASQSTATUS_NRESP 5 +#define SI4713_CMD_TX_ASQ_STATUS 0x34 + +#define SI4713_RDSBUFF_MODE_MASK 0x87 +#define SI4713_RDSBUFF_NARGS 7 +#define SI4713_RDSBUFF_NRESP 6 +#define SI4713_CMD_TX_RDS_BUFF 0x35 + +#define SI4713_RDSPS_PSID_MASK 0x1F +#define SI4713_RDSPS_NARGS 5 +#define SI4713_RDSPS_NRESP 1 +#define SI4713_CMD_TX_RDS_PS 0x36 + +#define SI4713_CMD_GPO_CTL 0x80 +#define SI4713_CMD_GPO_SET 0x81 + +/* + * Bits from status response + */ +#define SI4713_CTS (1<<7) +#define SI4713_ERR (1<<6) +#define SI4713_RDS_INT (1<<2) +#define SI4713_ASQ_INT (1<<1) +#define SI4713_STC_INT (1<<0) + +/* + * Property definitions + */ +#define SI4713_GPO_IEN 0x0001 +#define SI4713_DIG_INPUT_FORMAT 0x0101 +#define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 +#define SI4713_REFCLK_FREQ 0x0201 +#define SI4713_REFCLK_PRESCALE 0x0202 +#define SI4713_TX_COMPONENT_ENABLE 0x2100 +#define SI4713_TX_AUDIO_DEVIATION 0x2101 +#define SI4713_TX_PILOT_DEVIATION 0x2102 +#define SI4713_TX_RDS_DEVIATION 0x2103 +#define SI4713_TX_LINE_INPUT_LEVEL 0x2104 +#define SI4713_TX_LINE_INPUT_MUTE 0x2105 +#define SI4713_TX_PREEMPHASIS 0x2106 +#define SI4713_TX_PILOT_FREQUENCY 0x2107 +#define SI4713_TX_ACOMP_ENABLE 0x2200 +#define SI4713_TX_ACOMP_THRESHOLD 0x2201 +#define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 +#define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 +#define SI4713_TX_ACOMP_GAIN 0x2204 +#define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 +#define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 +#define SI4713_TX_ASQ_LEVEL_LOW 0x2301 +#define SI4713_TX_ASQ_DURATION_LOW 0x2302 +#define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 +#define SI4713_TX_ASQ_DURATION_HIGH 0x2304 +#define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 +#define SI4713_TX_RDS_PI 0x2C01 +#define SI4713_TX_RDS_PS_MIX 0x2C02 +#define SI4713_TX_RDS_PS_MISC 0x2C03 +#define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 +#define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 +#define SI4713_TX_RDS_PS_AF 0x2C06 +#define SI4713_TX_RDS_FIFO_SIZE 0x2C07 + +#define PREEMPHASIS_USA 75 +#define PREEMPHASIS_EU 50 +#define PREEMPHASIS_DISABLED 0 +#define FMPE_USA 0x00 +#define FMPE_EU 0x01 +#define FMPE_DISABLED 0x02 + +#define POWER_UP 0x01 +#define POWER_DOWN 0x00 + +#define MAX_RDS_PTY 31 +#define MAX_RDS_DEVIATION 90000 + +/* + * PSNAME is known to be defined as 8 character sized (RDS Spec). + * However, there is receivers which scroll PSNAME 8xN sized. + */ +#define MAX_RDS_PS_NAME 96 + +/* + * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) + * character sized (RDS Spec). + * However, there is receivers which scroll them as well. + */ +#define MAX_RDS_RADIO_TEXT 384 + +#define MAX_LIMITER_RELEASE_TIME 102390 +#define MAX_LIMITER_DEVIATION 90000 + +#define MAX_PILOT_DEVIATION 90000 +#define MAX_PILOT_FREQUENCY 19000 + +#define MAX_ACOMP_RELEASE_TIME 1000000 +#define MAX_ACOMP_ATTACK_TIME 5000 +#define MAX_ACOMP_THRESHOLD 0 +#define MIN_ACOMP_THRESHOLD (-40) +#define MAX_ACOMP_GAIN 20 + +#define SI4713_NUM_SUPPLIES 2 + +/* + * si4713_device - private data + */ +struct si4713_device { + /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ + struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrl_handler; + /* private data structures */ + struct { /* si4713 control cluster */ + /* This is one big cluster since the mute control + * powers off the device and after unmuting again all + * controls need to be set at once. The only way of doing + * that is by making it one big cluster. */ + struct v4l2_ctrl *mute; + struct v4l2_ctrl *rds_ps_name; + struct v4l2_ctrl *rds_radio_text; + struct v4l2_ctrl *rds_pi; + struct v4l2_ctrl *rds_deviation; + struct v4l2_ctrl *rds_pty; + struct v4l2_ctrl *compression_enabled; + struct v4l2_ctrl *compression_threshold; + struct v4l2_ctrl *compression_gain; + struct v4l2_ctrl *compression_attack_time; + struct v4l2_ctrl *compression_release_time; + struct v4l2_ctrl *pilot_tone_enabled; + struct v4l2_ctrl *pilot_tone_freq; + struct v4l2_ctrl *pilot_tone_deviation; + struct v4l2_ctrl *limiter_enabled; + struct v4l2_ctrl *limiter_deviation; + struct v4l2_ctrl *limiter_release_time; + struct v4l2_ctrl *tune_preemphasis; + struct v4l2_ctrl *tune_pwr_level; + struct v4l2_ctrl *tune_ant_cap; + }; + struct completion work; + struct regulator_bulk_data supplies[SI4713_NUM_SUPPLIES]; + int gpio_reset; + u32 power_state; + u32 rds_enabled; + u32 frequency; + u32 preemphasis; + u32 stereo; + u32 tune_rnl; +}; +#endif /* ifndef SI4713_I2C_H */ -- cgit v0.10.2 From 5173332c1e420d49829bd7e4bc5c83b0205037c5 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:38 -0300 Subject: [media] si4713: Modified i2c driver to handle cases where interrupts are not used Checks have been introduced at several places in the code to test if an interrupt is set or not. For devices which do not use the interrupt, to get a valid response, within a specified timeout, the device is polled instead. Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 4f3308f..5d26b9a 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -27,11 +27,11 @@ #include #include #include -#include #include #include #include #include +#include #include "si4713.h" @@ -213,6 +213,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 +229,39 @@ 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; + } + + DBG_BUFFER(&sdev->sd, "Response", response, respn); + if (!check_command_failed(response[0])) + return 0; - return 0; + if (client->irq) + return -EBUSY; + msleep(1); + } while (jiffies <= until_jiffies); + + return -EBUSY; } /* @@ -344,14 +354,15 @@ 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 */ - 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, }; @@ -369,6 +380,9 @@ static int si4713_powerup(struct si4713_device *sdev) 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,7 +394,8 @@ 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)) @@ -465,33 +480,39 @@ 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 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. */ + msleep(3); + } } /* @@ -1024,7 +1045,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; -- cgit v0.10.2 From d808f5bc728186ad7415dfaffa3c04d2e73ae027 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:39 -0300 Subject: [media] si4713: Reorganized includes in si4713.c/h Moved the header from si4713.c to si4713.h Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 5d26b9a..096947c7 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "si4713.h" diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h index 25cdea2..1410cd2 100644 --- a/drivers/media/radio/si4713/si4713.h +++ b/drivers/media/radio/si4713/si4713.h @@ -15,6 +15,7 @@ #ifndef SI4713_I2C_H #define SI4713_I2C_H +#include #include #include #include -- cgit v0.10.2 From e26fa6dccf9abb372963b766335611f5eb670891 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:40 -0300 Subject: [media] si4713: Bug fix for si4713_tx_tune_power() method in the i2c driver In the si4713_tx_tune_power() method, the args array element 'power' can take values between SI4713_MIN_POWER and SI4713_MAX_POWER. power = 0 is also valid. All the values (0 > power < SI4713_MIN_POWER) are illegal and hence are all mapped to SI4713_MIN_POWER. Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 096947c7..4931325 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -555,14 +555,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 + * 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, @@ -576,16 +576,16 @@ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, * .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, @@ -1462,9 +1462,9 @@ 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; -- cgit v0.10.2 From adc232592337d3ac4c5473ba8bdaf7c202bf215d Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:41 -0300 Subject: [media] si4713: HID blacklist Si4713 USB development board The Si4713 development board contains a Si4713 FM transmitter chip and is handled by the radio-usb-si4713 driver. The board reports itself as (10c4:8244) Cygnal Integrated Products, Inc. and misidentifies itself as a HID device in its USB interface descriptor. This patch ignores this device as an HID device and hence loads the custom driver. Signed-off-by: Dinesh Ram Signed-off-by: Jiri Kosina Acked-by: Jiri Kosina Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8c10f27..369a150 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2117,6 +2117,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) }, { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7655962..beee6d6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -241,6 +241,8 @@ #define USB_VENDOR_ID_CYGNAL 0x10c4 #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a +#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244 + #define USB_VENDOR_ID_CYPRESS 0x04b4 #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 #define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 -- cgit v0.10.2 From b874b39fcd2fbfec59b78c1c4f958a659d63579e Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:42 -0300 Subject: [media] si4713: Added the USB driver for Si4713 This is the USB driver for the Silicon Labs development board. It contains the Si4713 FM transmitter chip. Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig index ec640b8..a7c3ba8 100644 --- a/drivers/media/radio/si4713/Kconfig +++ b/drivers/media/radio/si4713/Kconfig @@ -1,3 +1,18 @@ +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 diff --git a/drivers/media/radio/si4713/Makefile b/drivers/media/radio/si4713/Makefile index a8c1194..ddaaf92 100644 --- a/drivers/media/radio/si4713/Makefile +++ b/drivers/media/radio/si4713/Makefile @@ -3,4 +3,5 @@ # 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/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c new file mode 100644 index 0000000..d978844 --- /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 +#include +#include +#include +#include +#include +#include +#include +/* V4l includes */ +#include +#include +#include +#include +#include +#include + +#include "si4713.h" + +/* driver and module definitions */ +MODULE_AUTHOR("Dinesh Ram "); +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 + */ +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 + */ +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, +}; + +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); -- cgit v0.10.2 From 99995dedea0e956ff58692fd795b8a8a0669e3a5 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:43 -0300 Subject: [media] si4713: Added MAINTAINERS entry for radio-usb-si4713 driver Hans Verkuil will maintain the USB driver for si4713 Signed-off-by: Dinesh Ram Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 8285ed4..c540ed6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7621,7 +7621,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://linuxtv.org S: Odd Fixes -F: drivers/media/radio/si4713-i2c.? +F: drivers/media/radio/si4713/si4713.? SI4713 FM RADIO TRANSMITTER PLATFORM DRIVER M: Eduardo Valentin @@ -7629,7 +7629,15 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://linuxtv.org S: Odd Fixes -F: drivers/media/radio/radio-si4713.c +F: drivers/media/radio/si4713/radio-platform-si4713.c + +SI4713 FM RADIO TRANSMITTER USB DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/si4713/radio-usb-si4713.c SIANO DVB DRIVER M: Mauro Carvalho Chehab -- cgit v0.10.2 From cc6d618fdf56df389e46be2f0c9f2d1579d8b9e6 Mon Sep 17 00:00:00 2001 From: Dinesh Ram Date: Tue, 15 Oct 2013 12:24:44 -0300 Subject: [media] si4713: move supply list to si4713_platform_data The supply list is needed by the platform driver, but not by the usb driver. So this information belongs to the platform data and should not be hardcoded in the subdevice driver. Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index f093af17..8760bbe 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -760,7 +760,14 @@ static struct regulator_init_data rx51_vintdig = { }, }; +static const char * const si4713_supply_names[] = { + "vio", + "vdd", +}; + static struct si4713_platform_data rx51_si4713_i2c_data __initdata_or_module = { + .supplies = ARRAY_SIZE(si4713_supply_names), + .supply_names = si4713_supply_names, .gpio_reset = RX51_FMTX_RESET_GPIO, }; diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 4931325..097d4e0 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -44,11 +44,6 @@ MODULE_AUTHOR("Eduardo Valentin "); 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 @@ -368,11 +363,12 @@ static int si4713_powerup(struct si4713_device *sdev) 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); @@ -396,11 +392,12 @@ static int si4713_powerup(struct si4713_device *sdev) 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); @@ -432,11 +429,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; } @@ -1381,13 +1380,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; @@ -1500,7 +1500,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); @@ -1524,7 +1524,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/si4713.h b/drivers/media/radio/si4713/si4713.h index 1410cd2..4837cf6 100644 --- a/drivers/media/radio/si4713/si4713.h +++ b/drivers/media/radio/si4713/si4713.h @@ -227,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/include/media/si4713.h b/include/media/si4713.h index ed7353e..f98a0a7 100644 --- a/include/media/si4713.h +++ b/include/media/si4713.h @@ -23,6 +23,8 @@ * Platform dependent definition */ struct si4713_platform_data { + const char * const *supply_names; + unsigned supplies; int gpio_reset; /* < 0 if not used */ }; -- cgit v0.10.2 From 8aab72bb0797321c6f6867f240da1e8c28bf040c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 Oct 2013 12:24:45 -0300 Subject: [media] si4713: si4713_set_rds_radio_text overwrites terminating \0 si4713_set_rds_radio_text will overwrite the terminating zero at the end of the rds radio text string in order to send out a carriage return as per the RDS spec. Use a separate char buffer for the CR instead of corrupting the control string. Signed-off-by: Hans Verkuil Tested-by: Eduardo Valentin Acked-by: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 097d4e0..17e0106 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -831,8 +831,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; @@ -856,7 +857,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; } -- cgit v0.10.2 From b2c6eedb1f252fe7ee55c27394023444244ac4ed Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Tue, 5 Nov 2013 11:18:43 -0300 Subject: [media] si4713: print product number Print the PN value, useful to check what chip the dev board has. Signed-off-by: Eduardo Valentin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 17e0106..6f28a2b 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -464,7 +464,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; -- cgit v0.10.2 From 9ca33ccd539e67f6042d813146bf1b65605fa5c4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Dec 2013 06:48:49 -0300 Subject: [media] si4713: coding style whitespace cleanups Fix most whitespace-related checkpatch errors/warnings. Signed-off-by: Hans Verkuil Acked-by: Eduardo Valentin Acked-by: Dinesh Ram Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 6f28a2b..4c3498f 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -50,12 +50,12 @@ MODULE_VERSION("0.0.1"); #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 @@ -269,9 +269,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, @@ -306,11 +306,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, @@ -352,8 +352,8 @@ static int si4713_powerup(struct si4713_device *sdev) 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 */ u8 args[SI4713_PWUP_NARGS] = { SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, @@ -515,8 +515,8 @@ static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) /* * 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) */ @@ -525,9 +525,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, @@ -555,11 +555,11 @@ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) /* * 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. + * 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 - 120 dBuV, unit/step 1 dB) * @antcap: value of antenna tuning capacitor (0 - 191) @@ -570,10 +570,10 @@ 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 */ u8 args[SI4713_TXPWR_NARGS] = { 0x00, @@ -602,12 +602,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) @@ -618,10 +618,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, @@ -651,11 +651,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 @@ -670,7 +670,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, @@ -1364,7 +1364,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; @@ -1440,8 +1440,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, @@ -1463,9 +1463,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, SI4713_MAX_POWER, 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, SI4713_MAX_ANTCAP, 1, 0); + V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, SI4713_MAX_ANTCAP, + 1, 0); if (hdl->error) { rval = hdl->error; -- cgit v0.10.2 From cb15da3636b038a9a60354b838cb37d8cab48dfe Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Dec 2013 06:48:49 -0300 Subject: [media] si4713: coding style time-related cleanups Fix the non-whitespace checkpatch errors/warnings. Replace msleep with usleep_range and the jiffies comparison with time_is_after_jiffies(). Signed-off-by: Hans Verkuil Acked-by: Eduardo Valentin Acked-by: Dinesh Ram Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 4c3498f..07d5153 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -252,8 +252,11 @@ static int si4713_send_command(struct si4713_device *sdev, const u8 command, if (client->irq) return -EBUSY; - msleep(1); - } while (jiffies <= until_jiffies); + if (usecs <= 1000) + usleep_range(usecs, 1000); + else + usleep_range(1000, 2000); + } while (time_is_after_jiffies(until_jiffies)); return -EBUSY; } @@ -505,11 +508,11 @@ static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) } if (jiffies_to_usecs(jiffies - start_jiffies) > usecs) return err < 0 ? err : -EIO; - /* We sleep here for 3 ms in order to avoid flooding the device + /* 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. */ - msleep(3); + usleep_range(3000, 4000); } } -- cgit v0.10.2 From 1426f61b600d632c57850b97198b4446d7a97aed Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 24 Oct 2013 04:27:11 -0300 Subject: [media] v4l: atmel-isi: remove SOF wait in start_streaming() when a userspace applications calls the VIDIOC_STREAMON ioctl. The V4L2 core calls the soc_camera_streamon function, which is responsible for starting the video stream. It does so by first starting the atmel-isi host by a call to the vb2_streamon function, and then starting the sensor by a call to the video.s_stream sensor subdev operation. That means we wait for a SOF in start_streaming() before call sensor's s_stream(). It is possible no VSYNC interrupt arrive as the sensor hasn't been started yet. To avoid such case, this patch remove the code to wait for the VSYNC interrupt. And such code is not necessary. Reported-by: Laurent Pinchart Signed-off-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 1044856..b46c0ed 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; @@ -207,12 +195,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); } @@ -407,43 +389,17 @@ 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 */ + /* Clear any pending 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; - } - spin_lock_irq(&isi->lock); - isi->state = ISI_STATE_WAIT_SOF; - isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC); 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 */ @@ -965,7 +921,6 @@ static int atmel_isi_probe(struct platform_device *pdev) 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); -- cgit v0.10.2 From c52c0cbfa70c3fbdbe4d592f4e2d80a1102dfa19 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 25 Nov 2013 12:13:50 -0300 Subject: [media] v4l: atmel-isi: Use devm_* managed allocators This simplifies error and cleanup code paths. Signed-off-by: Laurent Pinchart Acked-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index b46c0ed..faa7f8d 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -862,7 +862,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, @@ -870,12 +869,8 @@ 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; } @@ -884,7 +879,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; @@ -898,26 +892,20 @@ static int atmel_isi_probe(struct platform_device *pdev) 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); + + ret = clk_prepare(isi->pclk); + if (ret) + return ret; + isi->pdata = pdata; isi->active = NULL; spin_lock_init(&isi->lock); @@ -925,11 +913,11 @@ static int atmel_isi_probe(struct platform_device *pdev) INIT_LIST_HEAD(&isi->dma_desc_head); /* Get ISI_MCK, provided by programmable clock or external clock */ - isi->mck = clk_get(dev, "isi_mck"); + isi->mck = devm_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; + goto err_clk_get_mck; } ret = clk_prepare(isi->mck); @@ -964,9 +952,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; } @@ -983,7 +972,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; @@ -1005,9 +994,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: @@ -1019,13 +1006,8 @@ 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); +err_clk_get_mck: + clk_unprepare(isi->pclk); return ret; } -- cgit v0.10.2 From c01d568e7f835ed429145670e8876fddff2f99ba Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 25 Nov 2013 12:21:33 -0300 Subject: [media] v4l: atmel-isi: Defer clock (un)preparation to enable/disable time The PCLK and MCK clocks are prepared and unprepared at probe and remove time. Clock (un)preparation isn't needed before enabling/disabling the clocks, and the enable/disable operation happen in non-atomic context. We can thus defer (un)preparation to enable/disable time. Signed-off-by: Laurent Pinchart Acked-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index faa7f8d..ea8816c 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -721,13 +721,13 @@ 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); + ret = clk_prepare_enable(isi->mck); if (ret) { - clk_disable(isi->pclk); + clk_disable_unprepare(isi->pclk); return ret; } @@ -739,8 +739,8 @@ 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); + clk_disable_unprepare(isi->mck); + clk_disable_unprepare(isi->pclk); } static unsigned int isi_camera_poll(struct file *file, poll_table *pt) @@ -869,9 +869,6 @@ static int atmel_isi_remove(struct platform_device *pdev) isi->p_fb_descriptors, isi->fb_descriptors_phys); - clk_unprepare(isi->mck); - clk_unprepare(isi->pclk); - return 0; } @@ -902,10 +899,6 @@ static int atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(isi->pclk)) return PTR_ERR(isi->pclk); - ret = clk_prepare(isi->pclk); - if (ret) - return ret; - isi->pdata = pdata; isi->active = NULL; spin_lock_init(&isi->lock); @@ -916,27 +909,21 @@ static int atmel_isi_probe(struct platform_device *pdev) isi->mck = devm_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_mck; + return PTR_ERR(isi->mck); } - 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; + return ret; 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++) { @@ -1002,12 +989,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: -err_clk_get_mck: - clk_unprepare(isi->pclk); return ret; } -- cgit v0.10.2 From c768626479187f1fcc3697e5f150b7af259c657c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 2 Nov 2013 21:25:06 -0300 Subject: [media] v4l: atmel-isi: Reset the ISI when starting the stream The queue setup operation isn't the right place to reset the ISI. Move the reset call to the start streaming operation. Signed-off-by: Laurent Pinchart Acked-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index ea8816c..ae2c8c1 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -241,16 +241,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; @@ -390,6 +380,16 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; u32 sr = 0; + 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); spin_lock_irq(&isi->lock); /* Clear any pending interrupt */ -- cgit v0.10.2 From f389e89c35fbfe17ffcb94378c225c933c28bdb2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 22 Nov 2013 23:06:54 -0300 Subject: [media] v4l: atmel-isi: Make the MCK clock optional 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. Signed-off-by: Laurent Pinchart Acked-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index ae2c8c1..3e8d412 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -725,10 +725,12 @@ static int isi_camera_clock_start(struct soc_camera_host *ici) if (ret) return ret; - ret = clk_prepare_enable(isi->mck); - if (ret) { - clk_disable_unprepare(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; @@ -739,7 +741,8 @@ static void isi_camera_clock_stop(struct soc_camera_host *ici) { struct atmel_isi *isi = ici->priv; - clk_disable_unprepare(isi->mck); + if (!IS_ERR(isi->mck)) + clk_disable_unprepare(isi->mck); clk_disable_unprepare(isi->pclk); } @@ -883,7 +886,7 @@ 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; @@ -905,18 +908,21 @@ static int atmel_isi_probe(struct platform_device *pdev) 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 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)) { - dev_err(dev, "Failed to get isi_mck\n"); - return PTR_ERR(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; } - /* 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; - isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, &isi->fb_descriptors_phys, -- cgit v0.10.2 From 135983e8b392b424c7c9e117a981ad21e7c26893 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 4 Dec 2013 13:19:11 -0300 Subject: [media] v4l: atmel-isi: Fix color component ordering The ISI_CFG2.YCC_SWAP field controls color component ordering. The datasheet lists the following orderings for the memory formats. YCC_SWAP Byte 0 Byte 1 Byte 2 Byte 3 00: Default Cb(i) Y(i) Cr(i) Y(i+1) 01: Mode1 Cr(i) Y(i) Cb(i) Y(i+1) 10: Mode2 Y(i) Cb(i) Y(i+1) Cr(i) 11: Mode3 Y(i) Cr(i) Y(i+1) Cb(i) This is based on a sensor format set to CbYCrY (UYVY). The driver hardcodes the output memory format to YUYV, configure the ordering accordingly. Signed-off-by: Laurent Pinchart Acked-by: Josh Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 3e8d412..9c4cadc 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -112,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 */ -- cgit v0.10.2 From bd6f27458b0c50469ba1bb0a53e5ad1ac9e950d5 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 10 Dec 2013 09:25:47 -0300 Subject: [media] v4l: atmel-isi: Should clear bits before set the hardware register In the ISI driver it reads the config register to get original value, then set the correct FRATE_DIV and YCC_SWAP_MODE directly. This will cause some bits overlap. So we need to clear these bits first, then set correct value. This patch fix it. Signed-off-by: Josh Wu Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 9c4cadc..4835173 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -132,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); @@ -346,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; diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h index 6568230..2b02347 100644 --- a/include/media/atmel-isi.h +++ b/include/media/atmel-isi.h @@ -56,6 +56,7 @@ #define ISI_CFG1_FRATE_DIV_6 (5 << 8) #define ISI_CFG1_FRATE_DIV_7 (6 << 8) #define ISI_CFG1_FRATE_DIV_8 (7 << 8) +#define ISI_CFG1_FRATE_DIV_MASK (7 << 8) #define ISI_CFG1_DISCR (1 << 11) #define ISI_CFG1_FULL_MODE (1 << 12) @@ -66,6 +67,7 @@ #define ISI_CFG2_YCC_SWAP_MODE_1 (1 << 28) #define ISI_CFG2_YCC_SWAP_MODE_2 (2 << 28) #define ISI_CFG2_YCC_SWAP_MODE_3 (3 << 28) +#define ISI_CFG2_YCC_SWAP_MODE_MASK (3 << 28) #define ISI_CFG2_IM_VSIZE_OFFSET 0 #define ISI_CFG2_IM_HSIZE_OFFSET 16 #define ISI_CFG2_IM_VSIZE_MASK (0x7FF << ISI_CFG2_IM_VSIZE_OFFSET) -- cgit v0.10.2 From 9f1d0bdab24f536f82041fe2338f24b3b08d08a1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 25 Feb 2013 08:19:04 -0300 Subject: [media] em28xx: add support for Empia EM28178 New chip version, which is very similar than EM28174. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6454309..5da60da 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3054,6 +3054,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; diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index fc82540..f6076a5 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -501,8 +501,10 @@ 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) { + 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; @@ -641,7 +643,8 @@ 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 */ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index e0acc92..5ec6a4e 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -442,6 +442,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", @@ -734,6 +735,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; diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 0e04778..b769ceb 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -245,6 +245,7 @@ enum em28xx_chip_id { CHIP_ID_EM2874 = 65, CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, + CHIP_ID_EM28178 = 114, }; /* -- cgit v0.10.2 From c0ec1c4dd7d6b2bfb1eca116f9df4578d9193623 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 25 Feb 2013 08:24:18 -0300 Subject: [media] a8293: add small sleep in order to settle LNB voltage PCTV 461e requires that small delay. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 74fbb5d..780da58 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); -- cgit v0.10.2 From 3f8965e0e04e3b5d7dd221c3407ce86690e91a5d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 11 Dec 2013 09:33:03 -0300 Subject: [media] v4l: sh_vou: Fix warnings due to improper casts and printk formats Use the %zu and %pad printk specifiers to print size_t and dma_addr_t variables. This fixes warnings on platforms where dma_addr_t has a different size than int. Cc: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 4f30341..65c9f18 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; } -- cgit v0.10.2 From 17f335c304ac19d9b11814238fe8a7519d80e2ff Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 12 Dec 2013 16:38:51 -0300 Subject: [media] it913x: Add support for Avermedia H335 id 0x0335 Trivial USB ID addition for Avermedia H335. Signed-off-by: Malcolm Priestley Cc: stable@vger.kernel.org # v3.11+ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 4a53454..8407178 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 diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 1cb6899..fe95a58 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 */ }; -- cgit v0.10.2 From 0149a2e1498dba7937dd14bf924dedd6f0c15587 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Dec 2013 08:58:37 -0300 Subject: [media] media: Include linux/kernel.h for DIV_ROUND_UP() DIV_ROUND_UP() is defined in kernel.h which was not included by media-entity.h. Do exactly that. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 10df551..e004591 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -24,6 +24,7 @@ #define _MEDIA_ENTITY_H #include +#include #include #include -- cgit v0.10.2 From 567e2e9660c71372e8bd50efa9d37a0281e4abe1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 08:06:07 -0300 Subject: [media] si470x: don't use buffer on the stack for USB transfers You shouldn't use buffers allocated on the stack for USB transfers, always kmalloc them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index d6d4d60..cd74025 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); @@ -743,6 +746,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 467e955..4b76604 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; -- cgit v0.10.2 From 5df2def55073596f6f50eb3337ece6a4a60dfcf9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 08:06:38 -0300 Subject: [media] si470x: add check to test if this is really a si470x Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index cd74025..07ef405 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -635,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"); -- cgit v0.10.2 From 21326c461e10431767e817e858e66113336d361c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 08:51:25 -0300 Subject: [media] radio-raremono: add support for 'Thanko's Raremono' AM/FM/SW USB device Signed-off-by: Hans Verkuil Cc: Dinesh Ram Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index e92cec6..192f36f2 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -129,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 eb1a3a0..120e791 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -32,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 0000000..7b3bdbb --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * '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 and finished by Hans Verkuil + * . + * + * 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 "); +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); -- cgit v0.10.2 From 5313ba662b8eacec4a42abc9dd3df1cd0bf9c9ac Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 09:00:38 -0300 Subject: [media] MAINTAINERS: add entry for new radio-raremono radio driver Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index c540ed6..a00f661 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8488,6 +8488,14 @@ L: linux-xtensa@linux-xtensa.org S: Maintained F: arch/xtensa/ +THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-raremono.c + THERMAL M: Zhang Rui M: Eduardo Valentin -- cgit v0.10.2 From 9f7b62d9f961e0a839de7dc5547db0f5a612cfd4 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 18 Dec 2013 09:32:50 -0300 Subject: [media] s5p-jpeg: Split jpeg-hw.h to jpeg-hw-s5p.c and jpeg-hw-s5p.c Move function definitions from jpeg-hw.h to jpeg-hw-s5p.c, add "s5p" prefix and put function declarations in the jpeg-hw-s5p.h. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile index d18cb5e..faf6398 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-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 447022f..65922d9 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -29,7 +29,7 @@ #include #include "jpeg-core.h" -#include "jpeg-hw.h" +#include "jpeg-hw-s5p.h" static struct s5p_jpeg_fmt formats_enc[] = { { @@ -951,34 +951,36 @@ static void s5p_jpeg_device_run(void *priv) 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 @@ -987,31 +989,31 @@ static void s5p_jpeg_device_run(void *priv) 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); } @@ -1203,22 +1205,23 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) 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; @@ -1230,10 +1233,10 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) v4l2_m2m_buf_done(dst_buf, state); 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; } diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 4a4776b..7baadf3 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -42,10 +42,15 @@ #define EOI 0xd9 #define DHP 0xde +#define S5P_JPEG_ENCODE 0 +#define S5P_JPEG_DECODE 1 + /* Flags that indicate a format can be used for capture/output */ #define MEM2MEM_CAPTURE (1 << 0) #define MEM2MEM_OUTPUT (1 << 1) + + /** * struct s5p_jpeg - JPEG IP abstraction * @lock: the mutex protecting this structure diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c new file mode 100644 index 0000000..52407d7 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -0,0 +1,343 @@ +/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 +#include + +#include "jpeg-core.h" +#include "jpeg-regs.h" +#include "jpeg-hw-s5p.h" + +void s5p_jpeg_reset(void __iomem *regs) +{ + unsigned long reg; + + writel(1, regs + S5P_JPG_SW_RESET); + reg = readl(regs + S5P_JPG_SW_RESET); + /* no other way but polling for when JPEG IP becomes operational */ + while (reg != 0) { + cpu_relax(); + reg = readl(regs + S5P_JPG_SW_RESET); + } +} + +void s5p_jpeg_poweron(void __iomem *regs) +{ + writel(S5P_POWER_ON, regs + S5P_JPGCLKCON); +} + +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) +{ + unsigned long reg, m; + + m = S5P_MOD_SEL_565; + if (mode == S5P_JPEG_RAW_IN_565) + m = S5P_MOD_SEL_565; + else if (mode == S5P_JPEG_RAW_IN_422) + m = S5P_MOD_SEL_422; + + reg = readl(regs + S5P_JPGCMOD); + reg &= ~S5P_MOD_SEL_MASK; + reg |= m; + writel(reg, regs + S5P_JPGCMOD); +} + +void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGCMOD); + if (y16) + reg |= S5P_MODE_Y16; + else + reg &= ~S5P_MODE_Y16_MASK; + writel(reg, regs + S5P_JPGCMOD); +} + +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode) +{ + unsigned long reg, m; + + m = S5P_PROC_MODE_DECOMPR; + if (mode == S5P_JPEG_ENCODE) + m = S5P_PROC_MODE_COMPR; + else + m = S5P_PROC_MODE_DECOMPR; + reg = readl(regs + S5P_JPGMOD); + reg &= ~S5P_PROC_MODE_MASK; + reg |= m; + writel(reg, regs + S5P_JPGMOD); +} + +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +{ + unsigned long reg, m; + + if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) + m = S5P_SUBSAMPLING_MODE_420; + else + m = S5P_SUBSAMPLING_MODE_422; + + reg = readl(regs + S5P_JPGMOD); + reg &= ~S5P_SUBSAMPLING_MODE_MASK; + reg |= m; + writel(reg, regs + S5P_JPGMOD); +} + +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; +} + +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGDRI_U); + reg &= ~0xff; + reg |= (dri >> 8) & 0xff; + writel(reg, regs + S5P_JPGDRI_U); + + reg = readl(regs + S5P_JPGDRI_L); + reg &= ~0xff; + reg |= dri & 0xff; + writel(reg, regs + S5P_JPGDRI_L); +} + +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_QTBL); + reg &= ~S5P_QT_NUMt_MASK(t); + reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t); + writel(reg, regs + S5P_JPG_QTBL); +} + +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_HTBL); + reg &= ~S5P_HT_NUMt_AC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t); + writel(reg, regs + S5P_JPG_HTBL); +} + +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_HTBL); + reg &= ~S5P_HT_NUMt_DC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t); + writel(reg, regs + S5P_JPG_HTBL); +} + +void s5p_jpeg_y(void __iomem *regs, unsigned int y) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGY_U); + reg &= ~0xff; + reg |= (y >> 8) & 0xff; + writel(reg, regs + S5P_JPGY_U); + + reg = readl(regs + S5P_JPGY_L); + reg &= ~0xff; + reg |= y & 0xff; + writel(reg, regs + S5P_JPGY_L); +} + +void s5p_jpeg_x(void __iomem *regs, unsigned int x) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGX_U); + reg &= ~0xff; + reg |= (x >> 8) & 0xff; + writel(reg, regs + S5P_JPGX_U); + + reg = readl(regs + S5P_JPGX_L); + reg &= ~0xff; + reg |= x & 0xff; + writel(reg, regs + S5P_JPGX_L); +} + +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_RSTm_INT_EN_MASK; + if (enable) + reg |= S5P_RSTm_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_DATA_NUM_INT_EN_MASK; + if (enable) + reg |= S5P_DATA_NUM_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK; + if (enbl) + reg |= S5P_FINAL_MCU_NUM_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_TIMER_SE); + reg |= S5P_TIMER_INT_EN; + reg &= ~S5P_TIMER_INIT_MASK; + reg |= val & S5P_TIMER_INIT_MASK; + writel(reg, regs + S5P_JPG_TIMER_SE); +} + +void s5p_jpeg_timer_disable(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_TIMER_SE); + reg &= ~S5P_TIMER_INT_EN_MASK; + writel(reg, regs + S5P_JPG_TIMER_SE); +} + +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); +} + +void s5p_jpeg_clear_timer_stat(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_TIMER_SE); + reg &= ~S5P_TIMER_INT_STAT_MASK; + writel(reg, regs + S5P_JPG_TIMER_SE); +} + +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); + reg &= ~S5P_ENC_STREAM_BOUND_MASK; + reg |= S5P_ENC_STREAM_INT_EN; + reg |= size & S5P_ENC_STREAM_BOUND_MASK; + writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); +} + +int s5p_jpeg_enc_stream_stat(void __iomem *regs) +{ + return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) & + S5P_ENC_STREAM_INT_STAT_MASK); +} + +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); + reg &= ~S5P_ENC_STREAM_INT_MASK; + writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); +} + +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format) +{ + unsigned long reg, f; + + f = S5P_DEC_OUT_FORMAT_422; + if (format == S5P_JPEG_RAW_OUT_422) + f = S5P_DEC_OUT_FORMAT_422; + else if (format == S5P_JPEG_RAW_OUT_420) + f = S5P_DEC_OUT_FORMAT_420; + reg = readl(regs + S5P_JPG_OUTFORM); + reg &= ~S5P_DEC_OUT_FORMAT_MASK; + reg |= f; + writel(reg, regs + S5P_JPG_OUTFORM); +} + +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr) +{ + writel(addr, regs + S5P_JPG_JPGADR); +} + +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr) +{ + writel(addr, regs + S5P_JPG_IMGADR); +} + +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, + unsigned int j, unsigned int coef) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_COEF(i)); + reg &= ~S5P_COEFn_MASK(j); + reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j); + writel(reg, regs + S5P_JPG_COEF(i)); +} + +void s5p_jpeg_start(void __iomem *regs) +{ + writel(1, regs + S5P_JSTART); +} + +int s5p_jpeg_result_stat_ok(void __iomem *regs) +{ + return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK) + >> S5P_RESULT_STAT_SHIFT); +} + +int s5p_jpeg_stream_stat_ok(void __iomem *regs) +{ + return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK) + >> S5P_STREAM_STAT_SHIFT); +} + +void s5p_jpeg_clear_int(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTST); + writel(S5P_INT_RELEASE, regs + S5P_JPGCOM); + reg = readl(regs + S5P_JPGOPR); +} + +unsigned int s5p_jpeg_compressed_size(void __iomem *regs) +{ + unsigned long jpeg_size = 0; + + jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16; + jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8; + jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff); + + return (unsigned int)jpeg_size; +} 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 0000000..c11ebe8 --- /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 + * + * 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 +#include + +#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-hw.h b/drivers/media/platform/s5p-jpeg/jpeg-hw.h deleted file mode 100644 index b47e887..0000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw.h +++ /dev/null @@ -1,357 +0,0 @@ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - * - * 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_H_ -#define JPEG_HW_H_ - -#include -#include - -#include "jpeg-hw.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_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) -{ - unsigned long reg; - - writel(1, regs + S5P_JPG_SW_RESET); - reg = readl(regs + S5P_JPG_SW_RESET); - /* no other way but polling for when JPEG IP becomes operational */ - while (reg != 0) { - cpu_relax(); - reg = readl(regs + S5P_JPG_SW_RESET); - } -} - -static inline void 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) -{ - unsigned long reg, m; - - m = S5P_MOD_SEL_565; - if (mode == S5P_JPEG_RAW_IN_565) - m = S5P_MOD_SEL_565; - else if (mode == S5P_JPEG_RAW_IN_422) - m = S5P_MOD_SEL_422; - - reg = readl(regs + S5P_JPGCMOD); - reg &= ~S5P_MOD_SEL_MASK; - reg |= m; - writel(reg, regs + S5P_JPGCMOD); -} - -static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGCMOD); - if (y16) - reg |= S5P_MODE_Y16; - else - reg &= ~S5P_MODE_Y16_MASK; - writel(reg, regs + S5P_JPGCMOD); -} - -static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) -{ - unsigned long reg, m; - - m = S5P_PROC_MODE_DECOMPR; - if (mode == S5P_JPEG_ENCODE) - m = S5P_PROC_MODE_COMPR; - else - m = S5P_PROC_MODE_DECOMPR; - reg = readl(regs + S5P_JPGMOD); - reg &= ~S5P_PROC_MODE_MASK; - reg |= m; - writel(reg, regs + S5P_JPGMOD); -} - -static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) -{ - unsigned long reg, m; - - if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) - m = S5P_SUBSAMPLING_MODE_420; - else - m = S5P_SUBSAMPLING_MODE_422; - - reg = readl(regs + S5P_JPGMOD); - reg &= ~S5P_SUBSAMPLING_MODE_MASK; - reg |= m; - writel(reg, regs + S5P_JPGMOD); -} - -static inline unsigned int 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) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGDRI_U); - reg &= ~0xff; - reg |= (dri >> 8) & 0xff; - writel(reg, regs + S5P_JPGDRI_U); - - reg = readl(regs + S5P_JPGDRI_L); - reg &= ~0xff; - reg |= dri & 0xff; - writel(reg, regs + S5P_JPGDRI_L); -} - -static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_QTBL); - reg &= ~S5P_QT_NUMt_MASK(t); - reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t); - writel(reg, regs + S5P_JPG_QTBL); -} - -static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_HTBL); - reg &= ~S5P_HT_NUMt_AC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t); - writel(reg, regs + S5P_JPG_HTBL); -} - -static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_HTBL); - reg &= ~S5P_HT_NUMt_DC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t); - writel(reg, regs + S5P_JPG_HTBL); -} - -static inline void jpeg_y(void __iomem *regs, unsigned int y) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGY_U); - reg &= ~0xff; - reg |= (y >> 8) & 0xff; - writel(reg, regs + S5P_JPGY_U); - - reg = readl(regs + S5P_JPGY_L); - reg &= ~0xff; - reg |= y & 0xff; - writel(reg, regs + S5P_JPGY_L); -} - -static inline void jpeg_x(void __iomem *regs, unsigned int x) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGX_U); - reg &= ~0xff; - reg |= (x >> 8) & 0xff; - writel(reg, regs + S5P_JPGX_U); - - reg = readl(regs + S5P_JPGX_L); - reg &= ~0xff; - reg |= x & 0xff; - writel(reg, regs + S5P_JPGX_L); -} - -static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_RSTm_INT_EN_MASK; - if (enable) - reg |= S5P_RSTm_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_DATA_NUM_INT_EN_MASK; - if (enable) - reg |= S5P_DATA_NUM_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK; - if (enbl) - reg |= S5P_FINAL_MCU_NUM_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg |= S5P_TIMER_INT_EN; - reg &= ~S5P_TIMER_INIT_MASK; - reg |= val & S5P_TIMER_INIT_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - -static inline void jpeg_timer_disable(void __iomem *regs) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg &= ~S5P_TIMER_INT_EN_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - -static inline int 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) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg &= ~S5P_TIMER_INT_STAT_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - -static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); - reg &= ~S5P_ENC_STREAM_BOUND_MASK; - reg |= S5P_ENC_STREAM_INT_EN; - reg |= size & S5P_ENC_STREAM_BOUND_MASK; - writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); -} - -static inline int 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) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); - reg &= ~S5P_ENC_STREAM_INT_MASK; - writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); -} - -static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format) -{ - unsigned long reg, f; - - f = S5P_DEC_OUT_FORMAT_422; - if (format == S5P_JPEG_RAW_OUT_422) - f = S5P_DEC_OUT_FORMAT_422; - else if (format == S5P_JPEG_RAW_OUT_420) - f = S5P_DEC_OUT_FORMAT_420; - reg = readl(regs + S5P_JPG_OUTFORM); - reg &= ~S5P_DEC_OUT_FORMAT_MASK; - reg |= f; - writel(reg, regs + S5P_JPG_OUTFORM); -} - -static inline void 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) -{ - writel(addr, regs + S5P_JPG_IMGADR); -} - -static inline void jpeg_coef(void __iomem *regs, unsigned int i, - unsigned int j, unsigned int coef) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_COEF(i)); - reg &= ~S5P_COEFn_MASK(j); - reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j); - writel(reg, regs + S5P_JPG_COEF(i)); -} - -static inline void jpeg_start(void __iomem *regs) -{ - writel(1, regs + S5P_JSTART); -} - -static inline int 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) -{ - return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK) - >> S5P_STREAM_STAT_SHIFT); -} - -static inline void jpeg_clear_int(void __iomem *regs) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTST); - writel(S5P_INT_RELEASE, regs + S5P_JPGCOM); - reg = readl(regs + S5P_JPGOPR); -} - -static inline unsigned int jpeg_compressed_size(void __iomem *regs) -{ - unsigned long jpeg_size = 0; - - jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16; - jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8; - jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff); - - return (unsigned int)jpeg_size; -} - -#endif /* JPEG_HW_H_ */ -- cgit v0.10.2 From 80529ae5c13725e12ba0377e29b2160794ba6b25 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 18 Dec 2013 11:04:44 -0300 Subject: [media] s5p-jpeg: JPEG codec Add hardware API for the exynos4x12 on s5p-jpeg. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile index faf6398..a1a9169 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 jpeg-hw-s5p.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 65922d9..fbadbb0 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 + * Author: Jacek Anaszewski * * 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 @@ -30,58 +31,212 @@ #include "jpeg-core.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, + }, + { + .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, }, -}; -#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc) - -static struct s5p_jpeg_fmt formats_dec[] = { { - .name = "YUV 4:2:0 planar, YCbCr", + .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, - .h_align = 4, - .v_align = 4, - .types = MEM2MEM_CAPTURE, + .depth = 16, + .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:2 packed, YCbYCr", - .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, .depth = 16, - .colplanes = 1, + .colplanes = 4, .h_align = 4, - .v_align = 3, - .types = MEM2MEM_CAPTURE, + .v_align = 1, + .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 = "JPEG JFIF", - .fourcc = V4L2_PIX_FMT_JPEG, + .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 = "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 0 - high compression quality */ @@ -277,6 +432,58 @@ static inline void s5p_jpeg_set_hactblg(void __iomem *regs) 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)); +} + /* * ============================================================================ * Device file operations @@ -285,8 +492,8 @@ static inline void s5p_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) @@ -294,7 +501,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); @@ -315,16 +522,18 @@ 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->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); @@ -332,7 +541,12 @@ static int s5p_jpeg_open(struct file *file) } 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; @@ -352,11 +566,11 @@ static int s5p_jpeg_release(struct file *file) mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - mutex_unlock(&jpeg->lock); v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); kfree(ctx); + mutex_unlock(&jpeg->lock); return 0; } @@ -504,13 +718,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; @@ -524,8 +738,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; } @@ -536,10 +750,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, @@ -548,10 +763,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, @@ -598,29 +814,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; - for (k = 0; k < n; k++) { - struct s5p_jpeg_fmt *fmt = &formats[k]; - if (fmt->fourcc == pixelformat) + if (ctx->jpeg->variant->version == SJPEG_S5P) + ver_flag = SJPEG_FMT_FLAG_S5P; + else + ver_flag = SJPEG_FMT_FLAG_EXYNOS4; + + 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, @@ -656,7 +878,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, @@ -694,15 +916,16 @@ static int s5p_jpeg_try_fmt_vid_cap(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_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); + return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE); } static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, @@ -711,15 +934,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) @@ -727,6 +951,7 @@ 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; + unsigned int f_type; vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); if (!vq) @@ -740,7 +965,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) @@ -781,7 +1009,8 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, 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 */ @@ -1018,6 +1247,107 @@ static void s5p_jpeg_device_run(void *priv) 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) { struct s5p_jpeg_ctx *ctx = priv; @@ -1035,6 +1365,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, }; /* @@ -1241,6 +1577,69 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) 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 @@ -1251,13 +1650,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; @@ -1276,8 +1681,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; @@ -1299,8 +1704,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); @@ -1448,12 +1858,16 @@ static int s5p_jpeg_runtime_resume(struct device *dev) /* * 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. */ - s5p_jpeg_set_hdctbl(jpeg->regs); - s5p_jpeg_set_hdctblg(jpeg->regs); - s5p_jpeg_set_hactbl(jpeg->regs); - s5p_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); @@ -1482,27 +1896,60 @@ static const struct dev_pm_ops s5p_jpeg_pm_ops = { }; #ifdef CONFIG_OF -static const struct of_device_id s5p_jpeg_of_match[] = { - { .compatible = "samsung,s5pv210-jpeg" }, - { .compatible = "samsung,exynos4210-jpeg" }, - { /* sentinel */ }, +static struct s5p_jpeg_variant s5p_jpeg_drvdata = { + .version = SJPEG_S5P, + .jpeg_irq = s5p_jpeg_irq, }; -MODULE_DEVICE_TABLE(of, s5p_jpeg_of_match); + +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 = { - .of_match_table = of_match_ptr(s5p_jpeg_of_match), - .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 "); +MODULE_AUTHOR("Jacek Anaszewski "); 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 7baadf3..f482dbf 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 #include #include #include @@ -42,14 +43,46 @@ #define EOI 0xd9 #define DHP 0xde +/* Flags that indicate a format can be used for capture/output */ +#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 -/* Flags that indicate a format can be used for capture/output */ -#define MEM2MEM_CAPTURE (1 << 0) -#define MEM2MEM_OUTPUT (1 << 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 @@ -76,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); }; /** @@ -89,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; }; /** @@ -150,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 0000000..da8d6a1 --- /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 + * + * 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 +#include + +#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 0000000..c228d28 --- /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 + * + * 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-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 38e5081..33f2c73 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 + * Author: Jacek Anaszewski * * 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_ */ -- cgit v0.10.2 From f84339628d9224200e9145320da494da2d92cd50 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 21 Nov 2013 13:33:09 -0300 Subject: [media] s5p-jpeg: Retrieve "YCbCr subsampling" field from the jpeg header Make s5p_jpeg_parse_hdr function capable of parsing "YCbCr subsampling" field of a jpeg file header. Store the parsed value in the context. The information about source JPEG subsampling is required to make validation of destination format possible, which must be conducted for exynos4x12 device as the decoding process will not succeed if the destination format is set to YUV with subsampling lower than the one of the source JPEG image. With this knowledge the driver can adjust the destination format appropriately. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index fbadbb0..0ae3d2c 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -624,10 +624,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; @@ -666,7 +667,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 */ @@ -688,6 +697,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; } @@ -1438,7 +1465,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; -- cgit v0.10.2 From 337777a42d033b3be1a7f9af13eb8f5eb0cdd95a Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 22 Nov 2013 06:13:34 -0300 Subject: [media] s5p-jpeg: Ensure correct capture format for Exynos4x12 Adjust capture format to the Exynos4x12 device limitations, according to the subsampling value parsed from the source JPEG image header. If the capture format was set to YUV with subsampling lower than the one of the source JPEG image the decoding process would not succeed. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 0ae3d2c..ba8e4fc 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -358,6 +358,99 @@ 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 inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) { return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); @@ -941,7 +1034,9 @@ 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, f->fmt.pix.pixelformat, FMT_TYPE_CAPTURE); @@ -952,6 +1047,27 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } + /* + * 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); } -- cgit v0.10.2 From fdf9e2bcbb7e53759fde6a3f5dd9f63cc0268f40 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 21 Nov 2013 13:33:41 -0300 Subject: [media] s5p-jpeg: Allow for wider JPEG subsampling scope for Exynos4x12 encoder Exynos4x12 supports wider scope of subsampling modes than S5PC210. Adjust corresponding mask accordingly. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index ba8e4fc..3d5d994 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1249,7 +1249,8 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) 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, -- cgit v0.10.2 From 4a30d30b87e5aa444a7f5983cd5b7d2466f4cb31 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 21 Nov 2013 13:33:52 -0300 Subject: [media] s5p-jpeg: Synchronize V4L2_CID_JPEG_CHROMA_SUBSAMPLING control value When output queue fourcc is set to any flavour of YUV, the V4L2_CID_JPEG_CHROMA_SUBSAMPLING control value as well as its in-driver cached counterpart have to be updated with the subsampling property of the format so as to be able to provide correct information to the user space and preclude setting an illegal subsampling mode for Exynos4x12 encoder. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3d5d994..ea9cf73 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1094,6 +1094,7 @@ 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->fh.m2m_ctx, f->type); @@ -1119,6 +1120,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; } -- cgit v0.10.2 From 11fbea3ef3d2d47c5cba37cf275f043cb41ddbc7 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 21 Nov 2013 13:34:01 -0300 Subject: [media] s5p-jpeg: Ensure setting correct value of the chroma subsampling control Exynos4x12 has limitations regarding setting chroma subsampling of an output JPEG image. It cannot be lower than the subsampling of the raw source image. Also in case of V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY option the source image fourcc has to be V4L2_PIX_FMT_GREY. This patch implements try_ctrl callback containing mechanism that prevents setting invalid value of the V4L2_CID_JPEG_CHROMA_SUBSAMPLING control. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index ea9cf73..3c06487 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1213,6 +1213,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); @@ -1238,6 +1272,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, }; -- cgit v0.10.2 From 303b0a96f02c35ba5dfb460dd17c7f70b9da4003 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 18 Dec 2013 11:14:00 -0300 Subject: [media] s5p-jpeg: Adjust g_volatile_ctrl callback to Exynos4x12 needs Whereas S5PC210 device produces decoded JPEG subsampling values that map on V4L2_JPEG_CHROMA_SUBSAMPLNG values, the Exynos4x12 device doesn't. This patch adds helper function s5p_jpeg_to_user_subsampling, which performs suitable translation. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3c06487..a1c78c8 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -451,6 +451,13 @@ static int s5p_jpeg_adjust_fourcc_to_subsampling( 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); @@ -461,6 +468,21 @@ static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) return container_of(fh, struct s5p_jpeg_ctx, fh); } +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) @@ -1200,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; } -- cgit v0.10.2 From d67350f8c4e67f5eba627e1fd111f16257ca9c95 Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Thu, 12 Dec 2013 09:26:22 -0300 Subject: [media] dib8000: fix regression with dib807x Commit 173a64cb3fcf broke support for some dib807x versions. Fix it by providing backward compatibility with the older versions. [mkrufky@linuxtv.org: conflict handling and CodingStyle fixes] Signed-off-by: Olivier Grenie Acked-by: Patrick Boettcher Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 6dbbee4..1e03961 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2445,7 +2445,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 +2482,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 +2585,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; -- cgit v0.10.2 From 5ac64ba12aca3bef18e61c866583155a3bbf81c4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 13 Dec 2013 10:35:03 -0300 Subject: [media] dib8000: make 32 bits read atomic As the dvb-frontend kthread can be called anytime, it can race with some get status ioctl. So, it seems better to avoid one to race with the other while reading a 32 bits register. I can't see any other reason for having a mutex there at I2C, except to provide such kind of protection, as the I2C core already has a mutex to protect I2C transfers. Note: instead of this approach, it could eventually remove the dib8000 specific mutex for it, and either group the 4 ops into one xfer or to manually control the I2C mutex. The main advantage of the current approach is that the changes are smaller and more puntual. Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 1e03961..36c839c 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -157,15 +157,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 +178,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 +202,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])); } -- cgit v0.10.2 From 70315b3eafabd35ce5f2d43dc82517f66d8ce066 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 15 Dec 2013 07:41:20 -0200 Subject: [media] dib8000: Don't let tuner hang due to a call to get_frontend() Both dvbv5-scan and dvbv5-zap tools call FE_GET_PROPERTY inside the loop that checks for stats. If the frontend doesn't support DVBv5, it falls back to call the DVBv5 stats APIs(FE_READ_BER, FE_READ_SIGNAL, FE_READ_SNR and FE_READ_UNCORRECTED_BLOCKS). A call to FE_GET_PROPERTY makes dvb-frontend core to call get_frontend(). However, due to a race condition on dib8000 between dib8000_get_frontend and dib8000_tune, if get_frontend occurs too early, it causes the tune state machine to fail and not get any lock. This patch adds a workaround code that makes get_frontend() to just return if none of the frontends have a SYNC. This change fixed the issue with dvbv5-scan/dvbv5-zap, but a fine-tuned logic might be needed in the future, when we implement DVBv5 stats on this frontend. The procedure to test the bug and the fix is the one below: 1) tune into a non-existing frequency with: $ dvbv5-zap -I dvbv5 -c non_existing_freqs -m 679142857 -t3 2) tune/lock into an existing frequency with: $ dvbv5-zap -I dvbv5 -c isdb-test -m 479142857 or $ dvbv5-scan isdb-test In this case, 679 MHz carrier doesn't exist. Only 479 MHz does. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 36c839c..063232a 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3276,15 +3276,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) { -- cgit v0.10.2 From ad976187040dd898daa7b6dcc44c03f79074d072 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 15 Dec 2013 09:57:40 -0300 Subject: [media] dib8000: improves the auto search mode check logic The logic that detects if auto search mode should be used is too complex. Also, it doesn't cover all cases, as the dib8000_tune logic requires either auto mode or a fully specified manual mode. So, move it to a separate function and add some extra debug data to help identifying when it falled back to auto mode, because the manual settings are invalid. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 063232a..f11c9f8 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2873,6 +2873,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; @@ -2901,37 +2986,14 @@ static int dib8000_tune(struct dvb_frontend *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); -- cgit v0.10.2 From 51fea113429115977be274b0c3f8937bcc3eeae3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Dec 2013 04:16:59 -0300 Subject: [media] dib8000: report Interleaving 4 correctly On ISDB-T, the valid values for interleaving are 0, 1, 2 and 4. While the first 3 are properly reported, the last one is reported as 3 instead. Fix it. Tested with a Dektec DTA-2111 RF generator. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index f11c9f8..13fdc3d 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3429,9 +3429,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) { -- cgit v0.10.2 From 6ef06e78c74cc4b2f45ea2e9adbe6d52cd246a6b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Dec 2013 19:10:59 -0300 Subject: [media] dib8000: add DVBv5 stats The advantage of DVBv5 stats is that it allows adding proper scales to all measures. use it for this frontend. This patch adds a basic set of stats, basically cloning what's already provided by DVBv3 API. Latter patches will improve it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 13fdc3d..2dbf893 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -124,6 +124,8 @@ struct dib8000_state { u16 agc2_max; u16 agc2_min; #endif + + unsigned long get_stats_time; }; enum dib8000_power_mode { @@ -804,7 +806,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; } @@ -983,6 +985,32 @@ static u16 dib8000_identify(struct i2c_device *client) return value; } +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; + + 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->post_bit_error.len = 1; + c->post_bit_count.len = 1; + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + 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->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; +} + static int dib8000_reset(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; @@ -1088,6 +1116,8 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); + dib8000_reset_stats(fe); + return 0; } @@ -2983,6 +3013,8 @@ 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; @@ -3654,6 +3686,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; @@ -3691,6 +3725,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; } @@ -3797,6 +3832,111 @@ 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 }, +}; + +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, lock; + u32 snr, val; + u16 strength; + + /* Get Signal strength */ + dib8000_read_signal_strength(fe, &strength); + c->strength.stat[0].uvalue = strength; + + /* Check if 1 second was elapsed */ + if (!time_after(jiffies, state->get_stats_time)) + return 0; + state->get_stats_time = 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; + + /* UCB/BER measures require lock */ + if (!(stat & FE_HAS_LOCK)) { + c->block_error.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + 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; + return 0; + } + + /* Get UCB and post-BER measures */ + + /* FIXME: need to check if 1.25e6 bits already passed */ + 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; + + /* + * FIXME: this is refreshed on every second, but a time + * drift between dib8000 and PC clock may cause troubles + */ + dib8000_read_unc_blocks(fe, &val); + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += val; + + 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++) { + lock = dib8000_read_word(state, per_layer_regs[i].lock); + if (lock & 0x01) { + /* FIXME: need to check if 1.25e6 bits already passed */ + 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; + + /* + * FIXME: this is refreshed on every second, but a time + * drift between dib8000 and PC clock may cause troubles + */ + 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; + } + } + return 0; +} + int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) { struct dib8000_state *state = fe->demodulator_priv; -- cgit v0.10.2 From b4600d70c0927a9f61b26093c0f01db69f607a6f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Dec 2013 20:00:34 -0300 Subject: [media] dib8000: estimate strength in dBm Better to have Signal strength in dB. This takes a very rough estimation for the signal strength, that was calibrated using a Dektec DTA-2111 Gold RF generator and a Pixelview dib8076 stick. It estimates the signal strength using a linear equation where: - the max is -22.5 dBm, with returns 55953 - the min is -35.0 dBm, with returns 50110 With -22dBm, the signal strengh is returned as 65535. Unfortunately, the min strength generated with DTA-2111 is -35dBm. It should be noticed that approximating it by a linear equation is not right. I should probably be splitting it into 0.5 dB linear segments, in order to get a higher precision, just like it is done on mb86a20s, but that would force me to add some attenuators, in order to get dB levels below -35dBm, which is, btw, strong enough to get signal lock. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 2dbf893..faf469d 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dvb_math.h" @@ -1002,7 +1003,7 @@ static void dib8000_reset_stats(struct dvb_frontend *fe) c->post_bit_error.len = 1; c->post_bit_count.len = 1; - c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; c->strength.stat[0].uvalue = 0; c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; @@ -3847,12 +3848,30 @@ 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, lock; + u64 tmp; u32 snr, val; u16 strength; /* Get Signal strength */ dib8000_read_signal_strength(fe, &strength); - c->strength.stat[0].uvalue = strength; + + /* + * Estimate it in dBm + * This calculus was empirically determinated by measuring the signal + * strength generated by a DTA-2111 RF generator directly connected into + * a dib8076 device. The real value can actually be different on other + * devices, depending if LNA is enabled or not, if diversity is enabled, + * etc. + */ + if (strength == 65535) { + c->strength.stat[0].svalue = -22000; + } else { + tmp = strength * 25000L; + do_div(tmp, 11646); + c->strength.stat[0].svalue = tmp - 142569; + if (c->strength.stat[0].svalue > -22000) + c->strength.stat[0].svalue = -22000; + } /* Check if 1 second was elapsed */ if (!time_after(jiffies, state->get_stats_time)) -- cgit v0.10.2 From 42ff76bdb0bfe0675da507a35d893c8aa7c7a403 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Dec 2013 21:45:19 -0300 Subject: [media] dib8000: make a better estimation for dBm Use multiple linear segments to better interpolate the dBm for the signal strength. The table that converts from linear strength to dB was empirically determinated with the help of a signal generator (DTA-2111). The entries from -35dBm to -22.5dBm were taken using just the signal generator and the board. For the entries from -36dBm to -51dBm, a 16 dB tap was used, in order to extend its range. Signals below to -51dBm are just linearly interpolated. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index faf469d..7b10b73 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3843,35 +3843,108 @@ static const struct per_layer_regs per_layer_regs[] = { { 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 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, lock; - u64 tmp; u32 snr, val; + s32 db; u16 strength; /* Get Signal strength */ dib8000_read_signal_strength(fe, &strength); - - /* - * Estimate it in dBm - * This calculus was empirically determinated by measuring the signal - * strength generated by a DTA-2111 RF generator directly connected into - * a dib8076 device. The real value can actually be different on other - * devices, depending if LNA is enabled or not, if diversity is enabled, - * etc. - */ - if (strength == 65535) { - c->strength.stat[0].svalue = -22000; - } else { - tmp = strength * 25000L; - do_div(tmp, 11646); - c->strength.stat[0].svalue = tmp - 142569; - if (c->strength.stat[0].svalue > -22000) - c->strength.stat[0].svalue = -22000; - } + val = strength; + db = interpolate_value(val, + strength_to_db_table, + ARRAY_SIZE(strength_to_db_table)) - 131000; + c->strength.stat[0].svalue = db; /* Check if 1 second was elapsed */ if (!time_after(jiffies, state->get_stats_time)) -- cgit v0.10.2 From 7a9d85d5559fff9214a3c41d441c9642805ddad3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Dec 2013 04:55:26 -0300 Subject: [media] dib8000: Fix UCB measure with DVBv5 stats On dib8000, the block error count is a monotonic 32 bits register. With DVBv5 stats, we use a 64 bits counter, that it is reset when a new channel is tuned. Change the UCB counting start from 0 and to be returned with 64 bits, just like the API requests. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 7b10b73..ef0d9ec 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -119,6 +119,7 @@ struct dib8000_state { u8 longest_intlv_layer; u16 output_mode; + s64 init_ucb; #ifdef DIB8000_AGC_FREEZE u16 agc1_max; u16 agc1_min; @@ -986,10 +987,13 @@ 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)); @@ -1010,6 +1014,9 @@ static void dib8000_reset_stats(struct dvb_frontend *fe) c->block_error.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; } static int dib8000_reset(struct dvb_frontend *fe) @@ -3989,14 +3996,12 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; c->post_bit_count.stat[0].uvalue += 100000000; - /* - * FIXME: this is refreshed on every second, but a time - * drift between dib8000 and PC clock may cause troubles - */ dib8000_read_unc_blocks(fe, &val); + if (val < state->init_ucb) + state->init_ucb += 1L << 32; c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue += val; + c->block_error.stat[0].uvalue = val + state->init_ucb; if (state->revision < 0x8002) return 0; -- cgit v0.10.2 From 704f01bbc7e41f4210fe9fa58da511ad3369b70b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Dec 2013 10:08:20 -0300 Subject: [media] dib8000: be sure that stats are available before reading them On dib8000, the BER statistics are updated on every 1.25e6 bits. Adjust the code to only update the statistics after having it done. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index ef0d9ec..c67a3db 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -119,15 +119,17 @@ struct dib8000_state { u8 longest_intlv_layer; u16 output_mode; + /* for DVBv5 stats */ s64 init_ucb; + unsigned long jiffies_stats; + unsigned long jiffies_stats_layer[3]; + #ifdef DIB8000_AGC_FREEZE u16 agc1_max; u16 agc1_min; u16 agc2_max; u16 agc2_min; #endif - - unsigned long get_stats_time; }; enum dib8000_power_mode { @@ -1016,7 +1018,11 @@ static void dib8000_reset_stats(struct dvb_frontend *fe) c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; dib8000_read_unc_blocks(fe, &ucb); + state->init_ucb = -ucb; + state->jiffies_stats = 0; + memset(&state->jiffies_stats_layer, 0, + sizeof(state->jiffies_stats_layer)); } static int dib8000_reset(struct dvb_frontend *fe) @@ -3936,12 +3942,124 @@ static u32 interpolate_value(u32 value, struct linear_segments *segments, 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; + u32 tmp, denom; + int guard, rate_num, rate_denum, bits_per_symbol, nsegs; + int interleaving, 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); + time_us = time_us + time_us / guard; + 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, lock; - u32 snr, val; + int i; + u32 time_us, snr, val; s32 db; u16 strength; @@ -3953,55 +4071,59 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) ARRAY_SIZE(strength_to_db_table)) - 131000; c->strength.stat[0].svalue = db; - /* Check if 1 second was elapsed */ - if (!time_after(jiffies, state->get_stats_time)) - return 0; - state->get_stats_time = 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; - - /* UCB/BER measures require lock */ + /* UCB/BER/CNR measures require lock */ if (!(stat & FE_HAS_LOCK)) { + c->cnr.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; return 0; } - /* Get UCB and post-BER measures */ + /* Check if time for stats was elapsed */ + if (time_after(jiffies, state->jiffies_stats)) { + time_us = dib8000_get_time_us(fe, -1); + state->jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + + dprintk("Next all layers stats available in %u us.\n", time_us); + + /* 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; - /* FIXME: need to check if 1.25e6 bits already passed */ - dib8000_read_ber(fe, &val); - c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[0].uvalue += val; + 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; - c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[0].uvalue += 100000000; + /* Get UCB and post-BER measures */ - dib8000_read_unc_blocks(fe, &val); - if (val < state->init_ucb) - state->init_ucb += 1L << 32; + dib8000_read_ber(fe, &val); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += val; - c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue = val + state->init_ucb; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += 100000000; + + dib8000_read_unc_blocks(fe, &val); + if (val < state->init_ucb) + state->init_ucb += 1L << 32; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = val + state->init_ucb; + } if (state->revision < 0x8002) return 0; @@ -4011,25 +4133,27 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) c->post_bit_count.len = 4; for (i = 0; i < 3; i++) { - lock = dib8000_read_word(state, per_layer_regs[i].lock); - if (lock & 0x01) { - /* FIXME: need to check if 1.25e6 bits already passed */ - 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; - - /* - * FIXME: this is refreshed on every second, but a time - * drift between dib8000 and PC clock may cause troubles - */ - 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_after(jiffies, state->jiffies_stats_layer[i])) + continue; + time_us = dib8000_get_time_us(fe, i); + if (!time_us) + continue; + + state->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; + + 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; } return 0; } -- cgit v0.10.2 From 0400c5354ad09bf4c754132992bdde9ef3dbefcd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Dec 2013 11:34:22 -0300 Subject: [media] dib8000: improve block statistics PER/UCB statistics are collected once on each 1 second. However, it doesn't provide the total number of packets needed to calculate PER. Yet, as we know the bit rate, it is possible to estimate such number. So, do it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Patrick Boettcher diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index c67a3db..7539d7af 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -121,8 +121,9 @@ struct dib8000_state { /* for DVBv5 stats */ s64 init_ucb; - unsigned long jiffies_stats; - unsigned long jiffies_stats_layer[3]; + unsigned long per_jiffies_stats; + unsigned long ber_jiffies_stats; + unsigned long ber_jiffies_stats_layer[3]; #ifdef DIB8000_AGC_FREEZE u16 agc1_max; @@ -1006,6 +1007,7 @@ static void dib8000_reset_stats(struct dvb_frontend *fe) 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; @@ -1014,15 +1016,17 @@ static void dib8000_reset_stats(struct dvb_frontend *fe) 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->jiffies_stats = 0; - memset(&state->jiffies_stats_layer, 0, - sizeof(state->jiffies_stats_layer)); + 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) @@ -4059,7 +4063,9 @@ 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; - u32 time_us, snr, val; + int show_per_stats = 0; + u32 time_us = 0, snr, val; + u64 blocks; s32 db; u16 strength; @@ -4074,6 +4080,7 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) /* 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; @@ -4081,15 +4088,13 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) 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->jiffies_stats)) { - time_us = dib8000_get_time_us(fe, -1); - state->jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); - - dprintk("Next all layers stats available in %u us.\n", time_us); + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); /* Get SNR */ snr = dib8000_get_snr(fe); @@ -4108,7 +4113,34 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) c->cnr.stat[0].svalue = snr; c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - /* Get UCB and post-BER measures */ + /* Get UCB measures */ + dib8000_read_unc_blocks(fe, &val); + if (val < state->init_ucb) + state->init_ucb += 0x100000000L; + + 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 = 1250000UL * 1000000UL; + 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; @@ -4116,13 +4148,6 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; c->post_bit_count.stat[0].uvalue += 100000000; - - dib8000_read_unc_blocks(fe, &val); - if (val < state->init_ucb) - state->init_ucb += 1L << 32; - - c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue = val + state->init_ucb; } if (state->revision < 0x8002) @@ -4133,27 +4158,43 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) c->post_bit_count.len = 4; for (i = 0; i < 3; i++) { - if (!time_after(jiffies, state->jiffies_stats_layer[i])) - continue; - time_us = dib8000_get_time_us(fe, i); - if (!time_us) + unsigned nsegs = c->layer[i].segment_count; + + if (nsegs == 0 || nsegs > 13) continue; - state->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); + 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; + 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; + 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); - 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; - 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 = 1250000UL * 1000000UL; + 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; } -- cgit v0.10.2 From 395d00d1ca8947887fd0fbdec4fff90c4da21877 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 25 Feb 2013 08:39:16 -0300 Subject: [media] Montage M88DS3103 DVB-S/S2 demodulator driver DVB-S/S2 satellite television demodulator driver. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index bddbab4..6c46caf 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 + 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 f9cb43d..0c75a6a 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/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c new file mode 100644 index 0000000..91b3729 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -0,0 +1,1293 @@ +/* + * Montage M88DS3103 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "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) +{ + int ret; + u8 buf[1 + len]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + 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) +{ + int ret; + u8 buf[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 = sizeof(buf), + .buf = buf, + } + }; + + 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; +} + +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, i, 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) { + dev_dbg(&priv->i2c->dev, "%s: program init\n", __func__); + for (i = 0; i < len; i++) { + ret = m88ds3103_wr_reg(priv, init[i].reg, init[i].val); + if (ret) + goto err; + } + } + + u8tmp1 = 0; /* silence compiler warning */ + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + u8tmp1 = 0x00; + ts_clk = 0; + u8tmp = 0x04; + break; + case M88DS3103_TS_SERIAL_D7: + u8tmp1 = 0x20; + ts_clk = 0; + u8tmp = 0x04; + break; + case M88DS3103_TS_PARALLEL: + ts_clk = 24000; + u8tmp = 0x00; + break; + case M88DS3103_TS_PARALLEL_12: + ts_clk = 12000; + u8tmp = 0x00; + break; + case M88DS3103_TS_PARALLEL_16: + ts_clk = 16000; + u8tmp = 0x00; + break; + case M88DS3103_TS_PARALLEL_19_2: + ts_clk = 19200; + u8tmp = 0x00; + break; + case M88DS3103_TS_CI: + ts_clk = 6000; + u8tmp = 0x01; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); + ret = -EINVAL; + goto err; + } + + /* TS mode */ + ret = m88ds3103_wr_reg_mask(priv, 0xfd, u8tmp, 0x05); + 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 = (((c->symbol_rate / 1000) << 15) + (M88DS3103_MCLK_KHZ / 4)) / + (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 = (2 * s32tmp + M88DS3103_MCLK_KHZ) / (2 * 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 u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap); + int ret; + struct i2c_msg gate_open_msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = 2, + .buf = "\x03\x11", + } + }; + dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num); + + mutex_lock(&priv->i2c_mutex); + + /* open i2c-gate */ + ret = i2c_transfer(priv->i2c, gate_open_msg, 1); + if (ret != 1) { + mutex_unlock(&priv->i2c_mutex); + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d\n", + KBUILD_MODNAME, ret); + ret = -EREMOTEIO; + goto err; + } + + ret = i2c_transfer(priv->i2c, msg, num); + mutex_unlock(&priv->i2c_mutex); + if (ret < 0) + dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n", + KBUILD_MODNAME, ret); + + return ret; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct i2c_algorithm m88ds3103_tuner_i2c_algo = { + .master_xfer = m88ds3103_tuner_i2c_xfer, + .functionality = m88ds3103_tuner_i2c_func, +}; + +static void m88ds3103_release(struct dvb_frontend *fe) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + i2c_del_adapter(&priv->i2c_adapter); + kfree(priv); +} + +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(struct m88ds3103_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 dvb_frontend */ + memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; + + /* create i2c adapter for tuner */ + strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME, + sizeof(priv->i2c_adapter.name)); + priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo; + priv->i2c_adapter.algo_data = NULL; + i2c_set_adapdata(&priv->i2c_adapter, priv); + ret = i2c_add_adapter(&priv->i2c_adapter); + if (ret) { + dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n", + KBUILD_MODNAME); + goto err; + } + *tuner_i2c_adapter = &priv->i2c_adapter; + + 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 "); +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 0000000..287d62a --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -0,0 +1,108 @@ +/* + * Montage M88DS3103 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef M88DS3103_H +#define M88DS3103_H + +#include + +struct m88ds3103_config { + /* + * I2C address + * 0x68, ... + */ + u8 i2c_addr; + + /* + * clock + * 27000000 + */ + u32 clock; + + /* + * max bytes I2C provider is asked to write at once + * Note: Buffer is taken from the stack currently! + * Value must be set. + * 33, 65, ... + */ + u16 i2c_wr_max; + + /* + * TS output mode + */ +#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 + */ + u8 spec_inv:1; + + /* + * AGC polarity + */ + u8 agc_inv:1; + + /* + * clock output + */ +#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 + */ + u8 envelope_mode:1; + + 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 0000000..f3d0867 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -0,0 +1,218 @@ +/* + * Montage M88DS3103 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef M88DS3103_PRIV_H +#define M88DS3103_PRIV_H + +#include "dvb_frontend.h" +#include "m88ds3103.h" +#include "dvb_math.h" +#include + +#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}, + {0x33, 0x35}, + {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}, + {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}, + {0x33, 0x35}, + {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}, + {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 -- cgit v0.10.2 From 695efd010b1328b25462128e48856e87c8df4314 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 25 Feb 2013 08:43:32 -0300 Subject: [media] Montage M88TS2022 silicon tuner driver M88TS2022 is DVB-S/S2 RF tuner used usually in conjunction with Montage M88DS3103 DVB-S/S2 demodulator. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 15665de..ba2e365 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 308f108..efe82a9 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/m88ts2022.c b/drivers/media/tuners/m88ts2022.c new file mode 100644 index 0000000..0625e36 --- /dev/null +++ b/drivers/media/tuners/m88ts2022.c @@ -0,0 +1,664 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 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) +{ + int ret; + u8 buf[1 + len]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, + u8 *val, int len) +{ + int ret; + u8 buf[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 = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret == 2) { + memcpy(val, buf, len); + ret = 0; + } else { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* write single register */ +static int 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->i2c->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 = 0, div; + u8 buf[3], u8tmp, cap_code, lpf_mxdiv, div_max, div_min; + u16 N_reg, N, K; + u32 lpf_gm, lpf_coeff, gdiv28, frequency_khz, frequency_offset; + u32 freq_3db; + dev_dbg(&priv->i2c->dev, + "%s: frequency=%d symbol_rate=%d rolloff=%d\n", + __func__, c->frequency, c->symbol_rate, c->rolloff); + + if (c->symbol_rate < 5000000) + frequency_offset = 3000000; /* 3 MHz */ + else + frequency_offset = 0; + + frequency_khz = c->frequency + (frequency_offset / 1000); + + if (frequency_khz < 1103000) { + div = 2; + u8tmp = 0x1b; + } else { + div = 1; + u8tmp = 0x0b; + } + + buf[0] = u8tmp; + buf[1] = 0x40; + ret = m88ts2022_wr_regs(priv, 0x10, buf, 2); + if (ret) + goto err; + + K = DIV_ROUND_CLOSEST((priv->cfg->clock / 2), 1000000); + N = 1ul * frequency_khz * K * div * 2 / (priv->cfg->clock / 1000); + N += N % 2; + + if (N < 4095) + N_reg = N - 1024; + else if (N < 6143) + N_reg = N + 1024; + else + N_reg = N + 3072; + + buf[0] = (N_reg >> 8) & 0x3f; + buf[1] = (N_reg >> 0) & 0xff; + buf[2] = K - 8; + ret = m88ts2022_wr_regs(priv, 0x01, buf, 3); + if (ret) + goto err; + + priv->frequency_khz = 1ul * N * (priv->cfg->clock / 1000) / K / div / 2; + + dev_dbg(&priv->i2c->dev, + "%s: frequency=%d offset=%d K=%d N=%d div=%d\n", + __func__, priv->frequency_khz, + priv->frequency_khz - c->frequency, K, N, div); + + 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; + + gdiv28 = DIV_ROUND_CLOSEST(priv->cfg->clock / 1000000 * 1694, 1000); + + 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; + if (div_max > 63) + div_max = 63; + + freq_3db = 1ul * c->symbol_rate * 135 / 200 + 2000000; + freq_3db += frequency_offset; + if (freq_3db < 7000000) + freq_3db = 7000000; + if (freq_3db > 40000000) + freq_3db = 40000000; + + lpf_coeff = 3200; + lpf_gm = DIV_ROUND_CLOSEST(freq_3db * gdiv28, lpf_coeff * + (priv->cfg->clock / 1000)); + if (lpf_gm > 23) + lpf_gm = 23; + if (lpf_gm < 1) + lpf_gm = 1; + + lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff * + (priv->cfg->clock / 1000), freq_3db); + + if (lpf_mxdiv < div_min) { + lpf_gm++; + lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff * + (priv->cfg->clock / 1000), freq_3db); + } + + if (lpf_mxdiv > div_max) + lpf_mxdiv = 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->i2c->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->i2c->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->i2c->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->i2c->dev, "%s:\n", __func__); + + ret = m88ts2022_wr_reg(priv, 0x00, 0x00); + if (ret) + goto err; +err: + if (ret) + dev_dbg(&priv->i2c->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->i2c->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->i2c->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; + u8 u8tmp, gain1, gain2, gain3; + u16 gain, u16tmp; + int ret; + + ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp); + if (ret) + goto err; + + gain1 = (u8tmp >> 0) & 0x1f; + if (gain1 > 15) + gain1 = 15; + + ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp); + if (ret) + goto err; + + gain2 = (u8tmp >> 0) & 0x1f; + if (gain2 < 2) + gain2 = 2; + if (gain2 > 16) + gain2 = 16; + + ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp); + if (ret) + goto err; + + gain3 = (u8tmp >> 3) & 0x07; + if (gain3 > 6) + gain3 = 6; + + gain = gain1 * 265 + gain2 * 338 + gain3 * 285; + + /* scale value to 0x0000-0xffff */ + u16tmp = (0xffff - gain); + if (u16tmp < 59000) + u16tmp = 59000; + else if (u16tmp > 61500) + u16tmp = 61500; + + *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); +err: + if (ret) + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int m88ts2022_release(struct dvb_frontend *fe) +{ + struct m88ts2022_priv *priv = fe->tuner_priv; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + kfree(fe->tuner_priv); + return 0; +} + +static const struct dvb_tuner_ops m88ts2022_tuner_ops = { + .info = { + .name = "Montage M88TS2022", + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .release = m88ts2022_release, + + .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, +}; + +struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct m88ts2022_config *cfg) +{ + struct m88ts2022_priv *priv; + int ret; + u8 chip_id, u8tmp; + + priv = kzalloc(sizeof(struct m88ts2022_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; + priv->fe = fe; + + /* 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->i2c->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->i2c->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)); +err: + if (ret) { + dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); + kfree(priv); + return NULL; + } + + return fe; +} +EXPORT_SYMBOL(m88ts2022_attach); + +MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h new file mode 100644 index 0000000..fa1112c --- /dev/null +++ b/drivers/media/tuners/m88ts2022.h @@ -0,0 +1,72 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef M88TS2022_H +#define M88TS2022_H + +#include "dvb_frontend.h" + +struct m88ts2022_config { + /* + * I2C address + * 0x60, ... + */ + u8 i2c_addr; + + /* + * 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; +}; + +#if defined(CONFIG_MEDIA_TUNER_M88TS2022) || \ + (defined(CONFIG_MEDIA_TUNER_M88TS2022_MODULE) && defined(MODULE)) +extern struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct m88ts2022_config *cfg); +#else +static inline struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct m88ts2022_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h new file mode 100644 index 0000000..190299a --- /dev/null +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -0,0 +1,38 @@ +/* + * Montage M88TS2022 silicon tuner driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef M88TS2022_PRIV_H +#define M88TS2022_PRIV_H + +#include "m88ts2022.h" + +struct m88ts2022_priv { + const struct m88ts2022_config *cfg; + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + u32 frequency_khz; +}; + +struct m88ts2022_reg_val { + u8 reg; + u8 val; +}; + +#endif -- cgit v0.10.2 From ec573362afa202acca640629b321ac09b6911448 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 25 Feb 2013 09:01:13 -0300 Subject: [media] em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258] Device has following chips: Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index ca5ee6a..d6ba514 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -49,6 +49,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/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 5da60da..36853f1 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -356,6 +356,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}, @@ -2125,6 +2147,17 @@ struct em28xx_board em28xx_boards[] = { .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, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -2294,6 +2327,8 @@ struct usb_device_id em28xx_id_table[] = { .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); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 344042b..4f8f687 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -51,6 +51,8 @@ #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 "); @@ -808,6 +810,19 @@ 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, +}; + +static const struct m88ts2022_config em28xx_m88ts2022_config = { + .i2c_addr = 0x60, + .clock = 27000000, +}; /* ------------------------------------------------------------------ */ @@ -1330,6 +1345,44 @@ 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; + + /* 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 */ + if (!dvb_attach(m88ts2022_attach, dvb->fe[0], + i2c_adapter, + &em28xx_m88ts2022_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -ENODEV; + goto out_free; + } + + /* 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"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 6e26fba..aa35750 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -133,6 +133,7 @@ #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 -- cgit v0.10.2 From 7442554614cc958838f9ca113262b561aaf56370 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 6 Nov 2013 14:03:32 -0300 Subject: [media] MAINTAINERS: add M88DS3103 It is Montage M88DS3103 DVB-S/S2 demodulator driver. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index a00f661..5320c61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5347,6 +5347,16 @@ W: http://www.tazenda.demon.co.uk/phil/linux-hp S: Maintained F: arch/m68k/hp300/ +M88DS3103 MEDIA DRIVER +M: Antti Palosaari +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://palosaari.fi/linux/ +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +T: git git://linuxtv.org/anttip/media_tree.git +S: Maintained +F: drivers/media/dvb-frontends/m88ds3103* + M88RS2000 MEDIA DRIVER M: Malcolm Priestley L: linux-media@vger.kernel.org -- cgit v0.10.2 From 0d62f800b5f1398972a0d27ba136e72121db02d6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 6 Nov 2013 14:08:11 -0300 Subject: [media] MAINTAINERS: add M88TS2022 It is Montage M88TS2022 DVB-S/S2 silicon tuner driver. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 5320c61..71399a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5365,6 +5365,16 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ S: Maintained F: drivers/media/dvb-frontends/m88rs2000* +M88TS2022 MEDIA DRIVER +M: Antti Palosaari +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://palosaari.fi/linux/ +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +T: git git://linuxtv.org/anttip/media_tree.git +S: Maintained +F: drivers/media/tuners/m88ts2022* + MA901 MASTERKIT USB FM RADIO DRIVER M: Alexey Klimov L: linux-media@vger.kernel.org -- cgit v0.10.2 From 050ae82c35fd8bb72b27becb1b9d19c7f5978234 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 7 Nov 2013 17:01:31 -0300 Subject: [media] m88ts2022: do not use dynamic stack allocation I2C transfer were using dynamic stack allocation. Get rid of it. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c index 0625e36..4b3eec2 100644 --- a/drivers/media/tuners/m88ts2022.c +++ b/drivers/media/tuners/m88ts2022.c @@ -26,17 +26,22 @@ 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[1 + len]; + u8 buf[MAX_WR_XFER_LEN]; struct i2c_msg msg[1] = { { .addr = priv->cfg->i2c_addr, .flags = 0, - .len = sizeof(buf), + .len = 1 + len, .buf = buf, } }; + if (WARN_ON(len > MAX_WR_LEN)) + return -EINVAL; + buf[0] = reg; memcpy(&buf[1], val, len); @@ -57,8 +62,10 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, 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[len]; + u8 buf[MAX_RD_XFER_LEN]; struct i2c_msg msg[2] = { { .addr = priv->cfg->i2c_addr, @@ -68,11 +75,14 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, }, { .addr = priv->cfg->i2c_addr, .flags = I2C_M_RD, - .len = sizeof(buf), + .len = len, .buf = buf, } }; + if (WARN_ON(len > MAX_RD_LEN)) + return -EINVAL; + ret = i2c_transfer(priv->i2c, msg, 2); if (ret == 2) { memcpy(val, buf, len); -- cgit v0.10.2 From 63c80f70435c5ecac0f94a1331a7dee249ba345b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 7 Nov 2013 17:35:43 -0300 Subject: [media] m88ds3103: do not use dynamic stack allocation I2C transfer were using dynamic stack allocation. Get rid of it. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 91b3729..302c923 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -26,17 +26,22 @@ static struct dvb_frontend_ops m88ds3103_ops; 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[1 + len]; + u8 buf[MAX_WR_XFER_LEN]; struct i2c_msg msg[1] = { { .addr = priv->cfg->i2c_addr, .flags = 0, - .len = sizeof(buf), + .len = 1 + len, .buf = buf, } }; + if (WARN_ON(len > MAX_WR_LEN)) + return -EINVAL; + buf[0] = reg; memcpy(&buf[1], val, len); @@ -59,8 +64,10 @@ static int m88ds3103_wr_regs(struct m88ds3103_priv *priv, 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[len]; + u8 buf[MAX_RD_XFER_LEN]; struct i2c_msg msg[2] = { { .addr = priv->cfg->i2c_addr, @@ -70,11 +77,14 @@ static int m88ds3103_rd_regs(struct m88ds3103_priv *priv, }, { .addr = priv->cfg->i2c_addr, .flags = I2C_M_RD, - .len = sizeof(buf), + .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); -- cgit v0.10.2 From 44b9055b4b058d7b02bf0380158627f9be79b9e5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 19 Nov 2013 20:32:42 -0300 Subject: [media] m88ds3103: use I2C mux for tuner I2C adapter Switch standard I2C adapter to muxed I2C adapter. David reported that I2C adapter implementation caused deadlock. I discussed with Jean and he suggested to implement it as a multiplexed i2c adapter because tuner I2C bus could be seen like own I2C segment. Reported-by: David Howells Cc: Jean Delvare Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 6c46caf..dd12a1e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -37,7 +37,7 @@ config DVB_STV6110x config DVB_M88DS3103 tristate "Montage M88DS3103" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && I2C_MUX default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 302c923..e07e8d6 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1108,15 +1108,16 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, return 0; } -static u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter) +static void m88ds3103_release(struct dvb_frontend *fe) { - return I2C_FUNC_I2C; + struct m88ds3103_priv *priv = fe->demodulator_priv; + i2c_del_mux_adapter(priv->i2c_adapter); + kfree(priv); } -static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msg[], int num) +static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) { - struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap); + struct m88ds3103_priv *priv = mux_priv; int ret; struct i2c_msg gate_open_msg[1] = { { @@ -1126,43 +1127,31 @@ static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, .buf = "\x03\x11", } }; - dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num); mutex_lock(&priv->i2c_mutex); - /* open i2c-gate */ + /* open tuner I2C repeater for 1 xfer, closes automatically */ ret = i2c_transfer(priv->i2c, gate_open_msg, 1); if (ret != 1) { - mutex_unlock(&priv->i2c_mutex); - dev_warn(&priv->i2c->dev, - "%s: i2c wr failed=%d\n", + dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n", KBUILD_MODNAME, ret); - ret = -EREMOTEIO; - goto err; - } + if (ret >= 0) + ret = -EREMOTEIO; - ret = i2c_transfer(priv->i2c, msg, num); - mutex_unlock(&priv->i2c_mutex); - if (ret < 0) - dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n", - KBUILD_MODNAME, ret); + return ret; + } - return ret; -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; + return 0; } -static struct i2c_algorithm m88ds3103_tuner_i2c_algo = { - .master_xfer = m88ds3103_tuner_i2c_xfer, - .functionality = m88ds3103_tuner_i2c_func, -}; - -static void m88ds3103_release(struct dvb_frontend *fe) +static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv, + u32 chan) { - struct m88ds3103_priv *priv = fe->demodulator_priv; - i2c_del_adapter(&priv->i2c_adapter); - kfree(priv); + struct m88ds3103_priv *priv = mux_priv; + + mutex_unlock(&priv->i2c_mutex); + + return 0; } struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, @@ -1228,24 +1217,18 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, 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; - /* create i2c adapter for tuner */ - strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME, - sizeof(priv->i2c_adapter.name)); - priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo; - priv->i2c_adapter.algo_data = NULL; - i2c_set_adapdata(&priv->i2c_adapter, priv); - ret = i2c_add_adapter(&priv->i2c_adapter); - if (ret) { - dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n", - KBUILD_MODNAME); - goto err; - } - *tuner_i2c_adapter = &priv->i2c_adapter; - return &priv->fe; err: dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index f3d0867..322db4d 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -25,6 +25,7 @@ #include "m88ds3103.h" #include "dvb_math.h" #include +#include #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" #define M88DS3103_MCLK_KHZ 96000 @@ -38,7 +39,7 @@ struct m88ds3103_priv { fe_delivery_system_t delivery_system; fe_status_t fe_status; bool warm; /* FW running */ - struct i2c_adapter i2c_adapter; + struct i2c_adapter *i2c_adapter; }; struct m88ds3103_reg_val { -- cgit v0.10.2 From 39c0029e60fe72da96dabfef5b469184d2011fcd Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 19 Nov 2013 22:37:55 -0300 Subject: [media] m88ds3103: use kernel macro to round division DIV_ROUND_CLOSEST does the job and looks better. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index e07e8d6..bd9effa 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -460,8 +460,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - u16tmp = (((c->symbol_rate / 1000) << 15) + (M88DS3103_MCLK_KHZ / 4)) / - (M88DS3103_MCLK_KHZ / 2); + 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); @@ -484,7 +483,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) (tuner_frequency - c->frequency)); s32tmp = 0x10000 * (tuner_frequency - c->frequency); - s32tmp = (2 * s32tmp + M88DS3103_MCLK_KHZ) / (2 * M88DS3103_MCLK_KHZ); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ); if (s32tmp < 0) s32tmp += 0x10000; -- cgit v0.10.2 From 92676ac92738c29a439d959964f2a89d73150333 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 19 Nov 2013 23:06:39 -0300 Subject: [media] m88ds3103: fix TS mode config TS mode was configured wrongly. Reported-by: David Howells Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index bd9effa..f9d8967 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -321,32 +321,32 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) case M88DS3103_TS_SERIAL: u8tmp1 = 0x00; ts_clk = 0; - u8tmp = 0x04; + u8tmp = 0x46; break; case M88DS3103_TS_SERIAL_D7: u8tmp1 = 0x20; ts_clk = 0; - u8tmp = 0x04; + u8tmp = 0x46; break; case M88DS3103_TS_PARALLEL: ts_clk = 24000; - u8tmp = 0x00; + u8tmp = 0x42; break; case M88DS3103_TS_PARALLEL_12: ts_clk = 12000; - u8tmp = 0x00; + u8tmp = 0x42; break; case M88DS3103_TS_PARALLEL_16: ts_clk = 16000; - u8tmp = 0x00; + u8tmp = 0x42; break; case M88DS3103_TS_PARALLEL_19_2: ts_clk = 19200; - u8tmp = 0x00; + u8tmp = 0x42; break; case M88DS3103_TS_CI: ts_clk = 6000; - u8tmp = 0x01; + u8tmp = 0x43; break; default: dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); @@ -355,7 +355,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } /* TS mode */ - ret = m88ds3103_wr_reg_mask(priv, 0xfd, u8tmp, 0x05); + ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp); if (ret) goto err; -- cgit v0.10.2 From c19314b09bc0839736f2c74422b21de36a86e7b7 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Dec 2013 01:39:24 -0300 Subject: [media] m88ts2022: reimplement synthesizer calculations Used synthesizer is very typical integer-N PLL, with configurable reference frequency divider, output frequency divider and of course N itself. Most common method to calculate values is first select output divider, then calculate VCO frequency and finally calculate PLL N from VCO frequency. Do it that way. Also make some cleanups for filter logic and signal strength. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c index 4b3eec2..b11e740 100644 --- a/drivers/media/tuners/m88ts2022.c +++ b/drivers/media/tuners/m88ts2022.c @@ -175,27 +175,33 @@ 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 = 0, div; - u8 buf[3], u8tmp, cap_code, lpf_mxdiv, div_max, div_min; - u16 N_reg, N, K; - u32 lpf_gm, lpf_coeff, gdiv28, frequency_khz, frequency_offset; - u32 freq_3db; + 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->i2c->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 = 3000000; /* 3 MHz */ + frequency_offset_khz = 3000; /* 3 MHz */ else - frequency_offset = 0; + frequency_offset_khz = 0; - frequency_khz = c->frequency + (frequency_offset / 1000); + frequency_khz = c->frequency + frequency_offset_khz; if (frequency_khz < 1103000) { - div = 2; + div_out = 4; u8tmp = 0x1b; } else { - div = 1; + div_out = 2; u8tmp = 0x0b; } @@ -205,30 +211,30 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) if (ret) goto err; - K = DIV_ROUND_CLOSEST((priv->cfg->clock / 2), 1000000); - N = 1ul * frequency_khz * K * div * 2 / (priv->cfg->clock / 1000); - N += N % 2; + 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 (N < 4095) - N_reg = N - 1024; - else if (N < 6143) - N_reg = N + 1024; + if (pll_n < 4095) + u16tmp = pll_n - 1024; + else if (pll_n < 6143) + u16tmp = pll_n + 1024; else - N_reg = N + 3072; + u16tmp = pll_n + 3072; - buf[0] = (N_reg >> 8) & 0x3f; - buf[1] = (N_reg >> 0) & 0xff; - buf[2] = K - 8; + 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; - priv->frequency_khz = 1ul * N * (priv->cfg->clock / 1000) / K / div / 2; - dev_dbg(&priv->i2c->dev, - "%s: frequency=%d offset=%d K=%d N=%d div=%d\n", + "%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, K, N, div); + 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) @@ -284,7 +290,8 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) if (ret) goto err; - gdiv28 = DIV_ROUND_CLOSEST(priv->cfg->clock / 1000000 * 1694, 1000); + /* filters */ + gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U); ret = m88ts2022_wr_reg(priv, 0x04, gdiv28); if (ret) @@ -309,35 +316,20 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151); div_max = gdiv28 * 135 / 100; div_min = gdiv28 * 78 / 100; - if (div_max > 63) - div_max = 63; - - freq_3db = 1ul * c->symbol_rate * 135 / 200 + 2000000; - freq_3db += frequency_offset; - if (freq_3db < 7000000) - freq_3db = 7000000; - if (freq_3db > 40000000) - freq_3db = 40000000; - - lpf_coeff = 3200; - lpf_gm = DIV_ROUND_CLOSEST(freq_3db * gdiv28, lpf_coeff * - (priv->cfg->clock / 1000)); - if (lpf_gm > 23) - lpf_gm = 23; - if (lpf_gm < 1) - lpf_gm = 1; - - lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff * - (priv->cfg->clock / 1000), freq_3db); - - if (lpf_mxdiv < div_min) { - lpf_gm++; - lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff * - (priv->cfg->clock / 1000), freq_3db); - } + 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); - if (lpf_mxdiv > div_max) - lpf_mxdiv = div_max; +#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) @@ -492,44 +484,37 @@ static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) { struct m88ts2022_priv *priv = fe->tuner_priv; - u8 u8tmp, gain1, gain2, gain3; - u16 gain, u16tmp; 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; - if (gain1 > 15) - gain1 = 15; + gain1 = clamp(gain1, 0U, 15U); ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp); if (ret) goto err; gain2 = (u8tmp >> 0) & 0x1f; - if (gain2 < 2) - gain2 = 2; - if (gain2 > 16) - gain2 = 16; + gain2 = clamp(gain2, 2U, 16U); ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp); if (ret) goto err; gain3 = (u8tmp >> 3) & 0x07; - if (gain3 > 6) - gain3 = 6; + gain3 = clamp(gain3, 0U, 6U); gain = gain1 * 265 + gain2 * 338 + gain3 * 285; /* scale value to 0x0000-0xffff */ u16tmp = (0xffff - gain); - if (u16tmp < 59000) - u16tmp = 59000; - else if (u16tmp > 61500) - u16tmp = 61500; + u16tmp = clamp_val(u16tmp, 59000U, 61500U); *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); err: -- cgit v0.10.2 From 58b7450d56cacaa12d82ff9e2f5b461f65f2ade0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Dec 2013 13:11:21 -0300 Subject: [media] m88ds3103: remove unneeded AGC from inittab Optimal AGC is highly depended on used RF tuner and due to that it is already included to chip configuration. However, inittab has default AGC value, which was later replaced by one from config. Add also comment to all chip configuration options about default values and if those are needed to set or not. Reported-by: David Howells Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h index 287d62a..eaa5d10 100644 --- a/drivers/media/dvb-frontends/m88ds3103.h +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -26,26 +26,28 @@ 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 - * Note: Buffer is taken from the stack currently! - * Value must be set. + * 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 */ @@ -58,16 +60,19 @@ struct m88ds3103_config { /* * 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 @@ -76,9 +81,14 @@ struct m88ds3103_config { /* * DiSEqC envelope mode + * Default: 0 */ u8 envelope_mode:1; + /* + * AGC configuration + * Default: none, must set + */ u8 agc; }; diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index 322db4d..80c5a25 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -57,7 +57,6 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = { {0x30, 0x08}, {0x31, 0x40}, {0x32, 0x32}, - {0x33, 0x35}, {0x35, 0xff}, {0x3a, 0x00}, {0x37, 0x10}, @@ -139,7 +138,6 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = { {0x27, 0x31}, {0x30, 0x08}, {0x32, 0x32}, - {0x33, 0x35}, {0x35, 0xff}, {0x3a, 0x00}, {0x37, 0x10}, -- cgit v0.10.2 From dcaf0fbfd2fa5a36e6e1150576f7a8c3efc43f92 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Dec 2013 13:38:53 -0300 Subject: [media] m88ds3103: add default value for reg 56 Reg 0x56 should be programmed to 0x01. Add default to inittab. Reported-by: David Howells Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index 80c5a25..9cc29b4 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -71,6 +71,7 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = { {0x51, 0x36}, {0x52, 0x36}, {0x53, 0x36}, + {0x56, 0x01}, {0x63, 0x0f}, {0x64, 0x30}, {0x65, 0x40}, @@ -152,6 +153,7 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = { {0x51, 0x36}, {0x52, 0x36}, {0x53, 0x36}, + {0x56, 0x01}, {0x63, 0x0f}, {0x64, 0x10}, {0x65, 0x20}, -- cgit v0.10.2 From 06487dee53ae1574fb9637a4c243287ea92ab69d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Dec 2013 14:08:53 -0300 Subject: [media] m88ds3103: I/O optimize inittab write Write inittab using reg address auto-increment in order to reduce I/O a little bit. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index f9d8967..76bd85a 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -157,6 +157,38 @@ static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv, 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; @@ -214,7 +246,7 @@ 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, i, len; + int ret, len; const struct m88ds3103_reg_val *init; u8 u8tmp, u8tmp1, u8tmp2; u8 buf[2]; @@ -308,12 +340,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) /* program init table */ if (c->delivery_system != priv->delivery_system) { - dev_dbg(&priv->i2c->dev, "%s: program init\n", __func__); - for (i = 0; i < len; i++) { - ret = m88ds3103_wr_reg(priv, init[i].reg, init[i].val); - if (ret) - goto err; - } + ret = m88ds3103_wr_reg_val_tab(priv, init, len); + if (ret) + goto err; } u8tmp1 = 0; /* silence compiler warning */ -- cgit v0.10.2 From eafa2ad6e46c523da7d6970306167b24c861504f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Dec 2013 18:38:41 -0300 Subject: [media] m88ts2022: convert to Kernel I2C driver model Convert driver from proprietary DVB driver model to standard I2C driver model. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c index b11e740..1155603 100644 --- a/drivers/media/tuners/m88ts2022.c +++ b/drivers/media/tuners/m88ts2022.c @@ -32,7 +32,7 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, u8 buf[MAX_WR_XFER_LEN]; struct i2c_msg msg[1] = { { - .addr = priv->cfg->i2c_addr, + .addr = priv->client->addr, .flags = 0, .len = 1 + len, .buf = buf, @@ -45,11 +45,11 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, buf[0] = reg; memcpy(&buf[1], val, len); - ret = i2c_transfer(priv->i2c, msg, 1); + ret = i2c_transfer(priv->client->adapter, msg, 1); if (ret == 1) { ret = 0; } else { - dev_warn(&priv->i2c->dev, + dev_warn(&priv->client->dev, "%s: i2c wr failed=%d reg=%02x len=%d\n", KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; @@ -68,12 +68,12 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, u8 buf[MAX_RD_XFER_LEN]; struct i2c_msg msg[2] = { { - .addr = priv->cfg->i2c_addr, + .addr = priv->client->addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg->i2c_addr, + .addr = priv->client->addr, .flags = I2C_M_RD, .len = len, .buf = buf, @@ -83,12 +83,12 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, if (WARN_ON(len > MAX_RD_LEN)) return -EINVAL; - ret = i2c_transfer(priv->i2c, msg, 2); + ret = i2c_transfer(priv->client->adapter, msg, 2); if (ret == 2) { memcpy(val, buf, len); ret = 0; } else { - dev_warn(&priv->i2c->dev, + dev_warn(&priv->client->dev, "%s: i2c rd failed=%d reg=%02x len=%d\n", KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; @@ -144,7 +144,7 @@ static int m88ts2022_cmd(struct dvb_frontend *fe, }; for (i = 0; i < 2; i++) { - dev_dbg(&priv->i2c->dev, + dev_dbg(&priv->client->dev, "%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n", __func__, i, op, reg, mask, val); @@ -180,14 +180,14 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) 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->i2c->dev, + 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); + f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000); div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); if (c->symbol_rate < 5000000) @@ -230,7 +230,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) if (ret) goto err; - dev_dbg(&priv->i2c->dev, + 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, @@ -374,7 +374,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe) goto err; err: if (ret) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -397,7 +397,7 @@ static int m88ts2022_init(struct dvb_frontend *fe) {0x24, 0x02}, {0x12, 0xa0}, }; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&priv->client->dev, "%s:\n", __func__); ret = m88ts2022_wr_reg(priv, 0x00, 0x01); if (ret) @@ -407,13 +407,13 @@ static int m88ts2022_init(struct dvb_frontend *fe) if (ret) goto err; - switch (priv->cfg->clock_out) { + 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); + ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); if (ret) goto err; break; @@ -428,7 +428,7 @@ static int m88ts2022_init(struct dvb_frontend *fe) if (ret) goto err; - if (priv->cfg->loop_through) + if (priv->cfg.loop_through) u8tmp = 0xec; else u8tmp = 0x6c; @@ -444,7 +444,7 @@ static int m88ts2022_init(struct dvb_frontend *fe) } err: if (ret) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -452,21 +452,21 @@ static int m88ts2022_sleep(struct dvb_frontend *fe) { struct m88ts2022_priv *priv = fe->tuner_priv; int ret; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + 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->i2c->dev, "%s: failed=%d\n", __func__, 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->i2c->dev, "%s:\n", __func__); + dev_dbg(&priv->client->dev, "%s:\n", __func__); *frequency = priv->frequency_khz; return 0; @@ -475,7 +475,7 @@ static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { struct m88ts2022_priv *priv = fe->tuner_priv; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&priv->client->dev, "%s:\n", __func__); *frequency = 0; /* Zero-IF */ return 0; @@ -519,19 +519,10 @@ static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); err: if (ret) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); return ret; } -static int m88ts2022_release(struct dvb_frontend *fe) -{ - struct m88ts2022_priv *priv = fe->tuner_priv; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - - kfree(fe->tuner_priv); - return 0; -} - static const struct dvb_tuner_ops m88ts2022_tuner_ops = { .info = { .name = "Montage M88TS2022", @@ -539,8 +530,6 @@ static const struct dvb_tuner_ops m88ts2022_tuner_ops = { .frequency_max = 2150000, }, - .release = m88ts2022_release, - .init = m88ts2022_init, .sleep = m88ts2022_sleep, .set_params = m88ts2022_set_params, @@ -550,9 +539,11 @@ static const struct dvb_tuner_ops m88ts2022_tuner_ops = { .get_rf_strength = m88ts2022_get_rf_strength, }; -struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct m88ts2022_config *cfg) +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; @@ -560,13 +551,12 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, priv = kzalloc(sizeof(struct m88ts2022_priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; - dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); goto err; } - priv->cfg = cfg; - priv->i2c = i2c; - priv->fe = fe; + memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config)); + priv->client = client; /* check if the tuner is there */ ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp); @@ -591,7 +581,7 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, if (ret) goto err; - dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id); switch (chip_id) { case 0xc3: @@ -601,13 +591,13 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, goto err; } - switch (priv->cfg->clock_out) { + 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); + ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); if (ret) goto err; break; @@ -622,7 +612,7 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, if (ret) goto err; - if (priv->cfg->loop_through) + if (priv->cfg.loop_through) u8tmp = 0xec; else u8tmp = 0x6c; @@ -636,23 +626,52 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, if (ret) goto err; - dev_info(&priv->i2c->dev, + 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: - if (ret) { - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; - } + 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__); - return fe; + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = NULL; + kfree(priv); + + return 0; } -EXPORT_SYMBOL(m88ts2022_attach); + +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 "); diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h index fa1112c..1943b83 100644 --- a/drivers/media/tuners/m88ts2022.h +++ b/drivers/media/tuners/m88ts2022.h @@ -25,12 +25,6 @@ struct m88ts2022_config { /* - * I2C address - * 0x60, ... - */ - u8 i2c_addr; - - /* * clock * 16000000 - 32000000 */ @@ -54,19 +48,11 @@ struct m88ts2022_config { * 1 - 31 */ u8 clock_out_div:5; -}; -#if defined(CONFIG_MEDIA_TUNER_M88TS2022) || \ - (defined(CONFIG_MEDIA_TUNER_M88TS2022_MODULE) && defined(MODULE)) -extern struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct m88ts2022_config *cfg); -#else -static inline struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct m88ts2022_config *cfg) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif + /* + * pointer to DVB frontend + */ + struct dvb_frontend *fe; +}; #endif diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h index 190299a..a1fbb18 100644 --- a/drivers/media/tuners/m88ts2022_priv.h +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -24,8 +24,8 @@ #include "m88ts2022.h" struct m88ts2022_priv { - const struct m88ts2022_config *cfg; - struct i2c_adapter *i2c; + struct m88ts2022_config cfg; + struct i2c_client *client; struct dvb_frontend *fe; u32 frequency_khz; }; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 4f8f687..ddc0e60 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -89,6 +89,7 @@ struct em28xx_dvb { struct semaphore pll_mutex; bool dont_attach_fe1; int lna_gpio; + struct i2c_client *i2c_client_tuner; }; @@ -819,11 +820,6 @@ static const struct m88ds3103_config pctv_461e_m88ds3103_config = { .agc = 0x99, }; -static const struct m88ts2022_config em28xx_m88ts2022_config = { - .i2c_addr = 0x60, - .clock = 27000000, -}; - /* ------------------------------------------------------------------ */ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) @@ -1349,6 +1345,11 @@ static int em28xx_dvb_init(struct em28xx *dev) { /* 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, @@ -1361,13 +1362,12 @@ static int em28xx_dvb_init(struct em28xx *dev) } /* attach tuner */ - if (!dvb_attach(m88ts2022_attach, dvb->fe[0], - i2c_adapter, - &em28xx_m88ts2022_config)) { - dvb_frontend_detach(dvb->fe[0]); - result = -ENODEV; - goto out_free; - } + 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 = @@ -1445,6 +1445,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; -- cgit v0.10.2 From 4fc578768c81f2dd20eee037eaf1be44a19779b2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 8 Dec 2013 19:19:11 -0300 Subject: [media] m88ds3103: fix possible i2c deadlock Adapter is locked by I2C core already. Use unlocked i2c_transfer() version __i2c_transfer() to avoid deadlock. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 76bd85a..b5503ed 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1159,7 +1159,7 @@ static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) mutex_lock(&priv->i2c_mutex); /* open tuner I2C repeater for 1 xfer, closes automatically */ - ret = i2c_transfer(priv->i2c, gate_open_msg, 1); + 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); -- cgit v0.10.2 From 8a878dc4f6686ccb2f6e4d72e29825fe3427eeb0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 18 Dec 2013 12:38:42 -0300 Subject: [media] m88ds3103: fix some style issues reported by checkpatch.pl * remove Free Software Foundation postal address * add one pair of parenthesis * use sizeof(*foo), not sizeof(struct foo) Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index b5503ed..b8a7897 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "m88ds3103_priv.h" @@ -927,8 +923,9 @@ static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) if (signal > noise) { tmp = signal / noise; *snr = 100ul * intlog10(tmp) / (1 << 24); - } else + } else { *snr = 0; + } break; default: dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", @@ -1190,7 +1187,7 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, u8 chip_id, u8tmp; /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(struct m88ds3103_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h index eaa5d10..bbb7e3a 100644 --- a/drivers/media/dvb-frontends/m88ds3103.h +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef M88DS3103_H diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index 9cc29b4..84c3c06 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef M88DS3103_PRIV_H -- cgit v0.10.2 From b03129ff7b8ed4abe0b80e092ad71a6203c2d367 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 18 Dec 2013 12:46:45 -0300 Subject: [media] m88ts2022: fix some style issues reported by checkpatch.pl Latest checkpatch.pl has some new requirements for coding style. Fix some of those. * remove Free Software Foundation postal address * use sizeof(*foo), not sizeof(struct foo) Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c index 1155603..40c42de 100644 --- a/drivers/media/tuners/m88ts2022.c +++ b/drivers/media/tuners/m88ts2022.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Some calculations are taken from existing TS2020 driver. */ @@ -548,7 +544,7 @@ static int m88ts2022_probe(struct i2c_client *client, int ret; u8 chip_id, u8tmp; - priv = kzalloc(sizeof(struct m88ts2022_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h index 1943b83..659fa1b 100644 --- a/drivers/media/tuners/m88ts2022.h +++ b/drivers/media/tuners/m88ts2022.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef M88TS2022_H diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h index a1fbb18..0363dd8 100644 --- a/drivers/media/tuners/m88ts2022_priv.h +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef M88TS2022_PRIV_H -- cgit v0.10.2 From d22d32e117c19efa1761d871d9dab5e294b7b77d Mon Sep 17 00:00:00 2001 From: Robert Backhaus Date: Fri, 13 Dec 2013 09:59:10 -0300 Subject: [media] Add USB IDs for Winfast DTV Dongle Mini-D GIT_AUTHOR_DATE=1386943312 Add USB IDs for the WinFast DTV Dongle Mini. Device is tested and works fine under MythTV Signed-off-by: Robert Backhaus Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 8407178..f19a2cc 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -318,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 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index ecca036..fda5c64 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, -- cgit v0.10.2 From 4bf48150541b5f3335a6438f51b75f9d9d95e256 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 20 Dec 2013 08:11:31 -0200 Subject: [media] dib8000: fix compilation error As reported by kbuild test robot : with a random config: drivers/built-in.o: In function `dib8000_get_time_us.isra.16': >> dib8000.c:(.text+0x3075aa): undefined reference to `__udivdi3' Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 7539d7af..481ee49 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3951,7 +3951,7 @@ 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; + u64 time_us, tmp64; u32 tmp, denom; int guard, rate_num, rate_denum, bits_per_symbol, nsegs; int interleaving, fft_div; @@ -4048,7 +4048,9 @@ static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer) /* Estimate the period for the total bit rate */ time_us = rate_denum * (1008 * 1562500L); - time_us = time_us + time_us / guard; + tmp64 = time_us; + do_div(tmp64, guard); + time_us = time_us + tmp64; time_us += denom / 2; do_div(time_us, denom); -- cgit v0.10.2 From b8da9d3aaff26edcdf88751c6d2763f4337c79e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Dec 2013 07:35:22 -0300 Subject: [media] sn9c102: prepare for removal by moving it to staging During the last media summit meeting it was decided to move this driver to staging as the first step to removing it altogether. Most webcams covered by this driver are now supported by gspca. Nobody has the hardware to convert the remaining devices to gspca. This driver needs a major overhaul to have it conform to the latest frameworks and compliancy tests. Without hardware, however, this is next to impossible. Given the fact that this driver seems to be pretty much unused (it has been removed from Fedora several versions ago and nobody complained about that), we decided to drop this driver. This patch moves it to staging. Some time in 2014 we will drop it completely. Signed-off-by: Hans Verkuil Cc: Hans de Goede Cc: Luca Risolia Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt deleted file mode 100644 index b4f6704..0000000 --- a/Documentation/video4linux/sn9c102.txt +++ /dev/null @@ -1,592 +0,0 @@ - - SN9C1xx PC Camera Controllers - Driver for Linux - ============================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Optional device control through "sysfs" -9. Supported devices -10. Notes for V4L2 application developers -11. Video frame formats -12. Contact information -13. Credits - - -1. Copyright -============ -Copyright (C) 2004-2007 by Luca Risolia - - -2. Disclaimer -============= -SONiX is a trademark of SONiX Technology Company Limited, inc. -This software is not sponsored or developed by SONiX. - - -3. License -========== -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. - - -4. Overview and features -======================== -This driver attempts to support the video interface of the devices assembling -the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers -("SN9C1xx" from now on). - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the SN9C1xx driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- support for built-in microphone interface; -- support for any window resolutions and optional panning within the maximum - pixel area of image sensor; -- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both - directions (see "Notes for V4L2 application developers" paragraph); -- two different video formats for uncompressed or compressed data in low or - high compression quality (see also "Notes for V4L2 application developers" - and "Video frame formats" paragraphs); -- full support for the capabilities of many of the possible image sensors that - can be connected to the SN9C1xx bridges, including, for instance, red, green, - blue and global gain adjustments and exposure (see "Supported devices" - paragraph for details); -- use of default color settings for sunlight conditions; -- dynamic I/O interface for both SN9C1xx and image sensor control and - monitoring (see "Optional device control through 'sysfs'" paragraph); -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; -- no known bugs. - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - -To enable advanced debugging functionality on the device through /sysfs: - - # Multimedia devices - # - CONFIG_VIDEO_ADV_DEBUG=y - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone -interface. It is supported by the USB Audio driver thanks to the ALSA API: - - # Sound - # - CONFIG_SOUND=y - - # Advanced Linux Sound Architecture - # - CONFIG_SND=m - - # USB devices - # - CONFIG_SND_USB_AUDIO=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_SN9C102=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "sn9c102" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe sn9c102 - -Note that the module is called "sn9c102" for historic reasons, although it -does not just support the SN9C102. - -At this point all the devices supported by the driver and connected to the USB -ports should be recognized. You can invoke "dmesg" to analyze kernel messages -and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - -or, to isolate all the kernel messages generated by the driver: - - [user@localhost home]$ dmesg | grep sn9c102 - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - recognized camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: <0|n[,...]> -Description: Timeout for a video frame in seconds before returning an I/O - error; 0 for infinity. This parameter is specific for each - detected camera and can be changed at runtime thanks to the - /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only. It also shows some more - information about the hardware being detected. - This parameter can be changed at runtime thanks to the /sys - filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Optional device control through "sysfs" [1] -========================================== -If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the SN9C1xx and the image sensor -registers by using the "sysfs" filesystem interface. - -Every time a supported device is recognized, a write-only file named "green" is -created in the /sys/class/video4linux/videoX directory. You can set the green -channel's gain by writing the desired value to it. The value may range from 0 -to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, -SN9C105 and SN9C120 bridges. -Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red -gain control files are available in the same directory, for which accepted -values may range from 0 to 127. - -There are other four entries in the directory above for each registered camera: -"reg", "val", "i2c_reg" and "i2c_val". The first two files control the -SN9C1xx bridge, while the other two control the sensor chip. "reg" and -"i2c_reg" hold the values of the current register index where the following -reading/writing operations are addressed at through "val" and "i2c_val". Their -use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not -be created if the sensor does not actually support the standard I2C protocol or -its registers are not 8-bit long. Also, remember that you must be logged in as -root before writing to them. - -As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which is usually the product -identifier - of the camera registered as "/dev/video0": - - [root@localhost #] cd /sys/class/video4linux/video0 - [root@localhost #] echo 1 > i2c_reg - [root@localhost #] cat i2c_val - -Note that "cat" will fail if sensor registers cannot be read. - -Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: - - [root@localhost #] echo 0x11 > reg - [root@localhost #] echo 2 > val - -Note that the SN9C1xx always returns 0 when some of its registers are read. -To avoid race conditions, all the I/O accesses to the above files are -serialized. -The sysfs interface also provides the "frame_header" entry, which exports the -frame header of the most recent requested and captured video frame. The header -is always 18-bytes long and is appended to every video frame by the SN9C1xx -controllers. As an example, this additional information can be used by the user -application for implementing auto-exposure features via software. - -The following table describes the frame header exported by the SN9C101 and -SN9C102: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [3:0] Read channel gain control = (1+R_GAIN/8) - [7:4] Blue channel gain control = (1+B_GAIN/8) -0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0C 0xXX Not used -0x0D 0xXX Not used -0x0E 0xXX Not used -0x0F 0xXX Not used -0x10 0xXX Not used -0x11 0xXX Not used - -The following table describes the frame header exported by the SN9C103: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) -0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) - [7:4] -0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0D [1:0] Audio frame number - [ 2 ] 1 = Audio is recording -0x0E [7:0] Audio summation (low-byte) -0x0F [7:0] Audio summation (high-byte) -0x10 [7:0] Audio sample count -0x11 [7:0] Audio peak data in audio frame - -The AE area (sx, sy, ex, ey) in the active window can be set by programming the -registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit -corresponds to 32 pixels. - -[1] The frame headers exported by the SN9C105 and SN9C120 are not described. - - -9. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices assembling the SN9C1xx PC camera controllers: - -Vendor ID Product ID ---------- ---------- -0x0458 0x7025 -0x045e 0x00f5 -0x045e 0x00f7 -0x0471 0x0327 -0x0471 0x0328 -0x0c45 0x6001 -0x0c45 0x6005 -0x0c45 0x6007 -0x0c45 0x6009 -0x0c45 0x600d -0x0c45 0x6011 -0x0c45 0x6019 -0x0c45 0x6024 -0x0c45 0x6025 -0x0c45 0x6028 -0x0c45 0x6029 -0x0c45 0x602a -0x0c45 0x602b -0x0c45 0x602c -0x0c45 0x602d -0x0c45 0x602e -0x0c45 0x6030 -0x0c45 0x603f -0x0c45 0x6080 -0x0c45 0x6082 -0x0c45 0x6083 -0x0c45 0x6088 -0x0c45 0x608a -0x0c45 0x608b -0x0c45 0x608c -0x0c45 0x608e -0x0c45 0x608f -0x0c45 0x60a0 -0x0c45 0x60a2 -0x0c45 0x60a3 -0x0c45 0x60a8 -0x0c45 0x60aa -0x0c45 0x60ab -0x0c45 0x60ac -0x0c45 0x60ae -0x0c45 0x60af -0x0c45 0x60b0 -0x0c45 0x60b2 -0x0c45 0x60b3 -0x0c45 0x60b8 -0x0c45 0x60ba -0x0c45 0x60bb -0x0c45 0x60bc -0x0c45 0x60be -0x0c45 0x60c0 -0x0c45 0x60c2 -0x0c45 0x60c8 -0x0c45 0x60cc -0x0c45 0x60ea -0x0c45 0x60ec -0x0c45 0x60ef -0x0c45 0x60fa -0x0c45 0x60fb -0x0c45 0x60fc -0x0c45 0x60fe -0x0c45 0x6102 -0x0c45 0x6108 -0x0c45 0x610f -0x0c45 0x6130 -0x0c45 0x6138 -0x0c45 0x613a -0x0c45 0x613b -0x0c45 0x613c -0x0c45 0x613e - -The list above does not imply that all those devices work with this driver: up -until now only the ones that assemble the following pairs of SN9C1xx bridges -and image sensors are supported; kernel messages will always tell you whether -this is the case (see "Module loading" paragraph): - -Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 -------------------------------------------------------------------------------- -HV7131D Hynix Semiconductor | Yes No No No -HV7131R Hynix Semiconductor | No Yes Yes Yes -MI-0343 Micron Technology | Yes No No No -MI-0360 Micron Technology | No Yes Yes Yes -OV7630 OmniVision Technologies | Yes Yes Yes Yes -OV7660 OmniVision Technologies | No No Yes Yes -PAS106B PixArt Imaging | Yes No No No -PAS202B PixArt Imaging | Yes Yes No No -TAS5110C1B Taiwan Advanced Sensor | Yes No No No -TAS5110D Taiwan Advanced Sensor | Yes No No No -TAS5130D1B Taiwan Advanced Sensor | Yes No No No - -"Yes" means that the pair is supported by the driver, while "No" means that the -pair does not exist or is not supported by the driver. - -Only some of the available control settings of each image sensor are supported -through the V4L2 interface. - -Donations of new models for further testing and support would be much -appreciated. Non-available hardware will not be supported by the author of this -driver. - - -10. Notes for V4L2 application developers -========================================= -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - -Consistently with the hardware limits, this driver also supports image -downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. -However, the V4L2 API specifications don't correctly define how the scaling -factor can be chosen arbitrarily by the "negotiation" of the "source" and -"target" rectangles. To work around this flaw, we have added the convention -that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the -scaling factor is restored to 1. - -This driver supports two different video formats: the first one is the "8-bit -Sequential Bayer" format and can be used to obtain uncompressed video data -from the device through the current I/O method, while the second one provides -either "raw" compressed video data (without frame headers not related to the -compressed data) or standard JPEG (with frame headers). The compression quality -may vary from 0 to 1 and can be selected or queried thanks to the -VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum flexibility, -both the default active video format and the default compression quality -depend on how the image sensor being used is initialized. - - -11. Video frame formats [1] -======================= -The SN9C1xx PC Camera Controllers can send images in two possible video -formats over the USB: either native "Sequential RGB Bayer" or compressed. -The compression is used to achieve high frame rates. With regard to the -SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding -algorithm described below, while with regard to the SN9C105 and SN9C120 the -compression is based on the JPEG standard. -The current video format may be selected or queried from the user application -by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 -API specifications. - -The name "Sequential Bayer" indicates the organization of the red, green and -blue pixels in one video frame. Each pixel is associated with a 8-bit long -value and is disposed in memory according to the pattern shown below: - -B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] -G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] -... -... B[(n-1)(m-2)] G[(n-1)(m-1)] -... G[n(m-2)] R[n(m-1)] - -The above matrix also represents the sequential or progressive read-out mode of -the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. - -The Huffman compressed video frame consists of a bitstream that encodes for -every R, G, or B pixel the difference between the value of the pixel itself and -some reference pixel value. Pixels are organised in the Bayer pattern and the -Bayer sub-pixels are tracked individually and alternatingly. For example, in -the first line values for the B and G1 pixels are alternatingly encoded, while -in the second line values for the G2 and R pixels are alternatingly encoded. - -The pixel reference value is calculated as follows: -- the 4 top left pixels are encoded in raw uncompressed 8-bit format; -- the value in the top two rows is the value of the pixel left of the current - pixel; -- the value in the left column is the value of the pixel above the current - pixel; -- for all other pixels, the reference value is the average of the value of the - pixel on the left and the value of the pixel above the current pixel; -- there is one code in the bitstream that specifies the value of a pixel - directly (in 4-bit resolution); -- pixel values need to be clamped inside the range [0..255] for proper - decoding. - -The algorithm purely describes the conversion from compressed Bayer code used -in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional -steps are required to convert this to a color image (i.e. a color interpolation -algorithm). - -The following Huffman codes have been found: -0: +0 (relative to reference pixel value) -100: +4 -101: -4? -1110xxxx: set absolute value to xxxx.0000 -1101: +11 -1111: -11 -11001: +20 -110000: -20 -110001: ??? - these codes are apparently not used - -[1] The Huffman compression algorithm has been reverse-engineered and - documented by Bertrik Sikken. - - -12. Contact information -======================= -The author may be contacted by e-mail at . - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -13. Credits -=========== -Many thanks to following persons for their contribute (listed in alphabetical -order): - -- David Anderson for the donation of a webcam; -- Luca Capello for the donation of a webcam; -- Philippe Coval for having helped testing the PAS202BCA image sensor; -- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the - donation of a webcam; -- Dennis Heitmann for the donation of a webcam; -- Jon Hollstrom for the donation of a webcam; -- Nick McGill for the donation of a webcam; -- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB - image sensor; -- Stefano Mozzi, who donated 45 EU; -- Andrew Pearce for the donation of a webcam; -- John Pullan for the donation of a webcam; -- Bertrik Sikken, who reverse-engineered and documented the Huffman compression - algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and - implemented the first decoder; -- Ronny Standke for the donation of a webcam; -- Mizuno Takafumi for the donation of a webcam; -- an "anonymous" donator (who didn't want his name to be revealed) for the - donation of a webcam. -- an anonymous donator for the donation of four webcams and two boards with ten - image sensors. diff --git a/MAINTAINERS b/MAINTAINERS index 71399a2..ac86426 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9053,8 +9053,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://www.linux-projects.org S: Maintained -F: Documentation/video4linux/sn9c102.txt -F: drivers/media/usb/sn9c102/ +F: drivers/staging/media/sn9c102/ USB SUBSYSTEM M: Greg Kroah-Hartman diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index cfe8056..39d824e 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 0935f47..7ac4b14 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/sn9c102/Kconfig b/drivers/media/usb/sn9c102/Kconfig deleted file mode 100644 index 6ebaf29..0000000 --- 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 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 7ecd5a9..0000000 --- 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 8a917f06..0000000 --- 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 * - * * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 0f4e037..0000000 --- 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 * - * * - * 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 -#include - -#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 2cb44de..0000000 --- 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 * - * * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "" -#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, - " " - "\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 b3d2cc7..0000000 --- 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 * - * * - * 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 - -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 2dce5c9..0000000 --- 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 * - * * - * 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 ", - .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 4295887..0000000 --- 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 * - * * - * 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 ", - .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 1f5b09b..0000000 --- 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 * - * * - * 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 ", - .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 d973fc1..0000000 --- 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 * - * * - * 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 ", - .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 95986eb..0000000 --- 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 * - * * - * 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 ", - .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 803712c..0000000 --- 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 * - * * - * 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 ", - .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 7977795..0000000 --- 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 * - * * - * 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 ", - .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 81cd969..0000000 --- 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 * - * * - * 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 -#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 ", - .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 2e86fdc..0000000 --- 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 * - * * - * * - * Support for SN9C103, DAC Magnitude, exposure and green gain controls * - * added by Luca Risolia * - * * - * 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 -#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 ", - .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 3679970..0000000 --- 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 * - * * - * 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 -#include -#include -#include -#include -#include - -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 */ - - 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 04cdfdd..0000000 --- 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 * - * * - * 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 ", - .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 9372e6f..0000000 --- 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 * - * * - * 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 ", - .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 a30bbc4..0000000 --- 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 * - * * - * 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 ", - .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/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7728879..6a202170 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -33,6 +33,8 @@ source "drivers/staging/media/go7007/Kconfig" source "drivers/staging/media/msi3101/Kconfig" +source "drivers/staging/media/sn9c102/Kconfig" + source "drivers/staging/media/solo6x10/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 0bd1a88..2a15451 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ +obj-$(CONFIG_USB_SN9C102) += sn9c102/ diff --git a/drivers/staging/media/sn9c102/Kconfig b/drivers/staging/media/sn9c102/Kconfig new file mode 100644 index 0000000..d8ae235 --- /dev/null +++ b/drivers/staging/media/sn9c102/Kconfig @@ -0,0 +1,17 @@ +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 for more info. + + If you have webcams that are only supported by this driver and not by + the gspca driver, then contact the linux-media mailinglist. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/drivers/staging/media/sn9c102/Makefile b/drivers/staging/media/sn9c102/Makefile new file mode 100644 index 0000000..7ecd5a9 --- /dev/null +++ b/drivers/staging/media/sn9c102/Makefile @@ -0,0 +1,15 @@ +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/staging/media/sn9c102/sn9c102.h b/drivers/staging/media/sn9c102/sn9c102.h new file mode 100644 index 0000000..8a917f06 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102.h @@ -0,0 +1,214 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/staging/media/sn9c102/sn9c102.txt b/drivers/staging/media/sn9c102/sn9c102.txt new file mode 100644 index 0000000..b4f6704 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102.txt @@ -0,0 +1,592 @@ + + SN9C1xx PC Camera Controllers + Driver for Linux + ============================= + + - Documentation - + + +Index +===== +1. Copyright +2. Disclaimer +3. License +4. Overview and features +5. Module dependencies +6. Module loading +7. Module parameters +8. Optional device control through "sysfs" +9. Supported devices +10. Notes for V4L2 application developers +11. Video frame formats +12. Contact information +13. Credits + + +1. Copyright +============ +Copyright (C) 2004-2007 by Luca Risolia + + +2. Disclaimer +============= +SONiX is a trademark of SONiX Technology Company Limited, inc. +This software is not sponsored or developed by SONiX. + + +3. License +========== +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. + + +4. Overview and features +======================== +This driver attempts to support the video interface of the devices assembling +the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers +("SN9C1xx" from now on). + +The driver relies on the Video4Linux2 and USB core modules. It has been +designed to run properly on SMP systems as well. + +The latest version of the SN9C1xx driver can be found at the following URL: +http://www.linux-projects.org/ + +Some of the features of the driver are: + +- full compliance with the Video4Linux2 API (see also "Notes for V4L2 + application developers" paragraph); +- available mmap or read/poll methods for video streaming through isochronous + data transfers; +- automatic detection of image sensor; +- support for built-in microphone interface; +- support for any window resolutions and optional panning within the maximum + pixel area of image sensor; +- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both + directions (see "Notes for V4L2 application developers" paragraph); +- two different video formats for uncompressed or compressed data in low or + high compression quality (see also "Notes for V4L2 application developers" + and "Video frame formats" paragraphs); +- full support for the capabilities of many of the possible image sensors that + can be connected to the SN9C1xx bridges, including, for instance, red, green, + blue and global gain adjustments and exposure (see "Supported devices" + paragraph for details); +- use of default color settings for sunlight conditions; +- dynamic I/O interface for both SN9C1xx and image sensor control and + monitoring (see "Optional device control through 'sysfs'" paragraph); +- dynamic driver control thanks to various module parameters (see "Module + parameters" paragraph); +- up to 64 cameras can be handled at the same time; they can be connected and + disconnected from the host many times without turning off the computer, if + the system supports hotplugging; +- no known bugs. + + +5. Module dependencies +====================== +For it to work properly, the driver needs kernel support for Video4Linux and +USB. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + +To enable advanced debugging functionality on the device through /sysfs: + + # Multimedia devices + # + CONFIG_VIDEO_ADV_DEBUG=y + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, the modules below are +necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI_HCD=m + CONFIG_USB_OHCI_HCD=m + +The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone +interface. It is supported by the USB Audio driver thanks to the ALSA API: + + # Sound + # + CONFIG_SOUND=y + + # Advanced Linux Sound Architecture + # + CONFIG_SND=m + + # USB devices + # + CONFIG_SND_USB_AUDIO=m + +And finally: + + # USB Multimedia devices + # + CONFIG_USB_SN9C102=m + + +6. Module loading +================= +To use the driver, it is necessary to load the "sn9c102" module into memory +after every other module required: "videodev", "v4l2_common", "compat_ioctl32", +"usbcore" and, depending on the USB host controller you have, "ehci-hcd", +"uhci-hcd" or "ohci-hcd". + +Loading can be done as shown below: + + [root@localhost home]# modprobe sn9c102 + +Note that the module is called "sn9c102" for historic reasons, although it +does not just support the SN9C102. + +At this point all the devices supported by the driver and connected to the USB +ports should be recognized. You can invoke "dmesg" to analyze kernel messages +and verify that the loading process has gone well: + + [user@localhost home]$ dmesg + +or, to isolate all the kernel messages generated by the driver: + + [user@localhost home]$ dmesg | grep sn9c102 + + +7. Module parameters +==================== +Module parameters are listed below: +------------------------------------------------------------------------------- +Name: video_nr +Type: short array (min = 0, max = 64) +Syntax: <-1|n[,...]> +Description: Specify V4L2 minor mode number: + -1 = use next available + n = use minor number n + You can specify up to 64 cameras this way. + For example: + video_nr=-1,2,-1 would assign minor number 2 to the second + recognized camera and use auto for the first one and for every + other camera. +Default: -1 +------------------------------------------------------------------------------- +Name: force_munmap +Type: bool array (min = 0, max = 64) +Syntax: <0|1[,...]> +Description: Force the application to unmap previously mapped buffer memory + before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not + all the applications support this feature. This parameter is + specific for each detected camera. + 0 = do not force memory unmapping + 1 = force memory unmapping (save memory) +Default: 0 +------------------------------------------------------------------------------- +Name: frame_timeout +Type: uint array (min = 0, max = 64) +Syntax: <0|n[,...]> +Description: Timeout for a video frame in seconds before returning an I/O + error; 0 for infinity. This parameter is specific for each + detected camera and can be changed at runtime thanks to the + /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- +Name: debug +Type: ushort +Syntax: +Description: Debugging information level, from 0 to 3: + 0 = none (use carefully) + 1 = critical errors + 2 = significant information + 3 = more verbose messages + Level 3 is useful for testing only. It also shows some more + information about the hardware being detected. + This parameter can be changed at runtime thanks to the /sys + filesystem interface. +Default: 2 +------------------------------------------------------------------------------- + + +8. Optional device control through "sysfs" [1] +========================================== +If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, +it is possible to read and write both the SN9C1xx and the image sensor +registers by using the "sysfs" filesystem interface. + +Every time a supported device is recognized, a write-only file named "green" is +created in the /sys/class/video4linux/videoX directory. You can set the green +channel's gain by writing the desired value to it. The value may range from 0 +to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, +SN9C105 and SN9C120 bridges. +Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red +gain control files are available in the same directory, for which accepted +values may range from 0 to 127. + +There are other four entries in the directory above for each registered camera: +"reg", "val", "i2c_reg" and "i2c_val". The first two files control the +SN9C1xx bridge, while the other two control the sensor chip. "reg" and +"i2c_reg" hold the values of the current register index where the following +reading/writing operations are addressed at through "val" and "i2c_val". Their +use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not +be created if the sensor does not actually support the standard I2C protocol or +its registers are not 8-bit long. Also, remember that you must be logged in as +root before writing to them. + +As an example, suppose we were to want to read the value contained in the +register number 1 of the sensor register table - which is usually the product +identifier - of the camera registered as "/dev/video0": + + [root@localhost #] cd /sys/class/video4linux/video0 + [root@localhost #] echo 1 > i2c_reg + [root@localhost #] cat i2c_val + +Note that "cat" will fail if sensor registers cannot be read. + +Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: + + [root@localhost #] echo 0x11 > reg + [root@localhost #] echo 2 > val + +Note that the SN9C1xx always returns 0 when some of its registers are read. +To avoid race conditions, all the I/O accesses to the above files are +serialized. +The sysfs interface also provides the "frame_header" entry, which exports the +frame header of the most recent requested and captured video frame. The header +is always 18-bytes long and is appended to every video frame by the SN9C1xx +controllers. As an example, this additional information can be used by the user +application for implementing auto-exposure features via software. + +The following table describes the frame header exported by the SN9C101 and +SN9C102: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [3:0] Read channel gain control = (1+R_GAIN/8) + [7:4] Blue channel gain control = (1+B_GAIN/8) +0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0C 0xXX Not used +0x0D 0xXX Not used +0x0E 0xXX Not used +0x0F 0xXX Not used +0x10 0xXX Not used +0x11 0xXX Not used + +The following table describes the frame header exported by the SN9C103: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) +0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) + [7:4] +0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0D [1:0] Audio frame number + [ 2 ] 1 = Audio is recording +0x0E [7:0] Audio summation (low-byte) +0x0F [7:0] Audio summation (high-byte) +0x10 [7:0] Audio sample count +0x11 [7:0] Audio peak data in audio frame + +The AE area (sx, sy, ex, ey) in the active window can be set by programming the +registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit +corresponds to 32 pixels. + +[1] The frame headers exported by the SN9C105 and SN9C120 are not described. + + +9. Supported devices +==================== +None of the names of the companies as well as their products will be mentioned +here. They have never collaborated with the author, so no advertising. + +From the point of view of a driver, what unambiguously identify a device are +its vendor and product USB identifiers. Below is a list of known identifiers of +devices assembling the SN9C1xx PC camera controllers: + +Vendor ID Product ID +--------- ---------- +0x0458 0x7025 +0x045e 0x00f5 +0x045e 0x00f7 +0x0471 0x0327 +0x0471 0x0328 +0x0c45 0x6001 +0x0c45 0x6005 +0x0c45 0x6007 +0x0c45 0x6009 +0x0c45 0x600d +0x0c45 0x6011 +0x0c45 0x6019 +0x0c45 0x6024 +0x0c45 0x6025 +0x0c45 0x6028 +0x0c45 0x6029 +0x0c45 0x602a +0x0c45 0x602b +0x0c45 0x602c +0x0c45 0x602d +0x0c45 0x602e +0x0c45 0x6030 +0x0c45 0x603f +0x0c45 0x6080 +0x0c45 0x6082 +0x0c45 0x6083 +0x0c45 0x6088 +0x0c45 0x608a +0x0c45 0x608b +0x0c45 0x608c +0x0c45 0x608e +0x0c45 0x608f +0x0c45 0x60a0 +0x0c45 0x60a2 +0x0c45 0x60a3 +0x0c45 0x60a8 +0x0c45 0x60aa +0x0c45 0x60ab +0x0c45 0x60ac +0x0c45 0x60ae +0x0c45 0x60af +0x0c45 0x60b0 +0x0c45 0x60b2 +0x0c45 0x60b3 +0x0c45 0x60b8 +0x0c45 0x60ba +0x0c45 0x60bb +0x0c45 0x60bc +0x0c45 0x60be +0x0c45 0x60c0 +0x0c45 0x60c2 +0x0c45 0x60c8 +0x0c45 0x60cc +0x0c45 0x60ea +0x0c45 0x60ec +0x0c45 0x60ef +0x0c45 0x60fa +0x0c45 0x60fb +0x0c45 0x60fc +0x0c45 0x60fe +0x0c45 0x6102 +0x0c45 0x6108 +0x0c45 0x610f +0x0c45 0x6130 +0x0c45 0x6138 +0x0c45 0x613a +0x0c45 0x613b +0x0c45 0x613c +0x0c45 0x613e + +The list above does not imply that all those devices work with this driver: up +until now only the ones that assemble the following pairs of SN9C1xx bridges +and image sensors are supported; kernel messages will always tell you whether +this is the case (see "Module loading" paragraph): + +Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 +------------------------------------------------------------------------------- +HV7131D Hynix Semiconductor | Yes No No No +HV7131R Hynix Semiconductor | No Yes Yes Yes +MI-0343 Micron Technology | Yes No No No +MI-0360 Micron Technology | No Yes Yes Yes +OV7630 OmniVision Technologies | Yes Yes Yes Yes +OV7660 OmniVision Technologies | No No Yes Yes +PAS106B PixArt Imaging | Yes No No No +PAS202B PixArt Imaging | Yes Yes No No +TAS5110C1B Taiwan Advanced Sensor | Yes No No No +TAS5110D Taiwan Advanced Sensor | Yes No No No +TAS5130D1B Taiwan Advanced Sensor | Yes No No No + +"Yes" means that the pair is supported by the driver, while "No" means that the +pair does not exist or is not supported by the driver. + +Only some of the available control settings of each image sensor are supported +through the V4L2 interface. + +Donations of new models for further testing and support would be much +appreciated. Non-available hardware will not be supported by the author of this +driver. + + +10. Notes for V4L2 application developers +========================================= +This driver follows the V4L2 API specifications. In particular, it enforces two +rules: + +- exactly one I/O method, either "mmap" or "read", is associated with each +file descriptor. Once it is selected, the application must close and reopen the +device to switch to the other I/O method; + +- although it is not mandatory, previously mapped buffer memory should always +be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. +The same number of buffers as before will be allocated again to match the size +of the new video frames, so you have to map the buffers again before any I/O +attempts on them. + +Consistently with the hardware limits, this driver also supports image +downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. +However, the V4L2 API specifications don't correctly define how the scaling +factor can be chosen arbitrarily by the "negotiation" of the "source" and +"target" rectangles. To work around this flaw, we have added the convention +that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the +scaling factor is restored to 1. + +This driver supports two different video formats: the first one is the "8-bit +Sequential Bayer" format and can be used to obtain uncompressed video data +from the device through the current I/O method, while the second one provides +either "raw" compressed video data (without frame headers not related to the +compressed data) or standard JPEG (with frame headers). The compression quality +may vary from 0 to 1 and can be selected or queried thanks to the +VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum flexibility, +both the default active video format and the default compression quality +depend on how the image sensor being used is initialized. + + +11. Video frame formats [1] +======================= +The SN9C1xx PC Camera Controllers can send images in two possible video +formats over the USB: either native "Sequential RGB Bayer" or compressed. +The compression is used to achieve high frame rates. With regard to the +SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding +algorithm described below, while with regard to the SN9C105 and SN9C120 the +compression is based on the JPEG standard. +The current video format may be selected or queried from the user application +by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 +API specifications. + +The name "Sequential Bayer" indicates the organization of the red, green and +blue pixels in one video frame. Each pixel is associated with a 8-bit long +value and is disposed in memory according to the pattern shown below: + +B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] +G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] +... +... B[(n-1)(m-2)] G[(n-1)(m-1)] +... G[n(m-2)] R[n(m-1)] + +The above matrix also represents the sequential or progressive read-out mode of +the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. + +The Huffman compressed video frame consists of a bitstream that encodes for +every R, G, or B pixel the difference between the value of the pixel itself and +some reference pixel value. Pixels are organised in the Bayer pattern and the +Bayer sub-pixels are tracked individually and alternatingly. For example, in +the first line values for the B and G1 pixels are alternatingly encoded, while +in the second line values for the G2 and R pixels are alternatingly encoded. + +The pixel reference value is calculated as follows: +- the 4 top left pixels are encoded in raw uncompressed 8-bit format; +- the value in the top two rows is the value of the pixel left of the current + pixel; +- the value in the left column is the value of the pixel above the current + pixel; +- for all other pixels, the reference value is the average of the value of the + pixel on the left and the value of the pixel above the current pixel; +- there is one code in the bitstream that specifies the value of a pixel + directly (in 4-bit resolution); +- pixel values need to be clamped inside the range [0..255] for proper + decoding. + +The algorithm purely describes the conversion from compressed Bayer code used +in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional +steps are required to convert this to a color image (i.e. a color interpolation +algorithm). + +The following Huffman codes have been found: +0: +0 (relative to reference pixel value) +100: +4 +101: -4? +1110xxxx: set absolute value to xxxx.0000 +1101: +11 +1111: -11 +11001: +20 +110000: -20 +110001: ??? - these codes are apparently not used + +[1] The Huffman compression algorithm has been reverse-engineered and + documented by Bertrik Sikken. + + +12. Contact information +======================= +The author may be contacted by e-mail at . + +GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is +'FCE635A4'; the public 1024-bit key should be available at any keyserver; +the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. + + +13. Credits +=========== +Many thanks to following persons for their contribute (listed in alphabetical +order): + +- David Anderson for the donation of a webcam; +- Luca Capello for the donation of a webcam; +- Philippe Coval for having helped testing the PAS202BCA image sensor; +- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the + donation of a webcam; +- Dennis Heitmann for the donation of a webcam; +- Jon Hollstrom for the donation of a webcam; +- Nick McGill for the donation of a webcam; +- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB + image sensor; +- Stefano Mozzi, who donated 45 EU; +- Andrew Pearce for the donation of a webcam; +- John Pullan for the donation of a webcam; +- Bertrik Sikken, who reverse-engineered and documented the Huffman compression + algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and + implemented the first decoder; +- Ronny Standke for the donation of a webcam; +- Mizuno Takafumi for the donation of a webcam; +- an "anonymous" donator (who didn't want his name to be revealed) for the + donation of a webcam. +- an anonymous donator for the donation of four webcams and two boards with ten + image sensors. diff --git a/drivers/staging/media/sn9c102/sn9c102_config.h b/drivers/staging/media/sn9c102/sn9c102_config.h new file mode 100644 index 0000000..0f4e037 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_config.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 +#include + +#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/staging/media/sn9c102/sn9c102_core.c b/drivers/staging/media/sn9c102/sn9c102_core.c new file mode 100644 index 0000000..2cb44de --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_core.c @@ -0,0 +1,3434 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "" +#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, + " " + "\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/staging/media/sn9c102/sn9c102_devtable.h b/drivers/staging/media/sn9c102/sn9c102_devtable.h new file mode 100644 index 0000000..b3d2cc7 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_devtable.h @@ -0,0 +1,147 @@ +/*************************************************************************** + * Table of device identifiers of the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 + +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/staging/media/sn9c102/sn9c102_hv7131d.c b/drivers/staging/media/sn9c102/sn9c102_hv7131d.c new file mode 100644 index 0000000..2dce5c9 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_hv7131d.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_hv7131r.c b/drivers/staging/media/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 0000000..4295887 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_mi0343.c b/drivers/staging/media/sn9c102/sn9c102_mi0343.c new file mode 100644 index 0000000..1f5b09b --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mi0343.c @@ -0,0 +1,352 @@ +/*************************************************************************** + * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_mi0360.c b/drivers/staging/media/sn9c102/sn9c102_mi0360.c new file mode 100644 index 0000000..d973fc1 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,453 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_mt9v111.c b/drivers/staging/media/sn9c102/sn9c102_mt9v111.c new file mode 100644 index 0000000..95986eb --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mt9v111.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_ov7630.c b/drivers/staging/media/sn9c102/sn9c102_ov7630.c new file mode 100644 index 0000000..803712c --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_ov7630.c @@ -0,0 +1,626 @@ +/*************************************************************************** + * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2006-2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_ov7660.c b/drivers/staging/media/sn9c102/sn9c102_ov7660.c new file mode 100644 index 0000000..7977795 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_ov7660.c @@ -0,0 +1,538 @@ +/*************************************************************************** + * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_pas106b.c b/drivers/staging/media/sn9c102/sn9c102_pas106b.c new file mode 100644 index 0000000..81cd969 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_pas106b.c @@ -0,0 +1,302 @@ +/*************************************************************************** + * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 +#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 ", + .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/staging/media/sn9c102/sn9c102_pas202bcb.c b/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c new file mode 100644 index 0000000..2e86fdc --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c @@ -0,0 +1,335 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * * + * Support for SN9C103, DAC Magnitude, exposure and green gain controls * + * added by Luca Risolia * + * * + * 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 +#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 ", + .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/staging/media/sn9c102/sn9c102_sensor.h b/drivers/staging/media/sn9c102/sn9c102_sensor.h new file mode 100644 index 0000000..3679970 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_sensor.h @@ -0,0 +1,307 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 +#include +#include +#include +#include +#include + +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 */ + + 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/staging/media/sn9c102/sn9c102_tas5110c1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c new file mode 100644 index 0000000..04cdfdd --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_tas5110d.c b/drivers/staging/media/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 0000000..9372e6f --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 ", + .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/staging/media/sn9c102/sn9c102_tas5130d1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c new file mode 100644 index 0000000..a30bbc4 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c @@ -0,0 +1,165 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 ", + .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; +} -- cgit v0.10.2 From a03636cb21af1d22a894a5a863b2ba3c3687da63 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Dec 2013 09:04:44 -0300 Subject: [media] omap24xx/tcm825x: move to staging for future removal The omap24xx driver and the tcm825x sensor driver are the only two remaining drivers to still use the old deprecated v4l2-int-device API. Nobody maintains these drivers anymore. But unfortunately the v4l2-int-device API is used by out-of-tree drivers (MXC platform). This is a very bad situation since as long as this deprecated API stays in the kernel there is no reason for those out-of-tree drivers to convert. This patch moves v4l2-int-device and the two drivers that depend on it to staging in preparation for their removal. If someone would be interested in getting these drivers to work, then start with this since it's not very far from the state where they used to work: The branch is n800-cam. Porting to up-to-date APIs can then be done. David might have done some work in that area, so check with him first. Signed-off-by: Hans Verkuil Cc: Sakari Ailus Cc: David Cohen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 842654d..997cd66 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 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e03f177..abd25e3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -57,7 +57,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 diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c deleted file mode 100644 index 9252529..0000000 --- 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 - * - * Based on code from David Cohen - * - * 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 -#include -#include - -#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 "); -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 8ebab95..0000000 --- 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 - -#include - -#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/platform/Kconfig b/drivers/media/platform/Kconfig index 7f6ea65..b2a4403 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -91,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 1348ba1..e5269da 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/omap24xxcam-dma.c b/drivers/media/platform/omap24xxcam-dma.c deleted file mode 100644 index 9c00776..0000000 --- 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 - * - * Based on code from Andy Lowe and - * David Cohen . - * - * 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 -#include -#include - -#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 d2b440c..0000000 --- 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 - * - * Based on code from Andy Lowe - * - * 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 -#include -#include -#include -#include /* needed for videobufs */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#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 "); -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 7f6f791..0000000 --- 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 - * - * Based on code from Andy Lowe . - * - * 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 -#include -#include - -/* - * - * 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/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 8c05565..2189bfb 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 1a85eee..c6ae7ba 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-int-device.c b/drivers/media/v4l2-core/v4l2-int-device.c deleted file mode 100644 index f447349..0000000 --- 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 - * - * 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 -#include -#include -#include -#include - -#include - -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/staging/media/Kconfig b/drivers/staging/media/Kconfig index 6a202170..22b0c9d 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -33,6 +33,8 @@ source "drivers/staging/media/go7007/Kconfig" source "drivers/staging/media/msi3101/Kconfig" +source "drivers/staging/media/omap24xx/Kconfig" + source "drivers/staging/media/sn9c102/Kconfig" source "drivers/staging/media/solo6x10/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 2a15451..bedc62a 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -9,3 +9,5 @@ obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_USB_SN9C102) += sn9c102/ +obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/ +obj-$(CONFIG_VIDEO_TCM825X) += omap24xx/ diff --git a/drivers/staging/media/omap24xx/Kconfig b/drivers/staging/media/omap24xx/Kconfig new file mode 100644 index 0000000..82e569a --- /dev/null +++ b/drivers/staging/media/omap24xx/Kconfig @@ -0,0 +1,35 @@ +config VIDEO_V4L2_INT_DEVICE + tristate + +config VIDEO_OMAP2 + tristate "OMAP2 Camera Capture Interface driver (DEPRECATED)" + depends on VIDEO_DEV && ARCH_OMAP2 + select VIDEOBUF_DMA_SG + select VIDEO_V4L2_INT_DEVICE + ---help--- + This is a v4l2 driver for the TI OMAP2 camera capture interface + + It uses the deprecated int-device API. Since this driver is no + longer actively maintained and nobody is interested in converting + it to the subdev API, this driver will be removed soon. + + If you do want to keep this driver in the kernel, and are willing + to convert it to the subdev API, then please contact the linux-media + mailinglist. + +config VIDEO_TCM825X + tristate "TCM825x camera sensor support (DEPRECATED)" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_V4L2_INT_DEVICE + ---help--- + This is a driver for the Toshiba TCM825x VGA camera sensor. + It is used for example in Nokia N800. + + It uses the deprecated int-device API. Since this driver is no + longer actively maintained and nobody is interested in converting + it to the subdev API, this driver will be removed soon. + + If you do want to keep this driver in the kernel, and are willing + to convert it to the subdev API, then please contact the linux-media + mailinglist. diff --git a/drivers/staging/media/omap24xx/Makefile b/drivers/staging/media/omap24xx/Makefile new file mode 100644 index 0000000..c2e7175 --- /dev/null +++ b/drivers/staging/media/omap24xx/Makefile @@ -0,0 +1,5 @@ +omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o + +obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o +obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o +obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o diff --git a/drivers/staging/media/omap24xx/omap24xxcam-dma.c b/drivers/staging/media/omap24xx/omap24xxcam-dma.c new file mode 100644 index 0000000..9c00776 --- /dev/null +++ b/drivers/staging/media/omap24xx/omap24xxcam-dma.c @@ -0,0 +1,601 @@ +/* + * 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 + * + * Based on code from Andy Lowe and + * David Cohen . + * + * 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 +#include +#include + +#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/staging/media/omap24xx/omap24xxcam.c b/drivers/staging/media/omap24xx/omap24xxcam.c new file mode 100644 index 0000000..d2b440c --- /dev/null +++ b/drivers/staging/media/omap24xx/omap24xxcam.c @@ -0,0 +1,1888 @@ +/* + * 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 + * + * Based on code from Andy Lowe + * + * 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 +#include +#include +#include +#include /* needed for videobufs */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 "); +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/staging/media/omap24xx/omap24xxcam.h b/drivers/staging/media/omap24xx/omap24xxcam.h new file mode 100644 index 0000000..233bb40 --- /dev/null +++ b/drivers/staging/media/omap24xx/omap24xxcam.h @@ -0,0 +1,596 @@ +/* + * drivers/media/platform/omap24xxcam.h + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from Andy Lowe . + * + * 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 +#include +#include "v4l2-int-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/staging/media/omap24xx/tcm825x.c b/drivers/staging/media/omap24xx/tcm825x.c new file mode 100644 index 0000000..b1ae8e9 --- /dev/null +++ b/drivers/staging/media/omap24xx/tcm825x.c @@ -0,0 +1,937 @@ +/* + * drivers/media/i2c/tcm825x.c + * + * TCM825X camera sensor driver. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from David Cohen + * + * 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 +#include +#include "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 "); +MODULE_DESCRIPTION("TCM825x camera sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/omap24xx/tcm825x.h b/drivers/staging/media/omap24xx/tcm825x.h new file mode 100644 index 0000000..e2d1bcd --- /dev/null +++ b/drivers/staging/media/omap24xx/tcm825x.h @@ -0,0 +1,200 @@ +/* + * 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 + +#include "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/staging/media/omap24xx/v4l2-int-device.c b/drivers/staging/media/omap24xx/v4l2-int-device.c new file mode 100644 index 0000000..427a890 --- /dev/null +++ b/drivers/staging/media/omap24xx/v4l2-int-device.c @@ -0,0 +1,164 @@ +/* + * drivers/media/video/v4l2-int-device.c + * + * V4L2 internal ioctl interface. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * 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 +#include +#include +#include +#include + +#include "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/staging/media/omap24xx/v4l2-int-device.h b/drivers/staging/media/omap24xx/v4l2-int-device.h new file mode 100644 index 0000000..0286c95 --- /dev/null +++ b/drivers/staging/media/omap24xx/v4l2-int-device.h @@ -0,0 +1,305 @@ +/* + * include/media/v4l2-int-device.h + * + * V4L2 internal ioctl interface. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * 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 V4L2_INT_DEVICE_H +#define V4L2_INT_DEVICE_H + +#include + +#define V4L2NAMESIZE 32 + +/* + * + * The internal V4L2 device interface core. + * + */ + +enum v4l2_int_type { + v4l2_int_type_master = 1, + v4l2_int_type_slave +}; + +struct module; + +struct v4l2_int_device; + +struct v4l2_int_master { + int (*attach)(struct v4l2_int_device *slave); + void (*detach)(struct v4l2_int_device *slave); +}; + +typedef int (v4l2_int_ioctl_func)(struct v4l2_int_device *); +typedef int (v4l2_int_ioctl_func_0)(struct v4l2_int_device *); +typedef int (v4l2_int_ioctl_func_1)(struct v4l2_int_device *, void *); + +struct v4l2_int_ioctl_desc { + int num; + v4l2_int_ioctl_func *func; +}; + +struct v4l2_int_slave { + /* Don't touch master. */ + struct v4l2_int_device *master; + + char attach_to[V4L2NAMESIZE]; + + int num_ioctls; + struct v4l2_int_ioctl_desc *ioctls; +}; + +struct v4l2_int_device { + /* Don't touch head. */ + struct list_head head; + + struct module *module; + + char name[V4L2NAMESIZE]; + + enum v4l2_int_type type; + union { + struct v4l2_int_master *master; + struct v4l2_int_slave *slave; + } u; + + void *priv; +}; + +void v4l2_int_device_try_attach_all(void); + +int v4l2_int_device_register(struct v4l2_int_device *d); +void v4l2_int_device_unregister(struct v4l2_int_device *d); + +int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd); +int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg); + +/* + * + * Types and definitions for IOCTL commands. + * + */ + +enum v4l2_power { + V4L2_POWER_OFF = 0, + V4L2_POWER_ON, + V4L2_POWER_STANDBY, +}; + +/* Slave interface type. */ +enum v4l2_if_type { + /* + * Parallel 8-, 10- or 12-bit interface, used by for example + * on certain image sensors. + */ + V4L2_IF_TYPE_BT656, +}; + +enum v4l2_if_type_bt656_mode { + /* + * Modes without Bt synchronisation codes. Separate + * synchronisation signal lines are used. + */ + V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT, + V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT, + V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT, + /* + * Use Bt synchronisation codes. The vertical and horizontal + * synchronisation is done based on synchronisation codes. + */ + V4L2_IF_TYPE_BT656_MODE_BT_8BIT, + V4L2_IF_TYPE_BT656_MODE_BT_10BIT, +}; + +struct v4l2_if_type_bt656 { + /* + * 0: Frame begins when vsync is high. + * 1: Frame begins when vsync changes from low to high. + */ + unsigned frame_start_on_rising_vs:1; + /* Use Bt synchronisation codes for sync correction. */ + unsigned bt_sync_correct:1; + /* Swap every two adjacent image data elements. */ + unsigned swap:1; + /* Inverted latch clock polarity from slave. */ + unsigned latch_clk_inv:1; + /* Hs polarity. 0 is active high, 1 active low. */ + unsigned nobt_hs_inv:1; + /* Vs polarity. 0 is active high, 1 active low. */ + unsigned nobt_vs_inv:1; + enum v4l2_if_type_bt656_mode mode; + /* Minimum accepted bus clock for slave (in Hz). */ + u32 clock_min; + /* Maximum accepted bus clock for slave. */ + u32 clock_max; + /* + * Current wish of the slave. May only change in response to + * ioctls that affect image capture. + */ + u32 clock_curr; +}; + +struct v4l2_ifparm { + enum v4l2_if_type if_type; + union { + struct v4l2_if_type_bt656 bt656; + } u; +}; + +/* IOCTL command numbers. */ +enum v4l2_int_ioctl_num { + /* + * + * "Proper" V4L ioctls, as in struct video_device. + * + */ + vidioc_int_enum_fmt_cap_num = 1, + vidioc_int_g_fmt_cap_num, + vidioc_int_s_fmt_cap_num, + vidioc_int_try_fmt_cap_num, + vidioc_int_queryctrl_num, + vidioc_int_g_ctrl_num, + vidioc_int_s_ctrl_num, + vidioc_int_cropcap_num, + vidioc_int_g_crop_num, + vidioc_int_s_crop_num, + vidioc_int_g_parm_num, + vidioc_int_s_parm_num, + vidioc_int_querystd_num, + vidioc_int_s_std_num, + vidioc_int_s_video_routing_num, + + /* + * + * Strictly internal ioctls. + * + */ + /* Initialise the device when slave attaches to the master. */ + vidioc_int_dev_init_num = 1000, + /* Delinitialise the device at slave detach. */ + vidioc_int_dev_exit_num, + /* Set device power state. */ + vidioc_int_s_power_num, + /* + * Get slave private data, e.g. platform-specific slave + * configuration used by the master. + */ + vidioc_int_g_priv_num, + /* Get slave interface parameters. */ + vidioc_int_g_ifparm_num, + /* Does the slave need to be reset after VIDIOC_DQBUF? */ + vidioc_int_g_needs_reset_num, + vidioc_int_enum_framesizes_num, + vidioc_int_enum_frameintervals_num, + + /* + * + * VIDIOC_INT_* ioctls. + * + */ + /* VIDIOC_INT_RESET */ + vidioc_int_reset_num, + /* VIDIOC_INT_INIT */ + vidioc_int_init_num, + + /* + * + * Start of private ioctls. + * + */ + vidioc_int_priv_start_num = 2000, +}; + +/* + * + * IOCTL wrapper functions for better type checking. + * + */ + +#define V4L2_INT_WRAPPER_0(name) \ + static inline int vidioc_int_##name(struct v4l2_int_device *d) \ + { \ + return v4l2_int_ioctl_0(d, vidioc_int_##name##_num); \ + } \ + \ + static inline struct v4l2_int_ioctl_desc \ + vidioc_int_##name##_cb(int (*func) \ + (struct v4l2_int_device *)) \ + { \ + struct v4l2_int_ioctl_desc desc; \ + \ + desc.num = vidioc_int_##name##_num; \ + desc.func = (v4l2_int_ioctl_func *)func; \ + \ + return desc; \ + } + +#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \ + static inline int vidioc_int_##name(struct v4l2_int_device *d, \ + arg_type asterisk arg) \ + { \ + return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \ + (void *)(unsigned long)arg); \ + } \ + \ + static inline struct v4l2_int_ioctl_desc \ + vidioc_int_##name##_cb(int (*func) \ + (struct v4l2_int_device *, \ + arg_type asterisk)) \ + { \ + struct v4l2_int_ioctl_desc desc; \ + \ + desc.num = vidioc_int_##name##_num; \ + desc.func = (v4l2_int_ioctl_func *)func; \ + \ + return desc; \ + } + +V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *); +V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *); +V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *); +V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *); +V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *); +V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *); +V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *); +V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *); +V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); +V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); + +V4L2_INT_WRAPPER_0(dev_init); +V4L2_INT_WRAPPER_0(dev_exit); +V4L2_INT_WRAPPER_1(s_power, enum v4l2_power, ); +V4L2_INT_WRAPPER_1(g_priv, void, *); +V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *); +V4L2_INT_WRAPPER_1(g_needs_reset, void, *); +V4L2_INT_WRAPPER_1(enum_framesizes, struct v4l2_frmsizeenum, *); +V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *); + +V4L2_INT_WRAPPER_0(reset); +V4L2_INT_WRAPPER_0(init); + +#endif diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h deleted file mode 100644 index 0286c95..0000000 --- a/include/media/v4l2-int-device.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * include/media/v4l2-int-device.h - * - * V4L2 internal ioctl interface. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus - * - * 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 V4L2_INT_DEVICE_H -#define V4L2_INT_DEVICE_H - -#include - -#define V4L2NAMESIZE 32 - -/* - * - * The internal V4L2 device interface core. - * - */ - -enum v4l2_int_type { - v4l2_int_type_master = 1, - v4l2_int_type_slave -}; - -struct module; - -struct v4l2_int_device; - -struct v4l2_int_master { - int (*attach)(struct v4l2_int_device *slave); - void (*detach)(struct v4l2_int_device *slave); -}; - -typedef int (v4l2_int_ioctl_func)(struct v4l2_int_device *); -typedef int (v4l2_int_ioctl_func_0)(struct v4l2_int_device *); -typedef int (v4l2_int_ioctl_func_1)(struct v4l2_int_device *, void *); - -struct v4l2_int_ioctl_desc { - int num; - v4l2_int_ioctl_func *func; -}; - -struct v4l2_int_slave { - /* Don't touch master. */ - struct v4l2_int_device *master; - - char attach_to[V4L2NAMESIZE]; - - int num_ioctls; - struct v4l2_int_ioctl_desc *ioctls; -}; - -struct v4l2_int_device { - /* Don't touch head. */ - struct list_head head; - - struct module *module; - - char name[V4L2NAMESIZE]; - - enum v4l2_int_type type; - union { - struct v4l2_int_master *master; - struct v4l2_int_slave *slave; - } u; - - void *priv; -}; - -void v4l2_int_device_try_attach_all(void); - -int v4l2_int_device_register(struct v4l2_int_device *d); -void v4l2_int_device_unregister(struct v4l2_int_device *d); - -int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd); -int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg); - -/* - * - * Types and definitions for IOCTL commands. - * - */ - -enum v4l2_power { - V4L2_POWER_OFF = 0, - V4L2_POWER_ON, - V4L2_POWER_STANDBY, -}; - -/* Slave interface type. */ -enum v4l2_if_type { - /* - * Parallel 8-, 10- or 12-bit interface, used by for example - * on certain image sensors. - */ - V4L2_IF_TYPE_BT656, -}; - -enum v4l2_if_type_bt656_mode { - /* - * Modes without Bt synchronisation codes. Separate - * synchronisation signal lines are used. - */ - V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT, - V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT, - V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT, - /* - * Use Bt synchronisation codes. The vertical and horizontal - * synchronisation is done based on synchronisation codes. - */ - V4L2_IF_TYPE_BT656_MODE_BT_8BIT, - V4L2_IF_TYPE_BT656_MODE_BT_10BIT, -}; - -struct v4l2_if_type_bt656 { - /* - * 0: Frame begins when vsync is high. - * 1: Frame begins when vsync changes from low to high. - */ - unsigned frame_start_on_rising_vs:1; - /* Use Bt synchronisation codes for sync correction. */ - unsigned bt_sync_correct:1; - /* Swap every two adjacent image data elements. */ - unsigned swap:1; - /* Inverted latch clock polarity from slave. */ - unsigned latch_clk_inv:1; - /* Hs polarity. 0 is active high, 1 active low. */ - unsigned nobt_hs_inv:1; - /* Vs polarity. 0 is active high, 1 active low. */ - unsigned nobt_vs_inv:1; - enum v4l2_if_type_bt656_mode mode; - /* Minimum accepted bus clock for slave (in Hz). */ - u32 clock_min; - /* Maximum accepted bus clock for slave. */ - u32 clock_max; - /* - * Current wish of the slave. May only change in response to - * ioctls that affect image capture. - */ - u32 clock_curr; -}; - -struct v4l2_ifparm { - enum v4l2_if_type if_type; - union { - struct v4l2_if_type_bt656 bt656; - } u; -}; - -/* IOCTL command numbers. */ -enum v4l2_int_ioctl_num { - /* - * - * "Proper" V4L ioctls, as in struct video_device. - * - */ - vidioc_int_enum_fmt_cap_num = 1, - vidioc_int_g_fmt_cap_num, - vidioc_int_s_fmt_cap_num, - vidioc_int_try_fmt_cap_num, - vidioc_int_queryctrl_num, - vidioc_int_g_ctrl_num, - vidioc_int_s_ctrl_num, - vidioc_int_cropcap_num, - vidioc_int_g_crop_num, - vidioc_int_s_crop_num, - vidioc_int_g_parm_num, - vidioc_int_s_parm_num, - vidioc_int_querystd_num, - vidioc_int_s_std_num, - vidioc_int_s_video_routing_num, - - /* - * - * Strictly internal ioctls. - * - */ - /* Initialise the device when slave attaches to the master. */ - vidioc_int_dev_init_num = 1000, - /* Delinitialise the device at slave detach. */ - vidioc_int_dev_exit_num, - /* Set device power state. */ - vidioc_int_s_power_num, - /* - * Get slave private data, e.g. platform-specific slave - * configuration used by the master. - */ - vidioc_int_g_priv_num, - /* Get slave interface parameters. */ - vidioc_int_g_ifparm_num, - /* Does the slave need to be reset after VIDIOC_DQBUF? */ - vidioc_int_g_needs_reset_num, - vidioc_int_enum_framesizes_num, - vidioc_int_enum_frameintervals_num, - - /* - * - * VIDIOC_INT_* ioctls. - * - */ - /* VIDIOC_INT_RESET */ - vidioc_int_reset_num, - /* VIDIOC_INT_INIT */ - vidioc_int_init_num, - - /* - * - * Start of private ioctls. - * - */ - vidioc_int_priv_start_num = 2000, -}; - -/* - * - * IOCTL wrapper functions for better type checking. - * - */ - -#define V4L2_INT_WRAPPER_0(name) \ - static inline int vidioc_int_##name(struct v4l2_int_device *d) \ - { \ - return v4l2_int_ioctl_0(d, vidioc_int_##name##_num); \ - } \ - \ - static inline struct v4l2_int_ioctl_desc \ - vidioc_int_##name##_cb(int (*func) \ - (struct v4l2_int_device *)) \ - { \ - struct v4l2_int_ioctl_desc desc; \ - \ - desc.num = vidioc_int_##name##_num; \ - desc.func = (v4l2_int_ioctl_func *)func; \ - \ - return desc; \ - } - -#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \ - static inline int vidioc_int_##name(struct v4l2_int_device *d, \ - arg_type asterisk arg) \ - { \ - return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \ - (void *)(unsigned long)arg); \ - } \ - \ - static inline struct v4l2_int_ioctl_desc \ - vidioc_int_##name##_cb(int (*func) \ - (struct v4l2_int_device *, \ - arg_type asterisk)) \ - { \ - struct v4l2_int_ioctl_desc desc; \ - \ - desc.num = vidioc_int_##name##_num; \ - desc.func = (v4l2_int_ioctl_func *)func; \ - \ - return desc; \ - } - -V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *); -V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *); -V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *); -V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *); -V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *); -V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *); -V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *); -V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *); -V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *); -V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *); -V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *); -V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); -V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); -V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); -V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); - -V4L2_INT_WRAPPER_0(dev_init); -V4L2_INT_WRAPPER_0(dev_exit); -V4L2_INT_WRAPPER_1(s_power, enum v4l2_power, ); -V4L2_INT_WRAPPER_1(g_priv, void, *); -V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *); -V4L2_INT_WRAPPER_1(g_needs_reset, void, *); -V4L2_INT_WRAPPER_1(enum_framesizes, struct v4l2_frmsizeenum, *); -V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *); - -V4L2_INT_WRAPPER_0(reset); -V4L2_INT_WRAPPER_0(init); - -#endif -- cgit v0.10.2 From 0ff950a73dcd25b8d435043440c11901ac91654c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 14 Dec 2013 06:40:11 -0300 Subject: [media] em28xx: reduce the polling interval for GPI connected buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For GPI-connected buttons without (hardware) debouncing, the polling interval needs to be reduced to detect button presses properly. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 5ec6a4e..93a7d02 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_BUTTONS_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); @@ -546,7 +547,7 @@ static void em28xx_query_buttons(struct work_struct *work) } /* Schedule next poll */ schedule_delayed_work(&dev->buttons_query_work, - msecs_to_jiffies(EM28XX_BUTTONS_QUERY_INTERVAL)); + msecs_to_jiffies(dev->button_polling_interval)); } static int em28xx_register_snapshot_button(struct em28xx *dev) @@ -594,6 +595,7 @@ 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]; @@ -609,18 +611,18 @@ static void em28xx_init_buttons(struct em28xx *dev) if (addr_new && dev->num_button_polling_addresses >= EM28XX_NUM_BUTTON_ADDRESSES_MAX) { WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded."); - addr_new = 0; + 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) - addr_new = 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"); - addr_new = 0; + goto next_button; } } /* Add read address to list of polling addresses */ @@ -629,6 +631,11 @@ static void em28xx_init_buttons(struct em28xx *dev) 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++; } @@ -640,7 +647,7 @@ static void em28xx_init_buttons(struct em28xx *dev) INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons); schedule_delayed_work(&dev->buttons_query_work, - msecs_to_jiffies(EM28XX_BUTTONS_QUERY_INTERVAL)); + msecs_to_jiffies(dev->button_polling_interval)); } } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index aa35750..191ef35 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -681,6 +681,7 @@ struct em28xx { 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; -- cgit v0.10.2 From c57f87e62368c33ebda11a4993380c8e5a19a5c5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 16 Dec 2013 21:08:04 -0300 Subject: [media] anysee: fix non-working E30 Combo Plus DVB-T PLL was attached twice to frontend0 leaving frontend1 without a tuner. frontend0 is DVB-C and frontend1 is DVB-T. Cc: stable@vger.kernel.org Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 90cfa35..eeab79b 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); } -- cgit v0.10.2 From 347f7a3763601d7b466898d1f10080b7083ac4a3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 21 Dec 2013 05:42:11 -0200 Subject: [media] subdev autoselect only works if I2C and I2C_MUX is selected As reported by the kbuild test robot : > warning: (VIDEO_EM28XX_DVB) selects DVB_M88DS3103 which has unmet direct dependencies (MEDIA_SUPPORT && DVB_CORE && I2C && I2C_MUX) > drivers/built-in.o: In function `m88ds3103_release': > >> m88ds3103.c:(.text+0x1ab1af): undefined reference to `i2c_del_mux_adapter' > drivers/built-in.o: In function `m88ds3103_attach': > >> (.text+0x1ab342): undefined reference to `i2c_add_mux_adapter' There are 3 possible ways to fix it: 1) make em28xx dependent on I2C_MUX. That sounds wrong, as the em28xx bridge doesn't have i2c muxes on it, and just one frontend has. Well, subdevs could eventually be converted to, instead of using dvb i2c gate control, to use i2c mux support. That makes sense, but it takes time and lots of effort. Not sure if this will happen anytime soon. 2) MEDIA_SUBDRV_AUTOSELECT can be dependent of I2C and I2C_MUX. That means that users will need to manually enable I2C_MUX on some distributions. Not sure about others, but, on Fedora, this option is disabled. So, it can end by generating a number of complains from users that their devices suddenly stopped working after a Kernel upgrade, at least until all distros that ship Kernels with I2C_MUX enabled. 3) if MEDIA_SUBDRV_AUTOSELECT is selected, it will select I2C and I2C_MUX. Of course, MEDIA_SUBDRV_AUTOSELECT will need to inherit all dependencies that I2C and I2C_MUX have (only HAS_IOMEM). The disadvantage is that, if new dependencies are added on I2C, they'll also need to be added here. As the hole idea of autoselect is to let the user not bother about whatever frontend/tuner is used by a driver, IMHO, (3) is the better solution. Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 8270388..1d0758a 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 -- cgit v0.10.2 From 2310e3c41a71f3db88039aabbd8c9ab5a4d57e65 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 5 Dec 2013 08:38:40 -0300 Subject: [media] Add DT binding documentation for Samsung S5K5BAF camera sensor This patch adds the DT bindings documentation for Samsung S5K5BAF Image Sensor. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Acked-by: Mark Rutland Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt new file mode 100644 index 0000000..1f51e04 --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt @@ -0,0 +1,58 @@ +Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP +-------------------------------------------------------------------- + +Required properties: + +- compatible : "samsung,s5k5baf"; +- reg : I2C slave address of the sensor; +- vdda-supply : analog power supply 2.8V (2.6V to 3.0V); +- vddreg-supply : regulator input power supply 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0); +- vddio-supply : I/O power supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V); +- stbyn-gpios : GPIO connected to STDBYN pin; +- rstn-gpios : GPIO connected to RSTN pin; +- clocks : list of phandle and clock specifier pairs + according to common clock bindings for the + clocks described in clock-names; +- clock-names : should include "mclk" for the sensor's master clock; + +Optional properties: + +- clock-frequency : the frequency at which the "mclk" clock should be + configured to operate, in Hz; if this property is not + specified default 24 MHz value will be used. + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +media/video-interfaces.txt. The following are properties specific to those +nodes. + +endpoint node +------------- + +- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in + video-interfaces.txt. If present it should be <1> - the device + supports only one data lane without re-mapping. + +Example: + +s5k5bafx@2d { + compatible = "samsung,s5k5baf"; + reg = <0x2d>; + vdda-supply = <&cam_io_en_reg>; + vddreg-supply = <&vt_core_15v_reg>; + vddio-supply = <&vtcam_reg>; + stbyn-gpios = <&gpl2 0 1>; + rstn-gpios = <&gpl2 1 1>; + clock-names = "mclk"; + clocks = <&clock_cam 0>; + clock-frequency = <24000000>; + + port { + s5k5bafx_ep: endpoint { + remote-endpoint = <&csis1_ep>; + data-lanes = <1>; + }; + }; +}; -- cgit v0.10.2 From 7d459937dc09bb8e448d9985ec4623779427d8a5 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 5 Dec 2013 08:38:39 -0300 Subject: [media] Add driver for Samsung S5K5BAF camera sensor Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP. The device is exposed as two V4L2 subdevices: - S5K5BAF-CIS - the image sensor matrix, fixed 1600x1200 format, no controls. - S5K5BAF-ISP - the Image Signal Processor, formats up to 1600x1200, pre/post ISP cropping, downscaling via selection API, controls. [m.chehab@samsung.com: Whitespace cleanups] Signed-off-by: Sylwester Nawrocki Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index ac86426..08701bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7346,6 +7346,13 @@ L: linux-media@vger.kernel.org S: Supported F: drivers/media/i2c/s5c73m3/* +SAMSUNG S5K5BAF CAMERA DRIVER +M: Kyungmin Park +M: Andrzej Hajda +L: linux-media@vger.kernel.org +S: Supported +F: drivers/media/i2c/s5k5baf.c + SERIAL DRIVERS M: Greg Kroah-Hartman L: linux-serial@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 997cd66..fbce018 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -586,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 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index abd25e3..c815bb2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -66,6 +66,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/s5k5baf.c b/drivers/media/i2c/s5k5baf.c new file mode 100644 index 0000000..e3b44a8 --- /dev/null +++ b/drivers/media/i2c/s5k5baf.c @@ -0,0 +1,2043 @@ +/* + * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2013, Samsung Electronics Co., Ltd. + * Andrzej Hajda + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 (%d)\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=%d)\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[count + 1]; + int ret, n; + + s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); + if (state->error) + return; + + buf[0] = __constant_cpu_to_be16(REG_CMD_BUF); + for (n = 1; n <= count; ++n) + buf[n] = cpu_to_be16(*seq++); + + n *= 2; + ret = i2c_master_send(c, (char *)buf, n); + v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, + min(2 * count, 64), seq - count); + + if (ret != n) { + v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); + state->error = ret; + } +} + +#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 = fw->data + 2 * fw->count; + int i; + + if (fw == NULL) + return NULL; + + 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 "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From ea3f1a3db379788806a7b18f4cb5ede16c11bb80 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 18:08:03 -0300 Subject: [media] exynos4-is: Leave FIMC clocks enabled when runtime PM is disabled Driver should ensure a device can be also used normally when runtime PM is disabled. So enable the FIMC clock in probe() in such situation. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index f791569..a7dfd07 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -998,36 +998,39 @@ 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: + 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; -- cgit v0.10.2 From ced197bdde4eb4bd1cda708d5a9aa5cf900d3272 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 18:13:12 -0300 Subject: [media] exynos4-is: Activate mipi-csis in probe() if runtime PM is disabled Devices should also operate normally when runtime PM is not enabled. In case runtime PM is disabled activate the device already in probe(). Any related power domain needs to be then left permanently in active state by the platform. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 31dfc50..f3c3591 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -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); -- cgit v0.10.2 From 84f14456716ad860852f25cd144f195c5c66ca62 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 18:28:38 -0300 Subject: [media] exynos4-is: Enable FIMC-LITE clock if runtime PM is not used Ensure the device also works when runtime PM is disabled. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index d3b32b6..1234734 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1549,38 +1549,40 @@ 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_clk_put; + } 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: + clk_disable(fimc->clock); err_sd: fimc_lite_unregister_capture_subdev(fimc); -err_clk: +err_clk_put: fimc_lite_clk_put(fimc); return ret; } -- cgit v0.10.2 From da8cec30b03e8080494996b8edc0fcebfa5ef871 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 19:07:51 -0300 Subject: [media] exynos4-is: Correct clean up sequence on error path in fimc_is_probe() The memory allocator is being initialized before registering the subdevs so reverse the cleanup sequence to avoid trying unregister not registered subdevs. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 9770fa9..8cb70c2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -867,10 +867,10 @@ 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_irq: free_irq(is->irq, is); err_clk: -- cgit v0.10.2 From 283bf33bf3035ebd22e974857a672be070a73e2a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 19:10:43 -0300 Subject: [media] exynos4-is: Enable fimc-is clocks in probe() if runtime PM is disabled Ensure the device works also when runtime PM is disabled. This will allow to drop an incorrect dependency on PM_RUNTIME. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 8cb70c2..13a4228 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 @@ -871,6 +880,9 @@ 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); -- cgit v0.10.2 From 31a2d43d7c1160d12460b988636a48bdd40d1e40 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 19 Oct 2013 18:50:46 -0300 Subject: [media] exynos4-is: Remove dependency on PM_RUNTIME from Kconfig Now when the sub-drivers are fixed to work with runtime PM disabled this erroneous dependency can be removed. The CAM and ISP power domains should be left in active state by the platform if runtime PM is not used. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index d2d3b4b..01ed1ecd 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 -- cgit v0.10.2 From eaf95eeec47e41f30139eaad8c0f3d33c5059d22 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 16 Dec 2013 17:19:50 -0300 Subject: [media] v4l: omap4iss: use snprintf() to make smatch happy Smatch complains here because name is a 32 character buffer and we adding the "OMAP4 ISS " prefix as well for a total of 42 characters. The sd->name buffer can only hold 32 characters. I've changed it to use snprintf() to silence the overflow warning. Also I have removed the call to strlcpy() which is a no-op. Signed-off-by: Dan Carpenter Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index e4fc4a0..61fc350 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1247,8 +1247,7 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) v4l2_subdev_init(sd, &csi2_ops); sd->internal_ops = &csi2_internal_ops; sprintf(name, "CSI2%s", subname); - strlcpy(sd->name, "", sizeof(sd->name)); - sprintf(sd->name, "OMAP4 ISS %s", name); + snprintf(sd->name, sizeof(sd->name), "OMAP4 ISS %s", name); sd->grp_id = 1 << 16; /* group ID for iss subdevs */ v4l2_set_subdevdata(sd, csi2); -- cgit v0.10.2 From f7d40eea8e3e531f1517ab7eded552e8837ef5da Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 18 Dec 2013 04:29:24 -0300 Subject: [media] v4l: omap4iss: Restore irq flags correctly in omap4iss_video_buffer_next() The spin_lock_irqsave() macro is not nestable. The second call will overwrite the first record of "flags" so the IRQs will not be enabled correctly at the end of the function. In the current code, this function is always called from the IRQ handler so everything works fine and this fix doesn't change anything. Signed-off-by: Dan Carpenter Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 482b72f..8c7f350 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -476,9 +476,9 @@ struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) } if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { - spin_lock_irqsave(&pipe->lock, flags); + spin_lock(&pipe->lock); pipe->state &= ~ISS_PIPELINE_STREAM; - spin_unlock_irqrestore(&pipe->lock, flags); + spin_unlock(&pipe->lock); } buf = list_first_entry(&video->dmaqueue, struct iss_buffer, -- cgit v0.10.2 From 8de531b03271169458d5aad66934d097056f0d0c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 26 Dec 2013 23:07:22 -0300 Subject: [media] tvp5150: make read operations atomic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using two I2C operations between write and read, use just one i2c_transfer. That allows I2C mutexes to not let any other I2C transfer between the two. Reviewed-by: Frank Schäfer Tested-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 89c0b13..2ed05b6 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]); -- cgit v0.10.2 From 547bf2508146f0274e4b72793c5017ca276282b6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 26 Dec 2013 23:07:59 -0300 Subject: [media] tuner-xc2028: remove unused code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro is not used. remove it. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 4be5cf8..1057da5 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, \ -- cgit v0.10.2 From 0560f337f3282dc6bf77b13ac344ffe30d9ccce5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Dec 2013 15:28:26 -0300 Subject: [media] em28xx: move some video-specific functions to em28xx-video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we want to split the video handling to a separate module, move all video-specific functions to em28xx-video. No functional changes. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 36853f1..19827e7 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2529,113 +2529,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; diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index f6076a5..3012912d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -53,14 +53,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) \ @@ -603,24 +595,6 @@ init_audio: } EXPORT_SYMBOL_GPL(em28xx_audio_setup); -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); -} - const struct em28xx_led *em28xx_find_led(struct em28xx *dev, enum em28xx_led_role role) { @@ -696,227 +670,6 @@ int em28xx_capture_start(struct em28xx *dev, int start) 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); - - 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 */ -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; - } - 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_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; -} - int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) { int rc = 0; @@ -1282,18 +1035,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 */ diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index dd19c9f..51d4405 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -53,15 +53,23 @@ #define EM28XX_VERSION "0.2.0" +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"); + +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) { \ @@ -135,6 +143,257 @@ static struct em28xx_fmt format[] = { }, }; +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 + */ +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); +} + +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); +} + +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 */ +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 */ +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, 0, 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 ------------------------------------------------------------------*/ @@ -1817,6 +2076,113 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } +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; + } +} + +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); +} + int em28xx_register_analog_devices(struct em28xx *dev) { u8 val; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 191ef35..0259270 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -748,6 +748,7 @@ void em28xx_init_extension(struct em28xx *dev); void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ +void em28xx_tuner_setup(struct em28xx *dev); int em28xx_vb2_setup(struct em28xx *dev); int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); -- cgit v0.10.2 From ce67943e2dfd3bc8b7e86b428fc0dfc568730971 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Dec 2013 13:13:41 -0300 Subject: [media] em28xx: some cosmetic changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make easier for the next patches, do some cosmetic changes. No functional changes. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 19827e7..551cbc2 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2106,7 +2106,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, diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 51d4405..b3ede85 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -51,8 +51,6 @@ #define DRIVER_DESC "Empia em28xx based USB video device driver" -#define EM28XX_VERSION "0.2.0" - static unsigned int isoc_debug; module_param(isoc_debug, int, 0644); MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 0259270..7ae05eb 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -26,6 +26,8 @@ #ifndef _EM28XX_H #define _EM28XX_H +#define EM28XX_VERSION "0.2.0" + #include #include #include @@ -522,9 +524,12 @@ 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 */ + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + unsigned int is_audio_only:1; int audio_ifnum; @@ -544,10 +549,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 */ -- cgit v0.10.2 From 54a2a84ea9e8640b4f1df4e222e305d03bb64065 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Dec 2013 13:01:04 -0300 Subject: [media] em28xx: Fix em28xx deplock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When em28xx extensions are loaded/removed, there are two locks: a single static em28xx_devlist_mutex that registers each extension and the struct em28xx dev->lock. When extensions are registered, em28xx_devlist_mutex is taken first, and then dev->lock. Be sure that, when extensions are being removed, the same order will be used. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 551cbc2..154e6f0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3485,9 +3485,7 @@ 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; } @@ -3506,10 +3504,13 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); } + mutex_unlock(&dev->lock); em28xx_close_extension(dev); + /* NOTE: must be called BEFORE the resources are released */ + mutex_lock(&dev->lock); if (!dev->users) em28xx_release_resources(dev); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 3012912d..f773017 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1094,10 +1094,12 @@ void em28xx_close_extension(struct em28xx *dev) const struct em28xx_ops *ops = NULL; mutex_lock(&em28xx_devlist_mutex); + mutex_lock(&dev->lock); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->fini) ops->fini(dev); } + mutex_unlock(&dev->lock); list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); } -- cgit v0.10.2 From b64f8e9adc2eff4941ca0cf146a850c7b4173594 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Dec 2013 13:16:49 -0300 Subject: [media] em28xx: move analog-specific init to em28xx-video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are several init code inside em28xx-cards that are actually part of analog initialization. Move the code to em28x-video, in order to remove part of the mess. In thesis, no functional changes so far. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 154e6f0..541de6d 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2360,24 +2360,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; @@ -2782,58 +2764,8 @@ 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); - } - } - - em28xx_tuner_setup(dev); - - em28xx_init_camera(dev); } - static void request_module_async(struct work_struct *work) { struct em28xx *dev = container_of(work, @@ -2907,7 +2839,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; @@ -3034,15 +2965,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 */ @@ -3053,7 +2975,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 */ @@ -3067,75 +2989,16 @@ 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; + return retval; } } - /* - * 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); - /* 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; - } - msleep(3); - } - retval = em28xx_register_analog_devices(dev); - if (retval < 0) { + 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); return 0; @@ -3143,10 +3006,6 @@ 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; } @@ -3388,9 +3247,6 @@ 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); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b3ede85..3726af1 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2045,6 +2045,24 @@ static struct video_device em28xx_radio_template = { .ioctl_ops = &radio_ioctl_ops, }; +/* 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 ******************************/ @@ -2186,10 +2204,129 @@ int em28xx_register_analog_devices(struct em28xx *dev) u8 val; int ret; unsigned int maxw; + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + + if (dev->is_audio_only) { + /* This device does not support the v4l2 extension */ + return 0; + } printk(KERN_INFO "%s: v4l2 driver version %s\n", dev->name, EM28XX_VERSION); + 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); + + 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; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); @@ -2252,14 +2389,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; @@ -2289,7 +2428,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 */ @@ -2318,7 +2457,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; } } @@ -2327,13 +2466,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)); @@ -2346,5 +2486,17 @@ 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); + return 0; + +unregister_dev: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); +err: + return ret; } -- cgit v0.10.2 From 1f0ee65a3b6a3c78c4f6b10f8df5700a44ed3c36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 5 Jan 2014 08:43:40 -0300 Subject: [media] em28xx: unregister i2c bus 0 if bus 1 fails to register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the error handling logic, making it to unregister i2c bus 0, in case of a failure to register the second bus. Reported-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 541de6d..dbce4dc 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2989,6 +2989,9 @@ 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); + + em28xx_i2c_unregister(dev, 0); + return retval; } } -- cgit v0.10.2 From 01c2819330b1e0ec6b53dcfac76ad75ff2c8ba4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Dec 2013 13:27:02 -0300 Subject: [media] em28xx: make em28xx-video to be a separate module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all analog-specific code are at em28xx-video, convert it into an em28xx extension and load it as a separate module. Reviewed-by: Frank Schäfer Tested-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index d6ba514..a1fccf3 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 diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index ad6d485..3f850d5 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-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index d666741..c29f5c4 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 dbce4dc..b258693 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2159,6 +2159,8 @@ struct em28xx_board em28xx_boards[] = { .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 */ @@ -2780,11 +2782,12 @@ static void request_module_async(struct work_struct *work) 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.buttons || @@ -2813,18 +2816,12 @@ void em28xx_release_resources(struct em28xx *dev) { /*FIXME: I2C IR should be disconnected */ - em28xx_release_analog_resources(dev); - 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 */ @@ -2999,18 +2996,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); - retval = em28xx_register_analog_devices(dev); - if (retval < 0) - goto fail; - return 0; - -fail: - if (dev->def_i2c_bus) - em28xx_i2c_unregister(dev, 1); - em28xx_i2c_unregister(dev, 0); - - return retval; } /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ @@ -3213,6 +3199,7 @@ 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->has_video = has_video; dev->audio_ifnum = ifnum; /* Checks if audio is provided by some interface */ @@ -3252,10 +3239,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* 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) { @@ -3298,7 +3284,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (retval) { printk(DRIVER_NAME ": Failed to pre-allocate USB transfer buffers for DVB.\n"); - goto unlock_and_free; + goto err_free; } } @@ -3307,13 +3293,9 @@ 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); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index f773017..c416dd3 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -33,6 +33,18 @@ #include "em28xx.h" +#define DRIVER_AUTHOR "Ludovico Cavedon , " \ + "Markus Rechberger , " \ + "Mauro Carvalho Chehab , " \ + "Sascha Sommer " + +#define DRIVER_DESC "Empia em28xx based USB core driver" + +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; diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h new file mode 100644 index 0000000..bce4386 --- /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 + + 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 39f39c5..db3d655 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -27,6 +27,7 @@ #include #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 3726af1..7d11a16 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -38,6 +38,7 @@ #include #include "em28xx.h" +#include "em28xx-v4l.h" #include #include #include @@ -141,7 +142,7 @@ static struct em28xx_fmt format[] = { }, }; -int em28xx_vbi_supported(struct em28xx *dev) +static int em28xx_vbi_supported(struct em28xx *dev) { /* Modprobe option to manually disable */ if (disable_vbi == 1) @@ -164,7 +165,7 @@ int em28xx_vbi_supported(struct em28xx *dev) * em28xx_wake_i2c() * configure i2c attached devices */ -void em28xx_wake_i2c(struct em28xx *dev) +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, @@ -172,7 +173,7 @@ void em28xx_wake_i2c(struct em28xx *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); } -int em28xx_colorlevels_set_default(struct em28xx *dev) +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); @@ -190,7 +191,7 @@ int em28xx_colorlevels_set_default(struct em28xx *dev) return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); } -int em28xx_set_outfmt(struct em28xx *dev) +static int em28xx_set_outfmt(struct em28xx *dev) { int ret; u8 fmt, vinctrl; @@ -297,7 +298,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) } /* FIXME: this only function read values from dev */ -int em28xx_resolution_set(struct em28xx *dev) +static int em28xx_resolution_set(struct em28xx *dev) { int width, height; width = norm_maxw(dev); @@ -330,7 +331,7 @@ int em28xx_resolution_set(struct em28xx *dev) } /* Set USB alternate setting for analog video */ -int em28xx_set_alternate(struct em28xx *dev) +static int em28xx_set_alternate(struct em28xx *dev) { int errCode; int i; @@ -1020,7 +1021,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; @@ -1088,7 +1089,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; @@ -1625,7 +1626,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; @@ -1872,14 +1873,16 @@ 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) { - - /*FIXME: I2C IR should be disconnected */ + if (!dev->has_video) { + /* This device does not support the v4l2 extension */ + return 0; + } if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) @@ -1906,6 +1909,8 @@ void em28xx_release_analog_resources(struct em28xx *dev) video_device_release(dev->vdev); dev->vdev = NULL; } + + return 0; } /* @@ -1927,12 +1932,13 @@ static int em28xx_v4l2_close(struct file *filp) if (dev->users == 1) { /* the device is already disconnect, free the remaining resources */ + if (dev->disconnected) { em28xx_release_resources(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_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 */ @@ -1951,6 +1957,7 @@ static int em28xx_v4l2_close(struct file *filp) } } +exit: dev->users--; mutex_unlock(&dev->lock); return 0; @@ -2065,8 +2072,6 @@ static unsigned short msp3400_addrs[] = { /******************************** usb interface ******************************/ - - static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, const char *type_name) @@ -2140,7 +2145,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) } } -void em28xx_tuner_setup(struct em28xx *dev) +static void em28xx_tuner_setup(struct em28xx *dev) { struct tuner_setup tun_setup; struct v4l2_frequency f; @@ -2199,14 +2204,14 @@ void em28xx_tuner_setup(struct em28xx *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); } -int em28xx_register_analog_devices(struct em28xx *dev) +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) { + if (!dev->has_video) { /* This device does not support the v4l2 extension */ return 0; } @@ -2214,6 +2219,8 @@ int em28xx_register_analog_devices(struct em28xx *dev) printk(KERN_INFO "%s: v4l2 driver version %s\n", dev->name, EM28XX_VERSION); + 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"); @@ -2492,11 +2499,33 @@ int em28xx_register_analog_devices(struct em28xx *dev) /* initialize videobuf2 stuff */ em28xx_vb2_setup(dev); + 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 7ae05eb..9d6f43e 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -26,7 +26,7 @@ #ifndef _EM28XX_H #define _EM28XX_H -#define EM28XX_VERSION "0.2.0" +#define EM28XX_VERSION "0.2.1" #include #include @@ -474,6 +474,7 @@ struct em28xx_eeprom { #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 @@ -527,6 +528,7 @@ struct em28xx { unsigned int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */ unsigned char disconnected:1; /* device has been diconnected */ + unsigned int has_video:1; unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; unsigned int is_audio_only:1; @@ -723,14 +725,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, @@ -742,31 +739,17 @@ 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 */ -void em28xx_tuner_setup(struct em28xx *dev); -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_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); -- cgit v0.10.2 From 9634614f35ec17a4af8660c025122b477dad7d0b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 26 Dec 2013 12:41:03 -0300 Subject: [media] em28xx: improve extension information messages Add a message with consistent prints before and after each extension initialization, and provide a better text for module load. While here, add a missing sanity check for extension finish code at em28xx-v4l extension. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 2fdb66e..263886a 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -649,7 +649,8 @@ static int em28xx_audio_init(struct em28xx *dev) 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"); @@ -702,6 +703,7 @@ static int em28xx_audio_init(struct em28xx *dev) adev->sndcard = card; adev->udev = dev->udev; + em28xx_info("Audio extension successfully initialized\n"); return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index c416dd3..b810641e 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1069,7 +1069,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 ddc0e60..f72663a 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -274,7 +274,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); @@ -992,10 +992,11 @@ static int em28xx_dvb_init(struct em28xx *dev) 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) { @@ -1407,7 +1408,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); diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 93a7d02..eed7dd7 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -692,6 +692,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) @@ -785,6 +787,8 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; + em28xx_info("Input extension successfully initalized\n"); + return 0; error: diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 7d11a16..ada133c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2216,8 +2216,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) return 0; } - printk(KERN_INFO "%s: v4l2 driver version %s\n", - dev->name, EM28XX_VERSION); + em28xx_info("Registering V4L2 extension\n"); mutex_lock(&dev->lock); @@ -2499,6 +2498,8 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* initialize videobuf2 stuff */ em28xx_vb2_setup(dev); + em28xx_info("V4L2 extension successfully initialized\n"); + mutex_unlock(&dev->lock); return 0; -- cgit v0.10.2 From b99f0aadd33fad269c8e62b5bec8b5c012a44a56 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Dec 2013 00:16:13 -0300 Subject: [media] em28xx: check if a device has audio earlier Better to split chipset detection from the audio setup. So, move the detection code to em28xx_init_dev(). Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index b258693..95ba1ce 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2930,6 +2930,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); @@ -3199,6 +3209,7 @@ 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_mode.has_audio = has_audio; dev->has_video = has_video; dev->audio_ifnum = ifnum; diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b810641e..5d49af0 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -505,18 +505,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 || - 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 (!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); -- cgit v0.10.2 From d8992b0931c1f9b1254d57b26b2965327337e7c6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Dec 2013 11:14:59 -0300 Subject: [media] em28xx: unify module version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the same module version on all em28xx sub-modules, and use the same naming convention to describe the driver. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 263886a..a6eef06 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -747,7 +747,8 @@ static void __exit em28xx_alsa_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Markus Rechberger "); MODULE_AUTHOR("Mauro Carvalho Chehab "); -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-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 5d49af0..cef3fd4 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -38,8 +38,6 @@ "Mauro Carvalho Chehab , " \ "Sascha Sommer " -#define DRIVER_DESC "Empia em28xx based USB core driver" - MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index f72663a..7fa1c80 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -54,9 +54,11 @@ #include "m88ds3103.h" #include "m88ts2022.h" -MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface"); +MODULE_VERSION(EM28XX_VERSION); + static unsigned int debug; module_param(debug, int, 0644); diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index eed7dd7..f3b629d 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -836,7 +836,8 @@ static void __exit em28xx_rc_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mauro Carvalho Chehab "); -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-video.c b/drivers/media/usb/em28xx/em28xx-video.c index ada133c..8da96f6 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -50,8 +50,6 @@ "Mauro Carvalho Chehab , " \ "Sascha Sommer " -#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]"); @@ -78,7 +76,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); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 9d6f43e..e0369fb 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -27,6 +27,7 @@ #define _EM28XX_H #define EM28XX_VERSION "0.2.1" +#define DRIVER_DESC "Empia em28xx device driver" #include #include -- cgit v0.10.2 From 822b8deab870f8edc2d8a1848f61c054a8188de6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Jan 2014 05:27:47 -0300 Subject: [media] em28xx: prevent registering wrong interfaces for audio-only A few devices (em2860) use a separate interface for audio only Audio Vendor Class USB. That interface should not be used by Remote Controller, Analog TV or Digital TV. Prevents initializing all non-audio extensions for the audio only interface. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 7fa1c80..5c6be66 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -992,6 +992,11 @@ 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 */ return 0; @@ -1431,6 +1436,11 @@ 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; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index f3b629d..9a5dad9 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -673,6 +673,11 @@ static int em28xx_ir_init(struct em28xx *dev) u64 rc_type; u16 i2c_rc_dev_addr = 0; + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + if (dev->board.buttons) em28xx_init_buttons(dev); @@ -802,6 +807,11 @@ static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + em28xx_shutdown_buttons(dev); /* skip detach on non attached boards */ diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 8da96f6..9c44628 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1877,6 +1877,11 @@ static int em28xx_v4l2_open(struct file *filp) */ 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; @@ -2209,6 +2214,11 @@ static int em28xx_v4l2_init(struct em28xx *dev) 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; -- cgit v0.10.2 From e63fa180e81576d144606a1d4aa6976edde70cfd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Jan 2014 05:50:07 -0300 Subject: [media] em28xx: only initialize extensions on the main interface For devices with a separated audio-only interface (em2860), call em28xx_init_extension() only once. That fixes a bug with Kworld 305U (eb1a:e305): [ 658.730715] em2860 #0: V4L2 video device registered as video1 [ 658.730728] em2860 #0: V4L2 VBI device registered as vbi0 [ 658.736907] em2860 #0: Remote control support is not available for this card. [ 658.736965] em2860 #1: Remote control support is not available for this card. [ 658.737230] ------------[ cut here ]------------ [ 658.737246] WARNING: CPU: 2 PID: 60 at lib/list_debug.c:36 __list_add+0x8a/0xc0() [ 658.737256] list_add double add: new=ffff8800a9a40410, prev=ffff8800a9a40410, next=ffffffffa08720d0. [ 658.737266] Modules linked in: tuner_xc2028 netconsole rc_hauppauge em28xx_rc rc_core tuner_simple tuner_types tda9887 tda8290 tuner tvp5150 msp3400 em28xx_v4l em28xx tveeprom v4l2_common fuse ccm nf_conntrack_netbios_ns nf_conntrack_broadcast ipt_MASQUERADE ip6t_REJECT xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6tabl e_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security bnep iptable_raw vfat fat arc4 iwldvm mac80211 x86_pkg_temp_thermal coretemp kvm_intel nfsd iwlwifi snd_hda_codec_hdmi kvm snd_hda _codec_realtek snd_hda_intel snd_hda_codec auth_rpcgss nfs_acl cfg80211 lockd snd_hwdep snd_seq btusb sunrpc crc32_pclmul bluetooth crc32c_intel snd_seq_device snd_pcm uvcvideo r8169 ghash_clmulni_intel videobuf2_vmalloc videobuf2_memops videobuf2_core snd_page_alloc snd_timer snd videodev mei_me iTCO_wdt mii shpchp joydev mei media iTCO_vendor_support lpc_ich m icrocode soundcore rfkill serio_raw i2c_i801 mfd_core nouveau i915 ttm i2c_algo_bit drm_kms_helper drm i2c_core mxm_wmi wmi video [ 658.738601] CPU: 2 PID: 60 Comm: kworker/2:1 Not tainted 3.13.0-rc1+ #18 [ 658.738611] Hardware name: SAMSUNG ELECTRONICS CO., LTD. 550P5C/550P7C/SAMSUNG_NP1234567890, BIOS P04ABI.013.130220.dg 02/20/2013 [ 658.738624] Workqueue: events request_module_async [em28xx] [ 658.738646] 0000000000000009 ffff8802209dfc68 ffffffff816a3c96 ffff8802209dfcb0 [ 658.738700] ffff8802209dfca0 ffffffff8106aaad ffff8800a9a40410 ffffffffa08720d0 [ 658.738754] ffff8800a9a40410 0000000000000000 0000000000000080 ffff8802209dfd00 [ 658.738814] Call Trace: [ 658.738836] [] dump_stack+0x45/0x56 [ 658.738851] [] warn_slowpath_common+0x7d/0xa0 [ 658.738864] [] warn_slowpath_fmt+0x4c/0x50 [ 658.738880] [] ? em28xx_init_extension+0x1d/0x80 [em28xx] [ 658.738898] [] __list_add+0x8a/0xc0 [ 658.738911] [] em28xx_init_extension+0x38/0x80 [em28xx] [ 658.738927] [] request_module_async+0x19/0x110 [em28xx] [ 658.738942] [] process_one_work+0x1f5/0x510 [ 658.738954] [] ? process_one_work+0x193/0x510 [ 658.738967] [] worker_thread+0x11b/0x3a0 [ 658.738979] [] ? manage_workers.isra.24+0x2b0/0x2b0 [ 658.738992] [] kthread+0xff/0x120 [ 658.739005] [] ? kthread_create_on_node+0x250/0x250 [ 658.739017] [] ret_from_fork+0x7c/0xb0 [ 658.739029] [] ? kthread_create_on_node+0x250/0x250 [ 658.739040] ---[ end trace c1acd24b354108de ]--- [ 658.739051] em2860 #1: Remote control support is not available for this card. [ 658.742407] em28xx-audio.c: probing for em28xx Audio Vendor Class [ 658.742429] em28xx-audio.c: Copyright (C) 2006 Markus Rechberger [ 658.742440] em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab [ 658.744798] em28xx-audio.c: probing for em28xx Audio Vendor Class [ 658.744823] em28xx-audio.c: Copyright (C) 2006 Markus Rechberger [ 658.744836] em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab [ 658.746849] em28xx-audio.c: probing for em28xx Audio Vendor Class [ 658.746863] em28xx-audio.c: Copyright (C) 2006 Markus Rechberger [ 658.746874] em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab ... Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 95ba1ce..84167f0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2779,6 +2779,18 @@ 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) -- cgit v0.10.2 From bafd5d79fbc764f9dd977737e3b25d480ade2d5a Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 07:33:08 -0300 Subject: [media] ad9389b: verify EDID header Ignore EDIDs where the header is wrong. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index b06a7e5..e7f7171 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -978,7 +978,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 +992,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); @@ -1019,9 +1038,10 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) 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_err(sd, "%s: edid crc error\n", __func__); + v4l2_err(sd, "%s: edid crc or header error\n", __func__); state->have_monitor = false; ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); -- cgit v0.10.2 From d3ec7de4ef711bbd89ea9197e32ee347415740d5 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 07:55:42 -0300 Subject: [media] ad9389b: whitespace changes to improve readability Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index e7f7171..fa95203 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -150,7 +150,7 @@ static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val) /* 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 +321,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,60 +387,60 @@ 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); + (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, 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)); + ((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); + 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); + 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); + 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); + 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); + manual_cts ? "manual" : "automatic", N, CTS); v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", - vic_detect, vic_sent); + vic_detect, vic_sent); } } if (state->dv_timings.type == V4L2_DV_BT_656_1120) @@ -486,7 +486,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); @@ -599,7 +599,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; } @@ -686,14 +686,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 +724,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 +748,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 +800,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 +826,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; @@ -849,7 +849,7 @@ static void ad9389b_edid_handler(struct work_struct *work) 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; } } @@ -1019,7 +1019,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; @@ -1032,14 +1032,14 @@ 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_verify_crc(sd, segment) || - !edid_verify_header(sd, segment)) { + !edid_verify_header(sd, segment)) { /* edid crc error, force reread of edid segment */ v4l2_err(sd, "%s: edid crc or header error\n", __func__); state->have_monitor = false; @@ -1052,12 +1052,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; } @@ -1101,7 +1101,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) @@ -1160,7 +1160,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) { @@ -1183,7 +1183,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: -- cgit v0.10.2 From 51af70bf93592c51d1ed8db421bb6cce8a9aed2d Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 08:58:13 -0300 Subject: [media] ad9389b: remove rx-sense irq dependency Removed dependency on rx-sense interrupt, it's a leftover from obsolete code. Removing this simplifies the code. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index fa95203..cca7758 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,7 +138,7 @@ 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; } @@ -392,13 +387,11 @@ static int ad9389b_log_status(struct v4l2_subdev *sd) (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"); - } + 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"); v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", @@ -413,35 +406,33 @@ static int ad9389b_log_status(struct v4l2_subdev *sd) 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); - } + 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: ", @@ -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); - if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT)) + v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status); + + 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; } @@ -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; } @@ -845,7 +835,6 @@ 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, @@ -922,42 +911,28 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) 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,10 @@ 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 bool edid_block_verify_crc(u8 *edid_block) @@ -1042,7 +1021,6 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) !edid_verify_header(sd, segment)) { /* edid crc error, force reread of edid segment */ v4l2_err(sd, "%s: edid crc or header error\n", __func__); - state->have_monitor = false; ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); return false; -- cgit v0.10.2 From a6c98f56e7f4a46358ea4b10f40df419f9679e37 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 09:00:05 -0300 Subject: [media] ad9389b: retry setup if the state is inconsistent Retry setup if the device is powered off when it should be powered on. This state can be caused by rapid hotplug toggles. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index cca7758..83225d6 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -904,7 +904,7 @@ 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 */ @@ -947,6 +947,31 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) 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) { u8 sum = 0; -- cgit v0.10.2 From a62c6216707771b05df868b468f4494c8db9eb37 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 09:02:20 -0300 Subject: [media] adv7511: disable register reset by HPD Whenever the hotplug pin is pulled low the chip resets a whole bunch of registers. It turns out that this can be turned off on the adv7511. Do so, as this 'feature' introduces race conditions in setting up registers, particular when the hotplug pin bounces a lot. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 7c8d971..89ea266 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1038,6 +1038,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); -- cgit v0.10.2 From 3ecabed1ec98ef216bf52c3ca31fceb77af0b831 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 09:06:29 -0300 Subject: [media] adv7511: add VIC and audio CTS/N values to log_status Improve status logging. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 89ea266..f20450c 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); -- cgit v0.10.2 From 928b0fe7839dd5b91c75d70ca552ece3e893ece9 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 17 Dec 2013 09:17:10 -0300 Subject: [media] adv7511: verify EDID header Ignore EDID's where the header is wrong. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index f20450c..ee61894 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -965,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) @@ -1013,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); -- cgit v0.10.2 From 4a31a93a71e9d5d32945aa6f9a5de59ce3be2b94 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Tue, 10 Dec 2013 09:45:00 -0300 Subject: [media] adv7604: add support for all the digital input ports The adv7604 supports four digital input ports. This patch adds support for all of them, instead of just port A. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index a324106b..6372d31 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,10 +65,13 @@ 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; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; struct workqueue_struct *work_queues; @@ -516,7 +517,7 @@ static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); } static inline int edid_write_block(struct v4l2_subdev *sd, @@ -529,8 +530,6 @@ static inline int edid_write_block(struct v4l2_subdev *sd, 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); @@ -541,22 +540,19 @@ static inline int edid_write_block(struct v4l2_subdev *sd, 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); + 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) & 1) + if (rep_read(sd, 0x7d) & state->edid.present) break; mdelay(1); } if (i == 1000) { - v4l_err(client, "error enabling edid\n"); + v4l_err(client, "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; } @@ -574,6 +570,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 +624,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) { @@ -748,10 +769,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 +783,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 +822,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 +864,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 +874,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); @@ -900,7 +911,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: /* automatic */ - if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) { + if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { /* receiving DVI-D signal */ /* ADV7604 selects RGB limited range regardless of @@ -983,8 +994,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 +1023,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 +1030,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 +1047,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 +1166,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 +1186,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; } @@ -1216,7 +1222,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 +1311,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; @@ -1331,8 +1337,8 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, 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; @@ -1374,22 +1380,18 @@ 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: + if (is_analog_input(sd)) { /* enable */ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ - break; - case ADV7604_MODE_HDMI: + } else if (is_digital_input(sd)) { /* enable */ + hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input); 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 */ - 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); } } @@ -1405,9 +1407,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 +1433,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 +1461,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); } } @@ -1477,7 +1474,7 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input); - state->mode = input; + state->selected_input = input; disable_input(sd); @@ -1524,7 +1521,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 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; + fmt_change_digital = is_digital_input(sd) ? (io_read(sd, 0x6b) & 0xc0) : 0; if (fmt_change_digital) io_write(sd, 0x6c, fmt_change_digital); if (fmt_change || fmt_change_digital) { @@ -1545,7 +1542,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *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,19 +1556,41 @@ 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 + edid->blocks > state->edid_blocks) - edid->blocks = state->edid_blocks - edid->start_block; + if (edid->start_block > 1) + return -EINVAL; + 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; } @@ -1581,33 +1600,50 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi struct adv7604_state *state = to_state(sd); int err; - 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); + state->edid.present &= ~(1 << edid->pad); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); /* Disables I2C access to internal EDID ram from DDC port */ rep_write_and_or(sd, 0x77, 0xf0, 0x0); - state->edid_blocks = 0; + state->edid.blocks = 0; /* Fall back to a 16:9 aspect ratio */ state->aspect_ratio.numerator = 16; state->aspect_ratio.denominator = 9; + v4l2_dbg(2, debug, sd, "%s: clear edid\n", __func__); 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; + + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + state->edid.present &= ~(1 << edid->pad); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); + + 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) + state->edid.present |= edid->pad; + + err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); + if (err < 0) { v4l2_err(sd, "error %d writing edid\n", err); - return err; + return err; + } + + /* 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 **************/ @@ -1690,15 +1726,21 @@ 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 "); + "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); + 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 +1786,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" : ""); @@ -1906,6 +1951,7 @@ static int adv7604_core_init(struct v4l2_subdev *sd) ADI recommended setting [REF_01, c. 2.3.3] */ cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution for digital formats */ + rep_write(sd, 0x76, 0xc0); /* SPA location for port B, C and D */ /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ @@ -1918,7 +1964,7 @@ static int adv7604_core_init(struct v4l2_subdev *sd) 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, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */ return v4l2_ctrl_handler_setup(sd->ctrl_handler); } @@ -2020,7 +2066,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/include/media/adv7604.h b/include/media/adv7604.h index dc004bc..0c96e16 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -131,16 +131,20 @@ struct adv7604_platform_data { u8 i2c_vdp; }; -/* - * Mode of operation. - * This is used as the input argument of the s_routing video op. - */ -enum adv7604_mode { - ADV7604_MODE_COMP, - ADV7604_MODE_GR, - ADV7604_MODE_HDMI, +enum adv7604_input_port { + ADV7604_INPUT_HDMI_PORT_A, + ADV7604_INPUT_HDMI_PORT_B, + ADV7604_INPUT_HDMI_PORT_C, + ADV7604_INPUT_HDMI_PORT_D, + ADV7604_INPUT_VGA_RGB, + ADV7604_INPUT_VGA_COMP, }; +#define ADV7604_EDID_PORT_A 0 +#define ADV7604_EDID_PORT_B 1 +#define ADV7604_EDID_PORT_C 2 +#define ADV7604_EDID_PORT_D 3 + #define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) -- cgit v0.10.2 From f31b62e14a000f4e7bf37ad8a84b13cb2079de21 Mon Sep 17 00:00:00 2001 From: Mikhail Khelik Date: Fri, 20 Dec 2013 05:12:00 -0300 Subject: [media] adv7604: add hdmi driver strength adjustment The driver strength is board dependent, so set it from the platform_data. Signed-off-by: Mikhail Khelik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 6372d31..00ce271 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1942,7 +1942,12 @@ static int adv7604_core_init(struct v4l2_subdev *sd) /* 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 */ + + /* 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 - diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 0c96e16..22fd1ac 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -78,6 +78,12 @@ enum adv7604_op_format_sel { ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE2 = 0x8a, }; +enum adv7604_drive_strength { + ADV7604_DR_STR_MEDIUM_LOW = 1, + ADV7604_DR_STR_MEDIUM_HIGH = 2, + ADV7604_DR_STR_HIGH = 3, +}; + /* Platform dependent definition */ struct adv7604_platform_data { /* connector - HDMI or DVI? */ @@ -110,6 +116,11 @@ struct adv7604_platform_data { unsigned replicate_av_codes:1; unsigned invert_cbcr:1; + /* IO register 0x14 */ + enum adv7604_drive_strength dr_str_data; + enum adv7604_drive_strength dr_str_clk; + enum adv7604_drive_strength dr_str_sync; + /* IO register 0x30 */ unsigned output_bus_lsb_to_msb:1; -- cgit v0.10.2 From 547ed542ab570c690ac3cf773a7bdae5323119f0 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 10:01:17 -0300 Subject: [media] adv7604: support 1366x768 DMT Reduced Blanking Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 00ce271..9f80d2e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -161,6 +161,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, { }, -- cgit v0.10.2 From 1577461be7c95bfc14084267cd74e2671310153c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Dec 2013 10:02:43 -0300 Subject: [media] adv7604: adv7604_s_register clean up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 9f80d2e..ff8130f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -718,45 +718,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); -- cgit v0.10.2 From 9833239e13735dc318d1b76a6aec32b409d2653c Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:05:58 -0300 Subject: [media] adv7604: Receive CEA formats as RGB on VGA (RGB) input If the input is ADV7604_INPUT_VGA_RGB and RGB quantization range is set to V4L2_DV_RGB_RANGE_AUTO, video with CEA timings will be received as RGB. For ADV7604_INPUT_VGA_COMP, automatic CSC mode will be selected. See table 44 on page 205 in "ADV7604 Hardware Manual, Rev. F, August 2010" for details. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ff8130f..d4ac8dd 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -911,25 +911,41 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) { struct adv7604_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 */ - - /* 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); - } - } else { - /* receiving HDMI or analog signal, set automode */ + 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_read(sd, 0x05) & 0x80) { + /* Receiving HDMI signal + * Set automode */ io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + /* 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); } break; case V4L2_DV_RGB_RANGE_LIMITED: @@ -1709,7 +1725,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" -- cgit v0.10.2 From d261e8428b9fed1d42827677688c457e7197e5d1 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:17:15 -0300 Subject: [media] adv7604: select YPbPr if RGB_RANGE_FULL/LIMITED is set for VGA_COMP inputs Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d4ac8dd..d1de747 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -949,17 +949,26 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) } break; case V4L2_DV_RGB_RANGE_LIMITED: - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + if (state->selected_input == ADV7604_INPUT_VGA_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->selected_input == ADV7604_INPUT_VGA_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; } } - static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); -- cgit v0.10.2 From 3e86aa856d0b1f9d24c2dd83f4e31f93795f34c6 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Tue, 10 Dec 2013 09:55:18 -0300 Subject: [media] adv7604: set CEC address (SPA) in EDID Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d1de747..d8061c5 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -72,6 +72,7 @@ struct adv7604_state { u32 present; unsigned blocks; } edid; + u16 spa_port_a; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; struct workqueue_struct *work_queues; @@ -531,9 +532,6 @@ static inline int edid_write_block(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); - /* 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); @@ -1623,9 +1621,39 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi return 0; } +static int get_edid_spa_location(struct v4l2_subdev *sd, 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 = get_edid_spa_location(sd, edid->edid); + int tmp = 0; int err; if (edid->pad > ADV7604_EDID_PORT_D) @@ -1633,16 +1661,20 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi if (edid->start_block != 0) return -EINVAL; if (edid->blocks == 0) { - /* Pull down the hotplug pin */ + /* 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); - /* Disables I2C access to internal EDID ram from DDC port */ - rep_write_and_or(sd, 0x77, 0xf0, 0x0); - state->edid.blocks = 0; + 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; - v4l2_dbg(2, debug, sd, "%s: clear edid\n", __func__); + + 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) { @@ -1652,19 +1684,45 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi if (!edid->edid) return -EINVAL; + /* Disable hotplug and I2C access to EDID RAM from DDC port */ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); - state->edid.present &= ~(1 << edid->pad); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); + rep_write_and_or(sd, 0x77, 0xf0, 0x00); + + v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", + __func__, edid->pad, state->edid.present); + switch (edid->pad) { + case ADV7604_EDID_PORT_A: + state->spa_port_a = edid->edid[spa_loc]; + break; + case ADV7604_EDID_PORT_B: + rep_write(sd, 0x70, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); + rep_write(sd, 0x71, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + break; + case ADV7604_EDID_PORT_C: + rep_write(sd, 0x72, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); + rep_write(sd, 0x73, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + break; + case ADV7604_EDID_PORT_D: + rep_write(sd, 0x74, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); + rep_write(sd, 0x75, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + break; + } + rep_write(sd, 0x76, (spa_loc < 0) ? 0x00 : spa_loc); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc < 0) ? 0x00 : (spa_loc >> 2) & 0x40); + + if (spa_loc > 0) + edid->edid[spa_loc] = state->spa_port_a; 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]); - state->edid.present |= edid->pad; + 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\n", err); + v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); return err; } @@ -1984,7 +2042,6 @@ static int adv7604_core_init(struct v4l2_subdev *sd) ADI recommended setting [REF_01, c. 2.3.3] */ cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution for digital formats */ - rep_write(sd, 0x76, 0xc0); /* SPA location for port B, C and D */ /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ -- cgit v0.10.2 From dd08beb905cc170c596306661ddbe311765c771b Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Tue, 10 Dec 2013 09:57:09 -0300 Subject: [media] adv7604: improve EDID handling - split edid_write_block() - do not use edid->edid before the validity check - Return -EINVAL if edid->pad is invalid - Save both registers for SPA port A - Set SPA location to default value if it is not found Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d8061c5..501f40f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -72,7 +72,7 @@ struct adv7604_state { u32 present; unsigned blocks; } edid; - u16 spa_port_a; + u16 spa_port_a[2]; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; struct workqueue_struct *work_queues; @@ -510,22 +510,9 @@ 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 *)&state->edid.present); -} - 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; @@ -535,24 +522,19 @@ static inline int edid_write_block(struct v4l2_subdev *sd, 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, state->edid.present); +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) & state->edid.present) - break; - mdelay(1); - } - if (i == 1000) { - v4l_err(client, "error enabling edid (0x%x)\n", state->edid.present); - return -EIO; - } + v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - return 0; + v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); } static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) @@ -1621,7 +1603,7 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi return 0; } -static int get_edid_spa_location(struct v4l2_subdev *sd, const u8 *edid) +static int get_edid_spa_location(const u8 *edid) { u8 d; @@ -1652,9 +1634,10 @@ static int get_edid_spa_location(struct v4l2_subdev *sd, const u8 *edid) static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) { struct adv7604_state *state = to_state(sd); - int spa_loc = get_edid_spa_location(sd, edid->edid); + int spa_loc; int tmp = 0; int err; + int i; if (edid->pad > ADV7604_EDID_PORT_D) return -EINVAL; @@ -1684,35 +1667,43 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi if (!edid->edid) return -EINVAL; + 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); - v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", - __func__, edid->pad, state->edid.present); + 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 = edid->edid[spa_loc]; + 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, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); - rep_write(sd, 0x71, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + 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, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); - rep_write(sd, 0x73, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + 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, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); - rep_write(sd, 0x75, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); + 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 < 0) ? 0x00 : spa_loc); - rep_write_and_or(sd, 0x77, 0xbf, (spa_loc < 0) ? 0x00 : (spa_loc >> 2) & 0x40); + rep_write(sd, 0x76, spa_loc & 0xff); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); - if (spa_loc > 0) - edid->edid[spa_loc] = state->spa_port_a; + 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; @@ -1726,6 +1717,21 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi 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); -- cgit v0.10.2 From 94b130131115de6c7c6b6228379e61d270ab8966 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:23:03 -0300 Subject: [media] adv7604: remove connector type. Never used for anything useful May also be wrong if the receiver is connected to more than one connector. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 501f40f..c85f86c 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -77,7 +77,6 @@ struct adv7604_state { 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; @@ -1817,8 +1816,6 @@ 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" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); 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"), @@ -2138,7 +2135,6 @@ static int adv7604_probe(struct i2c_client *client, 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) { diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 22fd1ac..baf7250 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -86,9 +86,6 @@ enum adv7604_drive_strength { /* Platform dependent definition */ struct adv7604_platform_data { - /* connector - HDMI or DVI? */ - unsigned connector_hdmi:1; - /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ unsigned disable_pwrdnb:1; -- cgit v0.10.2 From ff4f80fdfaefb7906ed58ec36248591687f927b5 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:24:05 -0300 Subject: [media] adv7604: return immediately if the new input is equal to what is configured Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index c85f86c..4e7d39a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1479,7 +1479,11 @@ 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->selected_input = input; @@ -1524,6 +1528,8 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) u8 fmt_change, fmt_change_digital, tx_5v; u32 input_status; + v4l2_dbg(2, debug, sd, "%s: ", __func__); + /* format change */ fmt_change = io_read(sd, 0x43) & 0x98; if (fmt_change) @@ -2124,6 +2130,7 @@ 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) { -- cgit v0.10.2 From 14d03233517001849742e45803219755430a5eed Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:26:11 -0300 Subject: [media] adv7604: remove debouncing of ADV7604_FMT_CHANGE events ADV7604_FMT_CHANGE events was debounced in adv7604_isr() to avoid that a receiver with a unstable input signal would block the event handling for other inputs. This solution was prone to errors. A better protection agains interrupt blocking is to delay the call of the interrupt service routine in the adv7604 driver if too many interrupts are received within a given time. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 4e7d39a..442f70a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -78,7 +78,6 @@ struct adv7604_state { struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; - u32 prev_input_status; /* i2c clients */ struct i2c_client *i2c_avlink; @@ -1524,9 +1523,7 @@ 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; v4l2_dbg(2, debug, sd, "%s: ", __func__); @@ -1534,22 +1531,17 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) fmt_change = io_read(sd, 0x43) & 0x98; if (fmt_change) io_write(sd, 0x44, fmt_change); + fmt_change_digital = is_digital_input(sd) ? (io_read(sd, 0x6b) & 0xc0) : 0; if (fmt_change_digital) io_write(sd, 0x6c, fmt_change_digital); + 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; @@ -2129,7 +2121,6 @@ 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 */ -- cgit v0.10.2 From 5474b983c679e3df5ee3c2731c0b685915d5250d Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:33:41 -0300 Subject: [media] adv7604: improve HDMI audio handling - Mute audio before switching inputs to avoid noise/pops - Mute audio if audio FIFO over-/underflows (AD Recommended setting) - Reset FIFO if it over-/underflows (AD Recommended setting) Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 442f70a..b5dcea8 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1387,14 +1387,12 @@ static void enable_input(struct v4l2_subdev *sd) struct adv7604_state *state = to_state(sd); if (is_analog_input(sd)) { - /* enable */ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ } else if (is_digital_input(sd)) { - /* enable */ hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input); - 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 */ } else { v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", __func__, state->selected_input); @@ -1403,9 +1401,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 */ + 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 */ } @@ -2044,6 +2042,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 */ -- cgit v0.10.2 From 1e0b9156d52ac748c92d5aa7ae9dc602168b0b62 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 10:34:46 -0300 Subject: [media] adv7604: set restart_stdi_once flag when signal is lost If the restart_stdi_once trick fails to find a valid format the flag was never reset. Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index b5dcea8..85caf24 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1216,6 +1216,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; } -- cgit v0.10.2 From 5c6c634916cab638f474dca20b2fba942cfd6ffc Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 10:39:04 -0300 Subject: [media] adv7604: adjust gain and offset for DVI-D signals If the input signal is DVI-D and quantization range is RGB full range, gain and offset must be adjusted to get the right range on the output. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 85caf24..d5355d7 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -885,12 +885,72 @@ 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); - v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n", - __func__, state->rgb_quantization_range); + 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: @@ -908,7 +968,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; } - if (hdmi_read(sd, 0x05) & 0x80) { + if (hdmi_signal) { /* Receiving HDMI signal * Set automode */ io_write_and_or(sd, 0x02, 0x0f, 0xf0); @@ -924,24 +984,45 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) } 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 { + adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); + adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); + } } 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); - } else { - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + 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 { - /* 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); } break; } @@ -1367,7 +1448,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); -- cgit v0.10.2 From f24d229c1e17d40f81010ede53d9bf67327e21d2 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Tue, 10 Dec 2013 10:15:13 -0300 Subject: [media] adv7604: Enable HDMI_MODE interrupt Some sources are initially detected as DVI, and change to HDMI later. This must be detected to set the right RGB quantization range. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d5355d7..912c59e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1602,18 +1602,25 @@ static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { - u8 fmt_change, fmt_change_digital, tx_5v; + 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 = is_digital_input(sd) ? (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, @@ -1625,6 +1632,15 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 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) & 0x1e; if (tx_5v) { @@ -2138,7 +2154,7 @@ 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, 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); -- cgit v0.10.2 From d48eb48cd4d497e2e65c68d3ca3976a8dbfc992e Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 12 Dec 2013 10:13:35 -0300 Subject: [media] adv7604: return immediately if the new timings are equal to what is configured Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 912c59e..0bc9e1a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1423,6 +1423,11 @@ 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 ((is_analog_input(sd) && bt->pixelclock > 170000000) || -- cgit v0.10.2 From 9890869651fc8bc3876f2eb839ac403edb47560d Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Fri, 20 Dec 2013 05:14:57 -0300 Subject: [media] adv7604: sync polarities from platform data Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 0bc9e1a..be9699e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2126,9 +2126,10 @@ 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 */ + + /* 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 | diff --git a/include/media/adv7604.h b/include/media/adv7604.h index baf7250..d262a3a 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -113,6 +113,10 @@ struct adv7604_platform_data { unsigned replicate_av_codes:1; unsigned invert_cbcr:1; + /* IO register 0x06 */ + unsigned inv_vs_pol:1; + unsigned inv_hs_pol:1; + /* IO register 0x14 */ enum adv7604_drive_strength dr_str_data; enum adv7604_drive_strength dr_str_clk; -- cgit v0.10.2 From 591b72fe739cb2fcb8052eff02feaa2d4a52fbca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Dec 2013 10:05:13 -0300 Subject: [media] adv7604: initialize timings to CEA 640x480p59.94 This timing must be supported by all HDMI equipment, so that's a reasonable default. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index be9699e..71c8570 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2207,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; @@ -2234,7 +2236,8 @@ static int adv7604_probe(struct i2c_client *client, 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); -- cgit v0.10.2 From e78d834a2e39e86d733fdca41fc931dc5ecc4660 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 10:57:03 -0300 Subject: [media] adv7842: Re-worked query_dv_timings() This simplified the code quite a bit. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index b154f36..22fa4ca 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1314,6 +1314,8 @@ 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; @@ -1325,92 +1327,46 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, } 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; + + timings->type = V4L2_DV_BT_656_1120; + 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 (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; + freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8); } - /* 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; - - if (w != bt->width || h != bt->height) - continue; - - if (abs(freq - bt->pixelclock) > 1000000) - continue; - *timings = v4l2_dv_timings_presets[i]; - return 0; - } - - 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; + 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 { /* Interlaced? */ if (stdi.interlaced) { @@ -1437,6 +1393,8 @@ 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; -- cgit v0.10.2 From 6251e65f1ba6c1f4e461a1d97735812165c4499d Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 11:01:00 -0300 Subject: [media] adv7842: corrected setting of cp-register 0x91 and 0x8f Bit 6 of register 0x8f was cleared incorrectly (must be 1), and bit 4 of register 0x91 was set incorrectly (must be 0). These bits are undocumented, so we shouldn't modify them to values different from what the datasheet specifies. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 22fa4ca..6434a93 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -927,7 +927,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); @@ -1408,7 +1408,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); -- cgit v0.10.2 From c9f1f271d4b6455d5ecf43e393668a7c8f4cb833 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 11:14:26 -0300 Subject: [media] adv7842: properly enable/disable the irqs The method of disabling the irq-output pin caused many "empty" interrupts. Instead, actually disable/enable the interrupts by changing the interrupt masks. Also enable STORE_MASKED_IRQ in INT1 configuration, otherwise when HDMI events happen while the interrupt is masked those events will be ignored when the interrupt is unmasked. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 6434a93..cbbfa77 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1786,10 +1786,8 @@ 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); - /* 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); @@ -1810,6 +1808,8 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) if (irq_status[4]) io_write(sd, 0x9e, irq_status[4]); + adv7842_irq_enable(sd, true); + v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, irq_status[0], irq_status[1], irq_status[2], irq_status[3], irq_status[4]); @@ -1845,9 +1845,6 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) if (handled) *handled = true; - /* re-enable irq-pin output */ - io_write(sd, 0x40, irq_cfg); - return 0; } @@ -2446,7 +2443,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd, 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); -- cgit v0.10.2 From 7de5be44a523558b7b5330fba9d24e95a519c1ec Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:39:37 -0300 Subject: [media] adv7842: save platform data in state struct Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index cbbfa77..4f93526 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -61,6 +61,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; @@ -2730,6 +2731,9 @@ static int adv7842_probe(struct i2c_client *client, return -ENOMEM; } + /* platform data */ + state->pdata = *pdata; + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -2834,7 +2838,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; -- cgit v0.10.2 From 32dbc8d4d5b2307ade65a28679e904c84f64764e Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:40:43 -0300 Subject: [media] adv7842: added DE vertical position in SDP-io-sync Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4f93526..05d65a8 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2397,6 +2397,10 @@ static int adv7842_core_init(struct v4l2_subdev *sd, 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, 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); } /* todo, improve settings for sdram */ diff --git a/include/media/adv7842.h b/include/media/adv7842.h index c02201d..c023f88 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -131,6 +131,10 @@ struct adv7842_sdp_io_sync_adjustment { uint16_t hs_width; uint16_t de_beg; uint16_t de_end; + uint8_t de_v_beg_o; + uint8_t de_v_beg_e; + uint8_t de_v_end_o; + uint8_t de_v_end_e; }; /* Platform dependent definition */ -- cgit v0.10.2 From 69e9ba6f31e6ff93eecdbf6fbeff8e5320fd2155 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Dec 2013 05:44:27 -0300 Subject: [media] adv7842: support YCrCb analog input, receive CEA formats as RGB on VGA input Added support for YCrCb analog input. If input is ADV7842_MODE_RGB and RGB quantization range is set to V4L2_DV_RGB_RANGE_AUTO, then video with CEA timings will be received as RGB. For ADV7842_MODE_COMP, automatic CSC mode will be selected. See table 48 on page 281 in "ADV7842 Hardware Manual, Rev. 0, January 2011" for details. Make sure that when switching inputs the RGB quantization range is updated as well. Also updated the platform_data in ezkit to ensure that what was the old default value is now explicitly specified, so the behavior for that board is unchanged. Signed-off-by: Mats Randgaard Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Cc: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 82beedd..28bdd8b 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -1025,7 +1025,6 @@ static struct adv7842_platform_data adv7842_data = { .ain_sel = ADV7842_AIN10_11_12_NC_SYNC_4_1, .prim_mode = ADV7842_PRIM_MODE_SDP, .vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1, - .inp_color_space = ADV7842_INP_COLOR_SPACE_AUTO, .i2c_sdp_io = 0x40, .i2c_sdp = 0x41, .i2c_cp = 0x42, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 05d65a8..23f3c1f 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1034,34 +1034,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; } } @@ -1299,7 +1325,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) { @@ -1442,6 +1468,8 @@ 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: @@ -1586,6 +1614,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 @@ -1681,19 +1716,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; @@ -2112,7 +2147,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" @@ -2341,9 +2376,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)); @@ -2356,7 +2392,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 | @@ -2570,7 +2606,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); diff --git a/include/media/adv7842.h b/include/media/adv7842.h index c023f88..f4e9d0d 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -163,9 +163,6 @@ struct adv7842_platform_data { /* Video standard */ enum adv7842_vid_std_select vid_std_select; - /* Input Color Space */ - enum adv7842_inp_color_space inp_color_space; - /* Select output format */ enum adv7842_op_format_sel op_format_sel; -- cgit v0.10.2 From b38a1c4cf27ca8303b7c15f8df8247c081ba2723 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:46:21 -0300 Subject: [media] adv7842: set defaults spa-location For edid with no Source Physical Address (spa), set spa-location to default and use correct values from edid. Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 23f3c1f..8d0edd4 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -716,15 +716,15 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) } rep_write(sd, 0x76, spa_loc); } else { - /* default register values for SPA */ + /* Edid values for SPA location */ if (port == 0) { - /* port A SPA */ - rep_write(sd, 0x72, 0); - rep_write(sd, 0x73, 0); + /* port A */ + rep_write(sd, 0x72, val[0xc0]); + rep_write(sd, 0x73, val[0xc1]); } else { - /* port B SPA */ - rep_write(sd, 0x74, 0); - rep_write(sd, 0x75, 0); + /* port B */ + rep_write(sd, 0x74, val[0xc0]); + rep_write(sd, 0x75, val[0xc1]); } rep_write(sd, 0x76, 0xc0); } -- cgit v0.10.2 From 3c4da74fe55e52e2635b08af99471a5679a7a92e Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:52:39 -0300 Subject: [media] adv7842: 625/525 line standard jitter fix Both the PAL and NTSC standards are interlaced where a frame consist of two fields. Total number of lines in a frame in both systems are an odd number so the two fields will have different length. In the 625 line standard ("PAL") the odd field of the frame is transmitted first, while in the 525 standard ("NTSC") the even field is transmitted first. This adds the possibility to change output config between the fields and standards. This setting will reduce the "format-jitter" on the signal sent by the pixelport moving the difference between the fields to vertical front/back-porch only. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 8d0edd4..dcafc8e 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2345,15 +2345,55 @@ 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, 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, 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; @@ -2423,22 +2463,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); - 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); - } - /* todo, improve settings for sdram */ if (pdata->sd_ram_size >= 128) { sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ diff --git a/include/media/adv7842.h b/include/media/adv7842.h index f4e9d0d..5327ba3 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -197,7 +197,8 @@ struct adv7842_platform_data { struct adv7842_sdp_csc_coeff sdp_csc_coeff; - struct adv7842_sdp_io_sync_adjustment sdp_io_sync; + struct adv7842_sdp_io_sync_adjustment sdp_io_sync_625; + struct adv7842_sdp_io_sync_adjustment sdp_io_sync_525; /* i2c addresses */ u8 i2c_sdp_io; -- cgit v0.10.2 From 8e4e3631531050c6287a9fc3542c26b601f3e533 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:55:48 -0300 Subject: [media] adv7842: set default input in platform-data Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index dcafc8e..86db9fc 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2804,7 +2804,7 @@ static int adv7842_probe(struct i2c_client *client, 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; /* i2c access to adv7842? */ rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | diff --git a/include/media/adv7842.h b/include/media/adv7842.h index 5327ba3..c12de2d 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -160,6 +160,9 @@ struct adv7842_platform_data { /* Default mode */ enum adv7842_mode mode; + /* Default input */ + unsigned input; + /* Video standard */ enum adv7842_vid_std_select vid_std_select; -- cgit v0.10.2 From 84aeed5310d7ea589ee703636d45bb56de0adb7f Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 11:56:32 -0300 Subject: [media] adv7842: increase wait time Wait 5ms after main reset. The data-sheet doesn't specify the wait after i2c-controlled reset, so using same value as after pin-controlled reset. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 86db9fc..f16437c 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -533,7 +533,7 @@ static void main_reset(struct v4l2_subdev *sd) adv_smbus_write_byte_no_check(client, 0xff, 0x80); - mdelay(2); + mdelay(5); } /* ----------------------------------------------------------------------- */ -- cgit v0.10.2 From 77a09f8bf16c66c4875abe4d51939287df044242 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 11:58:08 -0300 Subject: [media] adv7842: remove connector type. Never used for anything useful May also be wrong if the receiver is connected to more than one connector. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index f16437c..ab44897 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -82,7 +82,6 @@ struct adv7842_state { bool is_cea_format; struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; - bool connector_hdmi; bool hdmi_port_a; /* i2c clients */ @@ -2166,8 +2165,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", @@ -2801,7 +2798,6 @@ static int adv7842_probe(struct i2c_client *client, 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 = pdata->input == ADV7842_SELECT_HDMI_PORT_A; diff --git a/include/media/adv7842.h b/include/media/adv7842.h index c12de2d..24fed119 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -139,9 +139,6 @@ struct adv7842_sdp_io_sync_adjustment { /* Platform dependent definition */ struct adv7842_platform_data { - /* connector - HDMI or DVI? */ - unsigned connector_hdmi:1; - /* chip reset during probe */ unsigned chip_reset:1; -- cgit v0.10.2 From 7de6fab1fdc81381834db690ec0511ec87acc4d3 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Tue, 10 Dec 2013 11:24:35 -0300 Subject: [media] adv7842: Use defines to select EDID port Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ab44897..a26c70c 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -22,8 +22,8 @@ * 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_02 - Analog devices, Software User Guide, UG-206, + * ADV7842 I2C Register Maps, Rev. 0, November 2010 */ @@ -587,10 +587,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 +679,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); @@ -703,44 +701,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 { - /* Edid values for SPA location */ - if (port == 0) { - /* port A */ - rep_write(sd, 0x72, val[0xc0]); - rep_write(sd, 0x73, val[0xc1]); - } else { - /* port B */ - rep_write(sd, 0x74, val[0xc0]); - rep_write(sd, 0x75, val[0xc1]); - } - 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; } @@ -1888,7 +1874,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; @@ -1901,20 +1887,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<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); diff --git a/include/media/adv7842.h b/include/media/adv7842.h index 24fed119..a4851bf 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -225,4 +225,8 @@ struct adv7842_platform_data { * deinterlacer. */ #define ADV7842_CMD_RAM_TEST _IO('V', BASE_VIDIOC_PRIVATE) +#define ADV7842_EDID_PORT_A 0 +#define ADV7842_EDID_PORT_B 1 +#define ADV7842_EDID_PORT_VGA 2 + #endif -- cgit v0.10.2 From 5b64b205392fcf3bbbc0cedddeb0aa33cd769801 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 5 Dec 2013 12:08:45 -0300 Subject: [media] adv7842: mute audio before switching inputs to avoid noise/pops Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index a26c70c..bbd80ac 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_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 */ @@ -491,6 +494,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); @@ -1459,14 +1467,12 @@ static void enable_input(struct v4l2_subdev *sd) 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", @@ -1477,9 +1483,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 */ } @@ -2432,6 +2438,9 @@ 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 | -- cgit v0.10.2 From fc2e991e86382abb5cd32010b92a63b15bd76f30 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 12:09:51 -0300 Subject: [media] adv7842: clear edid, if no edid just disable Edid-DDC access Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index bbd80ac..eab9a1b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -700,6 +700,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); @@ -2638,8 +2641,8 @@ static int adv7842_command_ram_test(struct v4l2_subdev *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); return ret; } -- cgit v0.10.2 From 6e9071f26864d83be6855f570a5ccc847b66028a Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 12:00:06 -0300 Subject: [media] adv7842: restart STDI once if format is not found 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. Copied from adv7604. Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index eab9a1b..ae7252c 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -85,6 +85,7 @@ struct adv7842_state { bool is_cea_format; struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; + bool restart_stdi_once; bool hdmi_port_a; /* i2c clients */ @@ -1345,6 +1346,7 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, /* 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; } @@ -1355,6 +1357,7 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, uint32_t freq; timings->type = V4L2_DV_BT_656_1120; + 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) + @@ -1391,21 +1394,50 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, } adv7842_fill_optional_dv_timings_fields(sd, timings); } else { - /* Interlaced? */ - if (stdi.interlaced) { - v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); - return -ERANGE; - } - + /* 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; } @@ -2804,6 +2836,7 @@ static int adv7842_probe(struct i2c_client *client, state->mode = pdata->mode; 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 | -- cgit v0.10.2 From 245b2b678c6fc65f0265c55f7a4fa0155ad6b235 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 12:14:02 -0300 Subject: [media] adv7842: support g_edid ioctl Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ae7252c..2920e6b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1910,6 +1910,46 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 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 (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; + + 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; +} + static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) { struct adv7842_state *state = to_state(sd); @@ -2722,6 +2762,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, }; -- cgit v0.10.2 From b82e2793476e7fd031ba9e3b0cad357d1534d1d1 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 12:14:45 -0300 Subject: [media] adv7842: i2c dummy clients registration Clear i2c_clients ptr when unregistered. Warn if configured i2c-addr is zero. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 2920e6b..108e1b0 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2805,8 +2805,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) @@ -2829,15 +2830,71 @@ 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, @@ -2939,21 +2996,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; @@ -2989,7 +3032,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; @@ -3008,7 +3051,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; } -- cgit v0.10.2 From 019aa8be84bf543677146e2586412a2905ba99c0 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Tue, 10 Dec 2013 12:01:59 -0300 Subject: [media] adv7842: enable HDMI/DVI mode irq Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 108e1b0..e6932f4 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1834,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); } } @@ -1847,7 +1850,7 @@ 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_status[6]; adv7842_irq_enable(sd, false); @@ -1857,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]) @@ -1869,12 +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]); adv7842_irq_enable(sd, true); - v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, + 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; @@ -1891,22 +1897,32 @@ 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]) - adv7842_s_detect_tx_5v_ctrl(sd); - - if (handled) - *handled = true; + /* 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; } -- cgit v0.10.2 From 1961b7203ffaebe13fb463ffc5bc5da30bb7f8e3 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 12:18:14 -0300 Subject: [media] adv7842: composite sd-ram test, clear timings before setting Must clear timings before setting after test to recover. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index e6932f4..ecbe3f2 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2696,6 +2696,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) @@ -2726,12 +2727,16 @@ 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, 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; } -- cgit v0.10.2 From f0ec17420e85cc701032a7d7fd1067d3435bd133 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Fri, 20 Dec 2013 06:02:24 -0300 Subject: [media] adv7842: obtain free-run mode from the platform_data The free-run mode can be board-specific. Also updated the platform_data in ezkit to ensure that what was the old default value is now explicitly specified, so the behavior for that board is unchanged. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Cc: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 28bdd8b..39a7969 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -1025,6 +1025,8 @@ static struct adv7842_platform_data adv7842_data = { .ain_sel = ADV7842_AIN10_11_12_NC_SYNC_4_1, .prim_mode = ADV7842_PRIM_MODE_SDP, .vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1, + .hdmi_free_run_enable = 1, + .sdp_free_run_auto = 1, .i2c_sdp_io = 0x40, .i2c_sdp = 0x41, .i2c_cp = 0x42, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ecbe3f2..518f1e2 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1624,8 +1624,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: @@ -2538,7 +2536,14 @@ static int adv7842_core_init(struct v4l2_subdev *sd) pdata->drive_strength.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_force << 3)); /* TODO from platform data */ cp_write(sd, 0x69, 0x14); /* Enable CP CSC */ diff --git a/include/media/adv7842.h b/include/media/adv7842.h index a4851bf..772cdec 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -192,8 +192,18 @@ struct adv7842_platform_data { unsigned sd_ram_size; /* ram size in MB */ unsigned sd_ram_ddr:1; /* ddr or sdr sdram */ - /* Free run */ - unsigned hdmi_free_run_mode; + /* HDMI free run, CP-reg 0xBA */ + unsigned hdmi_free_run_enable:1; + /* 0 = Mode 0: run when there is no TMDS clock + 1 = Mode 1: run when there is no TMDS clock or the + video resolution does not match programmed one. */ + unsigned hdmi_free_run_mode:1; + + /* SDP free run, CP-reg 0xDD */ + unsigned sdp_free_run_auto:1; + unsigned sdp_free_run_man_col_en:1; + unsigned sdp_free_run_cbar_en:1; + unsigned sdp_free_run_force:1; struct adv7842_sdp_csc_coeff sdp_csc_coeff; -- cgit v0.10.2 From 15058aac06dc706c549a66f0e254046cbc844b9b Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 5 Dec 2013 12:22:53 -0300 Subject: [media] adv7842: Composite sync adjustment Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 518f1e2..ba74863 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2439,6 +2439,10 @@ static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_ 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); @@ -2453,6 +2457,10 @@ static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_ 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); diff --git a/include/media/adv7842.h b/include/media/adv7842.h index 772cdec..5a7eb50 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -131,6 +131,10 @@ struct adv7842_sdp_io_sync_adjustment { uint16_t hs_width; uint16_t de_beg; uint16_t de_end; + uint8_t vs_beg_o; + uint8_t vs_beg_e; + uint8_t vs_end_o; + uint8_t vs_end_e; uint8_t de_v_beg_o; uint8_t de_v_beg_e; uint8_t de_v_end_o; -- cgit v0.10.2 From 834a8be1566f6db57a95954cc7bcc9d1e95aa80b Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Thu, 12 Dec 2013 10:10:57 -0300 Subject: [media] adv7842: return 0 if no change in s_dv_timings Return 0 if the new timings are equal to the current timings as it caused extra cp-loss/lock interrupts. Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ba74863..c697117 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1453,6 +1453,11 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, 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), -- cgit v0.10.2 From fe808f3c9342cbd77fb0102c462e8555e458b286 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Dec 2013 06:03:58 -0300 Subject: [media] adv7842: set LLC DLL phase from platform_data The correct LLC DLL phase depends on the board layout, so this should be part of the platform_data. Also updated the platform_data in ezkit to ensure that what was the old default value is now explicitly specified, so the behavior for that board is unchanged. Tested-by: Martin Bugge Signed-off-by: Hans Verkuil Cc: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 39a7969..66e9edb 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -1027,6 +1027,7 @@ static struct adv7842_platform_data adv7842_data = { .vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1, .hdmi_free_run_enable = 1, .sdp_free_run_auto = 1, + .llc_dll_phase = 0x10, .i2c_sdp_io = 0x40, .i2c_sdp = 0x41, .i2c_cp = 0x42, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index c697117..7898686 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1593,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 */ @@ -2609,8 +2606,7 @@ 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 */ diff --git a/include/media/adv7842.h b/include/media/adv7842.h index 5a7eb50..d72a8a7 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -192,6 +192,12 @@ struct adv7842_platform_data { unsigned sync:2; } drive_strength; + /* + * IO register 0x19: Adjustment to the LLC DLL phase in + * increments of 1/32 of a clock period. + */ + unsigned llc_dll_phase:5; + /* External RAM for 3-D comb or frame synchronizer */ unsigned sd_ram_size; /* ram size in MB */ unsigned sd_ram_ddr:1; /* ddr or sdr sdram */ -- cgit v0.10.2 From 0bb4e7ab643be0c97c378ba39b4e6f9dcf508749 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Dec 2013 10:09:51 -0300 Subject: [media] adv7842: initialize timings to CEA 640x480p59.94 This timing must be supported by all HDMI equipment, so that's a reasonable default. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 7898686..515a870 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2936,6 +2936,8 @@ 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; @@ -2962,6 +2964,7 @@ static int adv7842_probe(struct i2c_client *client, /* platform data */ state->pdata = *pdata; + state->timings = cea640x480; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); -- cgit v0.10.2 From 7f95c904b9d7a42c96893fddd48b7615f549c5ff Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Dec 2013 06:15:13 -0300 Subject: [media] adv7842: add drive strength enum and sync names with adv7604 Add a proper driver strength enum and use the same names in the platform data as with adv7604. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 515a870..1effc21 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2541,9 +2541,10 @@ static int adv7842_core_init(struct v4l2_subdev *sd) 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_and_or(sd, 0xba, 0xfc, pdata->hdmi_free_run_enable | diff --git a/include/media/adv7842.h b/include/media/adv7842.h index d72a8a7..3932209 100644 --- a/include/media/adv7842.h +++ b/include/media/adv7842.h @@ -108,6 +108,13 @@ enum adv7842_select_input { ADV7842_SELECT_SDP_YC, }; +enum adv7842_drive_strength { + ADV7842_DR_STR_LOW = 0, + ADV7842_DR_STR_MEDIUM_LOW = 1, + ADV7842_DR_STR_MEDIUM_HIGH = 2, + ADV7842_DR_STR_HIGH = 3, +}; + struct adv7842_sdp_csc_coeff { bool manual; uint16_t scaling; @@ -186,11 +193,9 @@ struct adv7842_platform_data { unsigned output_bus_lsb_to_msb:1; /* IO register 0x14 */ - struct { - unsigned data:2; - unsigned clock:2; - unsigned sync:2; - } drive_strength; + enum adv7842_drive_strength dr_str_data; + enum adv7842_drive_strength dr_str_clk; + enum adv7842_drive_strength dr_str_sync; /* * IO register 0x19: Adjustment to the LLC DLL phase in -- cgit v0.10.2 From 66c5e5927860159e58bb6d8ebe7ac886277d617b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 12 Dec 2013 03:49:54 -0300 Subject: [media] radio-bcm2048: fix missing unlock on error in bcm2048_rds_fifo_receive() Add the missing unlock before return from function bcm2048_rds_fifo_receive() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 494ec39..37ff899 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -1767,6 +1767,7 @@ static void bcm2048_rds_fifo_receive(struct bcm2048_device *bdev) bdev->rds_info.radio_text, bdev->fifo_size); if (err != 2) { dev_err(&bdev->client->dev, "RDS Read problem\n"); + mutex_unlock(&bdev->mutex); return; } -- cgit v0.10.2 From 5765f33c6c9ae68c7ad464931d21764911c6e2d1 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Thu, 12 Dec 2013 17:41:09 -0300 Subject: [media] cx231xx: Add missing selects for MEDIA_SUBDRV_AUTOSELECT The two drivers LGDT3305 and TDA18271C2DD were not autoselected, so the cx231xx_dvb module could not be loaded when MEDIA_SUBDRV_AUTOSELECT=y. Signed-off-by: Matthias Schwarzott Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 86feeea..f14c5e8 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 -- cgit v0.10.2 From 1134e9d1931cd42980fa7de741c1d4b30e6dd7ca Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Thu, 12 Dec 2013 17:41:10 -0300 Subject: [media] cx231xx: fix i2c debug prints Do not shift the already 7bit i2c address. Print a message also for write+read transactions. For write+read, print the read buffer correctly instead of using the write buffer. Signed-off-by: Matthias Schwarzott Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 96a5a09..a0d2235 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) { @@ -395,13 +395,21 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, } 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(" %02x", msgs[i].buf[byte]); + } /* 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(" %02x", msgs[i+1].buf[byte]); } i++; } else { -- cgit v0.10.2 From df739b5a8078a554b54456a7659d229b176b2f8c Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 16 Dec 2013 05:16:45 -0300 Subject: [media] Documentation/DocBook/media/v4l/subdev-formats.xml: fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xref to the v4l2-mbus-pixelcode-yuv8 table gets rendered as "Table 4.22, “YUV Formats”", so use the verb in the third person singular because it refers to "Table": s/list/lists/ Signed-off-by: Antonio Ospite Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index bfaef50..923d2b0 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -1178,7 +1178,7 @@ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. - list existing packet YUV + lists existing packet YUV formats and describes the organization of each pixel data in each sample. When a format pattern is split across multiple samples each of the samples in the pattern is described. -- cgit v0.10.2 From 3fe6d4b9620a653f46c5566016c8b060d21e0543 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 16 Dec 2013 05:16:46 -0300 Subject: [media] Documentation/DocBook/media/v4l: fix typo, s/packet/packed/ Change "packet" to "packed" where the doc is talking about packed data formats. Signed-off-by: Antonio Ospite Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 923d2b0..7331ce1 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -89,7 +89,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE. - The following tables list existing packet RGB formats. + The following tables list existing packed RGB formats. RGB formats @@ -615,7 +615,7 @@ - The following table lists existing packet Bayer formats. The data + The following table lists existing packed Bayer formats. The data organization is given as an example for the first pixel only.
@@ -1178,7 +1178,7 @@ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. - lists existing packet YUV + lists existing packed YUV formats and describes the organization of each pixel data in each sample. When a format pattern is split across multiple samples each of the samples in the pattern is described. -- cgit v0.10.2 From aa32f4c0bcce7dac55bf2639f4a6cf37c5c96934 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Dec 2013 05:45:37 -0300 Subject: [media] v4l2: move tracepoints to video_usercopy The (d)qbuf ioctls were traced in the low-level v4l2 ioctl function. The trace was outside the serialization lock, so that can affect the usefulness of the timing. In addition, the __user pointer was expected instead of a proper kernel pointer. By moving the tracepoints to video_usercopy we ensure that the trace calls use the correct kernel pointer, and that it happens right after the ioctl call to the driver, so certainly inside the serialization lock. In addition, we only trace if the call was successful. Signed-off-by: Hans Verkuil Acked-by: Wade Farnsworth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 1cc1749..b5aaaac 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -31,10 +31,6 @@ #include #include - -#define CREATE_TRACE_POINTS -#include - #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -395,11 +391,6 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } else ret = -ENOTTY; - if (cmd == VIDIOC_DQBUF) - trace_v4l2_dqbuf(vdev->minor, (struct v4l2_buffer *)arg); - else if (cmd == VIDIOC_QBUF) - trace_v4l2_qbuf(vdev->minor, (struct v4l2_buffer *)arg); - return ret; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 68e6b5e..707aef7 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -28,6 +28,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + /* 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; -- cgit v0.10.2 From 44687b2e81165164d3b921e383592cc0f5e062a0 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:35:57 -0300 Subject: [media] v4l: ti-vpe: create a scaler block library VPE and VIP IPs in DAR7x contain a scaler(SC) sub block. Create a library which will perform scaler block related configurations and hold SC register definitions. The functions provided by this library will be called by the vpe and vip drivers using a sc_data handle. The vpe_dev holds the sc_data handle. The handle represents an instance of the SC hardware, and the vpe driver uses it to access the scaler register offsets or helper functions to configure these registers. We move the SC register definitions to sc.h so that they aren't specific to VPE anymore. The register offsets are now relative to the sub-block, and not the VPE IP as a whole. In order for VPDMA to configure registers, it requires it's offset from the top level VPE module. A macro called GET_OFFSET_TOP is added to return the offset of the register relative to the VPE IP. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index cbf0a80..54c30b3 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 vpdma.o ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c new file mode 100644 index 0000000..f21dfbb --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.c @@ -0,0 +1,91 @@ +/* + * Scaler library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + * + * 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 +#include +#include +#include + +#include "sc.h" + +void sc_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0) +{ + *sc_reg0 |= CFG_SC_BYPASS; +} + +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 +} + +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 0000000..9248544 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + * + * 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 + +struct sc_data { + void __iomem *base; + struct resource *res; + + struct platform_device *pdev; +}; + +void sc_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0); +void sc_dump_regs(struct sc_data *sc); +struct sc_data *sc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 6697770..ecb85f9 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -43,6 +43,7 @@ #include "vpdma.h" #include "vpe_regs.h" +#include "sc.h" #define VPE_MODULE_NAME "vpe" @@ -324,9 +325,11 @@ 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 */ }; /* @@ -443,6 +446,9 @@ struct vpe_mmr_adb { 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) /* @@ -455,7 +461,8 @@ 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, sc_hdr, sc_regs, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00); }; @@ -749,18 +756,6 @@ static void set_csc_coeff_bypass(struct vpe_ctx *ctx) 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. @@ -769,6 +764,7 @@ 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; size_t mv_buf_size; int ret; @@ -806,7 +802,7 @@ 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); + sc_set_regs_bypass(ctx->dev->sc, &mmr_adb->sc_regs[0]); return 0; } @@ -922,28 +918,6 @@ 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); @@ -951,6 +925,8 @@ static void vpe_dump_regs(struct vpe_dev *dev) DUMPREG(CSC_CSC04); DUMPREG(CSC_CSC05); #undef DUMPREG + + sc_dump_regs(dev->sc); } static void add_out_dtd(struct vpe_ctx *ctx, int port) @@ -1965,7 +1941,6 @@ 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); @@ -1981,14 +1956,15 @@ 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); + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); if (!dev->base) { ret = -ENOMEM; goto v4l2_dev_unreg; @@ -2033,6 +2009,12 @@ 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->vpdma = vpdma_create(pdev); if (IS_ERR(dev->vpdma)) { ret = PTR_ERR(dev->vpdma); diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h index ed214e8..d8dbdd3 100644 --- a/drivers/media/platform/ti-vpe/vpe_regs.h +++ b/drivers/media/platform/ti-vpe/vpe_regs.h @@ -306,155 +306,6 @@ #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 -- cgit v0.10.2 From 0df20f9657693c420b10e8d18f1472e0dd47d634 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:35:58 -0300 Subject: [media] v4l: ti-vpe: support loading of scaler coefficients The SC block in VPE/VIP contains a SRAM within it. This internal memory requires to be loaded with appropriate scaler coefficients from a contiguous block of memory through VPDMA. The horizontal and vertical scaler each require 2 sets of scaler coefficients for luma and chroma scaling. The horizontal polyphase scaler requires coefficients for a 32 phase and 8 tap filter. Similarly, the vertical scaler requires coefficients for a 5 tap filter. The choice of the scaler coefficients depends on the scaling ratio. Add coefficient tables for different scaling ratios in sc_coeffs.h. In the case of horizontal downscaling, we need to consider the change in ratio caused by decimation performed by the horizontal scaler. In order to load the scaler coefficients via VPDMA, a configuration descriptor is used in block mode. The payload for the descriptor is the scaler coefficients copied to memory. Coefficients for each phase have to be placed in memory in a particular order understood by the scaler hardware. The choice of the scaler coefficients, and the loading of the coefficients from our tables to a contiguous buffer is managed by the functions sc_set_hs_coefficients and sc_set_vs_coefficients. The sc_data handle is now added with some parameters to describe the state of the coefficients loaded in the SC block. 'loaded_coeff_h' and 'loaded_coeff_v' hold the address of the last dma buffer which was used by VPDMA to copy coefficients. This information can be used by a vpe mem-to-mem context to decide whether it should load coefficients or not. 'hs_index' and 'vs_index' provide some optimization by preventing loading of coefficients if the scaling ratio didn't change between 2 contexts. 'load_coeff_h' and 'load_coeff_v' tell the vpe/vip driver whether we need to load the coefficients through VPDMA or not. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c index f21dfbb..417feb9 100644 --- a/drivers/media/platform/ti-vpe/sc.c +++ b/drivers/media/platform/ti-vpe/sc.c @@ -18,6 +18,7 @@ #include #include "sc.h" +#include "sc_coeff.h" void sc_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0) { @@ -61,6 +62,103 @@ void sc_dump_regs(struct sc_data *sc) #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; +} + struct sc_data *sc_create(struct platform_device *pdev) { struct sc_data *sc; diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h index 9248544..c89f3d1 100644 --- a/drivers/media/platform/ti-vpe/sc.h +++ b/drivers/media/platform/ti-vpe/sc.h @@ -161,15 +161,46 @@ #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_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0); 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); 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 0000000..5bfa5c0 --- /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, + * Dale Farnsworth, + * Archit Taneja, + * + * 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 -- cgit v0.10.2 From 773f06577b35f84f84de980e1be3eead8342b5e5 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:35:59 -0300 Subject: [media] v4l: ti-vpe: make vpe driver load scaler coefficients Make the driver allocate dma buffers to store horizontal and scaler coeffs. Use the scaler library api to choose and copy scaler coefficients to a the above buffers based on the scaling ratio. Since the SC block comes after the de-interlacer, make sure that the source height is doubled if de-interlacer was used. These buffers now need to be used by VPDMA to load the coefficients into the SRAM within SC. In device_run, add configuration descriptors which have payloads pointing to the scaler coefficients in memory. Use the members in sc_data handle to prevent addition of these descriptors if there isn't a need to re-load coefficients into SC. This comes helps unnecessary re-loading of the coefficients when we switch back and forth between vpe contexts. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index ecb85f9..50d6d0e 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -356,6 +356,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 */ @@ -765,6 +767,10 @@ 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; @@ -777,7 +783,6 @@ static int set_srcdst_params(struct vpe_ctx *ctx) const struct vpdma_data_format *mv = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; - ctx->deinterlacing = 1; /* * we make sure that the source image has a 16 byte aligned * stride, we need to do the same for the motion vector buffer @@ -788,6 +793,9 @@ static int set_srcdst_params(struct vpe_ctx *ctx) 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; + src_h <<= 1; } else { ctx->deinterlacing = 0; mv_buf_size = 0; @@ -802,6 +810,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); set_csc_coeff_bypass(ctx); + 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_set_regs_bypass(ctx->dev->sc, &mmr_adb->sc_regs[0]); return 0; @@ -1035,6 +1045,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) { @@ -1057,6 +1068,26 @@ 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); @@ -1180,6 +1211,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); @@ -1752,6 +1785,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)); @@ -1820,6 +1861,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); -- cgit v0.10.2 From bbee8b3933f5ddff85904aed9190eaca52c54d13 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:36:00 -0300 Subject: [media] v4l: ti-vpe: enable basic scaler support Add the required SC register configurations which lets us perform linear scaling for the supported range of horizontal and vertical scaling ratios. The horizontal scaler performs polyphase scaling using it's 8 tap 32 phase filter, decimation is performed when downscaling passes beyond 2x or 4x. The vertical scaler performs polyphase scaling using it's 5 tap 32 phase filter, it switches to a simpler form of scaling using the running average filter when the downscale ratio is more than 4x. Many of the SC features like peaking, trimming and non-linear scaling aren't implemented for now. Only the minimal register fields required for basic scaling operation are configured. The function to configure SC registers takes the sc_data handle, the source and destination widths and heights, and the scaler address data block offsets for the current context so that they can be configured. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c index 417feb9..93f0af54 100644 --- a/drivers/media/platform/ti-vpe/sc.c +++ b/drivers/media/platform/ti-vpe/sc.c @@ -20,11 +20,6 @@ #include "sc.h" #include "sc_coeff.h" -void sc_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0) -{ - *sc_reg0 |= CFG_SC_BYPASS; -} - void sc_dump_regs(struct sc_data *sc) { struct device *dev = &sc->pdev->dev; @@ -159,6 +154,133 @@ void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, 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; diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h index c89f3d1..60e411e 100644 --- a/drivers/media/platform/ti-vpe/sc.h +++ b/drivers/media/platform/ti-vpe/sc.h @@ -195,12 +195,14 @@ struct sc_data { struct platform_device *pdev; }; -void sc_set_regs_bypass(struct sc_data *sc, u32 *sc_reg0); 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/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 50d6d0e..dc2b94c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -440,9 +440,15 @@ 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]; @@ -463,8 +469,12 @@ 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_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, VPE_CSC_CSC00); }; @@ -810,9 +820,13 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); set_csc_coeff_bypass(ctx); + 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_set_regs_bypass(ctx->dev->sc, &mmr_adb->sc_regs[0]); + + 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; } -- cgit v0.10.2 From 6948082d1c9d5cdebc00b317b3b290292c635d53 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:36:01 -0300 Subject: [media] v4l: ti-vpe: create a color space converter block library VPE and VIP IPs in DAR7x contain a color space converter(CSC) sub block. Create a library which will perform CSC related configurations and hold CSC register definitions. The functions provided by this library will be called by the vpe and vip drivers using a csc_data handle. The vpe_dev holds the csc_data handle. The handle represents an instance of the CSC hardware, and the vpe driver uses it to access the CSC register offsets or helper functions to configure these registers. The CSC register offsets are now relative to the CSC block itself, so we need to use the macro GET_OFFSET_TOP to get the CSC register offset relative to the VPE IP in the vpe driver. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index 54c30b3..be680f8 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 sc.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 0000000..62e2fec --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.c @@ -0,0 +1,76 @@ +/* + * Color space converter library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + * + * 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 +#include +#include +#include + +#include "csc.h" + +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; +} + +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 0000000..57b5ed6 --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, + * Dale Farnsworth, + * Archit Taneja, + * + * 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); +struct csc_data *csc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index dc2b94c..6c4db57 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -44,6 +44,7 @@ #include "vpdma.h" #include "vpe_regs.h" #include "sc.h" +#include "csc.h" #define VPE_MODULE_NAME "vpe" @@ -330,6 +331,7 @@ struct vpe_dev { 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 */ }; /* @@ -475,7 +477,8 @@ static void init_adb_hdrs(struct vpe_ctx *ctx) 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, VPE_CSC_CSC00); + VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, + GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); }; /* @@ -758,16 +761,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; -} - /* * Set the shadow registers whose values are modified when either the * source or destination format is changed. @@ -819,7 +812,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); - set_csc_coeff_bypass(ctx); + + csc_set_coeff_bypass(ctx->dev->csc, &mmr_adb->csc_regs[5]); 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); @@ -942,15 +936,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(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) @@ -2074,6 +2063,12 @@ static int vpe_probe(struct platform_device *pdev) 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)) { ret = PTR_ERR(dev->vpdma); diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h index d8dbdd3..74283d7 100644 --- a/drivers/media/platform/ti-vpe/vpe_regs.h +++ b/drivers/media/platform/ti-vpe/vpe_regs.h @@ -306,42 +306,4 @@ #define VPE_FMD_FRAME_DIFF_MASK 0x000fffff #define VPE_FMD_FRAME_DIFF_SHIFT 0 -/* 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 -- cgit v0.10.2 From 6c4f4cbb585bbb124cadc2ed6ef7b8aeae5ce82e Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:36:02 -0300 Subject: [media] v4l: ti-vpe: Add helper to perform color conversion The CSC block can be used for color space conversion between YUV and RGB formats. It is configurable via a programmable set of coefficients. Add functionality to choose the appropriate CSC coefficients and program them in the CSC registers. We take the source and destination colorspace formats as the arguments, and choose the coefficient table accordingly. YUV to RGB coefficients are provided for standard and high definition colorspaces. The coefficients can also be limited or full range. For now, only full range coefficients are chosen. We would need some sort of control ioctl for the user to specify the range needed. Not sure if there is a generic control ioctl for this already? Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c index 62e2fec..acfea50 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti-vpe/csc.c @@ -16,9 +16,79 @@ #include #include #include +#include #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; @@ -45,6 +115,56 @@ 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; diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h index 57b5ed6..1ad2b6d 100644 --- a/drivers/media/platform/ti-vpe/csc.h +++ b/drivers/media/platform/ti-vpe/csc.h @@ -60,6 +60,9 @@ struct csc_data { 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 -- cgit v0.10.2 From 30496799b07137654d2892270d063a4559acff3d Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:36:03 -0300 Subject: [media] v4l: ti-vpe: enable CSC support for VPE Use the csc library functions to configure the CSC block in VPE. Some changes are required in try_fmt to handle the pix->colorspace parameter more correctly. Previously, we copied the source queue colorspace to the destination queue colorspace as we didn't support RGB formats. Now, we configure pix->colorspace based on the color format set(and the height of the image if it's a YUV format). Add basic RGB color formats to the list of supported vpe formats. If the destination format is RGB colorspace, we also need to use the RGB output port instead of the Luma and Chroma output ports. This requires configuring the output data descriptors differently. Also, make the default colorspace V4L2_COLORSPACE_SMPTE170M as that resembles the Standard Definition colorspace more closely. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 6c4db57..1296c53 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -267,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], + }, + }, }; /* @@ -689,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; @@ -813,7 +848,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); - csc_set_coeff_bypass(ctx->dev->csc, &mmr_adb->csc_regs[5]); + 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); @@ -1095,9 +1131,13 @@ static void device_run(void *priv) 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) { @@ -1133,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); @@ -1413,16 +1460,18 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, 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++) { @@ -1817,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; -- cgit v0.10.2 From b4fcdaf7654f9506f80d4e3f2b045a78333d62dc Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 12 Dec 2013 05:36:04 -0300 Subject: [media] v4l: ti-vpe: Add a type specifier to describe vpdma data format type The struct vpdma_data_format holds the color format depth and the data_type value needed to be programmed in the data descriptors. However, it doesn't tell what type of color format is it, i.e, whether it is RGB, YUV or Misc. This information is needed when by vpdma library when forming descriptors. We modify the depth parameter for the chroma portion of the NV12 format. For this, we check if the data_type value is C420. This isn't sufficient as there are many YUV and RGB vpdma formats which have the same data_type value. Hence, we need to hold the type of the color format for the above case, and possibly more cases in the future. Signed-off-by: Archit Taneja Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index f97253f..a268f68 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,7 +629,8 @@ 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 = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN); @@ -649,7 +680,8 @@ 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; diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index 62dd143..cf40f11 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -39,7 +39,14 @@ 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; }; -- cgit v0.10.2 From b18a8ff29d80b132018d33479e86ab8ecaee6b46 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:38 -0300 Subject: [media] vb2: push the mmap semaphore down to __buf_prepare() Rather than taking the mmap semaphore at a relatively high-level function, push it down to the place where it is really needed. It was placed in vb2_queue_or_prepare_buf() to prevent racing with other vb2 calls. The only way I can see that a race can happen is when two threads queue the same buffer. The solution for that it to introduce a PREPARING state. Moving it down offers opportunities to simplify the code. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 12df9fd..d3f7e8a 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -481,6 +481,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; @@ -1228,6 +1229,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); @@ -1237,12 +1239,31 @@ 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); @@ -1256,8 +1277,7 @@ 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; } @@ -1268,80 +1288,47 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, struct v4l2_buffer *, struct vb2_buffer *)) { - 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; + return -EBUSY; } 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) { /* 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; + return -EINVAL; } ret = __verify_planes_array(vb, b); if (ret) - goto unlock; + return ret; ret = handler(q, b, vb); - if (ret) - goto unlock; - - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + if (!ret) { + /* 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); + dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); + } return ret; } @@ -1390,6 +1377,9 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, return ret; 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; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 0ae974e..ea76652 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -142,6 +142,7 @@ enum vb2_fileio_flags { /** * enum vb2_buffer_state - current video buffer state * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control + * @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf * @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used @@ -154,6 +155,7 @@ enum vb2_fileio_flags { */ enum vb2_buffer_state { VB2_BUF_STATE_DEQUEUED, + VB2_BUF_STATE_PREPARING, VB2_BUF_STATE_PREPARED, VB2_BUF_STATE_QUEUED, VB2_BUF_STATE_ACTIVE, -- cgit v0.10.2 From f103b5d64e12198343edc9ef7ad3eb4c0e826adf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Jan 2014 07:03:09 -0200 Subject: [media] videobuf2: Fix CodingStyle Changeset b18a8ff29d80 added a comment violating the 80cols max size, with no good reason. Fix it. Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index d3f7e8a..1c933c5 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1246,15 +1246,16 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *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. + * 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); -- cgit v0.10.2 From 4138111a27859dcc56a5592c804dd16bb12a23d1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:39 -0300 Subject: [media] vb2: simplify qbuf/prepare_buf by removing callback The callback used to merge the common code of the qbuf/prepare_buf code can be removed now that the mmap_sem handling is pushed down to __buf_prepare(). This makes the code more readable. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1c933c5..ca255da 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1284,14 +1284,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) } 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 vb2_buffer *vb; - int ret; - if (q->fileio) { dprintk(1, "%s(): file io in progress\n", opname); return -EBUSY; @@ -1307,8 +1301,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, 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); return -EINVAL; @@ -1319,30 +1312,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, return -EINVAL; } - ret = __verify_planes_array(vb, b); - if (ret) - return ret; - - ret = handler(q, b, vb); - if (!ret) { - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); - - dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); - } - 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); } /** @@ -1362,20 +1332,68 @@ 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); + int ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); + 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; + } + + 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_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { - int ret; + 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: @@ -1400,29 +1418,11 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, if (q->streaming) __enqueue_in_driver(vb); - return 0; -} + /* Fill buffer information for the userspace */ + __fill_v4l2_buffer(vb, b); -/** - * vb2_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) -{ - return vb2_queue_or_prepare_buf(q, b, "qbuf", __vb2_qbuf); + dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); + return 0; } EXPORT_SYMBOL_GPL(vb2_qbuf); -- cgit v0.10.2 From 63faabfd89f4db9862ae2a663193e511419c67eb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:40 -0300 Subject: [media] vb2: fix race condition between REQBUFS and QBUF/PREPARE_BUF When preparing a buffer the queue lock is released for a short while if the memory mode is USERPTR (see __buf_prepare for the details), which would allow a race with a REQBUFS which can free the buffers. 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. If this happens, then the application does something really strange. The REQBUFS call can be retried safely, since this situation is transient. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index ca255da..a9a9c6a 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; } /** @@ -658,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 -- cgit v0.10.2 From b2f2f04719638322b9d792ecd25f70a55acf8270 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:41 -0300 Subject: [media] vb2: remove the 'fileio = NULL' hack The read/write implementation in vb2 reuses existing vb2 functions, but it sets q->fileio to NULL before calling them in order to skip the 'q->fileio != NULL' check. This works today due to the synchronous nature of read/write, but it 1) is ugly, and 2) will fail in an asynchronous use-case such as a thread queuing and dequeuing buffers. This last example will be necessary in order to implement vb2 DVB support. This patch removes the hack by splitting up the dqbuf/qbuf/streamon/streamoff functions into an external and an internal version. The external version checks q->fileio and then calls the internal version. The read/write implementation now just uses the internal version, removing the need to set q->fileio to NULL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index a9a9c6a..14a3604 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1307,11 +1307,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, const char *opname) { - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", opname); - return -EBUSY; - } - if (b->type != q->type) { dprintk(1, "%s(): invalid buffer type\n", opname); return -EINVAL; @@ -1353,9 +1348,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, */ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { - int ret = vb2_queue_or_prepare_buf(q, b, "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; @@ -1377,24 +1378,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL_GPL(vb2_prepare_buf); -/** - * vb2_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +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; @@ -1445,6 +1429,33 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); return 0; } + +/** + * vb2_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + 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); /** @@ -1593,37 +1604,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; @@ -1662,6 +1647,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); /** @@ -1701,29 +1716,11 @@ 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; @@ -1756,31 +1753,32 @@ 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; @@ -1800,6 +1798,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); /** @@ -2322,13 +2344,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); @@ -2371,12 +2388,6 @@ 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]; @@ -2393,10 +2404,10 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ 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; /* @@ -2426,8 +2437,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; } /* @@ -2447,10 +2457,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); } @@ -2462,10 +2468,10 @@ 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 @@ -2484,9 +2490,9 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Start streaming if required. */ if (!read && !q->streaming) { - ret = vb2_streamon(q, q->type); + ret = vb2_internal_streamon(q, q->type); if (ret) - goto end; + return ret; } } @@ -2495,11 +2501,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; } -- cgit v0.10.2 From 02f142ecd24aaf891324ffba8527284c1731b561 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:42 -0300 Subject: [media] vb2: retry start_streaming in case of insufficient buffers If start_streaming returns -ENOBUFS, then it will be retried the next time a buffer is queued. This means applications no longer need to know how many buffers need to be queued before STREAMON can be called. This is particularly useful for output stream I/O. If a DMA engine needs at least X buffers before it can start streaming, then for applications to get a buffer out as soon as possible they need to know the minimum number of buffers to queue before STREAMON can be called. You can't just try STREAMON after every buffer since on failure STREAMON will dequeue all your buffers. (Is that a bug or a feature? Frankly, I'm not sure). This patch simplifies applications substantially: they can just call STREAMON at the beginning and then start queuing buffers and the DMA engine will kick in automagically once enough buffers are available. This also fixes using write() to stream video: the fileio implementation calls streamon without having any queued buffers, which will fail today for any driver that requires a minimum number of buffers. Signed-off-by: Hans Verkuil Acked-by: Marek Szyprowski Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 14a3604..0d5b84f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1378,6 +1378,39 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL_GPL(vb2_prepare_buf); +/** + * 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"); @@ -1426,6 +1459,12 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) /* 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; } @@ -1575,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); @@ -1689,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. @@ -1738,12 +1783,9 @@ static int vb2_internal_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; } @@ -2313,15 +2355,15 @@ 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; } + /* + * Start streaming. + */ + ret = vb2_streamon(q, q->type); + if (ret) + goto err_reqbufs; + q->fileio = fileio; return ret; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index ea76652..bef53ce 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -252,10 +252,13 @@ struct vb2_buffer { * receive buffers with @buf_queue callback before * @start_streaming is called; the driver gets the number * of already queued buffers in count parameter; driver - * can return an error if hardware fails or not enough - * buffers has been queued, in such case all buffers that - * have been already given by the @buf_queue callback are - * invalidated. + * can return an error if hardware fails, in that case all + * buffers that have been already given by the @buf_queue + * callback are invalidated. + * If there were not enough queued buffers to start + * streaming, then this callback returns -ENOBUFS, and the + * vb2 core will retry calling @start_streaming when a new + * buffer is queued. * @stop_streaming: called when 'streaming' state must be disabled; driver * should stop any DMA transactions or wait until they * finish and give back all buffers it got from buf_queue() @@ -323,6 +326,9 @@ struct v4l2_fh; * @done_wq: waitqueue for processes waiting for buffers ready to be dequeued * @alloc_ctx: memory type/allocator-specific contexts for each plane * @streaming: current streaming state + * @retry_start_streaming: start_streaming() was called, but there were not enough + * buffers queued. If set, then retry calling start_streaming when + * queuing a new buffer. * @fileio: file io emulator internal data, used only if emulator is active */ struct vb2_queue { @@ -355,6 +361,7 @@ struct vb2_queue { unsigned int plane_sizes[VIDEO_MAX_PLANES]; unsigned int streaming:1; + unsigned int retry_start_streaming:1; struct vb2_fileio_data *fileio; }; -- cgit v0.10.2 From c108e660f9196a4cebea79fbd650ed6850934fcc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:43 -0300 Subject: [media] vb2: don't set index, don't start streaming for write() Two fixes: - there is no need to set the index when calling dqbuf: dqbuf will overwrite it. - __vb2_init_fileio already starts streaming for write(), so there is no need to do it again in __vb2_perform_fileio. It can never have worked anyway: either __vb2_init_fileio succeeds in starting streaming or it is never going to happen. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 0d5b84f..2552c46 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2445,7 +2445,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ memset(&fileio->b, 0, sizeof(fileio->b)); fileio->b.type = q->type; fileio->b.memory = q->memory; - fileio->b.index = index; ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); if (ret) @@ -2527,15 +2526,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Switch to the next buffer */ fileio->index = (index + 1) % q->num_buffers; - - /* - * Start streaming if required. - */ - if (!read && !q->streaming) { - ret = vb2_internal_streamon(q, q->type); - if (ret) - return ret; - } } /* -- cgit v0.10.2 From 79aeb3f3083a8a795467eae429cb2d6faf482f32 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:44 -0300 Subject: [media] vb2: return ENOBUFS in start_streaming in case of too few buffers This works together with the retry_start_streaming mechanism to allow userspace to start streaming even if not all required buffers have been queued. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Cc: Tomasz Stanislawski Cc: Kyungmin Park Acked-by: Kamil Debski Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index eac472b..b02aba4 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 52ac5e6..735ec47 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 c31bcf1..9d115cd 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/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 4ff3b6c..f0b41f8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1863,7 +1863,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-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 81b97db..c5059ba 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/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 45a0276..d73abca 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/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 3b036be..1f3b0f9 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -1201,6 +1201,8 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long addr; int ret; + if (count == 0) + return -ENOBUFS; ret = mutex_lock_interruptible(&video->lock); if (ret) goto streamoff; -- cgit v0.10.2 From 88e268702bfba78448abd20a31129458707383aa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:45 -0300 Subject: [media] vb2: Improve file I/O emulation to handle buffers in any order videobuf2 file I/O emulation assumed that buffers dequeued from the driver would return in the order they were enqueued in the driver. Improve the file I/O emulator's book-keeping to remove this assumption. Also set the buf->size properly if a write() dequeues a buffer and the VB2_FILEIO_WRITE_IMMEDIATELY flag is set. Based on an initial patch by Andy Walls. Signed-off-by: Hans Verkuil Reviewed-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 2552c46..098df28 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2355,6 +2355,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) goto err_reqbufs; fileio->bufs[i].queued = 1; } + fileio->index = q->num_buffers; } /* @@ -2430,15 +2431,11 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ } fileio = q->fileio; - 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. */ @@ -2451,12 +2448,18 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ 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]; } /* @@ -2519,13 +2522,10 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ 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; + if (fileio->index < q->num_buffers) + fileio->index++; } /* -- cgit v0.10.2 From dc18d1bea679c6b6bfdc570469160aec5c1d3689 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:46 -0300 Subject: [media] DocBook: drop the word 'only' There are already video output drivers that allow STREAMON without any buffers queued, and with the change in vb2 there are now more drivers like that. So saying "The ioctl will succeed only when at least one output buffer is in the incoming queue." isn't true. Just drop the word 'only'. We cannot say that it will also work if no output buffers are queued as long as not all drivers are converted to vb2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml index 716ea15..65dff55 100644 --- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml +++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml @@ -59,7 +59,7 @@ buffers are filled (if there are any empty buffers in the incoming queue) until VIDIOC_STREAMON has been called. Accordingly the output hardware is disabled, no video signal is produced until VIDIOC_STREAMON has been called. -The ioctl will succeed only when at least one output buffer is in the +The ioctl will succeed when at least one output buffer is in the incoming queue.The VIDIOC_STREAMOFF ioctl, apart of -- cgit v0.10.2 From 9db0fb182ea8a42c5bfd322b169d65728721fd71 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:23 -0300 Subject: [media] saa7134: move the queue data from saa7134_fh to saa7134_dev These fields are global, not per-filehandle. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 27d7ee7..f442405 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; } diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index e9aa94b..d4da18d 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 fb60da8..8f73058 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1018,8 +1018,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 +1056,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 +1081,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 +1092,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) @@ -1293,14 +1291,15 @@ static struct videobuf_queue *saa7134_queue(struct file *file) { struct video_device *vdev = video_devdata(file); struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; struct videobuf_queue *q = NULL; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: - q = &fh->cap; + q = &dev->cap; break; case VFL_TYPE_VBI: - q = &fh->vbi; + q = &dev->vbi; break; default: BUG(); @@ -1337,21 +1336,6 @@ static int video_open(struct file *file) 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 */ saa7134_tvaudio_setinput(dev,&card(dev).radio); @@ -1396,28 +1380,30 @@ video_poll(struct file *file, struct poll_table_struct *wait) { struct video_device *vdev = video_devdata(file); struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; struct videobuf_buffer *buf = NULL; unsigned int rc = 0; if (vdev->vfl_type == VFL_TYPE_VBI) - return videobuf_poll_stream(file, &fh->vbi, wait); + return 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); + 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)) 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) @@ -1427,11 +1413,11 @@ video_poll(struct file *file, struct poll_table_struct *wait) if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) rc = POLLIN|POLLRDNORM; - mutex_unlock(&fh->cap.vb_lock); + mutex_unlock(&dev->cap.vb_lock); return rc; err: - mutex_unlock(&fh->cap.vb_lock); + mutex_unlock(&dev->cap.vb_lock); return POLLERR; } @@ -1456,18 +1442,20 @@ static int video_release(struct file *file) /* stop video capture */ if (res_check(fh, RESOURCE_VIDEO)) { pm_qos_remove_request(&dev->qos_request); - videobuf_streamoff(&fh->cap); + 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); + 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 +1468,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; @@ -1560,7 +1542,7 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, 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; @@ -1686,7 +1668,7 @@ 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; } @@ -2489,9 +2471,31 @@ 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); +} + int saa7134_videoport_init(struct saa7134_dev *dev) { /* enable video output */ diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 8d1453a..96b7ccf 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -472,14 +472,6 @@ struct saa7134_fh { struct v4l2_fh fh; struct saa7134_dev *dev; 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 +581,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; @@ -773,6 +769,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); /* ----------------------------------------------------------- */ -- cgit v0.10.2 From 718bde1aa9e03fd49d69816c4facea55d69a4737 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:24 -0300 Subject: [media] saa7134: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index f442405..d3b4e56 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -1009,13 +1009,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) { @@ -1047,6 +1047,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) { @@ -1058,6 +1059,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]); @@ -1068,6 +1070,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 3022eb2..7c24f44 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -316,113 +316,6 @@ static int empress_streamoff(struct file *file, void *priv, 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; @@ -461,15 +354,9 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .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, }; @@ -501,9 +388,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 +420,15 @@ 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); + 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 +464,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-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 8f73058..7a52259 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -369,117 +369,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; @@ -868,7 +757,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 +812,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); @@ -1114,133 +1003,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) -{ - 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) +static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) { - 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: @@ -1250,7 +1062,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; @@ -1262,27 +1074,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; } /* ------------------------------------------------------------------ */ @@ -1434,7 +1234,7 @@ 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); } @@ -1703,8 +1503,8 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, if (res_check(fh, 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); } @@ -1712,21 +1512,6 @@ 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) { @@ -1882,13 +1667,13 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ mutex_lock(&dev->lock); if (fh && 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]); @@ -2157,14 +1942,14 @@ static int saa7134_overlay(struct file *file, void *f, unsigned int on) if (!res_get(dev, fh, 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)) 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); } @@ -2319,22 +2104,6 @@ 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, @@ -2369,9 +2138,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,10 +2171,7 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .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, }; @@ -2429,8 +2192,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; @@ -2438,17 +2248,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; @@ -2494,6 +2325,9 @@ 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) @@ -2537,6 +2371,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 96b7ccf..e0e5c70 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -410,6 +411,11 @@ 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 */ @@ -595,6 +601,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; @@ -622,6 +629,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; @@ -634,10 +642,12 @@ 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 */ @@ -757,9 +767,6 @@ 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_videoport_init(struct saa7134_dev *dev); diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 1666aab..8e4d1d9 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -164,6 +164,10 @@ enum v4l2_colorfx { * this driver */ #define V4L2_CID_USER_TI_VPE_BASE (V4L2_CID_USER_BASE + 0x1050) +/* The base for the saa7134 driver controls. + * We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_SAA7134_BASE (V4L2_CID_USER_BASE + 0x1060) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -- cgit v0.10.2 From b93a18d56057a6f8ccb79c5cd085dd31395331ff Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:25 -0300 Subject: [media] saa7134: cleanup radio/video/empress ioctl handling The video and empress nodes can share various ioctls. Drop the input/std ioctls from the radio node (out of spec). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 7c24f44..8617757 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -156,54 +156,6 @@ ts_mmap(struct file *file, struct vm_area_struct * vma) 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) { @@ -316,21 +268,6 @@ static int empress_streamoff(struct file *file, void *priv, return videobuf_streamoff(&dev->empress_tsq); } -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, @@ -343,7 +280,7 @@ 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, @@ -354,11 +291,15 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_dqbuf = empress_dqbuf, .vidioc_streamon = empress_streamon, .vidioc_streamoff = empress_streamoff, - .vidioc_enum_input = empress_enum_input, - .vidioc_g_input = empress_g_input, - .vidioc_s_input = empress_s_input, - .vidioc_s_std = empress_s_std, - .vidioc_g_std = empress_g_std, + .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, }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 7a52259..4f85662 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1512,11 +1512,9 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, return 0; } -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; @@ -1543,20 +1541,20 @@ static int saa7134_enum_input(struct file *file, void *priv, 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; @@ -1567,12 +1565,12 @@ 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); u32 radio_caps, video_caps, vbi_caps; @@ -1592,7 +1590,8 @@ 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) + /* For the empress video node priv == dev */ + if (saa7134_no_overlay <= 0 && priv != dev) video_caps |= V4L2_CAP_VIDEO_OVERLAY; vbi_caps = V4L2_CAP_VBI_CAPTURE; @@ -1618,14 +1617,18 @@ 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); + /* For the empress video node priv == dev */ + bool is_empress = priv == dev; unsigned long flags; unsigned int i; v4l2_std_id fixup; - if (!fh && res_locked(dev, RESOURCE_OVERLAY)) { + if (is_empress && res_locked(dev, RESOURCE_OVERLAY)) { /* Don't change the std from the mpeg device if overlay is active. */ return -EBUSY; @@ -1665,7 +1668,7 @@ 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 (!is_empress && res_check(priv, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); @@ -1682,23 +1685,16 @@ 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) @@ -1773,11 +1769,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) @@ -1804,12 +1799,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) @@ -1825,12 +1820,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; @@ -1839,12 +1834,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; @@ -1856,6 +1851,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) @@ -2076,34 +2072,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 const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, @@ -2167,11 +2135,7 @@ 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_g_input = radio_g_input, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, }; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index e0e5c70..3573aa2 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -767,7 +767,21 @@ extern unsigned int video_debug; extern struct video_device saa7134_video_template; extern struct video_device saa7134_radio_template; -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_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); -- cgit v0.10.2 From b9f63b25954495b5b3089f89918771e52c1605d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:26 -0300 Subject: [media] saa7134: remove dev from saa7134_fh, use saa7134_fh for empress node Use the saa7134_fh struct for the empress video node as well, drop the dev pointer from that struct since we can use drvdata for that. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 8617757..17e5fcd 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -85,6 +85,7 @@ static int ts_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh; int err; dprintk("open dev=%s\n", video_device_node_name(vdev)); @@ -94,12 +95,22 @@ static int ts_open(struct file *file) if (atomic_read(&dev->empress_users)) goto done; + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + err = -ENOMEM; + if (NULL == fh) + goto done; + + 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: @@ -109,7 +120,8 @@ done: 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); @@ -123,13 +135,15 @@ static int ts_release(struct file *file) atomic_dec(&dev->empress_users); + 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 (!dev->empress_started) ts_init_encoder(dev); @@ -142,7 +156,7 @@ 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; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_poll_stream(file, &dev->empress_tsq, wait); } @@ -151,7 +165,7 @@ ts_poll(struct file *file, struct poll_table_struct *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); } @@ -171,7 +185,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); @@ -188,7 +202,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); @@ -206,7 +220,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); @@ -224,7 +238,7 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, static int empress_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_reqbufs(&dev->empress_tsq, p); } @@ -232,21 +246,21 @@ static int empress_reqbufs(struct file *file, void *priv, static int empress_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); 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; + struct saa7134_dev *dev = video_drvdata(file); 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; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_dqbuf(&dev->empress_tsq, b, file->f_flags & O_NONBLOCK); @@ -255,7 +269,7 @@ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int empress_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_streamon(&dev->empress_tsq); } @@ -263,7 +277,7 @@ static int empress_streamon(struct file *file, void *priv, static int empress_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_dev *dev = file->private_data; + struct saa7134_dev *dev = video_drvdata(file); return videobuf_streamoff(&dev->empress_tsq); } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 4f85662..7ba42e2 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1090,8 +1090,7 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) static struct videobuf_queue *saa7134_queue(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 videobuf_queue *q = NULL; switch (vdev->vfl_type) { @@ -1134,7 +1133,6 @@ static int video_open(struct file *file) v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; - fh->dev = dev; if (vdev->vfl_type == VFL_TYPE_RADIO) { /* switch to radio mode */ @@ -1153,17 +1151,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, @@ -1179,15 +1178,15 @@ static unsigned int video_poll(struct file *file, struct poll_table_struct *wait) { struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); struct saa7134_fh *fh = file->private_data; - struct saa7134_dev *dev = fh->dev; struct videobuf_buffer *buf = NULL; unsigned int rc = 0; if (vdev->vfl_type == VFL_TYPE_VBI) return videobuf_poll_stream(file, &dev->vbi, wait); - if (res_check(fh,RESOURCE_VIDEO)) { + 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); @@ -1195,7 +1194,7 @@ video_poll(struct file *file, struct poll_table_struct *wait) 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 != dev->cap.ops->buf_prepare(&dev->cap, dev->cap.read_buf, dev->cap.field)) @@ -1224,8 +1223,8 @@ err: 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; @@ -1236,14 +1235,14 @@ static int video_release(struct file *file) spin_lock_irqsave(&dev->slock,flags); 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(&dev->cap); - res_free(dev,fh,RESOURCE_VIDEO); + res_free(dev, fh, RESOURCE_VIDEO); videobuf_mmap_free(&dev->cap); } if (dev->cap.read_buf) { @@ -1254,7 +1253,7 @@ static int video_release(struct file *file) /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { videobuf_stop(&dev->vbi); - res_free(dev,fh,RESOURCE_VBI); + res_free(dev, fh, RESOURCE_VBI); videobuf_mmap_free(&dev->vbi); } @@ -1283,8 +1282,7 @@ 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; @@ -1299,8 +1297,7 @@ 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; cmd.instance = file; @@ -1316,8 +1313,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)); @@ -1337,8 +1333,7 @@ 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; @@ -1356,8 +1351,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; @@ -1389,8 +1383,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; @@ -1441,8 +1434,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"); @@ -1457,8 +1449,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); @@ -1475,8 +1466,7 @@ static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, 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; @@ -1501,7 +1491,7 @@ 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); start_preview(dev); @@ -1572,6 +1562,7 @@ int saa7134_querycap(struct file *file, void *priv, { 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; @@ -1590,8 +1581,7 @@ int saa7134_querycap(struct file *file, void *priv, radio_caps |= V4L2_CAP_RDS_CAPTURE; video_caps = V4L2_CAP_VIDEO_CAPTURE; - /* For the empress video node priv == dev */ - if (saa7134_no_overlay <= 0 && priv != dev) + if (saa7134_no_overlay <= 0 && !fh->is_empress) video_caps |= V4L2_CAP_VIDEO_OVERLAY; vbi_caps = V4L2_CAP_VBI_CAPTURE; @@ -1622,13 +1612,12 @@ EXPORT_SYMBOL_GPL(saa7134_querycap); int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7134_dev *dev = video_drvdata(file); - /* For the empress video node priv == dev */ - bool is_empress = priv == dev; + struct saa7134_fh *fh = priv; unsigned long flags; unsigned int i; v4l2_std_id fixup; - if (is_empress && 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; @@ -1668,7 +1657,7 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) id = tvnorms[i].id; mutex_lock(&dev->lock); - if (!is_empress && res_check(priv, RESOURCE_OVERLAY)) { + if (!fh->is_empress && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); @@ -1699,8 +1688,7 @@ 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) @@ -1722,8 +1710,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) @@ -1734,8 +1721,7 @@ 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; @@ -1747,9 +1733,9 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr 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; @@ -1889,8 +1875,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; @@ -1901,8 +1886,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) && @@ -1923,10 +1907,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) { @@ -1935,19 +1918,19 @@ 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); 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); spin_unlock_irqrestore(&dev->slock, flags); - res_free(dev, fh, RESOURCE_OVERLAY); + res_free(dev, priv, RESOURCE_OVERLAY); } return 0; } @@ -1978,11 +1961,10 @@ static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static 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 @@ -2002,9 +1984,8 @@ static int saa7134_streamon(struct file *file, void *priv, static 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); @@ -2012,7 +1993,7 @@ static int saa7134_streamoff(struct file *file, void *priv, err = videobuf_streamoff(saa7134_queue(file)); if (err < 0) return err; - res_free(dev, fh, res); + res_free(dev, priv, res); return 0; } @@ -2020,8 +2001,7 @@ static int saa7134_streamoff(struct file *file, void *priv, 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; @@ -2031,8 +2011,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; @@ -2042,8 +2021,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; @@ -2062,8 +2040,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; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 3573aa2..d7bef5e 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -476,7 +476,7 @@ struct saa7134_dmaqueue { /* video filehandle status */ struct saa7134_fh { struct v4l2_fh fh; - struct saa7134_dev *dev; + bool is_empress; unsigned int resources; }; -- cgit v0.10.2 From ce791139ec7f0e5878221dba8d5773e27bf057d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:27 -0300 Subject: [media] saa7134: share resource management between normal and empress nodes The empress video node can share resource management with the normal video nodes, thus allowing for code sharing and making the empress node non-exclusive. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 17e5fcd..2ef670d 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -86,20 +86,11 @@ static int ts_open(struct file *file) struct video_device *vdev = video_devdata(file); struct saa7134_dev *dev = video_drvdata(file); struct saa7134_fh *fh; - int err; - - 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); - err = -ENOMEM; if (NULL == fh) - goto done; + return -ENOMEM; v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; @@ -110,12 +101,7 @@ static int ts_open(struct file *file) saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); - atomic_inc(&dev->empress_users); - err = 0; - -done: - mutex_unlock(&dev->empress_tsq.vb_lock); - return err; + return 0; } static int ts_release(struct file *file) @@ -123,17 +109,17 @@ static int ts_release(struct file *file) 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); @@ -145,6 +131,8 @@ ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct saa7134_dev *dev = video_drvdata(file); + if (res_locked(dev, RESOURCE_EMPRESS)) + return -EBUSY; if (!dev->empress_started) ts_init_encoder(dev); @@ -235,53 +223,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 = video_drvdata(file); - - return videobuf_reqbufs(&dev->empress_tsq, p); -} - -static int empress_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = video_drvdata(file); - - return videobuf_querybuf(&dev->empress_tsq, b); -} - -static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = video_drvdata(file); - - return videobuf_qbuf(&dev->empress_tsq, b); -} - -static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_dev *dev = video_drvdata(file); - - 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 = video_drvdata(file); - - return videobuf_streamon(&dev->empress_tsq); -} - -static int empress_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_dev *dev = video_drvdata(file); - - return videobuf_streamoff(&dev->empress_tsq); -} - static const struct v4l2_file_operations ts_fops = { .owner = THIS_MODULE, @@ -299,12 +240,12 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .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_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, @@ -375,6 +316,7 @@ 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) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 7ba42e2..5e2d61c1c 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -403,16 +403,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) { @@ -1091,11 +1081,12 @@ 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 = &dev->cap; + q = fh->is_empress ? &dev->empress_tsq : &dev->cap; break; case VFL_TYPE_VBI: q = &dev->vbi; @@ -1109,9 +1100,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; @@ -1935,30 +1927,34 @@ static int saa7134_overlay(struct file *file, void *priv, unsigned int on) 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_dev *dev = video_drvdata(file); @@ -1974,21 +1970,23 @@ 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; 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) @@ -1996,6 +1994,7 @@ static int saa7134_streamoff(struct file *file, void *priv, 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, diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index d7bef5e..2474e84 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -422,6 +422,7 @@ struct saa7134_board { #define RESOURCE_OVERLAY 1 #define RESOURCE_VIDEO 2 #define RESOURCE_VBI 4 +#define RESOURCE_EMPRESS 8 #define INTERLACE_AUTO 0 #define INTERLACE_ON 1 @@ -644,7 +645,6 @@ struct saa7134_dev { 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; @@ -705,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 */ @@ -782,6 +792,16 @@ 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); -- cgit v0.10.2 From a2004502919cce151ee6774378294000b587e4e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:28 -0300 Subject: [media] saa7134: add support for control events Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 2ef670d..a0af5c7 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -23,11 +23,12 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - #include #include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" /* ------------------------------------------------------------------ */ @@ -144,9 +145,16 @@ 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) { + 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); } @@ -255,6 +263,9 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .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, }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 5e2d61c1c..5cf9cc6 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -27,11 +27,13 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" #include +#include #include +#include "saa7134-reg.h" +#include "saa7134.h" + /* ------------------------------------------------------------------ */ unsigned int video_debug; @@ -1169,14 +1171,20 @@ 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, &dev->vbi, wait); + return rc | videobuf_poll_stream(file, &dev->vbi, wait); if (res_check(fh, RESOURCE_VIDEO)) { mutex_lock(&dev->cap.vb_lock); @@ -1201,15 +1209,14 @@ video_poll(struct file *file, struct poll_table_struct *wait) goto err; poll_wait(file, &buf->done, wait); - if (buf->state == VIDEOBUF_DONE || - buf->state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; + if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) + rc |= POLLIN | POLLRDNORM; mutex_unlock(&dev->cap.vb_lock); return rc; err: mutex_unlock(&dev->cap.vb_lock); - return POLLERR; + return rc | POLLERR; } static int video_release(struct file *file) @@ -1291,13 +1298,14 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) { 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; } /* ------------------------------------------------------------------ */ @@ -2097,6 +2105,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 = { @@ -2114,6 +2125,9 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* ----------------------------------------------------------- */ -- cgit v0.10.2 From 95075dd01e715dbc03936e3045e95068f0126416 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:29 -0300 Subject: [media] saa7134: use V4L2_IN_ST_NO_SIGNAL instead of NO_SYNC NO_SYNC was meant for DVB and shouldn't be used anymore. In this case NO_SIGNAL is a good alternative. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 5cf9cc6..2334bf9 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1524,7 +1524,7 @@ int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i) 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; } -- cgit v0.10.2 From d8100f44989a07e9ceefd0735862517abd9172b9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:30 -0300 Subject: [media] saa6752hs: drop compat control code The saa7134 driver is now converted to the control framework, so drop the control compat code in saa6752hs.c. Also add 'const' to several static arrays. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c index 8ac4b1f..8272c0b 100644 --- a/drivers/media/pci/saa7134/saa6752hs.c +++ b/drivers/media/pci/saa7134/saa6752hs.c @@ -33,11 +33,11 @@ #include #include #include +#include +#include #include #include #include -#include -#include #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, }; -- cgit v0.10.2 From 6052ba35207429b4878ccf803766d815d0edd3d4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:31 -0300 Subject: [media] saa6752hs: move to media/i2c This driver is independent from saa7134, so there is no reason why this shouldn't be in media/i2c like all other i2c media drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index fbce018..4aa9c53 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -654,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 c815bb2..48888ae 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 diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c new file mode 100644 index 0000000..8272c0b --- /dev/null +++ b/drivers/media/i2c/saa6752hs.c @@ -0,0 +1,790 @@ + /* + saa6752hs - i2c-driver for the saa6752hs by Philips + + Copyright (C) 2004 Andrew de Quincey + + AC-3 support: + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 +#define MPEG_VIDEO_MAX_BITRATE_MAX 27000 +#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 +#define MPEG_PID_MAX ((1 << 14) - 1) + + +MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +enum saa6752hs_videoformat { + SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ + SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ + SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */ + SAA6752HS_VF_SIF = 3, /* SIF video format: 352x288 */ + SAA6752HS_VF_UNKNOWN, +}; + +struct saa6752hs_mpeg_params { + /* transport streams */ + __u16 ts_pid_pmt; + __u16 ts_pid_audio; + __u16 ts_pid_video; + __u16 ts_pid_pcr; + + /* audio */ + enum v4l2_mpeg_audio_encoding au_encoding; + enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; + + /* video */ + enum v4l2_mpeg_video_aspect vi_aspect; + enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode; + __u32 vi_bitrate; + __u32 vi_bitrate_peak; +}; + +static const struct v4l2_format v4l2_format_table[] = +{ + [SAA6752HS_VF_D1] = + { .fmt = { .pix = { .width = 720, .height = 576 }}}, + [SAA6752HS_VF_2_3_D1] = + { .fmt = { .pix = { .width = 480, .height = 576 }}}, + [SAA6752HS_VF_1_2_D1] = + { .fmt = { .pix = { .width = 352, .height = 576 }}}, + [SAA6752HS_VF_SIF] = + { .fmt = { .pix = { .width = 352, .height = 288 }}}, + [SAA6752HS_VF_UNKNOWN] = + { .fmt = { .pix = { .width = 0, .height = 0}}}, +}; + +struct saa6752hs_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *video_bitrate_mode; + struct v4l2_ctrl *video_bitrate; + struct v4l2_ctrl *video_bitrate_peak; + }; + u32 revision; + int has_ac3; + struct saa6752hs_mpeg_params params; + enum saa6752hs_videoformat video_format; + v4l2_std_id standard; +}; + +enum saa6752hs_command { + SAA6752HS_COMMAND_RESET = 0, + SAA6752HS_COMMAND_STOP = 1, + SAA6752HS_COMMAND_START = 2, + SAA6752HS_COMMAND_PAUSE = 3, + SAA6752HS_COMMAND_RECONFIGURE = 4, + SAA6752HS_COMMAND_SLEEP = 5, + SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, + + SAA6752HS_COMMAND_MAX +}; + +static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6752hs_state, sd); +} + +/* ---------------------------------------------------------------------- */ + +static const u8 PAT[] = { + 0xc2, /* i2c register */ + 0x00, /* table number for encoder */ + + 0x47, /* sync */ + 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x00, /* tid(0) */ + 0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */ + + 0x00, 0x01, /* transport_stream_id(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xe0, 0x00, /* PMT PID */ + + 0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static const u8 PMT[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder */ + + 0x47, /* sync */ + 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* tid(2) */ + 0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe0, 0x00, /* PCR_PID */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */ + + 0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static const u8 PMT_AC3[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder(1) */ + 0x47, /* sync */ + + 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ + 0x10, /* PMT PID (0x0010) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* TID (2) */ + 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe1, 0x04, /* PCR_PID (0x0104) */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ + 0x6a, /* AC3 */ + 0x01, /* Descriptor_length(1) */ + 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ + + 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ +}; + +static const struct saa6752hs_mpeg_params param_defaults = +{ + .ts_pid_pmt = 16, + .ts_pid_video = 260, + .ts_pid_audio = 256, + .ts_pid_pcr = 259, + + .vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .vi_bitrate = 4000, + .vi_bitrate_peak = 6000, + .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + + .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, + .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, +}; + +/* ---------------------------------------------------------------------- */ + +static int saa6752hs_chip_command(struct i2c_client *client, + enum saa6752hs_command command) +{ + unsigned char buf[3]; + unsigned long timeout; + int status = 0; + + /* execute the command */ + switch(command) { + case SAA6752HS_COMMAND_RESET: + buf[0] = 0x00; + break; + + case SAA6752HS_COMMAND_STOP: + buf[0] = 0x03; + break; + + case SAA6752HS_COMMAND_START: + buf[0] = 0x02; + break; + + case SAA6752HS_COMMAND_PAUSE: + buf[0] = 0x04; + break; + + case SAA6752HS_COMMAND_RECONFIGURE: + buf[0] = 0x05; + break; + + case SAA6752HS_COMMAND_SLEEP: + buf[0] = 0x06; + break; + + case SAA6752HS_COMMAND_RECONFIGURE_FORCE: + buf[0] = 0x07; + break; + + default: + return -EINVAL; + } + + /* set it and wait for it to be so */ + i2c_master_send(client, buf, 1); + timeout = jiffies + HZ * 3; + for (;;) { + /* get the current status */ + buf[0] = 0x10; + i2c_master_send(client, buf, 1); + i2c_master_recv(client, buf, 1); + + if (!(buf[0] & 0x20)) + break; + if (time_after(jiffies,timeout)) { + status = -ETIMEDOUT; + break; + } + + msleep(10); + } + + /* delay a bit to let encoder settle */ + msleep(50); + + return status; +} + + +static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + i2c_master_send(client, buf, 2); +} + +static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) +{ + u8 buf[3]; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + i2c_master_send(client, buf, 3); +} + +static int saa6752hs_set_bitrate(struct i2c_client *client, + struct saa6752hs_state *h) +{ + struct saa6752hs_mpeg_params *params = &h->params; + int tot_bitrate; + int is_384k; + + /* set the bitrate mode */ + set_reg8(client, 0x71, + params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + + /* set the video bitrate */ + if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { + /* set the target bitrate */ + set_reg16(client, 0x80, params->vi_bitrate); + + /* set the max bitrate */ + set_reg16(client, 0x81, params->vi_bitrate_peak); + tot_bitrate = params->vi_bitrate_peak; + } else { + /* set the target bitrate (no max bitrate for CBR) */ + set_reg16(client, 0x81, params->vi_bitrate); + tot_bitrate = params->vi_bitrate; + } + + /* set the audio encoding */ + set_reg8(client, 0x93, + params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); + + /* set the audio bitrate */ + if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) + is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; + else + is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; + set_reg8(client, 0x94, is_384k); + tot_bitrate += is_384k ? 384 : 256; + + /* Note: the total max bitrate is determined by adding the video and audio + bitrates together and also adding an extra 768kbit/s to stay on the + safe side. If more control should be required, then an extra MPEG control + should be added. */ + tot_bitrate += 768; + if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX) + tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; + + /* set the total bitrate */ + set_reg16(client, 0xb1, tot_bitrate); + return 0; +} + +static int saa6752hs_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa6752hs_state *h = + container_of(ctrl->handler, struct saa6752hs_state, hdl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + /* peak bitrate shall be >= normal bitrate */ + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + h->video_bitrate_peak->val < h->video_bitrate->val) + h->video_bitrate_peak->val = h->video_bitrate->val; + break; + } + return 0; +} + +static int saa6752hs_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa6752hs_state *h = + container_of(ctrl->handler, struct saa6752hs_state, hdl); + struct saa6752hs_mpeg_params *params = &h->params; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + break; + case V4L2_CID_MPEG_STREAM_PID_PMT: + params->ts_pid_pmt = ctrl->val; + break; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + params->ts_pid_audio = ctrl->val; + break; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + params->ts_pid_video = ctrl->val; + break; + case V4L2_CID_MPEG_STREAM_PID_PCR: + params->ts_pid_pcr = ctrl->val; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + params->au_encoding = ctrl->val; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + params->au_l2_bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + params->au_ac3_bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + params->vi_aspect = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + params->vi_bitrate_mode = ctrl->val; + params->vi_bitrate = h->video_bitrate->val / 1000; + params->vi_bitrate_peak = h->video_bitrate_peak->val / 1000; + v4l2_ctrl_activate(h->video_bitrate_peak, + ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) +{ + unsigned char buf[9], buf2[4]; + struct saa6752hs_state *h = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned size; + u32 crc; + unsigned char localPAT[256]; + unsigned char localPMT[256]; + + /* Set video format - must be done first as it resets other settings */ + set_reg8(client, 0x41, h->video_format); + + /* Set number of lines in input signal */ + set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); + + /* set bitrate */ + saa6752hs_set_bitrate(client, h); + + /* Set GOP structure {3, 13} */ + set_reg16(client, 0x72, 0x030d); + + /* Set minimum Q-scale {4} */ + set_reg8(client, 0x82, 0x04); + + /* Set maximum Q-scale {12} */ + set_reg8(client, 0x83, 0x0c); + + /* Set Output Protocol */ + set_reg8(client, 0xd0, 0x81); + + /* Set video output stream format {TS} */ + set_reg8(client, 0xb0, 0x05); + + /* Set leading null byte for TS */ + set_reg16(client, 0xf6, leading_null_bytes); + + /* compute PAT */ + memcpy(localPAT, PAT, sizeof(PAT)); + localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f); + localPAT[18] = h->params.ts_pid_pmt & 0xff; + crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); + localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; + localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; + localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; + localPAT[sizeof(PAT) - 1] = crc & 0xFF; + + /* compute PMT */ + if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + size = sizeof(PMT_AC3); + memcpy(localPMT, PMT_AC3, size); + } else { + size = sizeof(PMT); + memcpy(localPMT, PMT, size); + } + localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); + localPMT[4] = h->params.ts_pid_pmt & 0xff; + localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); + localPMT[16] = h->params.ts_pid_pcr & 0xFF; + localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F); + localPMT[21] = h->params.ts_pid_video & 0xFF; + localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); + localPMT[26] = h->params.ts_pid_audio & 0xFF; + crc = crc32_be(~0, &localPMT[7], size - 7 - 4); + localPMT[size - 4] = (crc >> 24) & 0xFF; + localPMT[size - 3] = (crc >> 16) & 0xFF; + localPMT[size - 2] = (crc >> 8) & 0xFF; + localPMT[size - 1] = crc & 0xFF; + + /* Set Audio PID */ + set_reg16(client, 0xc1, h->params.ts_pid_audio); + + /* Set Video PID */ + set_reg16(client, 0xc0, h->params.ts_pid_video); + + /* Set PCR PID */ + set_reg16(client, 0xc4, h->params.ts_pid_pcr); + + /* Send SI tables */ + i2c_master_send(client, localPAT, sizeof(PAT)); + i2c_master_send(client, localPMT, size); + + /* mute then unmute audio. This removes buzzing artefacts */ + set_reg8(client, 0xa4, 1); + set_reg8(client, 0xa4, 0); + + /* start it going */ + saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); + + /* readout current state */ + buf[0] = 0xE1; + buf[1] = 0xA7; + buf[2] = 0xFE; + buf[3] = 0x82; + buf[4] = 0xB0; + i2c_master_send(client, buf, 5); + i2c_master_recv(client, buf2, 4); + + /* change aspect ratio */ + buf[0] = 0xE0; + buf[1] = 0xA7; + buf[2] = 0xFE; + buf[3] = 0x82; + buf[4] = 0xB0; + buf[5] = buf2[0]; + switch (h->params.vi_aspect) { + case V4L2_MPEG_VIDEO_ASPECT_16x9: + buf[6] = buf2[1] | 0x40; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + default: + buf[6] = buf2[1] & 0xBF; + break; + } + buf[7] = buf2[2]; + buf[8] = buf2[3]; + i2c_master_send(client, buf, 9); + + return 0; +} + +static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct saa6752hs_state *h = to_state(sd); + + if (h->video_format == SAA6752HS_VF_UNKNOWN) + h->video_format = SAA6752HS_VF_D1; + f->width = v4l2_format_table[h->video_format].fmt.pix.width; + f->height = v4l2_format_table[h->video_format].fmt.pix.height; + f->code = V4L2_MBUS_FMT_FIXED; + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + int dist_352, dist_480, dist_720; + + f->code = V4L2_MBUS_FMT_FIXED; + + dist_352 = abs(f->width - 352); + dist_480 = abs(f->width - 480); + dist_720 = abs(f->width - 720); + if (dist_720 < dist_480) { + f->width = 720; + f->height = 576; + } else if (dist_480 < dist_352) { + f->width = 480; + f->height = 576; + } else { + f->width = 352; + if (abs(f->height - 576) < abs(f->height - 288)) + f->height = 576; + else + f->height = 288; + } + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct saa6752hs_state *h = to_state(sd); + + if (f->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + /* + FIXME: translate and round width/height into EMPRESS + subsample type: + + type | PAL | NTSC + --------------------------- + SIF | 352x288 | 352x240 + 1/2 D1 | 352x576 | 352x480 + 2/3 D1 | 480x576 | 480x480 + D1 | 720x576 | 720x480 + */ + + saa6752hs_try_mbus_fmt(sd, f); + if (f->width == 720) + h->video_format = SAA6752HS_VF_D1; + else if (f->width == 480) + h->video_format = SAA6752HS_VF_2_3_D1; + else if (f->height == 576) + h->video_format = SAA6752HS_VF_1_2_D1; + else + h->video_format = SAA6752HS_VF_SIF; + return 0; +} + +static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa6752hs_state *h = to_state(sd); + + h->standard = std; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { + .try_ctrl = saa6752hs_try_ctrl, + .s_ctrl = saa6752hs_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { + .init = saa6752hs_init, + .s_std = saa6752hs_s_std, +}; + +static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { + .s_mbus_fmt = saa6752hs_s_mbus_fmt, + .try_mbus_fmt = saa6752hs_try_mbus_fmt, + .g_mbus_fmt = saa6752hs_g_mbus_fmt, +}; + +static const struct v4l2_subdev_ops saa6752hs_ops = { + .core = &saa6752hs_core_ops, + .video = &saa6752hs_video_ops, +}; + +static int saa6752hs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + u8 addr = 0x13; + u8 data[12]; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + if (h == NULL) + return -ENOMEM; + sd = &h->sd; + v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); + + i2c_master_send(client, &addr, 1); + i2c_master_recv(client, data, sizeof(data)); + h->revision = (data[8] << 8) | data[9]; + h->has_ac3 = 0; + if (h->revision == 0x0206) { + h->has_ac3 = 1; + v4l_info(client, "supports AC-3\n"); + } + h->params = param_defaults; + + hdl = &h->hdl; + v4l2_ctrl_handler_init(hdl, 14); + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_AUDIO_ENCODING, + h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + 0x0d, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, + ~((1 << V4L2_MPEG_AUDIO_L2_BITRATE_256K) | + (1 << V4L2_MPEG_AUDIO_L2_BITRATE_384K)), + V4L2_MPEG_AUDIO_L2_BITRATE_256K); + + if (h->has_ac3) + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + ~((1 << V4L2_MPEG_AUDIO_AC3_BITRATE_256K) | + (1 << V4L2_MPEG_AUDIO_AC3_BITRATE_384K)), + V4L2_MPEG_AUDIO_AC3_BITRATE_256K); + + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + ~(1 << V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000), + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + ~(1 << V4L2_MPEG_VIDEO_ENCODING_MPEG_2), + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_16x9, 0x01, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + h->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 1000000, 27000000, 1000, 8000000); + + v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + ~(1 << V4L2_MPEG_STREAM_TYPE_MPEG2_TS), + V4L2_MPEG_STREAM_TYPE_MPEG2_TS); + + h->video_bitrate_mode = v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + h->video_bitrate = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, 1000000, 27000000, 1000, 6000000); + v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_STREAM_PID_PMT, 0, (1 << 14) - 1, 1, 16); + v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_STREAM_PID_AUDIO, 0, (1 << 14) - 1, 1, 260); + v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_STREAM_PID_VIDEO, 0, (1 << 14) - 1, 1, 256); + v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, + V4L2_CID_MPEG_STREAM_PID_PCR, 0, (1 << 14) - 1, 1, 259); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(h); + return err; + } + v4l2_ctrl_cluster(3, &h->video_bitrate_mode); + v4l2_ctrl_handler_setup(hdl); + h->standard = 0; /* Assume 625 input lines */ + return 0; +} + +static int saa6752hs_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&to_state(sd)->hdl); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id saa6752hs_id[] = { + { "saa6752hs", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6752hs_id); + +static struct i2c_driver saa6752hs_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa6752hs", + }, + .probe = saa6752hs_probe, + .remove = saa6752hs_remove, + .id_table = saa6752hs_id, +}; + +module_i2c_driver(saa6752hs_driver); diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 15b90d6..7883393 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 3537548..58de9b0 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/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c deleted file mode 100644 index 8272c0b..0000000 --- a/drivers/media/pci/saa7134/saa6752hs.c +++ /dev/null @@ -1,790 +0,0 @@ - /* - saa6752hs - i2c-driver for the saa6752hs by Philips - - Copyright (C) 2004 Andrew de Quincey - - AC-3 support: - - Copyright (C) 2008 Hans Verkuil - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 -#define MPEG_VIDEO_MAX_BITRATE_MAX 27000 -#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 -#define MPEG_PID_MAX ((1 << 14) - 1) - - -MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); -MODULE_AUTHOR("Andrew de Quincey"); -MODULE_LICENSE("GPL"); - -enum saa6752hs_videoformat { - SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ - SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ - SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */ - SAA6752HS_VF_SIF = 3, /* SIF video format: 352x288 */ - SAA6752HS_VF_UNKNOWN, -}; - -struct saa6752hs_mpeg_params { - /* transport streams */ - __u16 ts_pid_pmt; - __u16 ts_pid_audio; - __u16 ts_pid_video; - __u16 ts_pid_pcr; - - /* audio */ - enum v4l2_mpeg_audio_encoding au_encoding; - enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; - enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; - - /* video */ - enum v4l2_mpeg_video_aspect vi_aspect; - enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode; - __u32 vi_bitrate; - __u32 vi_bitrate_peak; -}; - -static const struct v4l2_format v4l2_format_table[] = -{ - [SAA6752HS_VF_D1] = - { .fmt = { .pix = { .width = 720, .height = 576 }}}, - [SAA6752HS_VF_2_3_D1] = - { .fmt = { .pix = { .width = 480, .height = 576 }}}, - [SAA6752HS_VF_1_2_D1] = - { .fmt = { .pix = { .width = 352, .height = 576 }}}, - [SAA6752HS_VF_SIF] = - { .fmt = { .pix = { .width = 352, .height = 288 }}}, - [SAA6752HS_VF_UNKNOWN] = - { .fmt = { .pix = { .width = 0, .height = 0}}}, -}; - -struct saa6752hs_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct { /* video bitrate mode control cluster */ - struct v4l2_ctrl *video_bitrate_mode; - struct v4l2_ctrl *video_bitrate; - struct v4l2_ctrl *video_bitrate_peak; - }; - u32 revision; - int has_ac3; - struct saa6752hs_mpeg_params params; - enum saa6752hs_videoformat video_format; - v4l2_std_id standard; -}; - -enum saa6752hs_command { - SAA6752HS_COMMAND_RESET = 0, - SAA6752HS_COMMAND_STOP = 1, - SAA6752HS_COMMAND_START = 2, - SAA6752HS_COMMAND_PAUSE = 3, - SAA6752HS_COMMAND_RECONFIGURE = 4, - SAA6752HS_COMMAND_SLEEP = 5, - SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, - - SAA6752HS_COMMAND_MAX -}; - -static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa6752hs_state, sd); -} - -/* ---------------------------------------------------------------------- */ - -static const u8 PAT[] = { - 0xc2, /* i2c register */ - 0x00, /* table number for encoder */ - - 0x47, /* sync */ - 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */ - 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ - - 0x00, /* PSI pointer to start of table */ - - 0x00, /* tid(0) */ - 0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */ - - 0x00, 0x01, /* transport_stream_id(1) */ - - 0xc1, /* version_number(0), current_next_indicator(1) */ - - 0x00, 0x00, /* section_number(0), last_section_number(0) */ - - 0x00, 0x01, /* program_number(1) */ - - 0xe0, 0x00, /* PMT PID */ - - 0x00, 0x00, 0x00, 0x00 /* CRC32 */ -}; - -static const u8 PMT[] = { - 0xc2, /* i2c register */ - 0x01, /* table number for encoder */ - - 0x47, /* sync */ - 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */ - 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ - - 0x00, /* PSI pointer to start of table */ - - 0x02, /* tid(2) */ - 0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */ - - 0x00, 0x01, /* program_number(1) */ - - 0xc1, /* version_number(0), current_next_indicator(1) */ - - 0x00, 0x00, /* section_number(0), last_section_number(0) */ - - 0xe0, 0x00, /* PCR_PID */ - - 0xf0, 0x00, /* program_info_length(0) */ - - 0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ - 0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */ - - 0x00, 0x00, 0x00, 0x00 /* CRC32 */ -}; - -static const u8 PMT_AC3[] = { - 0xc2, /* i2c register */ - 0x01, /* table number for encoder(1) */ - 0x47, /* sync */ - - 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ - 0x10, /* PMT PID (0x0010) */ - 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ - - 0x00, /* PSI pointer to start of table */ - - 0x02, /* TID (2) */ - 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ - - 0x00, 0x01, /* program_number(1) */ - - 0xc1, /* version_number(0), current_next_indicator(1) */ - - 0x00, 0x00, /* section_number(0), last_section_number(0) */ - - 0xe1, 0x04, /* PCR_PID (0x0104) */ - - 0xf0, 0x00, /* program_info_length(0) */ - - 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ - 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ - 0x6a, /* AC3 */ - 0x01, /* Descriptor_length(1) */ - 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ - - 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ -}; - -static const struct saa6752hs_mpeg_params param_defaults = -{ - .ts_pid_pmt = 16, - .ts_pid_video = 260, - .ts_pid_audio = 256, - .ts_pid_pcr = 259, - - .vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, - .vi_bitrate = 4000, - .vi_bitrate_peak = 6000, - .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - - .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, - .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, -}; - -/* ---------------------------------------------------------------------- */ - -static int saa6752hs_chip_command(struct i2c_client *client, - enum saa6752hs_command command) -{ - unsigned char buf[3]; - unsigned long timeout; - int status = 0; - - /* execute the command */ - switch(command) { - case SAA6752HS_COMMAND_RESET: - buf[0] = 0x00; - break; - - case SAA6752HS_COMMAND_STOP: - buf[0] = 0x03; - break; - - case SAA6752HS_COMMAND_START: - buf[0] = 0x02; - break; - - case SAA6752HS_COMMAND_PAUSE: - buf[0] = 0x04; - break; - - case SAA6752HS_COMMAND_RECONFIGURE: - buf[0] = 0x05; - break; - - case SAA6752HS_COMMAND_SLEEP: - buf[0] = 0x06; - break; - - case SAA6752HS_COMMAND_RECONFIGURE_FORCE: - buf[0] = 0x07; - break; - - default: - return -EINVAL; - } - - /* set it and wait for it to be so */ - i2c_master_send(client, buf, 1); - timeout = jiffies + HZ * 3; - for (;;) { - /* get the current status */ - buf[0] = 0x10; - i2c_master_send(client, buf, 1); - i2c_master_recv(client, buf, 1); - - if (!(buf[0] & 0x20)) - break; - if (time_after(jiffies,timeout)) { - status = -ETIMEDOUT; - break; - } - - msleep(10); - } - - /* delay a bit to let encoder settle */ - msleep(50); - - return status; -} - - -static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) -{ - u8 buf[2]; - - buf[0] = reg; - buf[1] = val; - i2c_master_send(client, buf, 2); -} - -static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) -{ - u8 buf[3]; - - buf[0] = reg; - buf[1] = val >> 8; - buf[2] = val & 0xff; - i2c_master_send(client, buf, 3); -} - -static int saa6752hs_set_bitrate(struct i2c_client *client, - struct saa6752hs_state *h) -{ - struct saa6752hs_mpeg_params *params = &h->params; - int tot_bitrate; - int is_384k; - - /* set the bitrate mode */ - set_reg8(client, 0x71, - params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - - /* set the video bitrate */ - if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { - /* set the target bitrate */ - set_reg16(client, 0x80, params->vi_bitrate); - - /* set the max bitrate */ - set_reg16(client, 0x81, params->vi_bitrate_peak); - tot_bitrate = params->vi_bitrate_peak; - } else { - /* set the target bitrate (no max bitrate for CBR) */ - set_reg16(client, 0x81, params->vi_bitrate); - tot_bitrate = params->vi_bitrate; - } - - /* set the audio encoding */ - set_reg8(client, 0x93, - params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); - - /* set the audio bitrate */ - if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) - is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; - else - is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; - set_reg8(client, 0x94, is_384k); - tot_bitrate += is_384k ? 384 : 256; - - /* Note: the total max bitrate is determined by adding the video and audio - bitrates together and also adding an extra 768kbit/s to stay on the - safe side. If more control should be required, then an extra MPEG control - should be added. */ - tot_bitrate += 768; - if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX) - tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; - - /* set the total bitrate */ - set_reg16(client, 0xb1, tot_bitrate); - return 0; -} - -static int saa6752hs_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct saa6752hs_state *h = - container_of(ctrl->handler, struct saa6752hs_state, hdl); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - /* peak bitrate shall be >= normal bitrate */ - if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - h->video_bitrate_peak->val < h->video_bitrate->val) - h->video_bitrate_peak->val = h->video_bitrate->val; - break; - } - return 0; -} - -static int saa6752hs_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct saa6752hs_state *h = - container_of(ctrl->handler, struct saa6752hs_state, hdl); - struct saa6752hs_mpeg_params *params = &h->params; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - break; - case V4L2_CID_MPEG_STREAM_PID_PMT: - params->ts_pid_pmt = ctrl->val; - break; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: - params->ts_pid_audio = ctrl->val; - break; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: - params->ts_pid_video = ctrl->val; - break; - case V4L2_CID_MPEG_STREAM_PID_PCR: - params->ts_pid_pcr = ctrl->val; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - params->au_encoding = ctrl->val; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - params->au_l2_bitrate = ctrl->val; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - params->au_ac3_bitrate = ctrl->val; - break; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - params->vi_aspect = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - params->vi_bitrate_mode = ctrl->val; - params->vi_bitrate = h->video_bitrate->val / 1000; - params->vi_bitrate_peak = h->video_bitrate_peak->val / 1000; - v4l2_ctrl_activate(h->video_bitrate_peak, - ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - break; - default: - return -EINVAL; - } - return 0; -} - -static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) -{ - unsigned char buf[9], buf2[4]; - struct saa6752hs_state *h = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned size; - u32 crc; - unsigned char localPAT[256]; - unsigned char localPMT[256]; - - /* Set video format - must be done first as it resets other settings */ - set_reg8(client, 0x41, h->video_format); - - /* Set number of lines in input signal */ - set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); - - /* set bitrate */ - saa6752hs_set_bitrate(client, h); - - /* Set GOP structure {3, 13} */ - set_reg16(client, 0x72, 0x030d); - - /* Set minimum Q-scale {4} */ - set_reg8(client, 0x82, 0x04); - - /* Set maximum Q-scale {12} */ - set_reg8(client, 0x83, 0x0c); - - /* Set Output Protocol */ - set_reg8(client, 0xd0, 0x81); - - /* Set video output stream format {TS} */ - set_reg8(client, 0xb0, 0x05); - - /* Set leading null byte for TS */ - set_reg16(client, 0xf6, leading_null_bytes); - - /* compute PAT */ - memcpy(localPAT, PAT, sizeof(PAT)); - localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f); - localPAT[18] = h->params.ts_pid_pmt & 0xff; - crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); - localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; - localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; - localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; - localPAT[sizeof(PAT) - 1] = crc & 0xFF; - - /* compute PMT */ - if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { - size = sizeof(PMT_AC3); - memcpy(localPMT, PMT_AC3, size); - } else { - size = sizeof(PMT); - memcpy(localPMT, PMT, size); - } - localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); - localPMT[4] = h->params.ts_pid_pmt & 0xff; - localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); - localPMT[16] = h->params.ts_pid_pcr & 0xFF; - localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F); - localPMT[21] = h->params.ts_pid_video & 0xFF; - localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); - localPMT[26] = h->params.ts_pid_audio & 0xFF; - crc = crc32_be(~0, &localPMT[7], size - 7 - 4); - localPMT[size - 4] = (crc >> 24) & 0xFF; - localPMT[size - 3] = (crc >> 16) & 0xFF; - localPMT[size - 2] = (crc >> 8) & 0xFF; - localPMT[size - 1] = crc & 0xFF; - - /* Set Audio PID */ - set_reg16(client, 0xc1, h->params.ts_pid_audio); - - /* Set Video PID */ - set_reg16(client, 0xc0, h->params.ts_pid_video); - - /* Set PCR PID */ - set_reg16(client, 0xc4, h->params.ts_pid_pcr); - - /* Send SI tables */ - i2c_master_send(client, localPAT, sizeof(PAT)); - i2c_master_send(client, localPMT, size); - - /* mute then unmute audio. This removes buzzing artefacts */ - set_reg8(client, 0xa4, 1); - set_reg8(client, 0xa4, 0); - - /* start it going */ - saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); - - /* readout current state */ - buf[0] = 0xE1; - buf[1] = 0xA7; - buf[2] = 0xFE; - buf[3] = 0x82; - buf[4] = 0xB0; - i2c_master_send(client, buf, 5); - i2c_master_recv(client, buf2, 4); - - /* change aspect ratio */ - buf[0] = 0xE0; - buf[1] = 0xA7; - buf[2] = 0xFE; - buf[3] = 0x82; - buf[4] = 0xB0; - buf[5] = buf2[0]; - switch (h->params.vi_aspect) { - case V4L2_MPEG_VIDEO_ASPECT_16x9: - buf[6] = buf2[1] | 0x40; - break; - case V4L2_MPEG_VIDEO_ASPECT_4x3: - default: - buf[6] = buf2[1] & 0xBF; - break; - } - buf[7] = buf2[2]; - buf[8] = buf2[3]; - i2c_master_send(client, buf, 9); - - return 0; -} - -static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct saa6752hs_state *h = to_state(sd); - - if (h->video_format == SAA6752HS_VF_UNKNOWN) - h->video_format = SAA6752HS_VF_D1; - f->width = v4l2_format_table[h->video_format].fmt.pix.width; - f->height = v4l2_format_table[h->video_format].fmt.pix.height; - f->code = V4L2_MBUS_FMT_FIXED; - f->field = V4L2_FIELD_INTERLACED; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - int dist_352, dist_480, dist_720; - - f->code = V4L2_MBUS_FMT_FIXED; - - dist_352 = abs(f->width - 352); - dist_480 = abs(f->width - 480); - dist_720 = abs(f->width - 720); - if (dist_720 < dist_480) { - f->width = 720; - f->height = 576; - } else if (dist_480 < dist_352) { - f->width = 480; - f->height = 576; - } else { - f->width = 352; - if (abs(f->height - 576) < abs(f->height - 288)) - f->height = 576; - else - f->height = 288; - } - f->field = V4L2_FIELD_INTERLACED; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct saa6752hs_state *h = to_state(sd); - - if (f->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - - /* - FIXME: translate and round width/height into EMPRESS - subsample type: - - type | PAL | NTSC - --------------------------- - SIF | 352x288 | 352x240 - 1/2 D1 | 352x576 | 352x480 - 2/3 D1 | 480x576 | 480x480 - D1 | 720x576 | 720x480 - */ - - saa6752hs_try_mbus_fmt(sd, f); - if (f->width == 720) - h->video_format = SAA6752HS_VF_D1; - else if (f->width == 480) - h->video_format = SAA6752HS_VF_2_3_D1; - else if (f->height == 576) - h->video_format = SAA6752HS_VF_1_2_D1; - else - h->video_format = SAA6752HS_VF_SIF; - return 0; -} - -static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa6752hs_state *h = to_state(sd); - - h->standard = std; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { - .try_ctrl = saa6752hs_try_ctrl, - .s_ctrl = saa6752hs_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { - .init = saa6752hs_init, - .s_std = saa6752hs_s_std, -}; - -static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { - .s_mbus_fmt = saa6752hs_s_mbus_fmt, - .try_mbus_fmt = saa6752hs_try_mbus_fmt, - .g_mbus_fmt = saa6752hs_g_mbus_fmt, -}; - -static const struct v4l2_subdev_ops saa6752hs_ops = { - .core = &saa6752hs_core_ops, - .video = &saa6752hs_video_ops, -}; - -static int saa6752hs_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - u8 addr = 0x13; - u8 data[12]; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - if (h == NULL) - return -ENOMEM; - sd = &h->sd; - v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); - - i2c_master_send(client, &addr, 1); - i2c_master_recv(client, data, sizeof(data)); - h->revision = (data[8] << 8) | data[9]; - h->has_ac3 = 0; - if (h->revision == 0x0206) { - h->has_ac3 = 1; - v4l_info(client, "supports AC-3\n"); - } - h->params = param_defaults; - - hdl = &h->hdl; - v4l2_ctrl_handler_init(hdl, 14); - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_AUDIO_ENCODING, - h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - 0x0d, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, - ~((1 << V4L2_MPEG_AUDIO_L2_BITRATE_256K) | - (1 << V4L2_MPEG_AUDIO_L2_BITRATE_384K)), - V4L2_MPEG_AUDIO_L2_BITRATE_256K); - - if (h->has_ac3) - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_MPEG_AUDIO_AC3_BITRATE_384K, - ~((1 << V4L2_MPEG_AUDIO_AC3_BITRATE_256K) | - (1 << V4L2_MPEG_AUDIO_AC3_BITRATE_384K)), - V4L2_MPEG_AUDIO_AC3_BITRATE_256K); - - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - ~(1 << V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000), - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - ~(1 << V4L2_MPEG_VIDEO_ENCODING_MPEG_2), - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_MPEG_VIDEO_ASPECT_16x9, 0x01, - V4L2_MPEG_VIDEO_ASPECT_4x3); - - h->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 1000000, 27000000, 1000, 8000000); - - v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_MPEG_STREAM_TYPE_MPEG2_TS, - ~(1 << V4L2_MPEG_STREAM_TYPE_MPEG2_TS), - V4L2_MPEG_STREAM_TYPE_MPEG2_TS); - - h->video_bitrate_mode = v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - h->video_bitrate = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, 1000000, 27000000, 1000, 6000000); - v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_STREAM_PID_PMT, 0, (1 << 14) - 1, 1, 16); - v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_STREAM_PID_AUDIO, 0, (1 << 14) - 1, 1, 260); - v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_STREAM_PID_VIDEO, 0, (1 << 14) - 1, 1, 256); - v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, - V4L2_CID_MPEG_STREAM_PID_PCR, 0, (1 << 14) - 1, 1, 259); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(h); - return err; - } - v4l2_ctrl_cluster(3, &h->video_bitrate_mode); - v4l2_ctrl_handler_setup(hdl); - h->standard = 0; /* Assume 625 input lines */ - return 0; -} - -static int saa6752hs_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&to_state(sd)->hdl); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id saa6752hs_id[] = { - { "saa6752hs", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa6752hs_id); - -static struct i2c_driver saa6752hs_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa6752hs", - }, - .probe = saa6752hs_probe, - .remove = saa6752hs_remove, - .id_table = saa6752hs_id, -}; - -module_i2c_driver(saa6752hs_driver); -- cgit v0.10.2 From 8c7c2ddccede3d2538f332bf0462e1b8138405e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:32 -0300 Subject: [media] saa6752hs.h: drop empty header Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index a0af5c7..0a9047e 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/include/media/saa6752hs.h b/include/media/saa6752hs.h deleted file mode 100644 index 3b8686e..0000000 --- a/include/media/saa6752hs.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - saa6752hs.h - definition for saa6752hs MPEG encoder - - Copyright (C) 2003 Andrew de Quincey - - 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. -*/ - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v0.10.2 From a9fe3beee6c698a9802e1bb4928b107695d2f12e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:33 -0300 Subject: [media] saa7134: drop log_status for radio There are no controls for the radio node, so just drop support for this ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 2334bf9..88e6405 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2125,7 +2125,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, - .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -- cgit v0.10.2 From af2c5debe11f51d9af42accd9dd495478bb726fd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:34 -0300 Subject: [media] saa6588: after calling CMD_CLOSE, CMD_POLL is broken CMD_CLOSE sets data_available_for_read to 1, which is necessary to do the wakeup call, but it is never reset to 0. Because of this calling CMD_POLL afterwards will always return that data is available, even if there isn't any. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 70bc72e..54dd7a0 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -402,6 +402,7 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) 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 --- */ -- cgit v0.10.2 From a101b947d405b5c2c94f260867074a4466b7cbea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:35 -0300 Subject: [media] saa6588: remove unused CMD_OPEN Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 54dd7a0..21cf940 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -394,10 +394,6 @@ 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; diff --git a/include/media/saa6588.h b/include/media/saa6588.h index 2c3c442..1489a52 100644 --- a/include/media/saa6588.h +++ b/include/media/saa6588.h @@ -34,7 +34,6 @@ struct saa6588_command { }; /* These ioctls are internal to the kernel */ -#define SAA6588_CMD_OPEN _IOW('R', 1, int) #define SAA6588_CMD_CLOSE _IOW('R', 2, int) #define SAA6588_CMD_READ _IOR('R', 3, int) #define SAA6588_CMD_POLL _IOR('R', 4, int) -- cgit v0.10.2 From 09092787e0cf66e705b0d744f5f0c3b2b6495559 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:36 -0300 Subject: [media] saa6588: add support for non-blocking mode saa6588 always blocked while waiting for data, even if the filehandle was in non-blocking mode. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 21cf940..2960b5a 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); } @@ -408,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/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 92a06fd..c2aaadc 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -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/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 88e6405..36026b1 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1285,6 +1285,7 @@ static ssize_t radio_read(struct file *file, char __user *data, 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; diff --git a/include/media/saa6588.h b/include/media/saa6588.h index 1489a52..b5ec1aa 100644 --- a/include/media/saa6588.h +++ b/include/media/saa6588.h @@ -27,6 +27,7 @@ struct saa6588_command { unsigned int block_count; + bool nonblocking; int result; unsigned char __user *buffer; struct file *instance; -- cgit v0.10.2 From 581d88c470bce802cab00f92536917a315b09ad2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Dec 2013 08:28:37 -0300 Subject: [media] saa7134: don't set vfd->debug You can set this through sysfs, so don't mix the two. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index d3b4e56..1362b4a 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -803,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); -- cgit v0.10.2 From 65eccc7a8810b28e60572a4c4877d2a75814e7fd Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 20 Dec 2013 07:16:42 -0300 Subject: [media] fix coccinelle warnings drivers/staging/media/bcm2048/radio-bcm2048.c:2632:1-7: Replace memcpy with struct assignment /c/kernel-tests/src/cocci/drivers/staging/media/bcm2048/radio-bcm2048.c:744:1-7: Replace memcpy with struct assignment /c/kernel-tests/src/cocci/drivers/staging/media/bcm2048/radio-bcm2048.c:2360:3-9: Replace memcpy with struct assignment Generated by: coccinelle/misc/memcpy-assign.cocci CC: Hans Verkuil CC: Mauro Carvalho Chehab Signed-off-by: Fengguang Wu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 37ff899..9344dae 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -741,8 +741,7 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region) return -EINVAL; mutex_lock(&bdev->mutex); - memcpy(&bdev->region_info, ®ion_configs[region], - sizeof(struct region_info)); + bdev->region_info = region_configs[region]; mutex_unlock(&bdev->mutex); if (bdev->frequency < region_configs[region].bottom_frequency || @@ -2358,7 +2357,7 @@ static int bcm2048_vidioc_queryctrl(struct file *file, void *priv, for (i = 0; i < ARRAY_SIZE(bcm2048_v4l2_queryctrl); i++) { if (qc->id && qc->id == bcm2048_v4l2_queryctrl[i].id) { - memcpy(qc, &(bcm2048_v4l2_queryctrl[i]), sizeof(*qc)); + *qc = bcm2048_v4l2_queryctrl[i]; return 0; } } @@ -2630,8 +2629,7 @@ static int bcm2048_i2c_driver_probe(struct i2c_client *client, dev_dbg(&client->dev, "IRQ not configured. Using timeouts.\n"); } - memcpy(bdev->videodev, &bcm2048_viddev_template, - sizeof(bcm2048_viddev_template)); + *bdev->videodev = bcm2048_viddev_template; video_set_drvdata(bdev->videodev, bdev); if (video_register_device(bdev->videodev, VFL_TYPE_RADIO, radio_nr)) { dev_dbg(&client->dev, "Could not register video device.\n"); -- cgit v0.10.2 From 12d1ee1f2df0ee56441a76a0ad0984936b046aa8 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 20 Dec 2013 07:16:47 -0300 Subject: [media] fix coccinelle warnings drivers/staging/media/bcm2048/radio-bcm2048.c:2255:3-4: Unneeded semicolon Removes unneeded semicolon. Generated by: coccinelle/misc/semicolon.cocci CC: Hans Verkuil CC: Mauro Carvalho Chehab Signed-off-by: Fengguang Wu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 9344dae..b2cd3a8 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2252,7 +2252,7 @@ static ssize_t bcm2048_fops_read(struct file *file, char __user *buf, if (copy_to_user(buf+i, tmpbuf, 3)) { retval = -EFAULT; break; - }; + } i += 3; } -- cgit v0.10.2 From da4a733946aa360e2219ce2d33b8d25ce4aa1959 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 23 Oct 2013 16:14:51 -0300 Subject: [media] media: Remove OOM message after input_allocate_device Emitting an OOM message isn't necessary after input_allocate_device as there's a generic OOM and a dump_stack already done. Signed-off-by: Joe Perches Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index f329485..822b9f4 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/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 9a5dad9..61c061f 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -557,10 +557,8 @@ static int em28xx_register_snapshot_button(struct em28xx *dev) em28xx_info("Registering snapshot button...\n"); input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); + if (!input_dev) return -ENOMEM; - } usb_make_path(dev->udev, dev->snapshot_button_path, sizeof(dev->snapshot_button_path)); diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 78c9bc8..abf365a 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; } -- cgit v0.10.2 From f90580ca0133c533763a6cb3e632a21098a382df Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 26 Nov 2013 05:31:42 -0300 Subject: [media] videodev2: Set vb2_rect's width and height as unsigned As discussed on the media summit 2013, there is no reason for the width and height to be signed. Therefore this patch is an attempt to convert those fields from __s32 to __u32. Signed-off-by: Ricardo Ribalda Delgado Acked-by: Sakari Ailus (documentation and smiapp) Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 0c7195e..c4cac6d 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2523,6 +2523,18 @@ that used it. It was originally scheduled for removal in 2.6.35. +
+ V4L2 in Linux 3.14 + + + In struct v4l2_rect, the type +of width and height +fields changed from _s32 to _u32. + + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/dev-overlay.xml b/Documentation/DocBook/media/v4l/dev-overlay.xml index 40d1d76..cc6e0c5 100644 --- a/Documentation/DocBook/media/v4l/dev-overlay.xml +++ b/Documentation/DocBook/media/v4l/dev-overlay.xml @@ -346,17 +346,14 @@ rectangle, in pixels. rectangle, in pixels. Offsets increase to the right and down. - __s32 + __u32 width Width of the rectangle, in pixels. - __s32 + __u32 height - Height of the rectangle, in pixels. Width and -height cannot be negative, the fields are signed for hysterical -reasons. + Height of the rectangle, in pixels. diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 8469fe1..74b7f27 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -141,6 +141,14 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> + 3.14 + 2013-11-25 + rr + Set width and height as unsigned on v4l2_rect. + + + + 3.11 2013-05-26 hv @@ -501,7 +509,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.11 + Revision 3.14 &sub-common; diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml index bf7cc97..1f5ed64 100644 --- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml @@ -133,18 +133,14 @@ rectangle, in pixels. rectangle, in pixels. - __s32 + __u32 width Width of the rectangle, in pixels. - __s32 + __u32 height - Height of the rectangle, in pixels. Width -and height cannot be negative, the fields are signed for -hysterical reasons. - + Height of the rectangle, in pixels. diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 846b15f..85ec3ba 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 1c2303d..e5ddf47 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 7964634..d41c70e 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 0d2b4a8..36c504b 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -305,8 +305,8 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) min_hblank += (mt9v032->hratio - 1) * 10; - min_hblank = max((int)mt9v032->model->data->min_row_time - crop->width, - (int)min_hblank); + 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); return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); @@ -525,12 +525,14 @@ 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 / 4, MT9V032_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 4, MT9V032_WINDOW_HEIGHT_MIN), - __crop->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); @@ -580,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); diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index fbd48f0..8741cae 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2027,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; @@ -2120,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 6f40566..ccf5940 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/tvp5150.c b/drivers/media/i2c/tvp5150.c index 2ed05b6..542d252 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -863,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); @@ -873,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 */ @@ -889,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/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index c2aaadc..afcd53b 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. diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 36026b1..eb472b5 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1729,10 +1729,6 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr 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(dev, RESOURCE_OVERLAY)) return -EBUSY; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index cbd3a34..8e74fb7 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/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 437f1b0..6ae7bbe 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -207,8 +207,8 @@ enum v4l2_priority { struct v4l2_rect { __s32 left; __s32 top; - __s32 width; - __s32 height; + __u32 width; + __u32 height; }; struct v4l2_fract { -- cgit v0.10.2 From e0a973423efddc84262991c4a14a0daaaf56ca84 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Dec 2013 09:04:44 -0300 Subject: [media] davinci-vpfe: fix compile error The include for delay.h was missing. Signed-off-by: Hans Verkuil Cc: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 4171cfd..b942bf7 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -19,6 +19,7 @@ * Prabhakar Lad */ +#include #include "dm365_isif.h" #include "vpfe_mc_capture.h" -- cgit v0.10.2 From 951ed98eb8e4e36d35ad5088cbca94a6215c2965 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Jan 2014 08:05:01 -0200 Subject: [media] export em28xx_release_resources() symbol As reported by the kbuild test robot: All error/warnings: >> ERROR: "em28xx_release_resources" [drivers/media/usb/em28xx/em28xx-v4l.ko] undefined! Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 84167f0..39cf49c 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2839,6 +2839,7 @@ void em28xx_release_resources(struct em28xx *dev) /* Mark device as unused */ clear_bit(dev->devno, &em28xx_devused); }; +EXPORT_SYMBOL_GPL(em28xx_release_resources); /* * em28xx_init_dev() -- cgit v0.10.2 From 661112cb7e343c36d8d7899c1c9e6d37271aafd4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Dec 2013 11:36:51 -0300 Subject: [media] omap3isp: Cancel streaming when a fatal error occurs When a fatal error that prevents any further video streaming occurs in a pipeline, all queued buffers must be marked as erroneous and new buffers must be prevented from being queued. Implement this behaviour with a new omap3isp_pipeline_cancel_stream() function that can be used by submodules to cancel streaming. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index bb4e0a7..7e09c1d 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1057,6 +1057,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 * diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 72685ad..5b91f86 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -236,6 +236,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/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 3953aec..856fdf5 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -411,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; @@ -447,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); @@ -569,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 @@ -1105,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); diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 1ad470e..4e19407 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); -- cgit v0.10.2 From 112eee0c03eb86cb00f1c6769c7a0b05530d636b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Dec 2013 23:49:42 -0300 Subject: [media] omap3isp: Refactor modules stop failure handling Modules failing to stop are fatal errors for the preview engine only. Flag that condition separately from the other stop failures to prepare support for more fatal errors. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 7e09c1d..5807185 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; } } @@ -1225,6 +1232,7 @@ static int isp_reset(struct isp_device *isp) udelay(1); } + isp->stop_failure = false; isp->crashed = 0; return 0; } @@ -1636,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); } diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 5b91f86..081f5ec 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -154,6 +154,7 @@ struct isp_xclk { * regions. * @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. @@ -191,6 +192,7 @@ struct isp_device { /* 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; -- cgit v0.10.2 From 9554b7dbc62885f8524a63dafc8533422d9c637d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Dec 2013 11:13:13 -0300 Subject: [media] omap3isp: ccdc: Don't hang when the SBL fails to become idle Under abnormal conditions (such as glitches on the HSYNC/VSYNC signals) the CCDC output SBL can fail to become idle. The driver currently logs this condition to the kernel log and doesn't restart the CCDC. This results in CCDC video capture hanging without any notification to userspace. Cancel the pipeline and mark the CCDC as crashed instead of hanging. Userspace will be notified of the problem and will then be able to close and reopen the device to trigger a reset of the ISP. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 561c991..5db2c88 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; } -- cgit v0.10.2 From 2a9ecc17ed9f076ff199a4bf4ebd22b41badb505 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Thu, 26 Dec 2013 12:31:49 -0300 Subject: [media] media: soc_camera: rcar_vin: Add preliminary R-Car M2 support This adds R-Car M2 (R8A7791) VIN support. Both H2 and M2 variants look the same from the driver's point of view, so use GEN2 id for both. Signed-off-by: Valentine Barshak Acked-by: Laurent Pinchart [g.liakhovetski@gmx.de: removed changelog from commit message] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 6866bb4..3b1c05a 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 }, -- cgit v0.10.2 From 687ff8b0c489e8bb3a4a3da291e830fa7cfafe22 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 22 Dec 2013 11:17:46 -0300 Subject: [media] em28xx: fix I2S audio sample rate definitions and info output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The audio configuration in chip config register 0x00 and eeprom are always consistent. But currently the audio configuration #defines for the chip config register say 0x20 means 3 sample rates and 0x30 5 sample rates, while the eeprom info output says 0x20 means 1 sample rate and 0x30 3 sample rates. I've checked the datasheet excerpts I have and it seems that the meaning of these bits is different for em2820/40 (1 and 3 sample rates) and em2860+ (3 and 5 smaple rates). I have also checked my Hauppauge WinTV USB 2 (em2840) and the chip/eeprom audio config 0x20 matches the sample rates reproted by the USB device descriptor (32k only). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index cef3fd4..b5f4970 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -517,17 +517,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; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index c4ff973..f2d5f8a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -736,10 +736,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-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index b769ceb..311fb34 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 diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e0369fb..b792f22 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -295,8 +295,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. -- cgit v0.10.2 From 6f64eb0ed246c0e8b2a10bb4d2403ed05445da57 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Mon, 23 Dec 2013 08:12:21 -0300 Subject: [media] cx231xx: Add missing KERN_CONT to i2c debug prints Fix continuation lines. [m.chehab@samsung.com: This was actually part of a v2 patch meant to fix i2c debug prints. As version 1 was already applied, I'm applying here the diff and fixing the patch subject/description] Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index a0d2235..7c0f797 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -390,7 +390,7 @@ 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 @@ -398,7 +398,8 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, /* 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]); + printk(KERN_CONT "\n"); } /* read bytes */ dprintk2(2, "plus %s %s addr=0x%x len=%d:", @@ -409,21 +410,21 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, &msgs[i + 1]); if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i+1].len; byte++) - printk(" %02x", msgs[i+1].buf[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; -- cgit v0.10.2 From 383cc04c607351b140ba93a178a6fc2a9597f29d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 24 Dec 2013 08:42:03 -0300 Subject: [media] s5k5baf: Fix build warning Fixes the following warnings: drivers/media/i2c/s5k5baf.c: In function 's5k5baf_fw_parse': drivers/media/i2c/s5k5baf.c:362:3: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' [-Wformat=] drivers/media/i2c/s5k5baf.c:383:4: warning: format '%d' expects argument of type 'int', but argument 4 has type 'size_t' [-Wformat=] Signed-off-by: Sachin Kamat Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index e3b44a8..139bdd4 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -359,7 +359,7 @@ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, int ret; if (count < S5K5BAG_FW_TAG_LEN + 1) { - dev_err(dev, "firmware file too short (%d)\n", count); + dev_err(dev, "firmware file too short (%zu)\n", count); return -EINVAL; } @@ -379,7 +379,7 @@ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, f = (struct s5k5baf_fw *)d; if (count < 1 + 2 * f->count) { - dev_err(dev, "invalid firmware header (count=%d size=%d)\n", + dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); return -EINVAL; } -- cgit v0.10.2 From 7296e158b5a3340f44b64c86240e07922406e1c3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 24 Dec 2013 08:42:04 -0300 Subject: [media] s5k5baf: Fix checkpatch error Fixes the following error: ERROR: return is not a function, parentheses are not required FILE: drivers/media/i2c/s5k5baf.c:1353: Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 139bdd4..974b865 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1350,8 +1350,8 @@ static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) static int s5k5baf_is_bound_target(u32 target) { - return (target == V4L2_SEL_TGT_CROP_BOUNDS || - target == V4L2_SEL_TGT_COMPOSE_BOUNDS); + return target == V4L2_SEL_TGT_CROP_BOUNDS || + target == V4L2_SEL_TGT_COMPOSE_BOUNDS; } static int s5k5baf_get_selection(struct v4l2_subdev *sd, -- cgit v0.10.2 From c0ee62734e8e840b0096827f02b1aaac71ef5105 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 24 Dec 2013 08:42:05 -0300 Subject: [media] s5k5baf: Fix potential NULL pointer dereferencing Dereference 'fw' after the NULL check. Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 974b865..4b83811 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -548,12 +548,14 @@ static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) { struct s5k5baf_fw *fw = state->fw; - u16 *data = fw->data + 2 * fw->count; + 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; -- cgit v0.10.2 From 42f8f39e278b834ab2b90419f9acbdb02472cdeb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 24 Dec 2013 12:42:19 -0300 Subject: [media] [for,v3.14] sn9c102: fix build dependency This driver should only build if MEDIA_USB_SUPPORT is set. Signed-off-by: Hans Verkuil Reported-by: Jim Davis Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/sn9c102/Kconfig b/drivers/staging/media/sn9c102/Kconfig index d8ae235..c9aba59 100644 --- a/drivers/staging/media/sn9c102/Kconfig +++ b/drivers/staging/media/sn9c102/Kconfig @@ -1,6 +1,6 @@ config USB_SN9C102 tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 + depends on VIDEO_V4L2 && MEDIA_USB_SUPPORT ---help--- This driver is DEPRECATED, please use the gspca sonixb and sonixj modules instead. -- cgit v0.10.2 From cd19f7d3e39b3160595d56bb3e3a2bf4f7f4669c Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 27 Dec 2013 18:18:39 -0300 Subject: [media] as102: fix leaks at failure paths in as102_usb_probe() Failure handling is incomplete in as102_usb_probe(). The patch implements proper resource deallocations. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index 8759553..e4a6945 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -401,14 +401,21 @@ static int as102_usb_probe(struct usb_interface *intf, /* request buffer allocation for streaming */ ret = as102_alloc_usb_stream_buffer(as102_dev); if (ret != 0) - goto failed; + goto failed_stream; /* register dvb layer */ ret = as102_dvb_register(as102_dev); + if (ret != 0) + goto failed_dvb; return ret; +failed_dvb: + as102_free_usb_stream_buffer(as102_dev); +failed_stream: + usb_deregister_dev(intf, &as102_usb_class_driver); failed: + usb_put_dev(as102_dev->bus_adap.usb_dev); usb_set_intfdata(intf, NULL); kfree(as102_dev); return ret; -- cgit v0.10.2 From 6c49d79381f4bacacdef50a1902c92833e765c63 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 29 Dec 2013 19:47:18 -0300 Subject: [media] ec168: fix error return code The rest of the function uses ret to store the return value, even setting ret to i a few lines before this, so return ret instead of i. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index 5c68f39..0c2b377 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) -- cgit v0.10.2 From 58f087c9b670ea7001d640fadbf7e4e91e739d6b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 29 Dec 2013 19:47:35 -0300 Subject: [media] e4000: fix error return code Set the return variable to an error code as done elsewhere in the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 72971a8..40c1da7 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) -- cgit v0.10.2 From 06af15d1b6f45c60358feab88004472e5428f01c Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Tue, 24 Dec 2013 13:17:12 -0300 Subject: [media] m88rs2000: add m88rs2000_set_carrieroffset Set the carrier offset correctly using the default mclk values. Add function m88rs2000_get_mclk to calculate the mclk value against crystal frequency which will later be used for other functions. Add function m88rs2000_set_carrieroffset to calculate and set the offset value. variable offset becomes a signed value. Register 0x86 is set the appropriate value according to remainder value of frequency % 192857 calculation as shown. Signed-off-by: Malcolm Priestley Cc: stable@vger.kernel.org # v3.9+ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 4da5272..8091653 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -110,6 +110,52 @@ 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; @@ -540,9 +586,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,26 +612,18 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) if (ret < 0) return -ENODEV; - offset = tuner_freq - c->frequency; + offset = (s16)((s32)tuner_freq - c->frequency); - /* calculate offset assuming 96000kHz*/ - tmp = offset; - tmp *= 65536; - - tmp = (2 * tmp + 96000) / (2 * 96000); - if (tmp < 0) - tmp += 65536; - - offset = tmp & 0xffff; - - ret = m88rs2000_writereg(state, 0x9a, 0x30); - /* Unknown usually 0xc6 sometimes 0xc1 */ - reg = m88rs2000_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); + /* 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_set_carrieroffset(fe, offset); + if (ret < 0) + return -ENODEV; /* Reset Demod */ ret = m88rs2000_tab_set(state, fe_reset); diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 14ce31e..0a50ea9 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, -- cgit v0.10.2 From dd4491dfb9eb4fa3bfa7dc73ba989e69fbce2e10 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Tue, 24 Dec 2013 13:18:46 -0300 Subject: [media] m88rs2000: set symbol rate accurately Current setting of symbol rate is not very actuate causing loss of lock. Covert temp to u64 and use mclk to calculate from big number. Calculate symbol rate by dividing symbol rate by 1000 times 1 << 24 and dividing sum by mclk. Add other symbol rate settings to function registers 0xa0-0xa3. In set_frontend add changes to register 0xf1 this must be done prior call to fe_reset. Register 0x00 doesn't need a second write of 0x1 Applied after patch m88rs2000: add m88rs2000_set_carrieroffset Signed-off-by: Malcolm Priestley Cc: stable@vger.kernel.org # v3.9+ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 8091653..02699c1 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -160,24 +160,44 @@ 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; } @@ -307,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}, @@ -351,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}, @@ -625,8 +640,13 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) if (ret < 0) return -ENODEV; - /* Reset Demod */ - ret = m88rs2000_tab_set(state, fe_reset); + /* Reset demod by symbol rate */ + if (c->symbol_rate > 27500000) + ret = m88rs2000_writereg(state, 0xf1, 0xa4); + else + ret = m88rs2000_writereg(state, 0xf1, 0xbf); + + ret |= m88rs2000_tab_set(state, fe_reset); if (ret < 0) return -ENODEV; -- cgit v0.10.2 From 7a9d6b43f81aa5ad89afdbb26a1d6fd4dff4d635 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 28 Dec 2013 14:00:40 -0300 Subject: [media] m88rs2000: correct read status lock value The correct lock values is when bits of the value 0xee are set. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 02699c1..f9d04db 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -469,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) @@ -677,7 +677,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; } -- cgit v0.10.2 From 49c44802a79ca558c78d9b86f4c1ff6bb5ba0208 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 28 Dec 2013 14:02:44 -0300 Subject: [media] m88rs2000: Correct m88rs2000_set_fec settings Register 0x70 is used to set fec, register 0x76 is used to get fec Register 0x76 is set to 0x8. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index f9d04db..002b109 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -541,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) { @@ -650,12 +655,8 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) 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); -- cgit v0.10.2 From a6d8e68b60703eb4779c502ec15a627c505c34da Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 28 Dec 2013 14:04:51 -0300 Subject: [media] m88rs2000: Correct m88rs2000_get_fec Value of fec is achieved by the upper nibble bits 6,7 & 8. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 002b109..b235146 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -581,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; } -- cgit v0.10.2 From 6f546c5ff9b5abd64144708e00748084a91492c3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jan 2014 09:10:48 -0300 Subject: [media] vb2: Fix comment in __qbuf_dmabuf The comment incorrectly explains that the code verifies information provided by userspace, while verification has been performed earlier in reality. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 098df28..5a5fb7f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1138,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) { -- cgit v0.10.2 From dad4c41827c71a84c8455e19431278e8c1edf118 Mon Sep 17 00:00:00 2001 From: "Links (Markus)" Date: Sat, 4 Jan 2014 19:44:34 -0300 Subject: [media] cx231xx: add support for a CX23103 Video Grabber USB Add a new USB ID to the driver. Signed-off-by: Links (Markus) Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 528cce9..2ee03e4 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), -- cgit v0.10.2 From 46c704d77f785974ce095b6cf0c71b92d1b06706 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 28 Dec 2013 11:47:16 -0300 Subject: [media] em28xx: use usb_alloc_coherent() for audio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of allocating transfer buffers with kmalloc() use usb_alloc_coherent(). This patch should make it work also with ARM CPUs. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index a6eef06..e512043 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -64,16 +64,22 @@ static int em28xx_deinit_isoc_audio(struct em28xx *dev) dprintk("Stopping isoc\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; 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_unlink_urb(urb); - usb_free_urb(dev->adev.urb[i]); - dev->adev.urb[i] = NULL; + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->adev.transfer_buffer[i], + urb->transfer_dma); - kfree(dev->adev.transfer_buffer[i]); dev->adev.transfer_buffer[i] = NULL; + + usb_free_urb(urb); + dev->adev.urb[i] = NULL; } return 0; @@ -176,12 +182,8 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { struct urb *urb; int j, k; + void *buf; - 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"); @@ -192,10 +194,17 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) return -ENOMEM; } + buf = usb_alloc_coherent(dev->udev, sb_size, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) + return -ENOMEM; + dev->adev.transfer_buffer[i] = buf; + memset(buf, 0x80, sb_size); + urb->dev = dev->udev; urb->context = dev; urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->transfer_buffer = dev->adev.transfer_buffer[i]; urb->interval = 1; urb->complete = em28xx_audio_isocirq; -- cgit v0.10.2 From f5f894d19e2d40f1fb98a4799d85ed0904272cd6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 28 Dec 2013 21:16:26 -0300 Subject: [media] em28xx-audio: allocate URBs at device driver init Instead of allocating/deallocating URBs and transfer buffers every time stream is started/stopped, just do it once. That reduces the memory allocation pressure and makes the code that start/stop streaming a way simpler. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index e512043..30ee389 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 * - * Copyright (C) 2007-2011 Mauro Carvalho Chehab + * Copyright (C) 2007-2014 Mauro Carvalho Chehab * - Port to work with the in-kernel driver * - Cleanups, fixes, alsa-controls, etc. * @@ -70,16 +70,6 @@ static int em28xx_deinit_isoc_audio(struct em28xx *dev) usb_kill_urb(urb); else usb_unlink_urb(urb); - - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->adev.transfer_buffer[i], - urb->transfer_dma); - - dev->adev.transfer_buffer[i] = NULL; - - usb_free_urb(urb); - dev->adev.urb[i] = NULL; } return 0; @@ -174,53 +164,14 @@ static void em28xx_audio_isocirq(struct urb *urb) 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"); + /* Start streaming */ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - void *buf; - - 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; - } - - buf = usb_alloc_coherent(dev->udev, sb_size, GFP_ATOMIC, - &urb->transfer_dma); - if (!buf) - return -ENOMEM; - dev->adev.transfer_buffer[i] = buf; - memset(buf, 0x80, sb_size); - - 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 = 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; - } + 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"); @@ -643,13 +594,36 @@ 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 < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb = dev->adev.urb[i]; + + if (!dev->adev.urb[i]) + continue; + + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->adev.transfer_buffer[i], + urb->transfer_dma); + + usb_free_urb(urb); + dev->adev.urb[i] = NULL; + dev->adev.transfer_buffer[i] = NULL; + } +} + 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, i; + const int sb_size = EM28XX_NUM_AUDIO_PACKETS * + EM28XX_AUDIO_MAX_PACKET_SIZE; if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { /* This device does not support the extension (in this case @@ -662,7 +636,8 @@ static int em28xx_audio_init(struct em28xx *dev) 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); @@ -704,6 +679,47 @@ static int em28xx_audio_init(struct em28xx *dev) em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); } + /* Alloc URB and transfer buffers */ + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + void *buf; + + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, 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, sb_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 = 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; + } + } + err = snd_card_register(card); if (err < 0) { snd_card_free(card); @@ -728,6 +744,8 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } + em28xx_audio_free_urb(dev); + if (dev->adev.sndcard) { snd_card_free(dev->adev.sndcard); dev->adev.sndcard = NULL; -- cgit v0.10.2 From ebf044f46f138b2c034c544b6cb79e563f1f21fa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Jan 2014 06:39:51 -0300 Subject: [media] tuner-xc2028: Don't try to sleep twice Only send a power down command for the device if it is not already in power down state. That prevents a timeout when trying to talk with the device. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 1057da5..75afab7 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -709,6 +709,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) { @@ -881,7 +883,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) { @@ -891,6 +893,9 @@ fail: goto retry; } + /* Firmware didn't load. Put the device to sleep */ + xc2028_sleep(fe); + if (rc == -ENOENT) rc = -EINVAL; return rc; @@ -1276,6 +1281,10 @@ static int xc2028_sleep(struct dvb_frontend *fe) if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; + /* Device is already in sleep mode */ + if (priv->state == XC2028_SLEEP) + return 0; + tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); if (debug > 1) { tuner_dbg("Printing sleep stack trace:\n"); @@ -1289,7 +1298,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); @@ -1357,7 +1367,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) -- cgit v0.10.2 From 2276bf70a7433c2afec541257eab28a43ed6c9cf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Jan 2014 06:52:43 -0300 Subject: [media] tuner-xc2028: Don't read status if device is powered down That removes those timeout errors: [ 3675.930940] xc2028 19-0061: Device is Xceive 3028 version 1.0, firmware version 2.7 [ 3676.060487] xc2028 19-0061: divisor= 00 00 8d d0 (freq=567.250) [ 3676.349449] xc2028 19-0061: Putting xc2028/3028 into poweroff mode. [ 3698.247645] xc2028 19-0061: xc2028_get_reg 0002 called [ 3698.253276] em2860 #0: I2C transfer timeout on writing to addr 0xc2 [ 3698.253301] xc2028 19-0061: i2c input error: rc = -121 (should be 2) [ 3698.253327] xc2028 19-0061: xc2028_signal called [ 3698.253339] xc2028 19-0061: xc2028_get_reg 0002 called [ 3698.259283] em2860 #0: I2C transfer timeout on writing to addr 0xc2 [ 3698.259312] xc2028 19-0061: i2c input error: rc = -121 (should be 2) Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 75afab7..cca508d 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -267,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: @@ -913,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 */ @@ -960,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 */ @@ -1277,12 +1290,12 @@ static int xc2028_sleep(struct dvb_frontend *fe) if (rc < 0) return rc; - /* Avoid firmware reload on slow devices or if PM disabled */ - if (no_poweroff || priv->ctrl.disable_power_mgmt) + /* Device is already in sleep mode */ + if (!rc) return 0; - /* Device is already in sleep mode */ - if (priv->state == XC2028_SLEEP) + /* Avoid firmware reload on slow devices or if PM disabled */ + if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); -- cgit v0.10.2 From 5022a2088687baeda87b03889d0791b1b908266b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Dec 2013 00:28:57 -0300 Subject: [media] em28xx: properly implement AC97 wait code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of assuming that msleep() is precise, use a jiffies based code to wait for AC97 to be available. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b5f4970..b6dc332 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -252,16 +253,18 @@ EXPORT_SYMBOL_GPL(em28xx_toggle_reg_bits); */ 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"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b792f22..b0aa849 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -186,6 +186,9 @@ /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_XFER_TIMEOUT 20 +/* 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 -- cgit v0.10.2 From 4b83626ac29a66fce15256771dd550e8c4ea2c66 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Jan 2014 05:42:11 -0300 Subject: [media] em28xx: convert i2c wait completion logic to use jiffies The I2C wait completion/timeout logic currently assumes that msleep(5) will wait exaclty 5 ms. This is not true at all, as it depends on CONFIG_HZ. Convert it to use jiffies, in order to not wait for more time than needed. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index f2d5f8a..0af6d79 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "em28xx.h" #include "tuner-xc2028.h" @@ -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(EM2800_I2C_XFER_TIMEOUT); int ret; - int write_timeout; u8 b2[6]; if (len < 1 || len > 4) @@ -74,14 +75,14 @@ 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) { + if (ret == 0x94 + len - 1) { return -ENODEV; - } else if (ret < 0) { + } + if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; @@ -98,9 +99,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(EM2800_I2C_XFER_TIMEOUT); u8 buf2[4]; int ret; - int read_timeout; int i; if (len < 1 || len > 4) @@ -117,14 +118,14 @@ 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) { + if (ret == 0x94 + len - 1) { return -ENODEV; - } else if (ret < 0) { + } + if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; @@ -168,7 +169,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(EM2800_I2C_XFER_TIMEOUT); + int ret; if (len < 1 || len > 64) return -EOPNOTSUPP; @@ -191,16 +193,16 @@ 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) { + if (ret == 0x10) { return -ENODEV; - } else if (ret < 0) { - em28xx_warn("failed to read i2c transfer status from bridge (error=%i)\n", + } + if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", ret); return ret; } @@ -211,6 +213,7 @@ 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; } @@ -248,20 +251,18 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) /* 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; - } - } - return len; + if (ret == 0x10) + return -ENODEV; + + em28xx_warn("unknown i2c error (status=%i)\n", ret); + return -EIO; } /* -- cgit v0.10.2 From d1b7213b480a42c9bc78938a1b3f4bc14089467f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 5 Jan 2014 09:45:50 -0300 Subject: [media] em28xx: rename I2C timeout to EM28XX_I2C_XFER_TIMEOUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro is used by all em28xx devices, and not just em2800. Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 0af6d79..342f35a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -49,7 +49,7 @@ 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(EM2800_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); int ret; u8 b2[6]; @@ -99,7 +99,7 @@ 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(EM2800_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); u8 buf2[4]; int ret; int i; @@ -169,7 +169,7 @@ 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) { - unsigned long timeout = jiffies + msecs_to_jiffies(EM2800_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); int ret; if (len < 1 || len > 64) -- cgit v0.10.2 From d20e4ed6d30c6ecee315eea0efb3449c3591d09e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 28 Dec 2013 07:42:47 -0300 Subject: [media] em28xx: use a better value for I2C timeouts In the lack of a better spec, let's assume the timeout values compatible with SMBus spec: http://smbus.org/specs/smbus110.pdf at chapter 8 - Electrical Characteristics of SMBus devices Ok, SMBus is a subset of I2C, and not all devices will be following it, but the timeout value before this patch was not even following the spec. So, while we don't have a better guess for it, use 35 + 1 ms as the timeout. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b0aa849..efdf386 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -183,8 +183,21 @@ #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 -- cgit v0.10.2 From e63b009d6e772e3fe6df39345ca2f8949d4497e6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Jan 2014 05:42:11 -0300 Subject: [media] em28xx-i2c: Fix error code for I2C error transfers Follow the error codes for I2C as described at Documentation/i2c/fault-codes. In the case of the I2C status register (0x05), this is mapped into: - ENXIO - when reg 05 returns 0x10 - ETIMEDOUT - when the device is not temporarily not responding (e. g. reg 05 returning something not 0x10 or 0x00) - EIO - for generic I/O errors that don't fit into the above. In the specific case of 0-byte reads, used only during I2C device probing, it keeps returning -ENODEV. TODO: return EBUSY when reg 05 returns 0x20 on em2874 and upper. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 342f35a..76f9566 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -80,7 +80,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (ret == 0x80 + len - 1) return len; if (ret == 0x94 + len - 1) { - return -ENODEV; + return -ENXIO; } if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", @@ -90,7 +90,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) msleep(5); } em28xx_warn("write to i2c device at 0x%x timed out\n", addr); - return -EIO; + return -ETIMEDOUT; } /* @@ -123,7 +123,7 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (ret == 0x84 + len - 1) break; if (ret == 0x94 + len - 1) { - return -ENODEV; + return -ENXIO; } if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", @@ -199,7 +199,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, if (ret == 0) /* success */ return len; if (ret == 0x10) { - return -ENODEV; + return -ENXIO; } if (ret < 0) { em28xx_warn("failed to get i2c transfer status from bridge register (error=%i)\n", @@ -213,9 +213,8 @@ 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; + em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n", addr, ret); + return -ETIMEDOUT; } /* @@ -245,7 +244,7 @@ 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. */ @@ -259,10 +258,10 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) return ret; } if (ret == 0x10) - return -ENODEV; + return -ENXIO; em28xx_warn("unknown i2c error (status=%i)\n", ret); - return -EIO; + return -ETIMEDOUT; } /* @@ -318,7 +317,7 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, if (!ret) return len; else if (ret > 0) - return -ENODEV; + return -ENXIO; return ret; /* @@ -356,7 +355,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. */ @@ -369,7 +368,7 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, if (!ret) return len; else if (ret > 0) - return -ENODEV; + return -ENXIO; return ret; /* @@ -410,7 +409,7 @@ 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 (rc == -ENXIO) { if (i2c_debug) printk(" no device\n"); } @@ -498,11 +497,15 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, (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 == -ENXIO) { rt_mutex_unlock(&dev->i2c_bus_lock); - return rc; + return -ENODEV; } } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ -- cgit v0.10.2 From 50f0a9df275b81096fbc0878c6deb5d99311549e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 28 Dec 2013 08:23:30 -0300 Subject: [media] em28xx-i2c: cleanup I2C debug messages The I2C output messages is too polluted. Clean it a little bit, by: - use the proper core support for memory dumps; - hide most stuff under the i2c_debug umbrella; - add the missing KERN_CONT where needed; - use 2 levels or verbosity. Only the second one will show the I2C transfer data. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 76f9566..e8eb831 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -41,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() @@ -89,7 +89,8 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } msleep(5); } - em28xx_warn("write to i2c device at 0x%x timed out\n", addr); + if (i2c_debug) + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); return -ETIMEDOUT; } @@ -132,8 +133,11 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) } 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); @@ -213,7 +217,9 @@ 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 (status=%i)\n", addr, ret); + if (i2c_debug) + em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n", + addr, ret); return -ETIMEDOUT; } @@ -409,10 +415,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 == -ENXIO) { - if (i2c_debug) - printk(" no device\n"); - } return rc; } @@ -421,7 +423,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 +431,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 +439,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,7 +485,7 @@ 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", @@ -503,25 +497,41 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, * This code is only called during device probe. */ rc = i2c_check_for_device(i2c_bus, addr); - if (rc == -ENXIO) { + 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 -ENODEV; + 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); @@ -604,7 +614,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; @@ -635,20 +645,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) { -- cgit v0.10.2 From d845fb3ae5b97bda8810f450030c061b49a0b91b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Jan 2014 09:17:53 -0300 Subject: [media] em28xx-i2c: add timeout debug information if i2c_debug enabled If i2c_debug is enabled, we splicitly want to know when a device fails with timeout. If i2c_debug==2, this is already provided, for each I2C transfer that fails. However, most of the time, we don't need to go that far. We just want to know that I2C transfers fail. So, add such errors for normal (ret == 0x10) I2C aborted timeouts. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index e8eb831..7e17240 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -80,6 +80,9 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (ret == 0x80 + len - 1) return len; if (ret == 0x94 + len - 1) { + if (i2c_debug == 1) + em28xx_warn("R05 returned 0x%02x: I2C timeout", + ret); return -ENXIO; } if (ret < 0) { @@ -124,6 +127,9 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (ret == 0x84 + len - 1) break; if (ret == 0x94 + len - 1) { + if (i2c_debug == 1) + em28xx_warn("R05 returned 0x%02x: I2C timeout", + ret); return -ENXIO; } if (ret < 0) { @@ -203,6 +209,9 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, if (ret == 0) /* success */ return len; if (ret == 0x10) { + if (i2c_debug == 1) + em28xx_warn("I2C transfer timeout on writing to addr 0x%02x", + addr); return -ENXIO; } if (ret < 0) { @@ -263,8 +272,12 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) ret); return ret; } - if (ret == 0x10) + if (ret == 0x10) { + if (i2c_debug == 1) + em28xx_warn("I2C transfer timeout on writing to addr 0x%02x", + addr); return -ENXIO; + } em28xx_warn("unknown i2c error (status=%i)\n", ret); return -ETIMEDOUT; @@ -322,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) + else if (ret > 0) { + if (i2c_debug == 1) + em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout", + ret); return -ENXIO; + } return ret; /* @@ -373,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) + else if (ret > 0) { + if (i2c_debug == 1) + em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout", + ret); return -ENXIO; + } return ret; /* -- cgit v0.10.2 From d2849fa59d79dea7a6deeef13cc3efaafab3bf63 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Jan 2014 05:43:09 -0300 Subject: [media] em28xx-audio: use bInterval on em28xx-audio Just filling urb->interval with 1 is wrong, and causes a different behaviour with xHCI. With EHCI, the URB size is typically 192 bytes. However, as xHCI specifies intervals in microframes, the URB size becomes too short (24 bytes). With this patch, the interval will be properly initialized, and the device will behave the same if connected into a xHCI or an EHCI device port. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 30ee389..8e6f048 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -620,10 +620,13 @@ static int em28xx_audio_init(struct em28xx *dev) struct em28xx_audio *adev = &dev->adev; struct snd_pcm *pcm; struct snd_card *card; + struct usb_interface *intf; + struct usb_endpoint_descriptor *e, *ep = NULL; static int devnr; int err, i; const int sb_size = EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; + u8 alt; if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { /* This device does not support the extension (in this case @@ -679,6 +682,34 @@ static int em28xx_audio_init(struct em28xx *dev) em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); } + if (dev->audio_ifnum) + alt = 1; + else + alt = 7; + + intf = usb_ifnum_to_if(dev->udev, dev->audio_ifnum); + + if (intf->num_altsetting <= alt) { + em28xx_errdev("alt %d doesn't exist on interface %d\n", + dev->audio_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; + } + /* Alloc URB and transfer buffers */ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { struct urb *urb; @@ -707,11 +738,17 @@ static int em28xx_audio_init(struct em28xx *dev) urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->transfer_buffer = dev->adev.transfer_buffer[i]; - urb->interval = 1; + urb->interval = 1 << (ep->bInterval - 1); urb->complete = em28xx_audio_isocirq; urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; urb->transfer_buffer_length = sb_size; + if (!i) + dprintk("Will use ep 0x%02x on intf %d alt %d interval = %d (rcv isoc pipe: 0x%08x)\n", + EM28XX_EP_AUDIO, dev->audio_ifnum, alt, + urb->interval, + urb->pipe); + for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { urb->iso_frame_desc[j].offset = k; -- cgit v0.10.2 From 439c491c4c3e1d4f2be0a17d5789e0a19d85622c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Jan 2014 06:59:09 -0300 Subject: [media] em28xx-audio: Fix error path De-allocate memory and free sound if an error happens. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 8e6f048..4ee3488 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -692,6 +692,7 @@ static int em28xx_audio_init(struct em28xx *dev) if (intf->num_altsetting <= alt) { em28xx_errdev("alt %d doesn't exist on interface %d\n", dev->audio_ifnum, alt); + snd_card_free(card); return -ENODEV; } @@ -707,6 +708,7 @@ static int em28xx_audio_init(struct em28xx *dev) if (!ep) { em28xx_errdev("Couldn't find an audio endpoint"); + snd_card_free(card); return -ENODEV; } @@ -759,6 +761,7 @@ static int em28xx_audio_init(struct em28xx *dev) err = snd_card_register(card); if (err < 0) { + em28xx_audio_free_urb(dev); snd_card_free(card); return err; } -- cgit v0.10.2 From 1b3fd2d342667005855deae74200195695433259 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Jan 2014 05:53:24 -0300 Subject: [media] em28xx-audio: don't hardcode audio URB calculus The current code hardcodes the number of audio URBs, the number of packets per URB and the maximum URB size. This is not a good idea, as it: - wastes more bandwidth than necessary, by using a very large number of packets; - those constants are bound to an specific scenario, with a bandwidth of 48 kHz; - don't take the maximum endpoint size into account; - with urb->interval = 1 on xHCI, those constraints cause a "funny" setup: URBs with 64 packets inside, with only 24 bytes total. E. g. a complete waste of space. Change the code to do dynamic URB audio calculus and allocation. For now, use the same constraints as used before this patch, to avoid regressions. A good scenario (tested) seems to use those defines, instead: #define EM28XX_MAX_AUDIO_BUFS 8 #define EM28XX_MIN_AUDIO_PACKETS 2 But let's not do such change here, letting the optimization to happen on latter patches, after more tests. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 4ee3488..6c7d346 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -50,6 +50,8 @@ 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,7 +65,7 @@ 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()) @@ -168,7 +170,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) dprintk("Starting isoc transfers\n"); /* Start streaming */ - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + for (i = 0; i < dev->adev.num_urb; i++) { memset(dev->adev.transfer_buffer[i], 0x80, dev->adev.urb[i]->transfer_buffer_length); @@ -598,21 +600,35 @@ static void em28xx_audio_free_urb(struct em28xx *dev) { int i; - 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 (!dev->adev.urb[i]) + if (!urb) continue; - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->adev.transfer_buffer[i], - urb->transfer_dma); + if (dev->adev.transfer_buffer[i]) + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->adev.transfer_buffer[i], + urb->transfer_dma); usb_free_urb(urb); - dev->adev.urb[i] = NULL; - dev->adev.transfer_buffer[i] = NULL; } + 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_init(struct em28xx *dev) @@ -623,9 +639,8 @@ static int em28xx_audio_init(struct em28xx *dev) struct usb_interface *intf; struct usb_endpoint_descriptor *e, *ep = NULL; static int devnr; - int err, i; - const int sb_size = EM28XX_NUM_AUDIO_PACKETS * - EM28XX_AUDIO_MAX_PACKET_SIZE; + int err, i, ep_size, interval, num_urb, npackets; + int urb_size, bytes_per_transfer; u8 alt; if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { @@ -648,6 +663,9 @@ 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); @@ -712,25 +730,92 @@ static int em28xx_audio_init(struct em28xx *dev) return -ENODEV; } - /* Alloc URB and transfer buffers */ - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + 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->audio_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); + + /* 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) { + snd_card_free(card); + return -ENOMEM; + } + + dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_ATOMIC); + if (!dev->adev.urb) { + snd_card_free(card); + 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(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + urb = usb_alloc_urb(npackets, GFP_ATOMIC); if (!urb) { em28xx_errdev("usb_alloc_urb failed!\n"); em28xx_audio_free_urb(dev); + snd_card_free(card); return -ENOMEM; } dev->adev.urb[i] = urb; - buf = usb_alloc_coherent(dev->udev, sb_size, GFP_ATOMIC, + 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); + snd_card_free(card); return -ENOMEM; } dev->adev.transfer_buffer[i] = buf; @@ -739,23 +824,15 @@ static int em28xx_audio_init(struct em28xx *dev) 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 = dev->adev.transfer_buffer[i]; - urb->interval = 1 << (ep->bInterval - 1); + urb->transfer_buffer = buf; + urb->interval = interval; urb->complete = em28xx_audio_isocirq; - urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - - if (!i) - dprintk("Will use ep 0x%02x on intf %d alt %d interval = %d (rcv isoc pipe: 0x%08x)\n", - EM28XX_EP_AUDIO, dev->audio_ifnum, alt, - urb->interval, - urb->pipe); + urb->number_of_packets = npackets; + urb->transfer_buffer_length = ep_size * npackets; - for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; - j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { + for (j = k = 0; j < npackets; j++, k += ep_size) { urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - EM28XX_AUDIO_MAX_PACKET_SIZE; + urb->iso_frame_desc[j].length = ep_size; } } @@ -765,8 +842,6 @@ static int em28xx_audio_init(struct em28xx *dev) snd_card_free(card); return err; } - adev->sndcard = card; - adev->udev = dev->udev; em28xx_info("Audio extension successfully initialized\n"); return 0; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index efdf386..e76f993 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -481,9 +481,6 @@ 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 */ @@ -498,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; -- cgit v0.10.2 From 49677aef90de7834e7bb4b0adf95c3342c2c8668 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Jan 2014 13:29:16 -0300 Subject: [media] em28xx-audio: fix the period size in bytes If the period size is wrong, userspace will assume a wrong delay any may negociate an inadequate value. The em28xx devices use 8 for URB interval, in microframes, and the driver programs it to have 64 packets. That means that the IRQ sampling period is 125 * 8 * 64, with is equal to 64 ms. So, that's the minimal latency with the current settings. It is possible to program a lower latency, by using less than 64 packets, but that increases the amount of bandwitdh used, and the number of IRQ events per second. In any case, in order to support it, the driver logic should be changed to fill those parameters in realtime. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 6c7d346..f6fcee3 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -52,6 +52,7 @@ 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, \ @@ -217,15 +218,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, */ }; -- cgit v0.10.2 From 34906633faa69d523c032f37036b0bda6232268d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 09:22:29 -0300 Subject: [media] em28xx-audio: don't wait for lock in non-block mode Pulseaudio has the bad habit of stopping a streaming audio if a device, opened in non-block mode, waits. It is impossible to avoid em28xx to wait, as it will send commands via I2C, and other I2C operations may be happening (firmware transfers, Remote Controller polling, etc). Yet, as each em28xx subdriver locks em28xx-dev to protect the access to the hardware, it is possible to minimize the audio glitches by returning -EAGAIN to pulseaudio, if the lock is already taken by another subdriver. Reported-by: Antti Palosaari Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index f6fcee3..f004680 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -258,6 +258,13 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) runtime->hw = snd_em28xx_hw_capture; if ((dev->alt == 0 || dev->audio_ifnum) && 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->audio_ifnum) dev->alt = 1; else @@ -269,7 +276,6 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) /* Sets volume, mute, etc */ dev->mute = 0; - mutex_lock(&dev->lock); ret = em28xx_audio_analog_set(dev); if (ret < 0) goto err; @@ -441,11 +447,19 @@ 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 (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; @@ -470,9 +484,17 @@ 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 (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) @@ -494,9 +516,17 @@ 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 (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; @@ -524,9 +554,17 @@ 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 (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) -- cgit v0.10.2 From 966f4163751b456f526a0b0dc8e6a9814df6abd9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 10:10:34 -0300 Subject: [media] em28xx-audio: split URB initialization code The URB calculus code may eventually be moved to some other place, like at pcm open, if it ends by needing more setups, like working with different bit rates, or different audio latency. So, move it into a separate routine. That also makes the code more readable. No functional changes. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index f004680..13ba631 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -681,75 +681,14 @@ static int em28xx_audio_ep_packet_size(struct usb_device *udev, return size & 0x7ff; } -static int em28xx_audio_init(struct em28xx *dev) +static int em28xx_audio_urb_init(struct em28xx *dev) { - struct em28xx_audio *adev = &dev->adev; - struct snd_pcm *pcm; - struct snd_card *card; struct usb_interface *intf; struct usb_endpoint_descriptor *e, *ep = NULL; - static int devnr; - int err, i, ep_size, interval, num_urb, npackets; + int i, ep_size, interval, num_urb, npackets; int urb_size, bytes_per_transfer; u8 alt; - if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { - /* 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; - } - - 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-2014 Mauro Carvalho Chehab\n"); - - err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, - &card); - if (err < 0) - 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; - } - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); - pcm->info_flags = 0; - pcm->private_data = dev; - strcpy(pcm->name, "Empia 28xx Capture"); - - snd_card_set_dev(card, &dev->udev->dev); - strcpy(card->driver, "Em28xx-Audio"); - strcpy(card->shortname, "Em28xx Audio"); - strcpy(card->longname, "Empia Em28xx Audio"); - - INIT_WORK(&dev->wq_trigger, audio_trigger); - - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); - em28xx_cvol_new(card, dev, "Line In", AC97_LINE); - em28xx_cvol_new(card, dev, "Phone", AC97_PHONE); - em28xx_cvol_new(card, dev, "Microphone", AC97_MIC); - em28xx_cvol_new(card, dev, "CD", AC97_CD); - em28xx_cvol_new(card, dev, "AUX", AC97_AUX); - em28xx_cvol_new(card, dev, "PCM", AC97_PCM); - - em28xx_cvol_new(card, dev, "Master", AC97_MASTER); - em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE); - em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO); - em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER); - em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); - } - if (dev->audio_ifnum) alt = 1; else @@ -760,7 +699,6 @@ static int em28xx_audio_init(struct em28xx *dev) if (intf->num_altsetting <= alt) { em28xx_errdev("alt %d doesn't exist on interface %d\n", dev->audio_ifnum, alt); - snd_card_free(card); return -ENODEV; } @@ -776,7 +714,6 @@ static int em28xx_audio_init(struct em28xx *dev) if (!ep) { em28xx_errdev("Couldn't find an audio endpoint"); - snd_card_free(card); return -ENODEV; } @@ -833,13 +770,11 @@ static int em28xx_audio_init(struct em28xx *dev) sizeof(*dev->adev.transfer_buffer), GFP_ATOMIC); if (!dev->adev.transfer_buffer) { - snd_card_free(card); return -ENOMEM; } dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_ATOMIC); if (!dev->adev.urb) { - snd_card_free(card); kfree(dev->adev.transfer_buffer); return -ENOMEM; } @@ -855,7 +790,6 @@ static int em28xx_audio_init(struct em28xx *dev) if (!urb) { em28xx_errdev("usb_alloc_urb failed!\n"); em28xx_audio_free_urb(dev); - snd_card_free(card); return -ENOMEM; } dev->adev.urb[i] = urb; @@ -865,7 +799,6 @@ static int em28xx_audio_init(struct em28xx *dev) if (!buf) { em28xx_errdev("usb_alloc_coherent failed!\n"); em28xx_audio_free_urb(dev); - snd_card_free(card); return -ENOMEM; } dev->adev.transfer_buffer[i] = buf; @@ -886,6 +819,80 @@ static int em28xx_audio_init(struct em28xx *dev) } } + 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; + + if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { + /* 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; + } + + 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-2014 Mauro Carvalho Chehab\n"); + + err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, + &card); + if (err < 0) + 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; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Empia 28xx Capture"); + + snd_card_set_dev(card, &dev->udev->dev); + strcpy(card->driver, "Em28xx-Audio"); + strcpy(card->shortname, "Em28xx Audio"); + strcpy(card->longname, "Empia Em28xx Audio"); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); + em28xx_cvol_new(card, dev, "Line In", AC97_LINE); + em28xx_cvol_new(card, dev, "Phone", AC97_PHONE); + em28xx_cvol_new(card, dev, "Microphone", AC97_MIC); + em28xx_cvol_new(card, dev, "CD", AC97_CD); + em28xx_cvol_new(card, dev, "AUX", AC97_AUX); + em28xx_cvol_new(card, dev, "PCM", AC97_PCM); + + em28xx_cvol_new(card, dev, "Master", AC97_MASTER); + em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE); + em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO); + em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER); + em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); + } + + err = em28xx_audio_urb_init(dev); + if (err) { + snd_card_free(card); + return -ENODEV; + } + err = snd_card_register(card); if (err < 0) { em28xx_audio_free_urb(dev); -- cgit v0.10.2 From 8b1fa5798dec77d9d2cd81751772a580474aa17f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 10:21:42 -0300 Subject: [media] em28xx-audio: return -ENODEV when the device is disconnected If em28xx is disconnected, return -ENODEV to all PCM callbacks. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 13ba631..f3e3200 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -90,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 */ @@ -248,14 +254,17 @@ 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) { int nonblock = !!(substream->f_flags & O_NONBLOCK); @@ -324,6 +333,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"); @@ -363,6 +376,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; @@ -388,6 +404,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 */ @@ -414,6 +433,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 -ENODEV; + spin_lock_irqsave(&dev->adev.slock, flags); hwptr_done = dev->adev.hwptr_done_capture; spin_unlock_irqrestore(&dev->adev.slock, flags); @@ -435,6 +457,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; @@ -453,6 +480,9 @@ static int em28xx_vol_put(struct snd_kcontrol *kcontrol, int nonblock = 0; int rc; + if (dev->disconnected) + return -ENODEV; + if (substream) nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { @@ -488,6 +518,9 @@ static int em28xx_vol_get(struct snd_kcontrol *kcontrol, int nonblock = 0; int val; + if (dev->disconnected) + return -ENODEV; + if (substream) nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { @@ -520,6 +553,9 @@ static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol, int nonblock = 0; int rc; + if (dev->disconnected) + return -ENODEV; + if (substream) nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { @@ -558,6 +594,9 @@ static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol, int nonblock = 0; int val; + if (dev->disconnected) + return -ENODEV; + if (substream) nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { -- cgit v0.10.2 From fa1e1de6bb679f2c86da3311bbafee7eaf78f125 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 05:59:30 -0200 Subject: [media] nxt200x: increase write buffer size The buffer size on nxt200x is not enough: ... > Dec 20 10:52:04 rich kernel: [ 31.747949] nxt200x: nxt200x_writebytes: i2c wr reg=002c: len=255 is too big! ... Increase it to 256 bytes. Reported-by: Rich Freeman Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index fbca985..4bf0575 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -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" -- cgit v0.10.2 From ee97207c6e7e9a75f49e2abb7ecf944d319ed969 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 11:08:22 -0300 Subject: [media] em28xx: fix xc3028 demod and firmware setup on DVB Now that em28xx can be compiled without V4L support, we should call em28xx_setup_xc3028() on both em28xx-v4l and em28xx-dvb modules. Reported-by: Chris Lee Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 39cf49c..6efb902 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2768,6 +2768,55 @@ static void em28xx_card_setup(struct em28xx *dev) dev->tuner_type = tuner; } +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; + } +} +EXPORT_SYMBOL_GPL(em28xx_setup_xc3028); + static void request_module_async(struct work_struct *work) { struct em28xx *dev = container_of(work, diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 5c6be66..7dba175 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -828,11 +828,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"); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 9c44628..a1dcceb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2100,54 +2100,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -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; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e76f993..5d5d1b6 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -762,6 +762,7 @@ void em28xx_close_extension(struct em28xx *dev); 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-camera.c */ -- cgit v0.10.2 From 4773ab99aa8bda57de22bf54ddbaa1a941b25fb0 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 15 Nov 2013 02:29:22 -0300 Subject: [media] s5p-mfc: Add QP setting support for vp8 encoder Adds v4l2 controls to set MIN, MAX QP values and I, P frame QP for vp8 encoder. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 7a3b49b..e4db4ac 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3161,6 +3161,38 @@ V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. + + + V4L2_CID_MPEG_VIDEO_VPX_MIN_QP + integer + + Minimum quantization parameter for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_MAX_QP + integer + + Maximum quantization parameter for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP  + integer + + Quantization parameter for an I frame for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP  + integer + + Quantization parameter for a P frame for VP8. + + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 6920b54..d91f757 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -422,6 +422,10 @@ 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; }; /** diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index f0b41f8..cdf672c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -618,6 +618,38 @@ 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, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -1557,6 +1589,18 @@ 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; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); 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 461358c..b4886d6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1218,6 +1218,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/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fb46790..20840df 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -745,6 +745,10 @@ 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"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 8e4d1d9..28cddb4 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -558,6 +558,10 @@ enum v4l2_vp8_golden_frame_sel { V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, }; +#define V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (V4L2_CID_MPEG_BASE+507) +#define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) +#define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) +#define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) -- cgit v0.10.2 From b80cb8dc4162bc954cc71efec192ed89f2061573 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 3 Dec 2013 10:12:51 -0300 Subject: [media] media: s5p_mfc: remove s5p_mfc_get_node_type() function s5p_mfc_get_node_type() relies on get_index() helper function, which in turn relies on video_device index numbers assigned on driver registration. All this code is not really needed, because there is already access to respective video_device structures via common s5p_mfc_dev structure. This fixes the issues introduced by patch 1056e4388b0454917a512618c8416a98628fc9ce ("v4l2-dev: Fix race condition on __video_register_device"), which has been merged in v3.12-rc1. Signed-off-by: Marek Szyprowski Cc: stable@vger.kernel.org Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e46067a..e2aac59 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 d91f757..3874f8b 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 { -- cgit v0.10.2 From bbd8f3fef9d289fcfddaefccc2e5a2355da5d2f4 Mon Sep 17 00:00:00 2001 From: Kiran AVND Date: Mon, 16 Dec 2013 06:40:42 -0300 Subject: [media] s5p-mfc: Add controls to set vp8 enc profile Add v4l2 controls to set desired profile for VP8 encoder. Acceptable levels for VP8 encoder are 0: Version 0 1: Version 1 2: Version 2 3: Version 3 Signed-off-by: Kiran AVND Signed-off-by: Pawel Osciak Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index e4db4ac..a5a3188 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3193,6 +3193,15 @@ V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. Quantization parameter for a P frame for VP8. + + + V4L2_CID_MPEG_VIDEO_VPX_PROFILE  + integer + + Select the desired profile for VPx encoder. +Acceptable values are 0, 1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3. + + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 3874f8b..f723f1f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -417,6 +417,7 @@ struct s5p_mfc_vp8_enc_params { 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 cdf672c..91b6e02 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -650,6 +650,14 @@ static struct mfc_control controls[] = { .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) @@ -1601,6 +1609,9 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) 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); 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 b4886d6..f6ff2db 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. */ diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 20840df..6ff002b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -749,6 +749,7 @@ const char *v4l2_ctrl_get_name(u32 id) 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/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 28cddb4..2cbe605 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -562,6 +562,7 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) #define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) +#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) -- cgit v0.10.2 From 4609981f84a23e8d481502f4728e0fed910abe03 Mon Sep 17 00:00:00 2001 From: Tim Mester Date: Tue, 7 Jan 2014 01:29:24 -0300 Subject: [media] au8028: Fix cleanup on kzalloc fail Free what was allocated if there is a failure allocating transfer buffers. Stop the feed on a start feed error. The stop feed is not always called if start feed fails. If the feed is not stopped on error, then the driver will be stuck so that it can never start feeding again. [m.chehab@samsung.com: CodingStyle cleanup] Signed-off-by: Tim Mester Acked-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 9a6f156..3c718db 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -153,9 +153,11 @@ 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]); + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); + } } return 0; @@ -185,6 +187,9 @@ static int start_urb_transfer(struct au0828_dev *dev) if (!purb->transfer_buffer) { usb_free_urb(purb); dev->urbs[i] = NULL; + printk(KERN_ERR + "%s: failed big buffer allocation, err = %d\n", + __func__, ret); goto err; } @@ -217,6 +222,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 +257,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 +286,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 +318,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); @@ -375,6 +405,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: diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index ef1f57f..a00b400 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 { -- cgit v0.10.2 From f251b3e78cc57411627d825eae3c911da77b4035 Mon Sep 17 00:00:00 2001 From: Tim Mester Date: Tue, 7 Jan 2014 01:29:25 -0300 Subject: [media] au0828: Add option to preallocate digital transfer buffers Added command line parameter preallocate_big_buffers so that the digital transfer buffers can be allocated when the driver is registered. They do not have to be allocated every time a feed is started. Signed-off-by: Tim Mester Acked-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index bd9d19a..ab45a6f 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 3c718db..19fe049 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" +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 @@ -155,7 +159,9 @@ static int stop_urb_transfer(struct au0828_dev *dev) for (i = 0; i < URB_COUNT; i++) { if (dev->urbs[i]) { usb_kill_urb(dev->urbs[i]); - kfree(dev->urbs[i]->transfer_buffer); + if (!preallocate_big_buffers) + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); } } @@ -183,7 +189,12 @@ 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; @@ -334,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 */ @@ -424,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; } @@ -444,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 a00b400..5439772 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -262,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]; }; /* ----------------------------------------------------------- */ -- cgit v0.10.2 From 429df502d480aae10104e42181b8066a0e23bd3d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 13 Jan 2014 06:58:12 -0300 Subject: [media] solo6x10: fix broken PAL support The video_type was never set correctly for PAL: it's not a bool, instead it is a register value. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Reported-by: tomdev@freenet.de Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index d582c5b..ce9e5aa 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -964,7 +964,7 @@ static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) { struct solo_enc_dev *solo_enc = video_drvdata(file); - return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_PAL); + return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_625_50); } static int solo_enum_framesizes(struct file *file, void *priv, diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c index 7b26de3..47e72da 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -527,7 +527,7 @@ static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) return 0; } -int solo_set_video_type(struct solo_dev *solo_dev, bool type) +int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz) { int i; @@ -537,7 +537,8 @@ int solo_set_video_type(struct solo_dev *solo_dev, bool type) for (i = 0; i < solo_dev->nr_chans; i++) if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) return -EBUSY; - solo_dev->video_type = type; + solo_dev->video_type = is_50hz ? SOLO_VO_FMT_TYPE_PAL : + SOLO_VO_FMT_TYPE_NTSC; /* Reconfigure for the new standard */ solo_disp_init(solo_dev); solo_enc_init(solo_dev); @@ -551,7 +552,7 @@ static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) { struct solo_dev *solo_dev = video_drvdata(file); - return solo_set_video_type(solo_dev, std & V4L2_STD_PAL); + return solo_set_video_type(solo_dev, std & V4L2_STD_625_50); } static int solo_s_ctrl(struct v4l2_ctrl *ctrl) diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h index f1bbb8c..8964f8b 100644 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -398,7 +398,7 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev, int desc_cnt); /* Global s_std ioctl */ -int solo_set_video_type(struct solo_dev *solo_dev, bool type); +int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz); void solo_update_mode(struct solo_enc_dev *solo_enc); /* Set the threshold for motion detection */ -- cgit v0.10.2 From a622cc51879f70344c03d4e0de59728e9f5c31b9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 11:30:26 -0200 Subject: [media] sh_vou: comment unused vars Fix two warns below, by commenting the unused code: drivers/media/platform/sh_vou.c: In function 'sh_vou_configure_geometry': drivers/media/platform/sh_vou.c:446:49: warning: variable 'height_max' set but not used [-Wunused-but-set-variable] unsigned int black_left, black_top, width_max, height_max, ^ drivers/media/platform/sh_vou.c: In function 'sh_vou_isr': drivers/media/platform/sh_vou.c:1056:13: warning: variable 'side' set but not used [-Wunused-but-set-variable] static int side; ^ Cc: Guennadi Liakhovetski Cc: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 65c9f18..e5f1d4c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -443,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; @@ -451,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; @@ -1053,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); @@ -1081,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); -- cgit v0.10.2 From 6fcd3b619c06544b5d33493d0ab297a5b8e64b68 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 11:34:37 -0200 Subject: [media] radio-usb-si4713: make si4713_register_i2c_adapter static This function isn't used nowhere outside the same .c file. Fixes this warning: drivers/media/radio/si4713/radio-usb-si4713.c:418:5: warning: no previous prototype for 'si4713_register_i2c_adapter' [-Wmissing-prototypes] int si4713_register_i2c_adapter(struct si4713_usb_device *radio) ^ Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index d978844..f1e640d 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -415,7 +415,7 @@ static struct i2c_adapter si4713_i2c_adapter_template = { .algo = &si4713_algo, }; -int si4713_register_i2c_adapter(struct si4713_usb_device *radio) +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 */ -- cgit v0.10.2 From 5dc8526b90979c2cacdf32d1ea3b82de7744bd71 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 15:05:44 -0200 Subject: [media] dib8000: Properly represent long long integers When compiling with avr32, it gets those errors: drivers/media/dvb-frontends/dib8000.c: In function 'dib8000_get_stats': drivers/media/dvb-frontends/dib8000.c:4121: warning: integer constant is too large for 'long' type Fix integer representation to avoid overflow. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 481ee49..dd4a99c 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4118,7 +4118,7 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) /* Get UCB measures */ dib8000_read_unc_blocks(fe, &val); if (val < state->init_ucb) - state->init_ucb += 0x100000000L; + state->init_ucb += 0x100000000LL; c->block_error.stat[0].scale = FE_SCALE_COUNTER; c->block_error.stat[0].uvalue = val + state->init_ucb; @@ -4128,7 +4128,7 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) time_us = dib8000_get_time_us(fe, -1); if (time_us) { - blocks = 1250000UL * 1000000UL; + 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; @@ -4191,7 +4191,7 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) if (!time_us) time_us = dib8000_get_time_us(fe, i); if (time_us) { - blocks = 1250000UL * 1000000UL; + 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; -- cgit v0.10.2 From e4a3bc1c2c1aebb09ef12f0eb52cf5e6a1d5b9d3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 15:14:33 -0200 Subject: [media] dib8000: Fix a few warnings when compiled for avr32 drivers/media/dvb-frontends/dib8000.c: In function 'dib8000_get_time_us': drivers/media/dvb-frontends/dib8000.c:3957: warning: 'interleaving' may be used uninitialized in this function drivers/media/dvb-frontends/dib8000.c:3956: warning: 'rate_denum' may be used uninitialized in this function Those are actually false positives, but it doesn't hurt cleaning them. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index dd4a99c..1632d78 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3953,8 +3953,8 @@ static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer) int ini_layer, end_layer, i; u64 time_us, tmp64; u32 tmp, denom; - int guard, rate_num, rate_denum, bits_per_symbol, nsegs; - int interleaving, fft_div; + int guard, rate_num, rate_denum = 1, bits_per_symbol, nsegs; + int interleaving = 0, fft_div; if (layer >= 0) { ini_layer = layer; -- cgit v0.10.2 From c61c3094a630b2f795addc3a74fbd5ff7f704ca9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 15:30:24 -0200 Subject: [media] go7007-usb: only use go->dev after allocated Fixes those warnings: drivers/staging/media/go7007/go7007-usb.c: In function 'go7007_usb_probe': drivers/staging/media/go7007/go7007-usb.c:1060: warning: 'go' is used uninitialized in this function While here, comment a code that will never run. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 58684da..1d747de 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf, char *name; int video_pipe, i, v_urb_len; - dev_dbg(go->dev, "probing new GO7007 USB board\n"); + printk(KERN_DEBUG "go7007-usb: probing new board\n"); switch (id->driver_info) { case GO7007_BOARDID_MATRIX_II: @@ -1097,13 +1097,16 @@ static int go7007_usb_probe(struct usb_interface *intf, board = &board_px_tv402u; break; case GO7007_BOARDID_LIFEVIEW_LR192: - dev_err(go->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); + printk(KERN_ERR + "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); return -ENODEV; +#if 0 name = "Lifeview TV Walker Ultra"; board = &board_lifeview_lr192; break; +#endif case GO7007_BOARDID_SENSORAY_2250: - dev_info(go->dev, "Sensoray 2250 found\n"); + printk(KERN_INFO "Sensoray 2250 found\n"); name = "Sensoray 2250/2251"; board = &board_sensoray_2250; break; @@ -1112,8 +1115,9 @@ static int go7007_usb_probe(struct usb_interface *intf, board = &board_ads_usbav_709; break; default: - dev_err(go->dev, "unknown board ID %d!\n", - (unsigned int)id->driver_info); + printk(KERN_ERR + "unknown board ID %d!\n", + (unsigned int)id->driver_info); return -ENODEV; } -- cgit v0.10.2 From 986058e1ef342363a2d77233bbc07bb0a0264f6b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 15:39:14 -0200 Subject: [media] lirc_parallel: avoid name conflict on mn10300 arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "irq_handler" name is already defined there on a header file: /devel/v4l/temp/drivers/staging/media/lirc/lirc_parallel.c:223:13: error: conflicting types for ‘irq_handler’ static void irq_handler(void *blah) ^ In file included from /devel/v4l/temp/arch/mn10300/include/asm/reset-regs.h:16:0, from /devel/v4l/temp/arch/mn10300/include/asm/irq.h:18, from /devel/v4l/temp/include/linux/irq.h:24, from /devel/v4l/temp/arch/mn10300/include/asm/hardirq.h:16, from /devel/v4l/temp/include/linux/preempt_mask.h:5, from /devel/v4l/temp/include/linux/sched.h:25, from /devel/v4l/temp/include/linux/utsname.h:5, from /devel/v4l/temp/arch/mn10300/include/asm/elf.h:15, from /devel/v4l/temp/include/linux/elf.h:4, from /devel/v4l/temp/include/linux/module.h:14, from /devel/v4l/temp/drivers/staging/media/lirc/lirc_parallel.c:29: /devel/v4l/temp/arch/mn10300/include/asm/exceptions.h:107:24: note: previous declaration of ‘irq_handler’ was here extern asmlinkage void irq_handler(void); /devel/v4l/patchwork/drivers/staging/media/lirc/lirc_serial.c:653:20: error: conflicting types for ‘irq_handler’ static irqreturn_t irq_handler(int i, void *blah) ^ In file included from /devel/v4l/patchwork/arch/mn10300/include/asm/reset-regs.h:16:0, from /devel/v4l/patchwork/arch/mn10300/include/asm/irq.h:18, from /devel/v4l/patchwork/include/linux/irq.h:24, from /devel/v4l/patchwork/arch/mn10300/include/asm/hardirq.h:16, from /devel/v4l/patchwork/include/linux/preempt_mask.h:5, from /devel/v4l/patchwork/include/linux/sched.h:25, from /devel/v4l/patchwork/include/linux/utsname.h:5, from /devel/v4l/patchwork/arch/mn10300/include/asm/elf.h:15, from /devel/v4l/patchwork/include/linux/elf.h:4, from /devel/v4l/patchwork/include/linux/module.h:14, from /devel/v4l/patchwork/drivers/staging/media/lirc/lirc_serial.c:53: /devel/v4l/patchwork/arch/mn10300/include/asm/exceptions.h:107:24: note: previous declaration of ‘irq_handler’ was here extern asmlinkage void irq_handler(void); So, rename it, to avoid namespace conflicts. This patch fixes building media drivers with allyesconfig/almodconfig on mn10300 arch. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index 41d110f..0b58989 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -220,7 +220,7 @@ static void rbuf_write(int signal) wptr = nwptr; } -static void irq_handler(void *blah) +static void lirc_lirc_irq_handler(void *blah) { struct timeval tv; static struct timeval lasttv; @@ -659,7 +659,7 @@ static int __init lirc_parallel_init(void) goto exit_device_put; } ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, - pf, kf, irq_handler, 0, NULL); + pf, kf, lirc_lirc_irq_handler, 0, NULL); parport_put_port(pport); if (ppdevice == NULL) { pr_notice("parport_register_device() failed\n"); diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index 2e3a985..0be1f46 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -650,7 +650,7 @@ static void frbwrite(int l) rbwrite(l); } -static irqreturn_t irq_handler(int i, void *blah) +static irqreturn_t lirc_irq_handler(int i, void *blah) { struct timeval tv; int counter, dcd; @@ -852,7 +852,7 @@ static int lirc_serial_probe(struct platform_device *dev) return result; #endif - result = request_irq(irq, irq_handler, + result = request_irq(irq, lirc_irq_handler, (share_irq ? IRQF_SHARED : 0), LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { -- cgit v0.10.2 From eab924d0e2bdfd53c902162b0b499b8464c1fb4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 15:54:59 -0200 Subject: [media] tea575x: Fix build with ARCH=c6x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In file included from /devel/v4l/temp/include/asm-generic/page.h:23:0, from /devel/v4l/temp/arch/c6x/include/asm/page.h:9, from /devel/v4l/temp/include/asm-generic/io.h:14, from arch/c6x/include/generated/asm/io.h:1, from /devel/v4l/temp/drivers/media/radio/tea575x.c:23: /devel/v4l/temp/arch/c6x/include/asm/setup.h:17:27: error: unknown type name ‘phys_addr_t’ extern int c6x_add_memory(phys_addr_t start, unsigned long size); It seems that, on such arch, the includes from asm/ should be after the ones from linux/. The proper fix would be to patch the arch files, but, as this fix is trivial, apply it. Also, we generally put the asm includes after the linux ones, anyway. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c index cef0698..7c14060 100644 --- a/drivers/media/radio/tea575x.c +++ b/drivers/media/radio/tea575x.c @@ -20,12 +20,12 @@ * */ -#include #include #include #include #include #include +#include #include #include #include -- cgit v0.10.2 From 4e1702402e8a81dcd4f3bae14a88327e39fa018c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:18 -0300 Subject: [media] em28xx-v4l: fix device initialization in em28xx_v4l2_open() for radio and VBI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bail out on unsupported VFL_TYPE - em28xx_set_mode() needs to be called for VBI and radio mode, too - em28xx_wake_i2c() needs to be called for VBI and radio mode, too - em28xx_resolution_set() also needs to be called for VBI Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a1dcceb..efbc877 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1826,6 +1826,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", @@ -1846,15 +1850,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) { -- cgit v0.10.2 From 27ba0dac2d31d91a017d96cbe95e4a98d3670d2d Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:19 -0300 Subject: [media] em28xx: move usb buffer pre-allocation and transfer uninit from the core to the dvb extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6efb902..7ae6e50 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3339,26 +3339,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 err_free; - } } request_modules(dev); @@ -3416,7 +3396,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); - em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); } mutex_unlock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 7dba175..5ea563e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1018,6 +1018,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 */ @@ -1454,6 +1475,8 @@ static int em28xx_dvb_fini(struct em28xx *dev) 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. */ -- cgit v0.10.2 From 23e8642cc48171151cc89a33eb84c929dcdf2c19 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:20 -0300 Subject: [media] em28xx: move usb transfer uninit on device disconnect from the core to the v4l-extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 7ae6e50..ecf92d4 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3391,12 +3391,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) v4l2_device_disconnect(&dev->v4l2_dev); - if (dev->users) { + 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); - } mutex_unlock(&dev->lock); em28xx_close_extension(dev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index efbc877..a5a665b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1893,6 +1893,8 @@ static int em28xx_v4l2_fini(struct em28xx *dev) return 0; } + 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); -- cgit v0.10.2 From 5ad10de6ff29172f84fe6cdca74b16731385f1cf Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:21 -0300 Subject: [media] em28xx: move v4l2_device_disconnect() call from the core to the v4l extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ecf92d4..2f520a5 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3387,16 +3387,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) 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)); - - mutex_unlock(&dev->lock); - em28xx_close_extension(dev); /* NOTE: must be called BEFORE the resources are released */ diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a5a665b..f5f716e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1893,6 +1893,8 @@ static int em28xx_v4l2_fini(struct em28xx *dev) return 0; } + v4l2_device_disconnect(&dev->v4l2_dev); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); if (dev->radio_dev) { @@ -1921,6 +1923,9 @@ static int em28xx_v4l2_fini(struct em28xx *dev) dev->vdev = NULL; } + if (dev->users) + em28xx_warn("Device is open ! Deregistration and memory deallocation are deferred on close.\n"); + return 0; } -- cgit v0.10.2 From 25dd1652331644701a90462782ece6d0d3edf7d0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:23 -0300 Subject: [media] em28xx: move v4l2 dummy clock deregistration from the core to the v4l extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 2f520a5..fa35a30 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "em28xx.h" @@ -2880,8 +2879,6 @@ void em28xx_release_resources(struct em28xx *dev) if (dev->def_i2c_bus) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); - if (dev->clk) - v4l2_clk_unregister_fixed(dev->clk); usb_put_dev(dev->udev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f5f716e..32b4385 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -1923,6 +1924,11 @@ static int em28xx_v4l2_fini(struct em28xx *dev) dev->vdev = NULL; } + if (dev->clk) { + v4l2_clk_unregister_fixed(dev->clk); + dev->clk = NULL; + } + if (dev->users) em28xx_warn("Device is open ! Deregistration and memory deallocation are deferred on close.\n"); -- cgit v0.10.2 From f188da4395b85698d1a55e9fb278ea7e629fbe96 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:22 -0300 Subject: [media] em28xx-v4l: move v4l2_ctrl_handler freeing and v4l2_device unregistration to em28xx_v4l2_fini MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v4l2_ctrl_handler_free() and v4l2_device_unregister() are currently only called when the last user closes the device and the device is already disconnected. But that's wrong, we need to call these functions whenever the em28xx-v4l extension is closed and we can already do this if the device is still opened by some users. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 32b4385..d8ca37a 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1929,8 +1929,11 @@ static int em28xx_v4l2_fini(struct em28xx *dev) dev->clk = NULL; } + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); + if (dev->users) - em28xx_warn("Device is open ! Deregistration and memory deallocation are deferred on close.\n"); + em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n"); return 0; } @@ -1957,8 +1960,6 @@ static int em28xx_v4l2_close(struct file *filp) if (dev->disconnected) { em28xx_release_resources(dev); - v4l2_ctrl_handler_free(&dev->ctrl_handler); - v4l2_device_unregister(&dev->v4l2_dev); kfree(dev->alt_max_pkt_size_isoc); goto exit; } -- cgit v0.10.2 From 5a620c7c1cd74064e75a525738e9410b8851fa96 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:24 -0300 Subject: [media] em28xx: always call em28xx_release_resources() in the usb disconnect handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the usb device is disconnected, the resources are no longer available, so there is no reason to keep them registered. This will also fix the various sysfs group removal warnings which we can see since kernel 3.13. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index fa35a30..3b332d5 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2876,6 +2876,8 @@ void em28xx_release_resources(struct em28xx *dev) { /*FIXME: I2C IR should be disconnected */ + mutex_lock(&dev->lock); + if (dev->def_i2c_bus) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); @@ -2884,6 +2886,8 @@ void em28xx_release_resources(struct em28xx *dev) /* Mark device as unused */ clear_bit(dev->devno, &em28xx_devused); + + mutex_unlock(&dev->lock); }; EXPORT_SYMBOL_GPL(em28xx_release_resources); @@ -3386,13 +3390,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_close_extension(dev); - /* NOTE: must be called BEFORE the resources are released */ - - mutex_lock(&dev->lock); - 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-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d8ca37a..cda0dc3 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1959,7 +1959,6 @@ static int em28xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->disconnected) { - em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size_isoc); goto exit; } -- cgit v0.10.2 From e847022a8e1d732da96ddbd8d4170500f4c3f8b1 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 12 Jan 2014 13:24:25 -0300 Subject: [media] em28xx-v4l: fix the freeing of the video devices memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove some dead code from em28xx_v4l2_fini() and fix the leaking of the video, vbi and radio video_device struct memories. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index cda0dc3..587ff3f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1899,29 +1899,19 @@ static int em28xx_v4l2_fini(struct em28xx *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) { @@ -1955,9 +1945,7 @@ 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) { kfree(dev->alt_max_pkt_size_isoc); goto exit; @@ -1985,6 +1973,23 @@ exit: 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, @@ -2039,11 +2044,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 = { @@ -2069,9 +2073,9 @@ 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, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ -- cgit v0.10.2 From 229295f1d6713bc8c8ba900ba62bca03e34bba52 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 12:57:08 -0300 Subject: [media] em28xx-audio: fix return code on device disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alsa has an special non-negative return code to indicate device removal at snd_em28xx_capture_pointer(). Use it, instead of an error code. Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index f3e3200..47766b7 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -434,7 +434,7 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream dev = snd_pcm_substream_chip(substream); if (dev->disconnected) - return -ENODEV; + return SNDRV_PCM_POS_XRUN; spin_lock_irqsave(&dev->adev.slock, flags); hwptr_done = dev->adev.hwptr_done_capture; -- cgit v0.10.2 From 0cd03a0cb20c75a70a154b946dcf4b3461ceff36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 17:20:31 -0300 Subject: [media] em28xx-audio: simplify error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup the error handling code at em28xx-audio init. Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 47766b7..97d9105 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -893,10 +893,8 @@ static int em28xx_audio_init(struct em28xx *dev) 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; @@ -927,20 +925,23 @@ static int em28xx_audio_init(struct em28xx *dev) } err = em28xx_audio_urb_init(dev); - if (err) { - snd_card_free(card); - return -ENODEV; - } + if (err) + goto card_free; err = snd_card_register(card); - if (err < 0) { - em28xx_audio_free_urb(dev); - snd_card_free(card); - return err; - } + 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); + + return err; } static int em28xx_audio_fini(struct em28xx *dev) -- cgit v0.10.2 From 1fe2e3bff4f862d971207c846e4df61160c0e858 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 18:14:05 -0300 Subject: [media] em28xx-audio: disconnect before freeing URBs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit URBs might be in usage. Disconnect the device before freeing them. Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 97d9105..7f762db 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -956,6 +956,7 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } + snd_card_disconnect(dev->adev.sndcard); em28xx_audio_free_urb(dev); if (dev->adev.sndcard) { -- cgit v0.10.2 From aa929ad783c0762f276c8c810f1c7f00b12dca4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 19:22:07 -0300 Subject: [media] em28xx: print a message at disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That helps to identify if something fails and explain why em28xx struct is not freed (if it ever happens). Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 7f762db..26cf431 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -956,6 +956,8 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } + em28xx_info("Closing audio extension"); + snd_card_disconnect(dev->adev.sndcard); em28xx_audio_free_urb(dev); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 5ea563e..f4cdf9e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1472,6 +1472,8 @@ static int em28xx_dvb_fini(struct em28xx *dev) return 0; } + em28xx_info("Closing DVB extension"); + if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 61c061f..18f65d8 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -810,6 +810,8 @@ static int em28xx_ir_fini(struct em28xx *dev) return 0; } + em28xx_info("Closing input extension"); + em28xx_shutdown_buttons(dev); /* skip detach on non attached boards */ diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 587ff3f..1486d47 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1894,6 +1894,8 @@ static int em28xx_v4l2_fini(struct em28xx *dev) return 0; } + em28xx_info("Closing video extension"); + v4l2_device_disconnect(&dev->v4l2_dev); em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); -- cgit v0.10.2 From 6582af4c018afd222596d6244b6d685182035c75 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 12 Jan 2014 22:44:23 -0300 Subject: [media] em28xx: Fix usb diconnect logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that everything is extension, the usb disconnect logic should be the same. While here, fix the device name. Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 3b332d5..e08d65b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3379,12 +3379,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->disconnected = 1; - if (dev->is_audio_only) { - em28xx_close_extension(dev); - return; - } - - em28xx_info("disconnecting %s\n", dev->vdev->name); + em28xx_info("Disconnecting %s\n", dev->name); flush_request_modules(dev); -- cgit v0.10.2 From ebbfbc2006700f0b5701fb3efa44b55a09fba5d1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 02:53:44 -0300 Subject: [media] em28xx: push mutex down to extensions on .fini callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid circular mutex lock by pushing the dev->lock to the .fini callback on each extension. As em28xx-dvb, em28xx-alsa and em28xx-rc have their own data structures, and don't touch at the common structure during .fini, only em28xx-v4l needs to be locked. [ 90.994317] ====================================================== [ 90.994356] [ INFO: possible circular locking dependency detected ] [ 90.994395] 3.13.0-rc1+ #24 Not tainted [ 90.994427] ------------------------------------------------------- [ 90.994458] khubd/54 is trying to acquire lock: [ 90.994490] (&card->controls_rwsem){++++.+}, at: [] snd_ctl_dev_free+0x28/0x60 [snd] [ 90.994656] [ 90.994656] but task is already holding lock: [ 90.994688] (&dev->lock){+.+.+.}, at: [] em28xx_close_extension+0x31/0x90 [em28xx] [ 90.994843] [ 90.994843] which lock already depends on the new lock. [ 90.994843] [ 90.994874] [ 90.994874] the existing dependency chain (in reverse order) is: [ 90.994905] -> #1 (&dev->lock){+.+.+.}: [ 90.995057] [] __lock_acquire+0xb43/0x1330 [ 90.995121] [] lock_acquire+0xa2/0x120 [ 90.995182] [] mutex_lock_nested+0x5c/0x3c0 [ 90.995245] [] em28xx_vol_put_mute+0x1ba/0x1d0 [em28xx_alsa] [ 90.995309] [] snd_ctl_elem_write+0xfd/0x140 [snd] [ 90.995376] [] snd_ctl_ioctl+0xe2/0x810 [snd] [ 90.995442] [] do_vfs_ioctl+0x300/0x520 [ 90.995504] [] SyS_ioctl+0x81/0xa0 [ 90.995568] [] system_call_fastpath+0x16/0x1b [ 90.995630] -> #0 (&card->controls_rwsem){++++.+}: [ 90.995780] [] check_prevs_add+0x947/0x950 [ 90.995841] [] __lock_acquire+0xb43/0x1330 [ 90.995901] [] lock_acquire+0xa2/0x120 [ 90.995962] [] down_write+0x3b/0xa0 [ 90.996022] [] snd_ctl_dev_free+0x28/0x60 [snd] [ 90.996088] [] snd_device_free+0x65/0x140 [snd] [ 90.996154] [] snd_device_free_all+0x61/0xa0 [snd] [ 90.996219] [] snd_card_do_free+0x14/0x130 [snd] [ 90.996283] [] snd_card_free+0x84/0x90 [snd] [ 90.996349] [] em28xx_audio_fini+0x97/0xb0 [em28xx_alsa] [ 90.996411] [] em28xx_close_extension+0x56/0x90 [em28xx] [ 90.996475] [] em28xx_usb_disconnect+0x79/0x90 [em28xx] [ 90.996539] [] usb_unbind_interface+0x67/0x1d0 [ 90.996620] [] __device_release_driver+0x7f/0xf0 [ 90.996682] [] device_release_driver+0x25/0x40 [ 90.996742] [] bus_remove_device+0x11c/0x1a0 [ 90.996801] [] device_del+0x136/0x1d0 [ 90.996863] [] usb_disable_device+0xb0/0x290 [ 90.996923] [] usb_disconnect+0xb5/0x1d0 [ 90.996984] [] hub_port_connect_change+0xd6/0xad0 [ 90.997044] [] hub_events+0x313/0x9b0 [ 90.997105] [] hub_thread+0x35/0x170 [ 90.997165] [] kthread+0xff/0x120 [ 90.997226] [] ret_from_fork+0x7c/0xb0 [ 90.997287] [ 90.997287] other info that might help us debug this: [ 90.997287] [ 90.997318] Possible unsafe locking scenario: [ 90.997318] [ 90.997348] CPU0 CPU1 [ 90.997378] ---- ---- [ 90.997408] lock(&dev->lock); [ 90.997497] lock(&card->controls_rwsem); [ 90.997607] lock(&dev->lock); [ 90.997697] lock(&card->controls_rwsem); [ 90.997786] [ 90.997786] *** DEADLOCK *** [ 90.997786] [ 90.997817] 5 locks held by khubd/54: [ 90.997847] #0: (&__lockdep_no_validate__){......}, at: [] hub_events+0xb4/0x9b0 [ 90.998025] #1: (&__lockdep_no_validate__){......}, at: [] usb_disconnect+0x66/0x1d0 [ 90.998204] #2: (&__lockdep_no_validate__){......}, at: [] device_release_driver+0x1d/0x40 [ 90.998383] #3: (em28xx_devlist_mutex){+.+.+.}, at: [] em28xx_close_extension+0x27/0x90 [em28xx] [ 90.998567] #4: (&dev->lock){+.+.+.}, at: [] em28xx_close_extension+0x31/0x90 [em28xx] Reviewed-by: Frank Schäfer Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b6dc332..898fb9b 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1099,12 +1099,10 @@ void em28xx_close_extension(struct em28xx *dev) const struct em28xx_ops *ops = NULL; mutex_lock(&em28xx_devlist_mutex); - mutex_lock(&dev->lock); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->fini) ops->fini(dev); } - mutex_unlock(&dev->lock); list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1486d47..aabcafb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1896,6 +1896,8 @@ static int em28xx_v4l2_fini(struct em28xx *dev) em28xx_info("Closing video extension"); + mutex_lock(&dev->lock); + v4l2_device_disconnect(&dev->v4l2_dev); em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); @@ -1926,6 +1928,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (dev->users) em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n"); + mutex_unlock(&dev->lock); return 0; } -- cgit v0.10.2 From a02b9c238b408f69fc78d528b549b85001df98b8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 03:34:23 -0300 Subject: [media] em28xx: adjust period size at runtime While the current hardcoded period is ok for the current values, we may latter change the driver to work with different bit rates or with different latencies than 64ms. So, adust the period size at runtime. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 26cf431..74575e0 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -293,7 +293,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; @@ -803,6 +808,9 @@ static int em28xx_audio_urb_init(struct em28xx *dev) 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, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 5d5d1b6..10b8172 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -505,6 +505,8 @@ struct em28xx_audio { unsigned int hwptr_done_capture; struct snd_card *sndcard; + size_t period; + int users; spinlock_t slock; }; -- cgit v0.10.2 From 4b819724984042eff2f14d889b55e906e420fbb3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 04:31:31 -0300 Subject: [media] drxk: remove the option to load firmware asynchronously The option to load firmware asynchronously were added due to a requirement with a few versions of udev. It turns that this was a bad idea and caused regressions on drxk-based devices. So, we end by only letting the firmware to be loaded syncronously everywhere. So, let's remove the bad code. This patch partially reverts the changeset 8e30783b0b3. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h index f22eb9f..f6cb346 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 bf29a3f..cce94a7 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/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index f4cdf9e..881a813 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -375,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 = { @@ -385,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 = { @@ -399,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 = { @@ -408,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, }; @@ -420,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) -- cgit v0.10.2 From b49eb2bd71c39fb69ae9001dac9f07ae6259f9ef Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jan 2014 11:32:00 -0300 Subject: [media] em28xx-audio: flush work at .fini As a pending action might be still there at the work thread, flush it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 74575e0..1563f71 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -967,6 +967,8 @@ static int em28xx_audio_fini(struct em28xx *dev) em28xx_info("Closing audio extension"); snd_card_disconnect(dev->adev.sndcard); + flush_work(&dev->wq_trigger); + em28xx_audio_free_urb(dev); if (dev->adev.sndcard) { -- cgit v0.10.2 From 452f236fcf845ba95c2f984f3157493e07383792 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jan 2014 14:49:04 -0200 Subject: em28xx-alsa: Fix error patch for init/fini If something bad happens during init, we free the card data. However, we still keep it initialized, causing some dependent code to be called at .fini. Fix it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 1563f71..45bea1a 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -948,6 +948,7 @@ urb_free: card_free: snd_card_free(card); + adev->sndcard = NULL; return err; } @@ -966,12 +967,12 @@ static int em28xx_audio_fini(struct em28xx *dev) em28xx_info("Closing audio extension"); - snd_card_disconnect(dev->adev.sndcard); - flush_work(&dev->wq_trigger); + if (dev->adev.sndcard) { + snd_card_disconnect(dev->adev.sndcard); + flush_work(&dev->wq_trigger); - em28xx_audio_free_urb(dev); + em28xx_audio_free_urb(dev); - if (dev->adev.sndcard) { snd_card_free(dev->adev.sndcard); dev->adev.sndcard = NULL; } -- cgit v0.10.2 From 8068eb885a13e33244a8692c880eca478b467d9d Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 7 Jan 2014 12:50:47 -0300 Subject: [media] em28xx: make 'em28xx_ctrl_ops' static sparse warnings: (new ones prefixed by >>) >> drivers/media/usb/em28xx/em28xx-video.c:1151:28: sparse: symbol 'em28xx_ctrl_ops' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index aabcafb..36b69cb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1147,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, }; -- cgit v0.10.2 From 961717b41bc1103dcd30d293fd689a614fbfa90c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Mon, 13 Jan 2014 19:02:06 -0300 Subject: [media] em28xx: fix usb alternate setting for analog and digital video endpoints > 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code assumes that the analog + digital video endpoints are always at interface number 0 when changing the alternate setting. This seems to work fine for most existing devices. However, at least the SpeedLink VAD Laplace webcam has the video endpoint on interface number 3 (which fortunately doesn't cause any trouble because ist uses bulk transfers only). We already consider the actual interface number for audio endpoints, so rename the the audio_ifnum variable and use it for all device types. Also get get rid of a pointless (ifnum < 0) in em28xx-audio. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 45bea1a..50c4ab4 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -266,7 +266,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) 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->alt == 0 || dev->ifnum) && dev->adev.users == 0) { int nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { @@ -274,14 +274,14 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) return -EAGAIN; } else mutex_lock(&dev->lock); - if (dev->audio_ifnum) + if (dev->ifnum) dev->alt = 1; else dev->alt = 7; 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; @@ -733,16 +733,16 @@ static int em28xx_audio_urb_init(struct em28xx *dev) int urb_size, bytes_per_transfer; u8 alt; - if (dev->audio_ifnum) + if (dev->ifnum) alt = 1; else alt = 7; - intf = usb_ifnum_to_if(dev->udev, dev->audio_ifnum); + 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->audio_ifnum, alt); + dev->ifnum, alt); return -ENODEV; } @@ -766,7 +766,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev) 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->audio_ifnum, alt, + dev->ifnum, alt, interval, ep_size); @@ -877,7 +877,7 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; 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) */ diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e08d65b..6318e45 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3274,7 +3274,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->has_alsa_audio = has_audio; dev->audio_mode.has_audio = has_audio; dev->has_video = has_video; - dev->audio_ifnum = ifnum; + dev->ifnum = ifnum; /* Checks if audio is provided by some interface */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 881a813..a0a669e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -203,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; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 36b69cb..c3c9289 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -383,7 +383,7 @@ set_alt: } em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); + 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); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 10b8172..32d8a4b 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -549,8 +549,6 @@ struct em28xx { unsigned int has_alsa_audio:1; unsigned int is_audio_only:1; - int audio_ifnum; - struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_clk *clk; @@ -664,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 */ -- cgit v0.10.2 From 0191a2a28cdc34a57fee2c027c101a023f9a61f9 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Mon, 13 Jan 2014 19:02:07 -0300 Subject: [media] em28xx: fix check for audio only usb interfaces when changing the usb alternate setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we've been assuming that the video endpoints are always at usb interface 0. Hence, if vendor audio endpoints are provided at a separate interface, they were supposed to be at interface number > 0. Instead of checking for (interface number > 0) to determine if an interface is a pure audio interface, dev->is_audio_only should be checked. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 50c4ab4..332abf7 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -266,7 +266,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); runtime->hw = snd_em28xx_hw_capture; - if ((dev->alt == 0 || dev->ifnum) && dev->adev.users == 0) { + if ((dev->alt == 0 || dev->is_audio_only) && dev->adev.users == 0) { int nonblock = !!(substream->f_flags & O_NONBLOCK); if (nonblock) { @@ -274,10 +274,21 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) return -EAGAIN; } else mutex_lock(&dev->lock); - if (dev->ifnum) + 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->ifnum, dev->alt); -- cgit v0.10.2 From bf6e8aaa32081ff3f639334ca6b3ca0eaec5adc0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jan 2014 14:34:13 -0300 Subject: [media] em28xx-audio: provide an error code when URB submit fails Instead of just saying: [ 1646.412419] em2882/3 #0: submit of audio urb failed Print the reason why it failed, to help debugging and fixing it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 332abf7..e57616e 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -163,10 +163,9 @@ 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; } @@ -183,7 +182,8 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) 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; -- cgit v0.10.2 From b464f6b68e62d1664c34c8655cc3df192799338c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jan 2014 07:30:41 -0200 Subject: Revert "[media] go7007-usb: only use go->dev after allocated" This patch conflicts with commits 66a528c1c3d6, with also fixes the bug and got merged on v3.13-rc2. This reverts commit c61c3094a630b2f795addc3a74fbd5ff7f704ca9. diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 1d747de..58684da 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf, char *name; int video_pipe, i, v_urb_len; - printk(KERN_DEBUG "go7007-usb: probing new board\n"); + dev_dbg(go->dev, "probing new GO7007 USB board\n"); switch (id->driver_info) { case GO7007_BOARDID_MATRIX_II: @@ -1097,16 +1097,13 @@ static int go7007_usb_probe(struct usb_interface *intf, board = &board_px_tv402u; break; case GO7007_BOARDID_LIFEVIEW_LR192: - printk(KERN_ERR - "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); + dev_err(go->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); return -ENODEV; -#if 0 name = "Lifeview TV Walker Ultra"; board = &board_lifeview_lr192; break; -#endif case GO7007_BOARDID_SENSORAY_2250: - printk(KERN_INFO "Sensoray 2250 found\n"); + dev_info(go->dev, "Sensoray 2250 found\n"); name = "Sensoray 2250/2251"; board = &board_sensoray_2250; break; @@ -1115,9 +1112,8 @@ static int go7007_usb_probe(struct usb_interface *intf, board = &board_ads_usbav_709; break; default: - printk(KERN_ERR - "unknown board ID %d!\n", - (unsigned int)id->driver_info); + dev_err(go->dev, "unknown board ID %d!\n", + (unsigned int)id->driver_info); return -ENODEV; } -- cgit v0.10.2 From 793708fffffad737d8b0113e41f0d14a38e8765e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 14 Jan 2014 23:49:23 -0300 Subject: [media] au0828: Fix sparse non static symbol warning Fixes the following sparse warning: drivers/media/usb/au0828/au0828-dvb.c:36:5: warning: symbol 'preallocate_big_buffers' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 19fe049..4ae8b10 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -33,7 +33,7 @@ #include "mxl5007t.h" #include "tda18271.h" -int preallocate_big_buffers; +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"); -- cgit v0.10.2 From ff0284cc0bf13f08e19fbf364fafabe9e61a7342 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 15 Jan 2014 02:52:12 -0300 Subject: [media] em28xx-audio: remove needless check before usb_free_coherent() usb_free_coherent() is safe with NULL addr and this check is not required. Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index e57616e..05e9bd1 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -711,11 +711,9 @@ static void em28xx_audio_free_urb(struct em28xx *dev) if (!urb) continue; - if (dev->adev.transfer_buffer[i]) - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->adev.transfer_buffer[i], - urb->transfer_dma); + usb_free_coherent(dev->udev, urb->transfer_buffer_length, + dev->adev.transfer_buffer[i], + urb->transfer_dma); usb_free_urb(urb); } -- cgit v0.10.2 From b3c8154eb9a19d45355a53d81d2ccdbc22d0cdab Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 14 Jan 2014 23:46:06 -0300 Subject: [media] radio-usb-si4713: fix sparse non static symbol warnings Fixes the following sparse warnings: drivers/media/radio/si4713/radio-usb-si4713.c:226:31: warning: symbol 'start_seq' was not declared. Should it be static? drivers/media/radio/si4713/radio-usb-si4713.c:291:29: warning: symbol 'command_table' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index f1e640d..779855b 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -223,7 +223,7 @@ struct si4713_start_seq_table { * (0x03): Get serial number of the board (Response : CB000-00-00) * (0x06, 0x03, 0x03, 0x08, 0x01, 0x0f) : Get Component revision */ -struct si4713_start_seq_table start_seq[] = { +static struct si4713_start_seq_table start_seq[] = { { 1, { 0x03 } }, { 2, { 0x32, 0x7f } }, @@ -288,7 +288,7 @@ struct si4713_command_table { * Byte 4 : Number of arguments + 1 (for the command byte) * Byte 5 : Number of response bytes */ -struct si4713_command_table command_table[] = { +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 } }, -- cgit v0.10.2 From 1045d81d301055b162a3d308e1fb81d2b62a1f2a Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Sun, 12 Jan 2014 08:21:28 -0300 Subject: [media] m2m-deinterlace: fix allocated struct type 'xt' points to a dma_interleaved_template and not a dma_async_tx_descriptor. Signed-off-by: Jassi Brar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 65cab70..6bb86b5 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); -- cgit v0.10.2 From 1d504b649faf9f27de8ee0a5532e29b8d1fb2a02 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 14 Jan 2014 08:04:21 -0300 Subject: [media] media: st-rc: Add reset support Some of the SOCs hold the IRB IP in softreset state by default. For this IP to work driver needs to bring it out of softreset. This patch adds support to reset the IP via reset framework. Without this patch the driver can not work with SoCs which holds the IP in softreset. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 65120c2..8f0cddb 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -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; -- cgit v0.10.2 From caabb840663816efbe41c8eb87fdc133730a9b19 Mon Sep 17 00:00:00 2001 From: Monam Agarwal Date: Tue, 14 Jan 2014 09:01:14 -0300 Subject: [media] Staging: media: Fix quoted string split across line in as102_fe.c This patch fixes the following checkpatch.pl issues in as102/as102_fe.c WARNING: quoted string split across lines Signed-off-by: Monam Agarwal Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c index 72b2c48..426e61c 100644 --- a/drivers/staging/media/as102/as102_fe.c +++ b/drivers/staging/media/as102/as102_fe.c @@ -141,8 +141,8 @@ static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) if (as10x_cmd_get_demod_stats(&dev->bus_adap, (struct as10x_demod_stats *) &dev->demod_stats) < 0) { memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); - dprintk(debug, "as10x_cmd_get_demod_stats failed " - "(probably not tuned)\n"); + dprintk(debug, + "as10x_cmd_get_demod_stats failed (probably not tuned)\n"); } else { dprintk(debug, "demod status: fc: 0x%08x, bad fc: 0x%08x, " @@ -555,8 +555,8 @@ static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args, as102_fe_get_code_rate(params->code_rate_LP); } - dprintk(debug, "\thierarchy: 0x%02x " - "selected: %s code_rate_%s: 0x%02x\n", + dprintk(debug, + "\thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n", tune_args->hierarchy, tune_args->hier_select == HIER_HIGH_PRIORITY ? "HP" : "LP", -- cgit v0.10.2 From 15ddb513f2f9494e8dec8ba2462f288a324b0e04 Mon Sep 17 00:00:00 2001 From: Monam Agarwal Date: Tue, 14 Jan 2014 09:02:42 -0300 Subject: [media] Staging: media: Fix line length exceeding 80 characters in as102_fe.c This patch fixes the following checkpatch.pl issues in as102/as102_fe.c WARNING: line over 80 characters Signed-off-by: Monam Agarwal Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c index 426e61c..b686b76 100644 --- a/drivers/staging/media/as102/as102_fe.c +++ b/drivers/staging/media/as102/as102_fe.c @@ -238,7 +238,8 @@ static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) if (acquire) { if (elna_enable) - as10x_cmd_set_context(&dev->bus_adap, CONTEXT_LNA, dev->elna_cfg); + as10x_cmd_set_context(&dev->bus_adap, + CONTEXT_LNA, dev->elna_cfg); ret = as10x_cmd_turn_on(&dev->bus_adap); } else { -- cgit v0.10.2 From f52e9828d5f3001f11981d852dc9cbd3c8c5debe Mon Sep 17 00:00:00 2001 From: Monam Agarwal Date: Tue, 14 Jan 2014 11:27:08 -0300 Subject: [media] Staging: media: Fix line length exceeding 80 characters in as102_drv.c This patch fixes the following checkpatch.pl warning in as102/as102_drv.c WARNING: line over 80 characters in the file Signed-off-by: Monam Agarwal Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c index 62218db..a61896d 100644 --- a/drivers/staging/media/as102/as102_drv.c +++ b/drivers/staging/media/as102/as102_drv.c @@ -132,7 +132,8 @@ static int as10x_pid_filter(struct as102_dev_t *dev, filter.pid = pid; ret = as10x_cmd_add_PID_filter(bus_adap, &filter); - dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", + dprintk(debug, + "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", index, filter.idx, filter.pid, ret); break; } -- cgit v0.10.2 From c3aed262186841bf01feb9603885671ea567ebd9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jan 2014 17:35:15 -0300 Subject: [media] em28xx-cards: properly initialize the device bitmap Instead of just creating a long int, use DECLARE_BITMAP(). No functional changes. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6318e45..4d97a76 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -66,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; @@ -2885,7 +2885,7 @@ void em28xx_release_resources(struct em28xx *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); }; @@ -3094,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", @@ -3102,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) { @@ -3355,7 +3355,7 @@ err_free: kfree(dev); err: - clear_bit(nr, &em28xx_devused); + clear_bit(nr, em28xx_devused); err_no_slot: usb_put_dev(udev); -- cgit v0.10.2 From 587d1b06e07b4a079453c74ba9edf17d21931049 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jan 2014 16:27:55 -0300 Subject: [media] rc-core: reuse device numbers Before changeset d8b4b5822f51e, the remote controller device numbers were released when the device were unregistered. That helped to maintain some sanity, as, when USB devices are replugged, the remote controller would get the same number. Restore the same behaviour. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 46da365..02e2f38 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -22,6 +22,10 @@ #include #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); -- cgit v0.10.2 From 6c3df5da67f1f53df78c7e20cd53a481dc28eade Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 3 Dec 2013 10:14:29 -0300 Subject: [media] media: v4l2-dev: fix video device index assignment The side effect of commit 1056e4388b045 ("v4l2-dev: Fix race condition on __video_register_device") is the increased number of index value assigned on video_device registration. Before that commit video_devices were numbered from 0, after it, the indexes starts from 1, because get_index() always count the device, which is being registered. Some device drivers rely on video_device index number for internal purposes, i.e. s5p-mfc driver stopped working after that patch. This patch restores the old method of numbering the video_device indexes. Cc: stable@vger.kernel.org # for v3.12 Signed-off-by: Marek Szyprowski Acked-by: Sakari Ailus Acked-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index b5aaaac..0a30dbf 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) -- cgit v0.10.2