diff options
Diffstat (limited to 'linux/drivers/media')
56 files changed, 4109 insertions, 325 deletions
diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 71820ef4f..a86504587 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -102,7 +102,7 @@ if VIDEO_TUNER_CUSTOMIZE config TUNER_XC2028 tristate "XCeive xc2028/xc3028 tuners" - depends on I2C + depends on I2C && FW_LOADER default m if VIDEO_TUNER_CUSTOMIZE help Say Y here to include support for the xc2028/xc3028 tuners. @@ -160,7 +160,7 @@ config VIDEOBUF_GEN tristate config VIDEOBUF_DMA_SG - depends on PCI + depends on PCI || ARCH_PXA select VIDEOBUF_GEN tristate diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index d3d3c0e5e..3f5f9ad94 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2037,3 +2037,49 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_behold); + +/* + * Remote control for the Genius TVGO A11MCE + * Adrian Pardini <pardo.bsso@gmail.com> + */ +IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE] = { + /* Keys 0 to 9 */ + [0x48] = KEY_0, + [0x09] = KEY_1, + [0x1d] = KEY_2, + [0x1f] = KEY_3, + [0x19] = KEY_4, + [0x1b] = KEY_5, + [0x11] = KEY_6, + [0x17] = KEY_7, + [0x12] = KEY_8, + [0x16] = KEY_9, + + [0x54] = KEY_RECORD, /* recording */ + [0x06] = KEY_MUTE, /* mute */ + [0x10] = KEY_POWER, + [0x40] = KEY_LAST, /* recall */ + [0x4c] = KEY_CHANNELUP, /* channel / program + */ + [0x00] = KEY_CHANNELDOWN, /* channel / program - */ + [0x0d] = KEY_VOLUMEUP, + [0x15] = KEY_VOLUMEDOWN, + [0x4d] = KEY_OK, /* also labeled as Pause */ + [0x1c] = KEY_ZOOM, /* full screen and Stop*/ + [0x02] = KEY_MODE, /* AV Source or Rewind*/ + [0x04] = KEY_LIST, /* -/-- */ + /* small arrows above numbers */ + [0x1a] = KEY_NEXT, /* also Fast Forward */ + [0x0e] = KEY_PREVIOUS, /* also Rewind */ + /* these are in a rather non standard layout and have + an alternate name written */ + [0x1e] = KEY_UP, /* Video Setting */ + [0x0a] = KEY_DOWN, /* Video Default */ + [0x05] = KEY_LEFT, /* Snapshot */ + [0x0c] = KEY_RIGHT, /* Hide Panel */ + /* Four buttons without label */ + [0x49] = KEY_RED, + [0x0b] = KEY_GREEN, + [0x13] = KEY_YELLOW, + [0x50] = KEY_BLUE, +}; +EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce); diff --git a/linux/drivers/media/common/saa7146_core.c b/linux/drivers/media/common/saa7146_core.c index 7fb263dc1..c36634248 100644 --- a/linux/drivers/media/common/saa7146_core.c +++ b/linux/drivers/media/common/saa7146_core.c @@ -392,7 +392,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent } dev->revision &= 0xf; - /* remap the memory from virtual to physical adress */ + /* remap the memory from virtual to physical address */ err = pci_request_region(pci, 0, "saa7146"); if (err < 0) diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c index e2eae4ca7..627096688 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_net.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c @@ -703,7 +703,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) drop = 1; /* else: destination address matches the MAC address of our receiver device */ } - /* else: promiscious mode; pass everything up the stack */ + /* else: promiscuous mode; pass everything up the stack */ if (drop) { #ifdef ULE_DEBUG diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.c b/linux/drivers/media/dvb/dvb-usb/ttusb2.c index fc47d53f5..bafe82f66 100644 --- a/linux/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.c @@ -150,6 +150,7 @@ static int ttusb2_streaming_ctrl(struct dvb_usb_device *d, int onoff) static struct tda10086_config tda10086_config = { .demod_address = 0x0e, .invert = 0, + .diseqc_tone = 1, }; static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap) diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c index 0eef34b6f..a4b48bdf2 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.c +++ b/linux/drivers/media/dvb/frontends/tda10086.c @@ -107,9 +107,12 @@ static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, static int tda10086_init(struct dvb_frontend* fe) { struct tda10086_state* state = fe->demodulator_priv; + u8 t22k_off = 0x80; dprintk ("%s\n", __FUNCTION__); + if (state->config->diseqc_tone) + t22k_off = 0; // reset tda10086_write_byte(state, 0x00, 0x00); msleep(10); @@ -159,7 +162,7 @@ static int tda10086_init(struct dvb_frontend* fe) tda10086_write_byte(state, 0x3d, 0x80); // setup SEC - tda10086_write_byte(state, 0x36, 0x80); // all SEC off, no 22k tone + tda10086_write_byte(state, 0x36, t22k_off); // all SEC off, 22k tone tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); // } tone frequency tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); // } @@ -181,16 +184,20 @@ static void tda10086_diseqc_wait(struct tda10086_state *state) static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { struct tda10086_state* state = fe->demodulator_priv; + u8 t22k_off = 0x80; dprintk ("%s\n", __FUNCTION__); + if (state->config->diseqc_tone) + t22k_off = 0; + switch (tone) { case SEC_TONE_OFF: - tda10086_write_byte(state, 0x36, 0x80); + tda10086_write_byte(state, 0x36, t22k_off); break; case SEC_TONE_ON: - tda10086_write_byte(state, 0x36, 0x81); + tda10086_write_byte(state, 0x36, 0x01 + t22k_off); break; } @@ -203,9 +210,13 @@ static int tda10086_send_master_cmd (struct dvb_frontend* fe, struct tda10086_state* state = fe->demodulator_priv; int i; u8 oldval; + u8 t22k_off = 0x80; dprintk ("%s\n", __FUNCTION__); + if (state->config->diseqc_tone) + t22k_off = 0; + if (cmd->msg_len > 6) return -EINVAL; oldval = tda10086_read_byte(state, 0x36); @@ -213,7 +224,8 @@ static int tda10086_send_master_cmd (struct dvb_frontend* fe, for(i=0; i< cmd->msg_len; i++) { tda10086_write_byte(state, 0x48+i, cmd->msg[i]); } - tda10086_write_byte(state, 0x36, 0x88 | ((cmd->msg_len - 1) << 4)); + tda10086_write_byte(state, 0x36, (0x08 + t22k_off) + | ((cmd->msg_len - 1) << 4)); tda10086_diseqc_wait(state); @@ -226,16 +238,20 @@ static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minic { struct tda10086_state* state = fe->demodulator_priv; u8 oldval = tda10086_read_byte(state, 0x36); + u8 t22k_off = 0x80; dprintk ("%s\n", __FUNCTION__); + if (state->config->diseqc_tone) + t22k_off = 0; + switch(minicmd) { case SEC_MINI_A: - tda10086_write_byte(state, 0x36, 0x84); + tda10086_write_byte(state, 0x36, 0x04 + t22k_off); break; case SEC_MINI_B: - tda10086_write_byte(state, 0x36, 0x86); + tda10086_write_byte(state, 0x36, 0x06 + t22k_off); break; } diff --git a/linux/drivers/media/dvb/frontends/tda10086.h b/linux/drivers/media/dvb/frontends/tda10086.h index ed584a8f4..eeceaeee7 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.h +++ b/linux/drivers/media/dvb/frontends/tda10086.h @@ -33,6 +33,9 @@ struct tda10086_config /* does the "inversion" need inverted? */ u8 invert; + + /* do we need the diseqc signal with carrier? */ + u8 diseqc_tone; }; #if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) diff --git a/linux/drivers/media/dvb/frontends/xc5000.h b/linux/drivers/media/dvb/frontends/xc5000.h index e0e84562a..32a5f1c86 100644 --- a/linux/drivers/media/dvb/frontends/xc5000.h +++ b/linux/drivers/media/dvb/frontends/xc5000.h @@ -45,7 +45,8 @@ struct xc5000_config { /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 -#if defined(CONFIG_DVB_TUNER_XC5000) || defined(CONFIG_DVB_TUNER_XC5000_MODULE) +#if defined(CONFIG_DVB_TUNER_XC5000) || \ + (defined(CONFIG_DVB_TUNER_XC5000_MODULE) && defined(MODULE)) extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct xc5000_config *cfg); diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index 9268a82ba..14b00f57b 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -351,6 +351,7 @@ static struct s5h1420_config s5h1420_config = { static struct tda10086_config tda10086_config = { .demod_address = 0x0e, .invert = 0, + .diseqc_tone = 1, }; static u8 read_pwm(struct budget* budget) diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 7be7b0916..e999b2fc3 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -66,8 +66,25 @@ * Version 1.0.5 * - number of seek_retries changed to tune_timeout * - fixed problem with incomplete tune operations by own buffers - * - optimization of variables + * - optimization of variables and printf types * - improved error logging + * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net> + * Oliver Neukum <oliver@neukum.org> + * Version 1.0.6 + * - fixed coverity checker warnings in *_usb_driver_disconnect + * - probe()/open() race by correct ordering in probe() + * - DMA coherency rules by separate allocation of all buffers + * - use of endianness macros + * - abuse of spinlock, replaced by mutex + * - racy handling of timer in disconnect, + * replaced by delayed_work + * - racy interruptible_sleep_on(), + * replaced with wait_event_interruptible() + * - handle signals in read() + * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net> + * Oliver Neukum <oliver@neukum.org> + * Version 1.0.7 + * - usb autosuspend support * * ToDo: * - add seeking support @@ -80,10 +97,10 @@ /* driver definitions */ #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" #define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 5) +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 6) #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" -#define DRIVER_VERSION "1.0.5" +#define DRIVER_VERSION "1.0.6" /* kernel includes */ @@ -97,8 +114,12 @@ #include <linux/version.h> #include "compat.h" #include <linux/videodev2.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) +#include <linux/mutex.h> +#endif #include <media/v4l2-common.h> #include <media/rds.h> +#include <asm/unaligned.h> /* USB Device ID List */ @@ -401,6 +422,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); struct si470x_device { /* reference to USB and video device */ struct usb_device *usbdev; + struct usb_interface *intf; struct video_device *videodev; /* driver management */ @@ -410,10 +432,18 @@ struct si470x_device { unsigned short registers[RADIO_REGISTER_NUM]; /* RDS receive buffer */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct work_struct work; +#else + struct delayed_work work; +#endif + wait_queue_head_t read_queue; - struct timer_list timer; - spinlock_t lock; /* buffer locking */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) + struct mutex lock; /* buffer locking */ +#else + struct semaphore lock; /* buffer locking */ +#endif unsigned char *buffer; /* size is always multiple of three */ unsigned int buf_size; unsigned int rd_index; @@ -495,7 +525,8 @@ static int si470x_get_register(struct si470x_device *radio, int regnr) retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); if (retval >= 0) - radio->registers[regnr] = (buf[1] << 8) | buf[2]; + radio->registers[regnr] = be16_to_cpu(get_unaligned( + (unsigned short *) &buf[1])); return (retval < 0) ? -EINVAL : 0; } @@ -510,8 +541,8 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) int retval; buf[0] = REGISTER_REPORT(regnr); - buf[1] = (radio->registers[regnr] & 0xff00) >> 8; - buf[2] = (radio->registers[regnr] & 0x00ff); + put_unaligned(cpu_to_be16(radio->registers[regnr]), + (unsigned short *) &buf[1]); retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); @@ -534,9 +565,9 @@ static int si470x_get_all_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = - (buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) | - buf[regnr * RADIO_REGISTER_SIZE + 2]; + radio->registers[regnr] = be16_to_cpu(get_unaligned( + (unsigned short *) + &buf[regnr * RADIO_REGISTER_SIZE + 1])); return (retval < 0) ? -EINVAL : 0; } @@ -559,7 +590,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) (void *) &buf, sizeof(buf), &size, usb_timeout); if (size != sizeof(buf)) printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_register: " - "return size differs: %d != %ld\n", size, sizeof(buf)); + "return size differs: %d != %zu\n", size, sizeof(buf)); if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " "usb_interrupt_msg returned %d\n", retval); @@ -567,8 +598,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) radio->registers[STATUSRSSI + regnr] = - (buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) | - buf[regnr * RADIO_REGISTER_SIZE + 2]; + be16_to_cpu(get_unaligned((unsigned short *) + &buf[regnr * RADIO_REGISTER_SIZE + 1])); return (retval < 0) ? -EINVAL : 0; } @@ -601,7 +632,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) (!timed_out)); if (timed_out) printk(KERN_WARNING DRIVER_NAME - ": seek does not finish after %d ms\n", tune_timeout); + ": seek does not finish after %u ms\n", tune_timeout); /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; @@ -748,9 +779,17 @@ static int si470x_stop(struct si470x_device *radio) */ static int si470x_rds_on(struct si470x_device *radio) { + int retval; + /* sysconfig 1 */ + mutex_lock(&radio->lock); radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; - return si470x_set_register(radio, SYSCONFIG1); + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; + mutex_unlock(&radio->lock); + + return retval; } @@ -764,6 +803,11 @@ static int si470x_rds_on(struct si470x_device *radio) */ static void si470x_rds(struct si470x_device *radio) { + unsigned char blocknum; + unsigned short bler; /* rds block errors */ + unsigned short rds; + unsigned char tmpbuf[3]; + /* get rds blocks */ if (si470x_get_rds_registers(radio) < 0) return; @@ -776,69 +820,58 @@ static void si470x_rds(struct si470x_device *radio) return; } - /* copy four RDS blocks to internal buffer */ - if (spin_trylock(&radio->lock)) { - unsigned char blocknum; - unsigned short bler; /* rds block errors */ - unsigned short rds; - unsigned char tmpbuf[3]; - unsigned char i; - - /* process each rds block */ - for (blocknum = 0; blocknum < 4; blocknum++) { - switch (blocknum) { - default: - bler = (radio->registers[STATUSRSSI] & - STATUSRSSI_BLERA) >> 9; - rds = radio->registers[RDSA]; - break; - case 1: - bler = (radio->registers[READCHAN] & - READCHAN_BLERB) >> 14; - rds = radio->registers[RDSB]; - break; - case 2: - bler = (radio->registers[READCHAN] & - READCHAN_BLERC) >> 12; - rds = radio->registers[RDSC]; - break; - case 3: - bler = (radio->registers[READCHAN] & - READCHAN_BLERD) >> 10; - rds = radio->registers[RDSD]; - break; - }; - - /* Fill the V4L2 RDS buffer */ - tmpbuf[0] = rds & 0x00ff; /* LSB */ - tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */ - tmpbuf[2] = blocknum; /* offset name */ - tmpbuf[2] |= blocknum << 3; /* received offset */ - if (bler > max_rds_errors) - tmpbuf[2] |= 0x80; /* uncorrectable errors */ - else if (bler > 0) - tmpbuf[2] |= 0x40; /* corrected error(s) */ - - /* copy RDS block to internal buffer */ - for (i = 0; i < 3; i++) { - radio->buffer[radio->wr_index] = tmpbuf[i]; - radio->wr_index++; - } - - /* wrap write pointer */ - if (radio->wr_index >= radio->buf_size) - radio->wr_index = 0; - - /* check for overflow */ - if (radio->wr_index == radio->rd_index) { - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - } + /* copy all four RDS blocks to internal buffer */ + mutex_lock(&radio->lock); + for (blocknum = 0; blocknum < 4; blocknum++) { + switch (blocknum) { + default: + bler = (radio->registers[STATUSRSSI] & + STATUSRSSI_BLERA) >> 9; + rds = radio->registers[RDSA]; + break; + case 1: + bler = (radio->registers[READCHAN] & + READCHAN_BLERB) >> 14; + rds = radio->registers[RDSB]; + break; + case 2: + bler = (radio->registers[READCHAN] & + READCHAN_BLERC) >> 12; + rds = radio->registers[RDSC]; + break; + case 3: + bler = (radio->registers[READCHAN] & + READCHAN_BLERD) >> 10; + rds = radio->registers[RDSD]; + break; + }; + + /* Fill the V4L2 RDS buffer */ + put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf); + tmpbuf[2] = blocknum; /* offset name */ + tmpbuf[2] |= blocknum << 3; /* received offset */ + if (bler > max_rds_errors) + tmpbuf[2] |= 0x80; /* uncorrectable errors */ + else if (bler > 0) + tmpbuf[2] |= 0x40; /* corrected error(s) */ + + /* copy RDS block to internal buffer */ + memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); + radio->wr_index += 3; + + /* wrap write pointer */ + if (radio->wr_index >= radio->buf_size) + radio->wr_index = 0; + + /* check for overflow */ + if (radio->wr_index == radio->rd_index) { + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; } - spin_unlock(&radio->lock); } + mutex_unlock(&radio->lock); /* wake up read queue */ if (radio->wr_index != radio->rd_index) @@ -847,29 +880,18 @@ static void si470x_rds(struct si470x_device *radio) /* - * si470x_timer - rds timer function - */ -static void si470x_timer(unsigned long data) -{ - struct si470x_device *radio = (struct si470x_device *) data; - - schedule_work(&radio->work); -} - - -/* * si470x_work - rds work function */ static void si470x_work(struct work_struct *work) { struct si470x_device *radio = container_of(work, struct si470x_device, - work); + work.work); if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) return; si470x_rds(radio); - mod_timer(&radio->timer, jiffies + msecs_to_jiffies(rds_poll_time)); + schedule_delayed_work(&radio->work, msecs_to_jiffies(rds_poll_time)); } @@ -886,49 +908,49 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval = 0; + unsigned int block_count = 0; /* switch on rds reception */ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { si470x_rds_on(radio); - schedule_work(&radio->work); + schedule_delayed_work(&radio->work, + msecs_to_jiffies(rds_poll_time)); } /* block if no new data available */ while (radio->wr_index == radio->rd_index) { if (file->f_flags & O_NONBLOCK) return -EWOULDBLOCK; - interruptible_sleep_on(&radio->read_queue); + if (wait_event_interruptible(radio->read_queue, + radio->wr_index != radio->rd_index) < 0) + return -EINTR; } /* calculate block count from byte count */ count /= 3; /* copy RDS block out of internal buffer and to user buffer */ - if (spin_trylock(&radio->lock)) { - unsigned int block_count = 0; - while (block_count < count) { - if (radio->rd_index == radio->wr_index) - break; - - /* always transfer rds complete blocks */ - if (copy_to_user(buf, - &radio->buffer[radio->rd_index], 3)) - /* retval = -EFAULT; */ - break; + mutex_lock(&radio->lock); + while (block_count < count) { + if (radio->rd_index == radio->wr_index) + break; - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; + /* always transfer rds complete blocks */ + if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) + /* retval = -EFAULT; */ + break; - /* increment counters */ - block_count++; - buf += 3; - retval += 3; - } + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; - spin_unlock(&radio->lock); + /* increment counters */ + block_count++; + buf += 3; + retval += 3; } + mutex_unlock(&radio->lock); return retval; } @@ -945,7 +967,8 @@ static unsigned int si470x_fops_poll(struct file *file, /* switch on rds reception */ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { si470x_rds_on(radio); - schedule_work(&radio->work); + schedule_delayed_work(&radio->work, + msecs_to_jiffies(rds_poll_time)); } poll_wait(file, &radio->read_queue, pts); @@ -963,10 +986,22 @@ static unsigned int si470x_fops_poll(struct file *file, static int si470x_fops_open(struct inode *inode, struct file *file) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval; radio->users++; - if (radio->users == 1) - return si470x_start(radio); + + retval = usb_autopm_get_interface(radio->intf); + if (retval < 0) { + radio->users--; + return -EIO; + } + + if (radio->users == 1) { + retval = si470x_start(radio); + if (retval < 0) + usb_autopm_put_interface(radio->intf); + return retval; + } return 0; } @@ -978,6 +1013,7 @@ static int si470x_fops_open(struct inode *inode, struct file *file) static int si470x_fops_release(struct inode *inode, struct file *file) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval; if (!radio) return -ENODEV; @@ -985,13 +1021,14 @@ static int si470x_fops_release(struct inode *inode, struct file *file) radio->users--; if (radio->users == 0) { /* stop rds reception */ - del_timer_sync(&radio->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&radio->work); /* cancel read processes */ wake_up_interruptible(&radio->read_queue); - return si470x_stop(radio); + retval = si470x_stop(radio); + usb_autopm_put_interface(radio->intf); + return retval; } return 0; @@ -1363,71 +1400,116 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct si470x_device *radio; + int retval = -ENOMEM; - /* memory and interface allocations */ - radio = kmalloc(sizeof(struct si470x_device), GFP_KERNEL); + /* private data allocation */ + radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); if (!radio) - return -ENOMEM; + goto err_initial; + + /* video device allocation */ radio->videodev = video_device_alloc(); - if (!radio->videodev) { - kfree(radio); - return -ENOMEM; - } + if (!radio->videodev) + goto err_radio; + + /* initial configuration */ memcpy(radio->videodev, &si470x_viddev_template, sizeof(si470x_viddev_template)); radio->users = 0; radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + mutex_init(&radio->lock); video_set_drvdata(radio->videodev, radio); - if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { - printk(KERN_WARNING DRIVER_NAME - ": Could not register video device\n"); - video_device_release(radio->videodev); - kfree(radio); - return -EIO; - } - usb_set_intfdata(intf, radio); /* show some infos about the specific device */ - if (si470x_get_all_registers(radio) < 0) { - video_device_release(radio->videodev); - kfree(radio); - return -EIO; - } - printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4x ChipID=0x%4.4x\n", + retval = -EIO; + if (si470x_get_all_registers(radio) < 0) + goto err_all; + printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); /* check if firmware is current */ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) - < RADIO_SW_VERSION_CURRENT) + < RADIO_SW_VERSION_CURRENT) { + printk(KERN_WARNING DRIVER_NAME + ": This driver is known to work with " + "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); + printk(KERN_WARNING DRIVER_NAME + ": but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); printk(KERN_WARNING DRIVER_NAME - ": This driver is known to work with chip version %d, " - "but the device has firmware %d.\n" - DRIVER_NAME - "If you have some trouble using this driver, please " - "report to V4L ML at video4linux-list@redhat.com\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE, - RADIO_SW_VERSION_CURRENT); + ": If you have some trouble using this driver,\n"); + printk(KERN_WARNING DRIVER_NAME + ": please report to V4L ML at " + "video4linux-list@redhat.com\n"); + } /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ - /* rds initialization */ + /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); - if (!radio->buffer) { - video_device_release(radio->videodev); - kfree(radio); - return -ENOMEM; - } + if (!radio->buffer) + goto err_all; + + /* rds buffer configuration */ radio->wr_index = 0; radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); - /* prepare polling via eventd */ - INIT_WORK(&radio->work, si470x_work); - init_timer(&radio->timer); - radio->timer.function = si470x_timer; - radio->timer.data = (unsigned long) radio; + /* prepare rds work function */ + INIT_DELAYED_WORK(&radio->work, si470x_work); + + /* register video device */ + if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { + printk(KERN_WARNING DRIVER_NAME + ": Could not register video device\n"); + goto err_all; + } + usb_set_intfdata(intf, radio); + + return 0; +err_all: + video_device_release(radio->videodev); + kfree(radio->buffer); +err_radio: + kfree(radio); +err_initial: + return retval; +} + + +/* + * si470x_usb_driver_suspend - suspend the device + */ +static int si470x_usb_driver_suspend(struct usb_interface *intf, + pm_message_t message) +{ + struct si470x_device *radio = usb_get_intfdata(intf); + + printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); + + cancel_delayed_work_sync(&radio->work); + + return 0; +} + + +/* + * si470x_usb_driver_resume - resume the device + */ +static int si470x_usb_driver_resume(struct usb_interface *intf) +{ + struct si470x_device *radio = usb_get_intfdata(intf); + + printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); + + mutex_lock(&radio->lock); + if (radio->users && radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) + schedule_delayed_work(&radio->work, + msecs_to_jiffies(rds_poll_time)); + mutex_unlock(&radio->lock); return 0; } @@ -1440,14 +1522,11 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) { struct si470x_device *radio = usb_get_intfdata(intf); + cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); - if (radio) { - del_timer_sync(&radio->timer); - flush_scheduled_work(); - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - } + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); } @@ -1455,10 +1534,13 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) * si470x_usb_driver - usb driver interface */ static struct usb_driver si470x_usb_driver = { - .name = DRIVER_NAME, - .probe = si470x_usb_driver_probe, - .disconnect = si470x_usb_driver_disconnect, - .id_table = si470x_usb_driver_id_table, + .name = DRIVER_NAME, + .probe = si470x_usb_driver_probe, + .disconnect = si470x_usb_driver_disconnect, + .suspend = si470x_usb_driver_suspend, + .resume = si470x_usb_driver_resume, + .id_table = si470x_usb_driver_id_table, + .supports_autosuspend = 1, }; diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 37072a21d..d9fc92a82 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -836,4 +836,49 @@ config USB_STKWEBCAM endif # V4L_USB_DRIVERS +config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 + select VIDEOBUF_DMA_SG + help + SoC Camera is a common API to several cameras, not connecting + over a bus like PCI or USB. For example some i2c camera connected + directly to the data bus of an SoC. + +config SOC_CAMERA_MT9M001 + tristate "mt9m001 support" + depends on SOC_CAMERA + select GPIO_PCA953X if MT9M001_PCA9536_SWITCH + help + This driver supports MT9M001 cameras from Micron, monochrome + and colour models. + +config MT9M001_PCA9536_SWITCH + bool "pca9536 datawidth switch for mt9m001" + depends on SOC_CAMERA_MT9M001 + help + Select this if your MT9M001 camera uses a PCA9536 I2C GPIO + extender to switch between 8 and 10 bit datawidth modes + +config SOC_CAMERA_MT9V022 + tristate "mt9v022 support" + depends on SOC_CAMERA + select GPIO_PCA953X if MT9V022_PCA9536_SWITCH + help + This driver supports MT9V022 cameras from Micron + +config MT9V022_PCA9536_SWITCH + bool "pca9536 datawidth switch for mt9v022" + depends on SOC_CAMERA_MT9V022 + help + Select this if your MT9V022 camera uses a PCA9536 I2C GPIO + extender to switch between 8 and 10 bit datawidth modes + +config VIDEO_PXA27x + tristate "PXA27x Quick Capture Interface driver" + depends on VIDEO_DEV && PXA27x + select SOC_CAMERA + ---help--- + This is a v4l2 driver for the PXA27x Quick Capture Interface + endif # VIDEO_CAPTURE_DRIVERS diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index cd4ee0584..a03f06c74 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -137,5 +137,10 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o +obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c index 479ca4b46..0cd7f1d5e 100644 --- a/linux/drivers/media/video/arv.c +++ b/linux/drivers/media/video/arv.c @@ -30,7 +30,10 @@ #include "compat.h" #include <linux/videodev.h> #include <media/v4l2-common.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) #include <linux/mutex.h> +#endif #include <asm/uaccess.h> #include <asm/m32r.h> @@ -116,7 +119,11 @@ struct ar_device { int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) struct mutex lock; +#else + struct semaphore lock; +#endif }; static int video_nr = -1; /* video device number (first free) */ diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 98270aaaf..00be77d4f 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -4445,7 +4445,7 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input) gpio_bits(0x200,0x000); mdelay(1); - /* create a new conection */ + /* create a new connection */ gpio_bits(0x480,0x080); gpio_bits(0x480,0x480); mdelay(1); diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 063bbcc58..0a9e01627 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -2801,6 +2801,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on) if (on) { fh->ov.tvnorm = btv->tvnorm; new = videobuf_pci_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); } else { new = NULL; diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index f5d65f675..dce005b14 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -33,7 +33,7 @@ #include <linux/pci.h> #include <asm/delay.h> -#include <sound/driver.h> +#include "compat.h" #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -43,7 +43,6 @@ #include <sound/tlv.h> #endif -#include "compat.h" #include "cx88.h" #include "cx88-reg.h" diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index 30ac9795b..1f641273c 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -667,13 +667,19 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) struct cx88_core *core = drv->core; /* Fail a request for hardware if the device is busy. */ - if (core->active_type_id != CX88_BOARD_NONE) + if (core->active_type_id != CX88_BOARD_NONE && + core->active_type_id != drv->type_id) return -EBUSY; if (drv->advise_acquire) { - core->active_type_id = drv->type_id; - drv->advise_acquire(drv); + mutex_lock(&drv->core->lock); + core->active_ref++; + if (core->active_type_id == CX88_BOARD_NONE) { + core->active_type_id = drv->type_id; + drv->advise_acquire(drv); + } + mutex_unlock(&drv->core->lock); mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); } @@ -686,12 +692,14 @@ static int cx8802_request_release(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; - if (drv->advise_release) + mutex_lock(&drv->core->lock); + if (drv->advise_release && --core->active_ref == 0) { drv->advise_release(drv); core->active_type_id = CX88_BOARD_NONE; mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); } + mutex_unlock(&drv->core->lock); return 0; } diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index ad0a86c15..bf1e2da46 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -346,6 +346,7 @@ struct cx88_core { /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; enum cx88_board_type active_type_id; + int active_ref; }; struct cx8800_dev; diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c index 9ac8e8682..258d46407 100644 --- a/linux/drivers/media/video/dpc7146.c +++ b/linux/drivers/media/video/dpc7146.c @@ -88,11 +88,24 @@ struct dpc int cur_input; /* current input */ }; +static int dpc_check_clients(struct device *dev, void *data) +{ + struct dpc* dpc = data; + struct i2c_client *client = i2c_verify_client(dev); + + if( !client ) + return 0; + + if( I2C_SAA7111A == client->addr ) + dpc->saa7111a = client; + + return 0; +} + /* fixme: add vbi stuff here */ static int dpc_probe(struct saa7146_dev* dev) { struct dpc* dpc = NULL; - struct i2c_client *client; dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); if( NULL == dpc ) { @@ -116,9 +129,7 @@ static int dpc_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - list_for_each_entry(client, &dpc->i2c_adapter.clients, list) - if( I2C_SAA7111A == client->addr ) - dpc->saa7111a = client; + device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients); /* check if all devices are present */ if( 0 == dpc->saa7111a ) { diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index abbd38c1e..0f7a0bd86 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -13,7 +13,8 @@ config VIDEO_EM28XX module will be called em28xx config VIDEO_EM28XX_ALSA - depends on VIDEO_EM28XX + depends on VIDEO_EM28XX && SND + select SND_PCM tristate "Empia EM28xx ALSA audio module" ---help--- This is an ALSA driver for some Empia 28xx based TV cards. diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index e97446778..37e893455 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -300,8 +300,11 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); /* Sets volume, mute, etc */ + dev->mute = 0; + mutex_lock(&dev->lock); ret = em28xx_audio_analog_set(dev); + mutex_unlock(&dev->lock); if (ret < 0) goto err; @@ -337,7 +340,9 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) dprintk("closing device\n"); dev->mute = 1; + mutex_lock(&dev->lock); em28xx_audio_analog_set(dev); + mutex_unlock(&dev->lock); if (dev->adev->users == 0 && dev->adev->shutdown == 1) { dprintk("audio users: %d\n", dev->adev->users); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 1a9236e24..db07cbbe3 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -394,15 +394,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, }; diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index a0ec1cfbc..a77cb8099 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -114,7 +114,8 @@ u32 em28xx_request_buffers(struct em28xx *dev, u32 count) const size_t imagesize = PAGE_ALIGN(dev->frame_size); /*needs to be page aligned cause the buffers can be mapped individually! */ void *buff = NULL; u32 i; - em28xx_coredbg("requested %i buffers with size %zi", count, imagesize); + em28xx_coredbg("requested %i buffers with size %zi\n", + count, imagesize); if (count > EM28XX_NUM_FRAMES) count = EM28XX_NUM_FRAMES; @@ -201,7 +202,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (reg_debug){ printk(ret < 0 ? " failed!\n" : "%02x values: ", ret); for (byte = 0; byte < len; byte++) { - printk(" %02x", buf[byte]); + printk(" %02x", (unsigned char)buf[byte]); } printk("\n"); } @@ -228,7 +229,8 @@ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) 0x0000, reg, &val, 1, HZ); if (reg_debug) - printk(ret < 0 ? " failed!\n" : "%02x\n", val); + printk(ret < 0 ? " failed!\n" : + "%02x\n", (unsigned char) val); if (ret < 0) return ret; @@ -305,26 +307,31 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, */ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { - int ret; + int ret, i; u8 addr = reg & 0x7f; if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0) return ret; if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0) return ret; - if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) - return ret; - else if (((u8) ret) & 0x01) { - em28xx_warn ("AC97 command still being executed: not handled properly!\n"); + + /* Wait up to 50 ms for AC97 command to complete */ + for (i = 0; i < 10; i++) { + if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) + return ret; + if (!((u8) ret) & 0x01) + return 0; + msleep(5); } + em28xx_warn ("AC97 command still being executed: not handled properly!\n"); return 0; } -int em28xx_set_audio_source(struct em28xx *dev) +static int em28xx_set_audio_source(struct em28xx *dev) { static char *enable = "\x08\x08"; static char *disable = "\x08\x88"; char *video = enable, *line = disable; - int ret, no_ac97; + int ret; u8 input; if (dev->is_em2800) { @@ -344,11 +351,9 @@ int em28xx_set_audio_source(struct em28xx *dev) switch (dev->ctl_ainput) { case EM28XX_AMUX_VIDEO: input = EM28XX_AUDIO_SRC_TUNER; - no_ac97 = 1; break; case EM28XX_AMUX_LINE_IN: input = EM28XX_AUDIO_SRC_LINE; - no_ac97 = 1; break; case EM28XX_AMUX_AC97_VIDEO: input = EM28XX_AUDIO_SRC_LINE; @@ -364,12 +369,11 @@ int em28xx_set_audio_source(struct em28xx *dev) ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); if (ret < 0) return ret; + msleep(5); - if (no_ac97) - return 0; - - /* Sets AC97 mixer registers */ - + /* Sets AC97 mixer registers + This is seems to be needed, even for non-ac97 configs + */ ret = em28xx_write_ac97(dev, VIDEO_AC97, video); if (ret < 0) return ret; @@ -388,9 +392,10 @@ int em28xx_audio_analog_set(struct em28xx *dev) s[0] |= 0x1f - dev->volume; s[1] |= 0x1f - dev->volume; - if (dev->mute) - s[1] |= 0x80; + /* Mute */ + s[1] |= 0x80; ret = em28xx_write_ac97(dev, MASTER_AC97, s); + if (ret < 0) return ret; @@ -408,6 +413,11 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); + /* Unmute device */ + if (!dev->mute) + s[1] &= ~0x80; + ret = em28xx_write_ac97(dev, MASTER_AC97, s); + return ret; } EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); @@ -730,7 +740,7 @@ static void em28xx_isocIrq(struct urb *urb) continue; } if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { + urb->iso_frame_desc[i].length) { em28xx_isocdbg("packet bigger than packet size"); continue; } @@ -776,8 +786,11 @@ void em28xx_uninit_isoc(struct em28xx *dev) for (i = 0; i < EM28XX_NUM_BUFS; i++) { if (dev->urb[i]) { usb_kill_urb(dev->urb[i]); - if (dev->transfer_buffer[i]){ - usb_buffer_free(dev->udev,(EM28XX_NUM_PACKETS*dev->max_pkt_size),dev->transfer_buffer[i],dev->urb[i]->transfer_dma); + if (dev->transfer_buffer[i]) { + usb_buffer_free(dev->udev, + dev->urb[i]->transfer_buffer_length, + dev->transfer_buffer[i], + dev->urb[i]->transfer_dma); } usb_free_urb(dev->urb[i]); } @@ -795,7 +808,10 @@ int em28xx_init_isoc(struct em28xx *dev) { /* change interface to 3 which allows the biggest packet sizes */ int i, errCode; - const int sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size; + int sb_size; + + em28xx_set_alternate(dev); + sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size; /* reset streaming vars */ dev->frame_current = NULL; @@ -804,7 +820,7 @@ int em28xx_init_isoc(struct em28xx *dev) /* allocate urbs */ for (i = 0; i < EM28XX_NUM_BUFS; i++) { struct urb *urb; - int j, k; + int j; /* allocate transfer buffer */ urb = usb_alloc_urb(EM28XX_NUM_PACKETS, GFP_KERNEL); if (!urb){ @@ -812,7 +828,9 @@ int em28xx_init_isoc(struct em28xx *dev) em28xx_uninit_isoc(dev); return -ENOMEM; } - dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL,&urb->transfer_dma); + dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, + GFP_KERNEL, + &urb->transfer_dma); if (!dev->transfer_buffer[i]) { em28xx_errdev ("unable to allocate %i bytes for transfer buffer %i\n", @@ -825,22 +843,22 @@ int em28xx_init_isoc(struct em28xx *dev) urb->dev = dev->udev; urb->context = dev; urb->pipe = usb_rcvisocpipe(dev->udev, 0x82); - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = 1; urb->transfer_buffer = dev->transfer_buffer[i]; urb->complete = em28xx_isocIrq; urb->number_of_packets = EM28XX_NUM_PACKETS; urb->transfer_buffer_length = sb_size; - for (j = k = 0; j < EM28XX_NUM_PACKETS; - j++, k += dev->max_pkt_size) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->max_pkt_size; + for (j = 0; j < EM28XX_NUM_PACKETS; j++) { + urb->iso_frame_desc[j].offset = j * dev->max_pkt_size; + urb->iso_frame_desc[j].length = dev->max_pkt_size; } dev->urb[i] = urb; } /* submit urbs */ + em28xx_coredbg("Submitting %d urbs of %d packets (%d each)\n", + EM28XX_NUM_BUFS, EM28XX_NUM_PACKETS, dev->max_pkt_size); for (i = 0; i < EM28XX_NUM_BUFS; i++) { errCode = usb_submit_urb(dev->urb[i], GFP_KERNEL); if (errCode) { @@ -857,40 +875,31 @@ int em28xx_init_isoc(struct em28xx *dev) int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; - dev->alt = alt; - if (dev->alt == 0) { - int i; -#if 1 /* Always try to get the maximum size value */ - for(i=0;i< dev->num_alt; i++) - if(dev->alt_max_pkt_size[i]>dev->alt_max_pkt_size[dev->alt]) - dev->alt=i; -#endif -#if 0 /* Should be dependent of horizontal size */ - if(dev->is_em2800){ /* always use the max packet size for em2800 based devices */ - for(i=0;i< dev->num_alt; i++) - if(dev->alt_max_pkt_size[i]>dev->alt_max_pkt_size[dev->alt]) - dev->alt=i; - }else{ - unsigned int min_pkt_size = dev->field_size / 137; /* FIXME: empiric magic number */ - em28xx_coredbg("minimum isoc packet size: %u", min_pkt_size); - dev->alt = 7; - for (i = 0; i < dev->num_alt; i ++) - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { - dev->alt = i; + int i; + unsigned int min_pkt_size = dev->bytesperline+4; + + /* When image size is bigger than a ceirtain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->frame_size > 720*240*2) + min_pkt_size *= 2; + + for (i = 0; i < dev->num_alt; i++) + if (dev->alt_max_pkt_size[i] >= min_pkt_size) break; - } - } -#endif - } + dev->alt = i; if (dev->alt != prev_alt) { + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, - dev->max_pkt_size); + 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); + dev->alt, errCode); return errCode; } } diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 7af56469e..4999897ed 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -211,7 +211,7 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); } - em28xx_set_audio_source(dev); + em28xx_audio_analog_set(dev); } /* Usage lock check functions */ @@ -852,6 +852,63 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int em28xx_reg_len(int reg) +{ + switch (reg) { + case AC97LSB_REG: + case HSCALELOW_REG: + case VSCALELOW_REG: + return 2; + default: + return 1; + } +} + +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_register *reg) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int ret; + + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + + if (em28xx_reg_len(reg->reg) == 1) { + ret = em28xx_read_reg(dev, reg->reg); + if (ret < 0) + return ret; + + reg->val = ret; + } else { + u64 val = 0; + ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, + reg->reg, (char *)&val, 2); + if (ret < 0) + return ret; + + reg->val = cpu_to_le64((__u64)val); + } + + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_register *reg) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + u64 buf; + + buf = le64_to_cpu((__u64)reg->val); + + return em28xx_write_regs(dev, reg->reg, (char *)&buf, + em28xx_reg_len(reg->reg)); +} +#endif + + static int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cc) { @@ -1354,8 +1411,6 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) filp->private_data = fh; if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { - em28xx_set_alternate(dev); - dev->width = norm_maxw(dev); dev->height = norm_maxh(dev); dev->frame_size = dev->width * dev->height * 2; @@ -1364,6 +1419,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev->hscale = 0; dev->vscale = 0; + em28xx_set_alternate(dev); em28xx_capture_start(dev, 1); em28xx_resolution_set(dev); @@ -1805,6 +1861,10 @@ static const struct video_device em28xx_video_template = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif .tvnorms = V4L2_STD_ALL, .current_norm = V4L2_STD_PAL, @@ -1827,6 +1887,10 @@ static struct video_device em28xx_radio_template = { .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif }; /******************************** usb interface *****************************************/ @@ -2159,6 +2223,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, snprintf(dev->name, 29, "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; + dev->alt = -1; /* Checks if audio is provided by some interface */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 2a797f622..980989f11 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -36,7 +36,7 @@ #define UNSET -1 /* maximum number of em28xx boards */ -#define EM28XX_MAXBOARDS 1 /*FIXME: should be bigger */ +#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ /* maximum number of frames that can be queued */ #define EM28XX_NUM_FRAMES 5 @@ -360,7 +360,6 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg); int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); -int em28xx_set_audio_source(struct em28xx *dev); int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); diff --git a/linux/drivers/media/video/indycam.c b/linux/drivers/media/video/indycam.c index 5c2c4029f..84b9e4f2b 100644 --- a/linux/drivers/media/video/indycam.c +++ b/linux/drivers/media/video/indycam.c @@ -326,7 +326,7 @@ static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) // initialize err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq); if (err) { - printk(KERN_ERR "IndyCam initalization failed\n"); + printk(KERN_ERR "IndyCam initialization failed\n"); err = -EIO; goto out_detach_client; } diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c index b1d965bcd..90451230d 100644 --- a/linux/drivers/media/video/ks0127.c +++ b/linux/drivers/media/video/ks0127.c @@ -765,7 +765,6 @@ static struct i2c_client ks0127_client_tmpl = .addr = 0, .adapter = NULL, .driver = &i2c_driver_ks0127, - .usage_count = 0 }; static int ks0127_found_proc(struct i2c_adapter *adapter, int addr, int kind) diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index 5e00e1fe3..1613ffb54 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -1244,6 +1244,7 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, c->default_value = 48; c->flags = 0; break; + case V4L2_CID_MEYE_SHARPNESS: case V4L2_CID_SHARPNESS: c->type = V4L2_CTRL_TYPE_INTEGER; strcpy(c->name, "Sharpness"); @@ -1251,7 +1252,12 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, c->maximum = 63; c->step = 1; c->default_value = 32; - c->flags = 0; + + /* Continue to report legacy private SHARPNESS ctrl but + * say it is disabled in preference to ctrl in the spec + */ + c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : + V4L2_CTRL_FLAG_DISABLED; break; case V4L2_CID_PICTURE: c->type = V4L2_CTRL_TYPE_INTEGER; @@ -1317,6 +1323,7 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, meye.params.agc = c->value; break; case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); meye.params.sharpness = c->value; @@ -1361,6 +1368,7 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, c->value = meye.params.agc; break; case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: c->value = meye.params.sharpness; break; case V4L2_CID_PICTURE: diff --git a/linux/drivers/media/video/mt20xx.c b/linux/drivers/media/video/mt20xx.c index cdf7450b5..5a8a53d8a 100644 --- a/linux/drivers/media/video/mt20xx.c +++ b/linux/drivers/media/video/mt20xx.c @@ -379,7 +379,7 @@ static struct dvb_tuner_ops mt2032_tuner_ops = { .get_frequency = microtune_get_frequency, }; -// Initalization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 +// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 static int mt2032_init(struct dvb_frontend *fe) { struct microtune_priv *priv = fe->tuner_priv; diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c new file mode 100644 index 000000000..3c5867c37 --- /dev/null +++ b/linux/drivers/media/video/mt9m001.c @@ -0,0 +1,694 @@ +/* + * Driver for MT9M001 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#include <asm/gpio.h> + +/* mt9m001 i2c address 0x5d + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +/* mt9m001 selected register addresses */ +#define MT9M001_CHIP_VERSION 0x00 +#define MT9M001_ROW_START 0x01 +#define MT9M001_COLUMN_START 0x02 +#define MT9M001_WINDOW_HEIGHT 0x03 +#define MT9M001_WINDOW_WIDTH 0x04 +#define MT9M001_HORIZONTAL_BLANKING 0x05 +#define MT9M001_VERTICAL_BLANKING 0x06 +#define MT9M001_OUTPUT_CONTROL 0x07 +#define MT9M001_SHUTTER_WIDTH 0x09 +#define MT9M001_FRAME_RESTART 0x0b +#define MT9M001_SHUTTER_DELAY 0x0c +#define MT9M001_RESET 0x0d +#define MT9M001_READ_OPTIONS1 0x1e +#define MT9M001_READ_OPTIONS2 0x20 +#define MT9M001_GLOBAL_GAIN 0x35 +#define MT9M001_CHIP_ENABLE 0xF1 + +static const struct soc_camera_data_format mt9m001_colour_formats[] = { + { + .name = "RGB Bayer (sRGB)", + .depth = 16, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +static const struct soc_camera_data_format mt9m001_monochrome_formats[] = { + { + .name = "Monochrome 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_Y16, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + }, +}; + +struct mt9m001 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + int switch_gpio; + unsigned char autoexposure; + unsigned char datawidth; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = mt9m001->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + return i2c_smbus_write_word_data(mt9m001->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int mt9m001_init(struct soc_camera_device *icd) +{ + int ret; + + /* Disable chip, synchronous option update */ + dev_dbg(icd->vdev->dev, "%s\n", __FUNCTION__); + + ret = reg_write(icd, MT9M001_RESET, 1); + if (ret >= 0) + ret = reg_write(icd, MT9M001_RESET, 0); + if (ret >= 0) + ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9m001_release(struct soc_camera_device *icd) +{ + /* Disable the chip */ + reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + return 0; +} + +static int mt9m001_start_capture(struct soc_camera_device *icd) +{ + /* Switch to master "normal" mode */ + if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 2) < 0) + return -EIO; + return 0; +} + +static int mt9m001_stop_capture(struct soc_camera_device *icd) +{ + /* Stop sensor readout */ + if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 0) < 0) + return -EIO; + return 0; +} + +static int bus_switch_request(struct mt9m001 *mt9m001, + struct soc_camera_link *icl) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + int ret; + unsigned int gpio = icl->gpio; + + if (gpio != NO_GPIO) { + /* We have a data bus switch. */ + ret = gpio_request(gpio, "mt9m001"); + if (ret < 0) { + dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n", + gpio); + return ret; + } + + ret = gpio_direction_output(gpio, 0); + if (ret < 0) { + dev_err(&mt9m001->client->dev, + "Cannot set GPIO %u to output\n", gpio); + gpio_free(gpio); + return ret; + } + } + + mt9m001->switch_gpio = gpio; +#else + mt9m001->switch_gpio = NO_GPIO; +#endif + return 0; +} + +static void bus_switch_release(struct mt9m001 *mt9m001) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + if (mt9m001->switch_gpio != NO_GPIO) + gpio_free(mt9m001->switch_gpio); +#endif +} + +static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + if (mt9m001->switch_gpio == NO_GPIO) + return -ENODEV; + + gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit); + return 0; +#else + return -ENODEV; +#endif +} + +static int mt9m001_set_capture_format(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect, unsigned int flags) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + unsigned int width_flag = flags & (IS_DATAWIDTH_10 | IS_DATAWIDTH_9 | + IS_DATAWIDTH_8); + int ret; + const u16 hblank = 9, vblank = 25; + + /* MT9M001 has all capture_format parameters fixed */ + if (!(flags & IS_MASTER) || + !(flags & IS_PCLK_SAMPLE_RISING) || + !(flags & IS_HSYNC_ACTIVE_HIGH) || + !(flags & IS_VSYNC_ACTIVE_HIGH)) + return -EINVAL; + + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; + + if ((mt9m001->datawidth != 10 && (width_flag == IS_DATAWIDTH_10)) || + (mt9m001->datawidth != 9 && (width_flag == IS_DATAWIDTH_9)) || + (mt9m001->datawidth != 8 && (width_flag == IS_DATAWIDTH_8))) { + /* data width switch requested */ + if (mt9m001->switch_gpio == NO_GPIO) + return -EINVAL; + + /* Well, we actually only can do 10 or 8 bits... */ + if (width_flag == IS_DATAWIDTH_9) + return -EINVAL; + ret = bus_switch_act(mt9m001, + width_flag == IS_DATAWIDTH_8); + if (ret < 0) + return ret; + + mt9m001->datawidth = width_flag == IS_DATAWIDTH_8 ? 8 : 10; + } + + /* Blanking and start values - default... */ + ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); + if (ret >= 0) + ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); + + /* The caller provides a supported format, as verified per + * call to icd->try_fmt_cap() */ + if (ret >= 0) + ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9M001_ROW_START, rect->top); + if (ret >= 0) + ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); + if (ret >= 0) + ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, + rect->height + icd->y_skip_top - 1); + if (ret >= 0 && mt9m001->autoexposure) { + ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + vblank); + if (ret >= 0) { + const struct v4l2_queryctrl *qctrl = + soc_camera_find_qctrl(icd->ops, + V4L2_CID_EXPOSURE); + icd->exposure = (524 + (rect->height + icd->y_skip_top + + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + 1048 + qctrl->minimum; + } + } + + return ret < 0 ? ret : 0; +} + +static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height < 32 + icd->y_skip_top) + f->fmt.pix.height = 32 + icd->y_skip_top; + if (f->fmt.pix.height > 1024 + icd->y_skip_top) + f->fmt.pix.height = 1024 + icd->y_skip_top; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 1280) + f->fmt.pix.width = 1280; + f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */ + + return 0; +} + +static int mt9m001_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9m001->client->addr) + return -ENODEV; + + id->ident = mt9m001->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m001_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9m001->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m001_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9m001->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static unsigned int mt9m001_get_datawidth(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + return mt9m001->datawidth; +} + +const struct v4l2_queryctrl mt9m001_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl); +static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl); + +static struct soc_camera_ops mt9m001_ops = { + .owner = THIS_MODULE, + .init = mt9m001_init, + .release = mt9m001_release, + .start_capture = mt9m001_start_capture, + .stop_capture = mt9m001_stop_capture, + .set_capture_format = mt9m001_set_capture_format, + .try_fmt_cap = mt9m001_try_fmt_cap, + .formats = NULL, /* Filled in later depending on the */ + .num_formats = 0, /* camera type and data widths */ + .get_datawidth = mt9m001_get_datawidth, + .controls = mt9m001_controls, + .num_controls = ARRAY_SIZE(mt9m001_controls), + .get_control = mt9m001_get_control, + .set_control = mt9m001_set_control, + .get_chip_id = mt9m001_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9m001_get_register, + .set_register = mt9m001_set_register, +#endif +}; + +static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9M001_READ_OPTIONS2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x8000); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9m001->autoexposure; + break; + } + return 0; +} + +static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + const struct v4l2_queryctrl *qctrl; + int data; + + qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9M001_READ_OPTIONS2, 0x8000); + else + data = reg_clear(icd, MT9M001_READ_OPTIONS2, 0x8000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->value <= qctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = qctrl->default_value - qctrl->minimum; + data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&icd->dev, "Setting gain %d\n", data); + data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = qctrl->maximum - qctrl->default_value - 1; + unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + reg_read(icd, MT9M001_GLOBAL_GAIN), data); + data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + + /* Success */ + icd->gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + /* mt9m001 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 + + range / 2) / range + 1; + + dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n", + reg_read(icd, MT9M001_SHUTTER_WIDTH), shutter); + if (reg_write(icd, MT9M001_SHUTTER_WIDTH, shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + mt9m001->autoexposure = 0; + } + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) { + const u16 vblank = 25; + if (reg_write(icd, MT9M001_SHUTTER_WIDTH, icd->height + + icd->y_skip_top + vblank) < 0) + return -EIO; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = (524 + (icd->height + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + 1048 + qctrl->minimum; + mt9m001->autoexposure = 1; + } else + mt9m001->autoexposure = 0; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9m001_video_probe(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + s32 data; + int ret; + + /* We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Enable the chip */ + data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1); + dev_dbg(&icd->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(icd, MT9M001_CHIP_VERSION); + + /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ + switch (data) { + case 0x8411: + case 0x8421: + mt9m001->model = V4L2_IDENT_MT9M001C12ST; + mt9m001_ops.formats = mt9m001_colour_formats; + mt9m001_ops.num_formats = ARRAY_SIZE(mt9m001_colour_formats); + break; + case 0x8431: + mt9m001->model = V4L2_IDENT_MT9M001C12STM; + mt9m001_ops.formats = mt9m001_monochrome_formats; + if (mt9m001->client->dev.platform_data) + mt9m001_ops.num_formats = ARRAY_SIZE(mt9m001_monochrome_formats); + else + mt9m001_ops.num_formats = 1; + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9M001 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + data == 0x8431 ? "C12STM" : "C12ST"); + + /* Now that we know the model, we can start video */ + ret = soc_camera_video_start(icd); + if (ret) + goto eisis; + + return 0; + +eisis: +ei2c: + return ret; +} + +static void mt9m001_video_remove(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, + mt9m001->icd.dev.parent, mt9m001->icd.vdev); + soc_camera_video_stop(&mt9m001->icd); +} + +static int mt9m001_probe(struct i2c_client *client) +{ + struct mt9m001 *mt9m001; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9M001 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL); + if (!mt9m001) + return -ENOMEM; + + mt9m001->client = client; + i2c_set_clientdata(client, mt9m001); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9m001->icd; + icd->probe = mt9m001_video_probe; + icd->remove = mt9m001_video_remove; + icd->ops = &mt9m001_ops; + icd->control = &client->dev; + icd->x_min = 20; + icd->y_min = 12; + icd->x_current = 20; + icd->y_current = 12; + icd->width_min = 48; + icd->width_max = 1280; + icd->height_min = 32; + icd->height_max = 1024; + icd->y_skip_top = 1; + icd->iface = icl->bus_id; + /* Default datawidth - this is the only width this camera (normally) + * supports. It is only with extra logic that it can support + * other widths. Therefore it seems to be a sensible default. */ + mt9m001->datawidth = 10; + /* Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width */ + mt9m001->autoexposure = 1; + + ret = bus_switch_request(mt9m001, icl); + if (ret) + goto eswinit; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + bus_switch_release(mt9m001); +eswinit: + kfree(mt9m001); + return ret; +} + +static int mt9m001_remove(struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9m001->icd); + bus_switch_release(mt9m001); + kfree(mt9m001); + + return 0; +} + +static struct i2c_driver mt9m001_i2c_driver = { + .driver = { + .name = "mt9m001", + }, + .probe = mt9m001_probe, + .remove = mt9m001_remove, +}; + +static int __init mt9m001_mod_init(void) +{ + return i2c_add_driver(&mt9m001_i2c_driver); +} + +static void __exit mt9m001_mod_exit(void) +{ + i2c_del_driver(&mt9m001_i2c_driver); +} + +module_init(mt9m001_mod_init); +module_exit(mt9m001_mod_exit); + +MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c new file mode 100644 index 000000000..9b406e419 --- /dev/null +++ b/linux/drivers/media/video/mt9v022.c @@ -0,0 +1,804 @@ +/* + * Driver for MT9V022 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#include <asm/gpio.h> + +/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +static char *sensor_type; +module_param(sensor_type, charp, S_IRUGO); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"\n"); + +/* mt9v022 selected register addresses */ +#define MT9V022_CHIP_VERSION 0x00 +#define MT9V022_COLUMN_START 0x01 +#define MT9V022_ROW_START 0x02 +#define MT9V022_WINDOW_HEIGHT 0x03 +#define MT9V022_WINDOW_WIDTH 0x04 +#define MT9V022_HORIZONTAL_BLANKING 0x05 +#define MT9V022_VERTICAL_BLANKING 0x06 +#define MT9V022_CHIP_CONTROL 0x07 +#define MT9V022_SHUTTER_WIDTH1 0x08 +#define MT9V022_SHUTTER_WIDTH2 0x09 +#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a +#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V022_RESET 0x0c +#define MT9V022_READ_MODE 0x0d +#define MT9V022_MONITOR_MODE 0x0e +#define MT9V022_PIXEL_OPERATION_MODE 0x0f +#define MT9V022_LED_OUT_CONTROL 0x1b +#define MT9V022_ADC_MODE_CONTROL 0x1c +#define MT9V022_ANALOG_GAIN 0x34 +#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 +#define MT9V022_PIXCLK_FV_LV 0x74 +#define MT9V022_DIGITAL_TEST_PATTERN 0x7f +#define MT9V022_AEC_AGC_ENABLE 0xAF +#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD + +/* Progressive scan, master, defaults */ +#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 + +static const struct soc_camera_data_format mt9v022_formats[] = { + { + .name = "RGB Bayer (sRGB)", + .depth = 8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "RGB Bayer (sRGB)", + .depth = 10, + .fourcc = V4L2_PIX_FMT_SBGGR16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "Monochrome 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_Y16, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + }, +}; + +struct mt9v022 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + int switch_gpio; + u16 chip_control; + unsigned char datawidth; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = mt9v022->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + return i2c_smbus_write_word_data(mt9v022->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int mt9v022_init(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + int ret; + + /* Almost the default mode: master, parallel, simultaneous, and an + * undocumented bit 0x200, which is present in table 7, but not in 8, + * plus snapshot mode to disable scan for now */ + mt9v022->chip_control |= 0x10; + ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret >= 0) + reg_write(icd, MT9V022_READ_MODE, 0x300); + + /* All defaults */ + if (ret >= 0) + /* AEC, AGC on */ + ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); + if (ret >= 0) + ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); + if (ret >= 0) + /* default - auto */ + ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); + if (ret >= 0) + ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9v022_release(struct soc_camera_device *icd) +{ + /* Nothing? */ + return 0; +} + +static int mt9v022_start_capture(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + if (reg_write(icd, MT9V022_CHIP_CONTROL, + mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int mt9v022_stop_capture(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; + if (reg_write(icd, MT9V022_CHIP_CONTROL, + mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + int ret; + unsigned int gpio = icl->gpio; + + if (gpio != NO_GPIO) { + /* We have a data bus switch. */ + ret = gpio_request(gpio, "mt9v022"); + if (ret < 0) { + dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio); + return ret; + } + + ret = gpio_direction_output(gpio, 0); + if (ret < 0) { + dev_err(&mt9v022->client->dev, + "Cannot set GPIO %u to output\n", gpio); + gpio_free(gpio); + return ret; + } + } + + mt9v022->switch_gpio = gpio; +#else + mt9v022->switch_gpio = NO_GPIO; +#endif + return 0; +} + +static void bus_switch_release(struct mt9v022 *mt9v022) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + if (mt9v022->switch_gpio != NO_GPIO) + gpio_free(mt9v022->switch_gpio); +#endif +} + +static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + if (mt9v022->switch_gpio == NO_GPIO) + return -ENODEV; + + gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit); + return 0; +#else + return -ENODEV; +#endif +} + +static int mt9v022_set_capture_format(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect, unsigned int flags) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + unsigned int width_flag = flags & (IS_DATAWIDTH_10 | IS_DATAWIDTH_9 | + IS_DATAWIDTH_8); + u16 pixclk = 0; + int ret; + + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; + + /* The caller provides a supported format, as verified per call to + * icd->try_fmt_cap(), datawidth is from our supported format list */ + switch (pixfmt) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) + return -EINVAL; + break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SBGGR16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) + return -EINVAL; + break; + case 0: + /* No format change, only geometry */ + break; + default: + return -EINVAL; + } + + /* Like in example app. Contradicts the datasheet though */ + ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (ret >= 0) { + if (ret & 1) /* Autoexposure */ + ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + 43); + else + ret = reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + 43); + } + /* Setup frame format: defaults apart from width and height */ + if (ret >= 0) + ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9V022_ROW_START, rect->top); + if (ret >= 0) + /* Default 94, Phytec driver says: + * "width + horizontal blank >= 660" */ + ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, + rect->width > 660 - 43 ? 43 : + 660 - rect->width); + if (ret >= 0) + ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); + if (ret >= 0) + ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); + if (ret >= 0) + ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, + rect->height + icd->y_skip_top); + + if (ret < 0) + return ret; + + dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); + + if ((mt9v022->datawidth != 10 && (width_flag == IS_DATAWIDTH_10)) || + (mt9v022->datawidth != 9 && (width_flag == IS_DATAWIDTH_9)) || + (mt9v022->datawidth != 8 && (width_flag == IS_DATAWIDTH_8))) { + /* data width switch requested */ + if (mt9v022->switch_gpio == NO_GPIO) + return -EINVAL; + + /* Well, we actually only can do 10 or 8 bits... */ + if (width_flag == IS_DATAWIDTH_9) + return -EINVAL; + + ret = bus_switch_act(mt9v022, + width_flag == IS_DATAWIDTH_8); + if (ret < 0) + return ret; + + mt9v022->datawidth = width_flag == IS_DATAWIDTH_8 ? 8 : 10; + } + + if (flags & IS_PCLK_SAMPLE_RISING) + pixclk |= 0x10; + + if (!(flags & IS_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & IS_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); + if (ret < 0) + return ret; + + if (!(flags & IS_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + +static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height < 32 + icd->y_skip_top) + f->fmt.pix.height = 32 + icd->y_skip_top; + if (f->fmt.pix.height > 480 + icd->y_skip_top) + f->fmt.pix.height = 480 + icd->y_skip_top; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 752) + f->fmt.pix.width = 752; + f->fmt.pix.width &= ~0x03; /* ? */ + + return 0; +} + +static int mt9v022_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9v022->client->addr) + return -ENODEV; + + id->ident = mt9v022->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v022_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9v022->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9v022_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9v022->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static unsigned int mt9v022_get_datawidth(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + return mt9v022->datawidth; +} + +const struct v4l2_queryctrl mt9v022_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Analog Gain", + .minimum = 64, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9v022_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl); +static int mt9v022_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl); + +static struct soc_camera_ops mt9v022_ops = { + .owner = THIS_MODULE, + .init = mt9v022_init, + .release = mt9v022_release, + .start_capture = mt9v022_start_capture, + .stop_capture = mt9v022_stop_capture, + .set_capture_format = mt9v022_set_capture_format, + .try_fmt_cap = mt9v022_try_fmt_cap, + .formats = mt9v022_formats, + .num_formats = ARRAY_SIZE(mt9v022_formats), + .get_datawidth = mt9v022_get_datawidth, + .controls = mt9v022_controls, + .num_controls = ARRAY_SIZE(mt9v022_controls), + .get_control = mt9v022_get_control, + .set_control = mt9v022_set_control, + .get_chip_id = mt9v022_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9v022_get_register, + .set_register = mt9v022_set_register, +#endif +}; + +static int mt9v022_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9V022_READ_MODE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x10); + break; + case V4L2_CID_HFLIP: + data = reg_read(icd, MT9V022_READ_MODE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x20); + break; + case V4L2_CID_EXPOSURE_AUTO: + data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x1); + break; + case V4L2_CID_AUTOGAIN: + data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x2); + break; + } + return 0; +} + +static int mt9v022_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + int data; + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9V022_READ_MODE, 0x10); + else + data = reg_clear(icd, MT9V022_READ_MODE, 0x10); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(icd, MT9V022_READ_MODE, 0x20); + else + data = reg_clear(icd, MT9V022_READ_MODE, 0x20); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + /* mt9v022 has minimum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + /* Datasheet says 16 to 64. autogain only works properly + * after setting gain to maximum 14. Larger values + * produce "white fly" noise effect. On the whole, + * manually setting analog gain does no good. */ + unsigned long gain = ((ctrl->value - qctrl->minimum) * + 10 + range / 2) / range + 4; + if (gain >= 32) + gain &= ~1; + /* The user wants to set gain manually, hope, she + * knows, what she's doing... Switch AGC off. */ + + if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + + dev_info(&icd->dev, "Setting gain from %d to %lu\n", + reg_read(icd, MT9V022_ANALOG_GAIN), gain); + if (reg_write(icd, MT9V022_ANALOG_GAIN, gain) < 0) + return -EIO; + icd->gain = ctrl->value; + } + break; + case V4L2_CID_EXPOSURE: + /* mt9v022 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + unsigned long shutter = ((ctrl->value - qctrl->minimum) * + 479 + range / 2) / range + 1; + /* The user wants to set shutter width manually, hope, + * she knows, what she's doing... Switch AEC off. */ + + if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) + return -EIO; + + dev_dbg(&icd->dev, "Shutter width from %d to %lu\n", + reg_read(icd, MT9V022_TOTAL_SHUTTER_WIDTH), + shutter); + if (reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + } + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->value) + data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + else + data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + if (data < 0) + return -EIO; + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) + data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + else + data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + if (data < 0) + return -EIO; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9v022_video_probe(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + s32 data; + int ret; + + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Read out the chip version register */ + data = reg_read(icd, MT9V022_CHIP_VERSION); + + /* must be 0x1311 or 0x1313 */ + if (data != 0x1311 && data != 0x1313) { + ret = -ENODEV; + dev_info(&icd->dev, "No MT9V022 detected, ID register 0x%x\n", + data); + goto ei2c; + } + + /* Soft reset */ + ret = reg_write(icd, MT9V022_RESET, 1); + if (ret < 0) + goto ei2c; + /* 15 clock cycles */ + udelay(200); + if (reg_read(icd, MT9V022_RESET)) { + dev_err(&icd->dev, "Resetting MT9V022 failed!\n"); + goto ei2c; + } + + /* Set monochrome or colour sensor type */ + if (sensor_type && (!strcmp("colour", sensor_type) || + !strcmp("color", sensor_type))) { + ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); + mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; + } else { + ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11); + mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; + } + + if (ret >= 0) + ret = soc_camera_video_start(icd); + if (ret < 0) + goto eisis; + + dev_info(&icd->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? + "monochrome" : "colour"); + + return 0; + +eisis: +ei2c: + return ret; +} + +static void mt9v022_video_remove(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, + mt9v022->icd.dev.parent, mt9v022->icd.vdev); + soc_camera_video_stop(&mt9v022->icd); +} + +static int mt9v022_probe(struct i2c_client *client) +{ + struct mt9v022 *mt9v022; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9V022 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL); + if (!mt9v022) + return -ENOMEM; + + mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; + mt9v022->client = client; + i2c_set_clientdata(client, mt9v022); + + icd = &mt9v022->icd; + icd->probe = mt9v022_video_probe; + icd->remove = mt9v022_video_remove; + icd->ops = &mt9v022_ops; + icd->control = &client->dev; + icd->x_min = 1; + icd->y_min = 4; + icd->x_current = 1; + icd->y_current = 4; + icd->width_min = 48; + icd->width_max = 752; + icd->height_min = 32; + icd->height_max = 480; + icd->y_skip_top = 1; + icd->iface = icl->bus_id; + /* Default datawidth - this is the only width this camera (normally) + * supports. It is only with extra logic that it can support + * other widths. Therefore it seems to be a sensible default. */ + mt9v022->datawidth = 10; + + ret = bus_switch_request(mt9v022, icl); + if (ret) + goto eswinit; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + bus_switch_release(mt9v022); +eswinit: + kfree(mt9v022); + return ret; +} + +static int mt9v022_remove(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9v022->icd); + bus_switch_release(mt9v022); + kfree(mt9v022); + + return 0; +} + +static struct i2c_driver mt9v022_i2c_driver = { + .driver = { + .name = "mt9v022", + }, + .probe = mt9v022_probe, + .remove = mt9v022_remove, +}; + +static int __init mt9v022_mod_init(void) +{ + return i2c_add_driver(&mt9v022_i2c_driver); +} + +static void __exit mt9v022_mod_exit(void) +{ + i2c_del_driver(&mt9v022_i2c_driver); +} + +module_init(mt9v022_mod_init); +module_exit(mt9v022_mod_exit); + +MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index 354ecaf8d..97cc92c82 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -150,10 +150,33 @@ struct mxb static struct saa7146_extension extension; +static int mxb_check_clients(struct device *dev, void *data) +{ + struct mxb* mxb = data; + struct i2c_client *client = i2c_verify_client(dev); + + if( !client ) + return 0; + + if( I2C_ADDR_TEA6420_1 == client->addr ) + mxb->tea6420_1 = client; + if( I2C_ADDR_TEA6420_2 == client->addr ) + mxb->tea6420_2 = client; + if( I2C_TEA6415C_2 == client->addr ) + mxb->tea6415c = client; + if( I2C_ADDR_TDA9840 == client->addr ) + mxb->tda9840 = client; + if( I2C_SAA7111 == client->addr ) + mxb->saa7111a = client; + if( 0x60 == client->addr ) + mxb->tuner = client; + + return 0; +} + static int mxb_probe(struct saa7146_dev* dev) { struct mxb* mxb = NULL; - struct i2c_client *client; int result; if ((result = request_module("saa7111")) < 0) { @@ -196,20 +219,7 @@ static int mxb_probe(struct saa7146_dev* dev) } /* loop through all i2c-devices on the bus and look who is there */ - list_for_each_entry(client, &mxb->i2c_adapter.clients, list) { - if( I2C_ADDR_TEA6420_1 == client->addr ) - mxb->tea6420_1 = client; - if( I2C_ADDR_TEA6420_2 == client->addr ) - mxb->tea6420_2 = client; - if( I2C_TEA6415C_2 == client->addr ) - mxb->tea6415c = client; - if( I2C_ADDR_TDA9840 == client->addr ) - mxb->tda9840 = client; - if( I2C_SAA7111 == client->addr ) - mxb->saa7111a = client; - if( 0x60 == client->addr ) - mxb->tuner = client; - } + device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients); /* check if all devices are present */ if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c diff --git a/linux/drivers/media/video/planb.c b/linux/drivers/media/video/planb.c index 358dc2996..e9860c883 100644 --- a/linux/drivers/media/video/planb.c +++ b/linux/drivers/media/video/planb.c @@ -50,7 +50,10 @@ #include <asm/pgtable.h> #include <asm/page.h> #include <asm/irq.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) #include <linux/mutex.h> +#endif #include "planb.h" #include "saa7196.h" diff --git a/linux/drivers/media/video/planb.h b/linux/drivers/media/video/planb.h index e21b5735c..944e4960a 100644 --- a/linux/drivers/media/video/planb.h +++ b/linux/drivers/media/video/planb.h @@ -174,7 +174,11 @@ struct planb { int user; unsigned int tab_size; int maxlines; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) struct mutex lock; +#else + struct semaphore lock; +#endif unsigned int irq; /* interrupt number */ volatile unsigned int intr_mask; struct pci_dev *dev; /* Our PCI device */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2.h b/linux/drivers/media/video/pvrusb2/pvrusb2.h index 074533e9c..1a9a4baf1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2.h @@ -27,7 +27,7 @@ might want to increase this - however the driver operation will not be impaired if it is too small. Instead additional units just won't have an ID assigned and it might not be possible to specify - module paramters for those extra units. */ + module parameters for those extra units. */ #define PVR_NUM 20 #endif /* __PVRUSB2_H */ diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index b09443b09..6e8474fc0 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -542,7 +542,7 @@ int pwc_handle_frame(struct pwc_device *pdev) } if (pdev->read_frame != NULL) { - /* Decompression is a lenghty process, so it's outside of the lock. + /* Decompression is a lengthy process, so it's outside of the lock. This gives the isoc_handler the opportunity to fill more frames in the mean time. */ diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c new file mode 100644 index 000000000..11aecad76 --- /dev/null +++ b/linux/drivers/media/video/pxa_camera.c @@ -0,0 +1,937 @@ +/* + * V4L2 Driver for PXA camera host + * + * Copyright (C) 2006, Sascha Hauer, Pengutronix + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * 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 <asm/io.h> + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/time.h> +#include <linux/version.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/clk.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/soc_camera.h> + +#include <linux/videodev2.h> + +#include <asm/dma.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/camera.h> + +#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) +#define PXA_CAM_DRV_NAME "pxa27x-camera" + +#define CICR0_IRQ_MASK (CICR0_TOM | CICR0_RDAVM | CICR0_FEM | CICR0_EOLM | \ + CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ + CICR0_EOFM | CICR0_FOM) + +static DEFINE_MUTEX(camera_lock); + +/* + * Structures + */ + +/* buffer for one video frame */ +struct pxa_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + const struct soc_camera_data_format *fmt; + + /* our descriptor list needed for the PXA DMA engine */ + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + size_t sg_size; + int inwork; +}; + +struct pxa_framebuffer_queue { + dma_addr_t sg_last_dma; + struct pxa_dma_desc *sg_last_cpu; +}; + +struct pxa_camera_dev { + struct device *dev; + /* PXA27x is only supposed to handle one camera on its Quick Capture + * interface. If anyone ever builds hardware to enable more than + * one camera, they will have to modify this driver too */ + struct soc_camera_device *icd; + struct clk *clk; + + unsigned int irq; + void __iomem *base; + unsigned int dma_chan_y; + + enum v4l2_buf_type type; + + struct pxacamera_platform_data *pdata; + struct resource *res; + unsigned long platform_flags; + unsigned long platform_mclk_10khz; + + struct list_head capture; + + spinlock_t lock; + + int dma_running; + + struct pxa_buffer *active; +}; + +static const char *pxa_cam_driver_description = "PXA_Camera"; + +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* + * Videobuf operations + */ +static int +pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + *size = icd->width * icd->height * ((icd->current_fmt->depth + 7) >> 3); + + if (0 == *count) + *count = 32; + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__, + &buf->vb, buf->vb.baddr, buf->vb.bsize); + + /* This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(&buf->vb, 0, 0); + videobuf_dma_unmap(vq, dma); + videobuf_dma_free(dma); + + if (buf->sg_cpu) + dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, + buf->sg_dma); + buf->sg_cpu = NULL; + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); + int i, ret; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__, + vb, vb->baddr, vb->bsize); + + /* Added list head initialization on alloc */ + WARN_ON(!list_empty(&vb->queue)); + +#ifdef DEBUG + /* This can be useful if you want to see if we actually fill + * the buffer with something */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + BUG_ON(NULL == icd->current_fmt); + + /* I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours */ + buf->inwork = 1; + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); + if (0 != vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + unsigned int size = vb->size; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + if (buf->sg_cpu) + dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, + buf->sg_dma); + + buf->sg_size = (dma->sglen + 1) * sizeof(struct pxa_dma_desc); + buf->sg_cpu = dma_alloc_coherent(pcdev->dev, buf->sg_size, + &buf->sg_dma, GFP_KERNEL); + if (!buf->sg_cpu) { + ret = -ENOMEM; + goto fail; + } + + dev_dbg(&icd->dev, "nents=%d size: %d sg=0x%p\n", + dma->sglen, size, dma->sglist); + for (i = 0; i < dma->sglen; i++) { + struct scatterlist *sg = dma->sglist; + unsigned int dma_len = sg_dma_len(&sg[i]), xfer_len; + + /* CIBR0 */ + buf->sg_cpu[i].dsadr = pcdev->res->start + 0x28; + buf->sg_cpu[i].dtadr = sg_dma_address(&sg[i]); + /* PXA270 Developer's Manual 27.4.4.1: + * round up to 8 bytes */ + xfer_len = (min(dma_len, size) + 7) & ~7; + if (xfer_len & 7) + dev_err(&icd->dev, "Unaligned buffer: " + "dma_len %u, size %u\n", dma_len, size); + buf->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | + DCMD_INCTRGADDR | xfer_len; + size -= dma_len; + buf->sg_cpu[i].ddadr = buf->sg_dma + (i + 1) * + sizeof(struct pxa_dma_desc); + } + buf->sg_cpu[dma->sglen - 1].ddadr = DDADR_STOP; + buf->sg_cpu[dma->sglen - 1].dcmd |= DCMD_ENDIRQEN; + + vb->state = VIDEOBUF_PREPARED; + } + + buf->inwork = 0; + + return 0; + +fail: + free_buffer(vq, buf); +out: + buf->inwork = 0; + return ret; +} + +static void +pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); + struct pxa_buffer *active = pcdev->active; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + int nents = dma->sglen; + unsigned long flags; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__, + vb, vb->baddr, vb->bsize); + spin_lock_irqsave(&pcdev->lock, flags); + + list_add_tail(&vb->queue, &pcdev->capture); + + vb->state = VIDEOBUF_ACTIVE; + + if (!pcdev->active) { + CIFR |= CIFR_RESET_F; + DDADR(pcdev->dma_chan_y) = buf->sg_dma; + DCSR(pcdev->dma_chan_y) = DCSR_RUN; + pcdev->active = buf; + CICR0 |= CICR0_ENB; + } else { + struct videobuf_dmabuf *active_dma = + videobuf_to_dma(&active->vb); + /* Stop DMA engine */ + DCSR(pcdev->dma_chan_y) = 0; + + /* Add the descriptors we just initialized to the currently + * running chain + */ + active->sg_cpu[active_dma->sglen - 1].ddadr = buf->sg_dma; + + /* Setup a dummy descriptor with the DMA engines current + * state + */ + /* CIBR0 */ + buf->sg_cpu[nents].dsadr = pcdev->res->start + 0x28; + buf->sg_cpu[nents].dtadr = DTADR(pcdev->dma_chan_y); + buf->sg_cpu[nents].dcmd = DCMD(pcdev->dma_chan_y); + + if (DDADR(pcdev->dma_chan_y) == DDADR_STOP) { + /* The DMA engine is on the last descriptor, set the + * next descriptors address to the descriptors + * we just initialized + */ + buf->sg_cpu[nents].ddadr = buf->sg_dma; + } else { + buf->sg_cpu[nents].ddadr = DDADR(pcdev->dma_chan_y); + } + + /* The next descriptor is the dummy descriptor */ + DDADR(pcdev->dma_chan_y) = buf->sg_dma + nents * + sizeof(struct pxa_dma_desc); + +#ifdef DEBUG + if (CISR & CISR_IFO_0) { + dev_warn(pcdev->dev, "FIFO overrun\n"); + DDADR(pcdev->dma_chan_y) = pcdev->active->sg_dma; + + CICR0 &= ~CICR0_ENB; + CIFR |= CIFR_RESET_F; + DCSR(pcdev->dma_chan_y) = DCSR_RUN; + CICR0 |= CICR0_ENB; + } else +#endif + DCSR(pcdev->dma_chan_y) = DCSR_RUN; + } + + spin_unlock_irqrestore(&pcdev->lock, flags); + +} + +static void pxa_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); +#ifdef DEBUG + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__, + vb, vb->baddr, vb->bsize); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + dev_dbg(&icd->dev, "%s (active)\n", __FUNCTION__); + break; + case VIDEOBUF_QUEUED: + dev_dbg(&icd->dev, "%s (queued)\n", __FUNCTION__); + break; + case VIDEOBUF_PREPARED: + dev_dbg(&icd->dev, "%s (prepared)\n", __FUNCTION__); + break; + default: + dev_dbg(&icd->dev, "%s (unknown)\n", __FUNCTION__); + break; + } +#endif + + free_buffer(vq, buf); +} + +static void pxa_camera_dma_irq_y(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + struct pxa_buffer *buf; + unsigned long flags; + unsigned int status; + struct videobuf_buffer *vb; + + spin_lock_irqsave(&pcdev->lock, flags); + + status = DCSR(pcdev->dma_chan_y); + if (status & DCSR_BUSERR) { + dev_err(pcdev->dev, "%s: Bus Error\n", __FUNCTION__); + DCSR(pcdev->dma_chan_y) |= DCSR_BUSERR; + goto out; + } + + if (!(status & DCSR_ENDINTR)) { + dev_err(pcdev->dev, "%s: unknown dma interrupt source. " + "status: 0x%08x\n", __FUNCTION__, status); + goto out; + } + + DCSR(pcdev->dma_chan_y) |= DCSR_ENDINTR; + + if (!pcdev->active) { + dev_err(pcdev->dev, "%s: no active buf\n", __FUNCTION__); + goto out; + } + + vb = &pcdev->active->vb; + buf = container_of(vb, struct pxa_buffer, vb); + WARN_ON(buf->inwork || list_empty(&vb->queue)); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __FUNCTION__, + vb, vb->baddr, vb->bsize); + + /* _init is used to debug races, see comment in pxa_is_reqbufs() */ + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + DCSR(pcdev->dma_chan_y) = 0; + CICR0 &= ~CICR0_ENB; + goto out; + } + + pcdev->active = list_entry(pcdev->capture.next, struct pxa_buffer, + vb.queue); + +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static struct videobuf_queue_ops pxa_video_ops = { + .buf_setup = pxa_videobuf_setup, + .buf_prepare = pxa_videobuf_prepare, + .buf_queue = pxa_videobuf_queue, + .buf_release = pxa_videobuf_release, +}; + +static int mclk_get_divisor(struct pxa_camera_dev *pcdev) +{ + unsigned int mclk_10khz = pcdev->platform_mclk_10khz; + unsigned long div; + unsigned long lcdclk; + + lcdclk = clk_get_rate(pcdev->clk) / 10000; + + /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here + * they get a nice Oops */ + div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1; + + dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, " + "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div); + + return div; +} + +static void pxa_is_activate(struct pxa_camera_dev *pcdev) +{ + struct pxacamera_platform_data *pdata = pcdev->pdata; + u32 cicr4 = 0; + + dev_dbg(pcdev->dev, "Registered platform device at %p data %p\n", + pcdev, pdata); + + if (pdata && pdata->init) { + dev_dbg(pcdev->dev, "%s: Init gpios\n", __FUNCTION__); + pdata->init(pcdev->dev); + } + + if (pdata && pdata->power) { + dev_dbg(pcdev->dev, "%s: Power on camera\n", __FUNCTION__); + pdata->power(pcdev->dev, 1); + } + + if (pdata && pdata->reset) { + dev_dbg(pcdev->dev, "%s: Releasing camera reset\n", + __FUNCTION__); + pdata->reset(pcdev->dev, 1); + } + + CICR0 = 0x3FF; /* disable all interrupts */ + + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + cicr4 |= CICR4_PCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + cicr4 |= CICR4_MCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_PCP) + cicr4 |= CICR4_PCP; + if (pcdev->platform_flags & PXA_CAMERA_HSP) + cicr4 |= CICR4_HSP; + if (pcdev->platform_flags & PXA_CAMERA_VSP) + cicr4 |= CICR4_VSP; + + CICR4 = mclk_get_divisor(pcdev) | cicr4; + + clk_enable(pcdev->clk); +} + +static void pxa_is_deactivate(struct pxa_camera_dev *pcdev) +{ + struct pxacamera_platform_data *board = pcdev->pdata; + + clk_disable(pcdev->clk); + + if (board && board->reset) { + dev_dbg(pcdev->dev, "%s: Asserting camera reset\n", + __FUNCTION__); + board->reset(pcdev->dev, 0); + } + + if (board && board->power) { + dev_dbg(pcdev->dev, "%s: Power off camera\n", __FUNCTION__); + board->power(pcdev->dev, 0); + } +} + +static irqreturn_t pxa_camera_irq(int irq, void *data) +{ + struct pxa_camera_dev *pcdev = data; + unsigned int status = CISR; + + dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status); + + CISR = status; + + return IRQ_HANDLED; +} + +/* The following two functions absolutely depend on the fact, that + * there can be only one camera on PXA quick capture interface */ +static int pxa_is_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + int ret; + + mutex_lock(&camera_lock); + + if (pcdev->icd) { + ret = -EBUSY; + goto ebusy; + } + + dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n", + icd->devnum); + + pxa_is_activate(pcdev); + ret = icd->ops->init(icd); + + if (!ret) + pcdev->icd = icd; + +ebusy: + mutex_unlock(&camera_lock); + + return ret; +} + +static void pxa_is_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + BUG_ON(icd != pcdev->icd); + + dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n", + icd->devnum); + + /* disable capture, disable interrupts */ + CICR0 = 0x3ff; + /* Stop DMA engine */ + DCSR(pcdev->dma_chan_y) = 0; + + icd->ops->release(icd); + + pxa_is_deactivate(pcdev); + + pcdev->icd = NULL; +} + +static int pxa_is_set_capture_format(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned int datawidth = 0, dw, bpp; + u32 cicr0, cicr4 = 0; + int ret; + + /* If requested data width is supported by the platform, use it */ + switch (icd->cached_datawidth) { + case 10: + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10) + datawidth = IS_DATAWIDTH_10; + break; + case 9: + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9) + datawidth = IS_DATAWIDTH_9; + break; + case 8: + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8) + datawidth = IS_DATAWIDTH_8; + } + if (!datawidth) + return -EINVAL; + + ret = icd->ops->set_capture_format(icd, pixfmt, rect, + datawidth | + (pcdev->platform_flags & PXA_CAMERA_MASTER ? + IS_MASTER : 0) | + (pcdev->platform_flags & PXA_CAMERA_HSP ? + 0 : IS_HSYNC_ACTIVE_HIGH) | + (pcdev->platform_flags & PXA_CAMERA_VSP ? + 0 : IS_VSYNC_ACTIVE_HIGH) | + (pcdev->platform_flags & PXA_CAMERA_PCP ? + 0 : IS_PCLK_SAMPLE_RISING)); + if (ret < 0) + return ret; + + /* Datawidth is now guaranteed to be equal to one of the three values. + * We fix bit-per-pixel equal to data-width... */ + switch (datawidth) { + case IS_DATAWIDTH_10: + icd->cached_datawidth = 10; + dw = 4; + bpp = 0x40; + break; + case IS_DATAWIDTH_9: + icd->cached_datawidth = 9; + dw = 3; + bpp = 0x20; + break; + default: + /* Actually it can only be 8 now, + * default is just to silence compiler warnings */ + case IS_DATAWIDTH_8: + icd->cached_datawidth = 8; + dw = 2; + bpp = 0; + } + + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + cicr4 |= CICR4_PCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + cicr4 |= CICR4_MCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_PCP) + cicr4 |= CICR4_PCP; + if (pcdev->platform_flags & PXA_CAMERA_HSP) + cicr4 |= CICR4_HSP; + if (pcdev->platform_flags & PXA_CAMERA_VSP) + cicr4 |= CICR4_VSP; + + cicr0 = CICR0; + if (cicr0 & CICR0_ENB) + CICR0 = cicr0 & ~CICR0_ENB; + CICR1 = CICR1_PPL_VAL(rect->width - 1) | bpp | dw; + CICR2 = 0; + CICR3 = CICR3_LPF_VAL(rect->height - 1) | + CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); + CICR4 = mclk_get_divisor(pcdev) | cicr4; + + /* CIF interrupts are not used, only DMA */ + CICR0 = (pcdev->platform_flags & PXA_CAMERA_MASTER ? + 0 : (CICR0_SL_CAP_EN | CICR0_SIM_SP)) | + CICR0_DMAEN | CICR0_IRQ_MASK | (cicr0 & CICR0_ENB); + + return 0; +} + +static int pxa_is_try_fmt_cap(struct soc_camera_host *ici, + struct v4l2_format *f) +{ + /* limit to pxa hardware capabilities */ + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > 2048) + f->fmt.pix.height = 2048; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 2048) + f->fmt.pix.width = 2048; + f->fmt.pix.width &= ~0x01; + + return 0; +} + +static int pxa_is_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + /* This is for locking debugging only. I removed spinlocks and now I + * check whether .prepare is ever called on a linked buffer, or whether + * a dma IRQ can occur for an in-work or unlinked buffer. Until now + * it hadn't triggered */ + for (i = 0; i < p->count; i++) { + struct pxa_buffer *buf = container_of(icf->vb_vidq.bufs[i], + struct pxa_buffer, vb); + buf->inwork = 0; + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +static unsigned int pxa_is_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct pxa_buffer *buf; + + buf = list_entry(icf->vb_vidq.stream.next, struct pxa_buffer, + vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN|POLLRDNORM; + + return 0; +} + +static int pxa_is_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the firendly caller:-> */ + strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); + cap->version = PXA_CAM_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +/* Should beallocated dynamically too, but we have only one. */ +static struct soc_camera_host pxa_soc_camera_host = { + .drv_name = PXA_CAM_DRV_NAME, + .vbq_ops = &pxa_video_ops, + .add = pxa_is_add_device, + .remove = pxa_is_remove_device, + .msize = sizeof(struct pxa_buffer), + .set_capture_format = pxa_is_set_capture_format, + .try_fmt_cap = pxa_is_try_fmt_cap, + .reqbufs = pxa_is_reqbufs, + .poll = pxa_is_poll, + .querycap = pxa_is_querycap, +}; + +static int pxa_camera_probe(struct platform_device *pdev) +{ + struct pxa_camera_dev *pcdev; + struct resource *res; + void __iomem *base; + unsigned int irq; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) { + err = -ENODEV; + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "%s: Could not allocate pcdev\n", + __FUNCTION__); + err = -ENOMEM; + goto exit; + } + + pcdev->clk = clk_get(&pdev->dev, "CAMCLK"); + if (IS_ERR(pcdev->clk)) { + err = PTR_ERR(pcdev->clk); + goto exit_kfree; + } + + dev_set_drvdata(&pdev->dev, pcdev); + pcdev->res = res; + + pcdev->pdata = pdev->dev.platform_data; + pcdev->platform_flags = pcdev->pdata->flags; + if (!pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | + PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10)) { + /* Platform hasn't set available data widths. This is bad. + * Warn and use a default. */ + dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " + "data widths, using default 10 bit\n"); + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; + } + pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz; + if (!pcdev->platform_mclk_10khz) { + dev_warn(&pdev->dev, + "mclk_10khz == 0! Please, fix your platform data. " + "Using default 20MHz\n"); + pcdev->platform_mclk_10khz = 2000; + } + + INIT_LIST_HEAD(&pcdev->capture); + spin_lock_init(&pcdev->lock); + + /* + * Request the regions. + */ + if (!request_mem_region(res->start, res->end - res->start + 1, + PXA_CAM_DRV_NAME)) { + err = -EBUSY; + goto exit_clk; + } + + base = ioremap(res->start, res->end - res->start + 1); + if (!base) { + err = -ENOMEM; + goto exit_release; + } + pcdev->irq = irq; + pcdev->base = base; + pcdev->dev = &pdev->dev; + + /* request dma */ + pcdev->dma_chan_y = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (pcdev->dma_chan_y < 0) { + dev_err(pcdev->dev, "Can't request DMA for Y\n"); + err = -ENOMEM; + goto exit_iounmap; + } + dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chan_y); + + DRCMR68 = pcdev->dma_chan_y | DRCMR_MAPVLD; + + /* request irq */ + err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, + pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_free_dma; + } + + pxa_soc_camera_host.priv = pcdev; + pxa_soc_camera_host.dev.parent = &pdev->dev; + pxa_soc_camera_host.nr = pdev->id; + err = soc_camera_host_register(&pxa_soc_camera_host, THIS_MODULE); + if (err) + goto exit_free_irq; + + return 0; + +exit_free_irq: + free_irq(pcdev->irq, pcdev); +exit_free_dma: + pxa_free_dma(pcdev->dma_chan_y); +exit_iounmap: + iounmap(base); +exit_release: + release_mem_region(res->start, res->end - res->start + 1); +exit_clk: + clk_put(pcdev->clk); +exit_kfree: + kfree(pcdev); +exit: + return err; +} + +static int __devexit pxa_camera_remove(struct platform_device *pdev) +{ + struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); + struct resource *res; + + clk_put(pcdev->clk); + + pxa_free_dma(pcdev->dma_chan_y); + free_irq(pcdev->irq, pcdev); + + soc_camera_host_unregister(&pxa_soc_camera_host); + + iounmap(pcdev->base); + + res = pcdev->res; + release_mem_region(res->start, res->end - res->start + 1); + + kfree(pcdev); + + dev_info(&pdev->dev, "%s: PXA Camera driver unloaded\n", __FUNCTION__); + + return 0; +} + +/* + * Suspend the Camera Module. + */ +static int pxa_camera_suspend(struct platform_device *pdev, pm_message_t level) +{ + struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "camera suspend\n"); + disable_irq(pcdev->irq); + return 0; +} + +/* + * Resume the Camera Module. + */ +static int pxa_camera_resume(struct platform_device *pdev) +{ + struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "camera resume\n"); + enable_irq(pcdev->irq); + +/* if (pcdev) { */ /* FIXME: dev in use? */ +/* DRCMR68 = pcdev->dma_chan_y | DRCMR_MAPVLD; */ +/* DRCMR69 = pcdev->dma_chan_cb | DRCMR_MAPVLD; */ +/* DRCMR70 = pcdev->dma_chan_cr | DRCMR_MAPVLD; */ +/* } */ + + return 0; +} + + +static struct platform_driver pxa_camera_driver = { + .driver = { + .name = PXA_CAM_DRV_NAME, + }, + .probe = pxa_camera_probe, + .remove = __exit_p(pxa_camera_remove), + .suspend = pxa_camera_suspend, + .resume = pxa_camera_resume, +}; + + +static int __devinit pxa_camera_init(void) +{ + return platform_driver_register(&pxa_camera_driver); +} + +static void __exit pxa_camera_exit(void) +{ + return platform_driver_unregister(&pxa_camera_driver); +} + +module_init(pxa_camera_init); +module_exit(pxa_camera_exit); + +MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index 49ff12d08..6f480f61e 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -22,7 +22,7 @@ #include <linux/time.h> #include <linux/wait.h> #include <linux/module.h> -#include <sound/driver.h> +#include "compat.h" #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 92219be5f..af9dfe35d 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3992,6 +3992,44 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_GENIUS_TVGO_A11MCE] = { + /* Adrian Pardini <pardo.bsso@gmail.com> */ + .name = "Genius TVGO AM11MCE", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0xf000, + .inputs = {{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x2000, + .tv = 1 + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x2000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x1000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x6000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5130,6 +5168,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_409: case SAA7134_BOARD_BEHOLD_505FM: case SAA7134_BOARD_BEHOLD_507_9FM: + case SAA7134_BOARD_GENIUS_TVGO_A11MCE: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index 0f9c06942..862d1f6a4 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -1312,9 +1312,8 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) static int saa7134_resume(struct pci_dev *pci_dev) { - struct saa7134_dev *dev = pci_get_drvdata(pci_dev); - unsigned int flags; + unsigned long flags; pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev); diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 02a4f7dbd..3ca2378b9 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -841,6 +841,7 @@ static struct tda1004x_config ads_tech_duo_config = { static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, + .diseqc_tone = 0, }; /* ================================================================== @@ -955,9 +956,9 @@ static int dvb_init(struct saa7134_dev *dev) configure_tda827x_fe(dev, &tda827x_lifeview_config); break; case SAA7134_BOARD_FLYDVB_TRIO: - if(! use_frontend) { //terrestrial + if(! use_frontend) { /* terrestrial */ configure_tda827x_fe(dev, &lifeview_trio_config); - } else { //satellite + } else { /* satellite */ dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 09414dd6d..225f075e5 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -406,6 +406,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x8000000; polling = 50; //ms break; + case SAA7134_BOARD_GENIUS_TVGO_A11MCE: + ir_codes = ir_codes_genius_tvgo_a11mce; + mask_keycode = 0xff; + mask_keydown = 0xf00000; + polling = 50; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 29d71cbac..d89255d03 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -44,7 +44,6 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,64) #include "i2c-compat.h" #else -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #endif @@ -261,6 +260,7 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_607_9FM 129 #define SAA7134_BOARD_BEHOLD_M6 130 #define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131 +#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c new file mode 100644 index 000000000..904e9dfc1 --- /dev/null +++ b/linux/drivers/media/video/soc_camera.c @@ -0,0 +1,973 @@ +/* + * camera image capture (abstract) bus driver + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This driver provides an interface between platform-specific camera + * busses and camera devices. It should be used if the camera is + * connected not over a "proper" bus like PCI or USB, but over a + * special bus, like, for example, the Quick Capture interface on PXA270 + * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. + * It can handle multiple cameras and / or multiple busses, which can + * be used, e.g., in stereo-vision applications. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/vmalloc.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/soc_camera.h> + +static LIST_HEAD(hosts); +static LIST_HEAD(devices); +static DEFINE_MUTEX(list_lock); +static DEFINE_MUTEX(video_lock); + +const static struct soc_camera_data_format* +format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->ops->num_formats; i++) + if (icd->ops->formats[i].fourcc == fourcc) + return icd->ops->formats + i; + return NULL; +} + +static int soc_camera_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + enum v4l2_field field; + const struct soc_camera_data_format *fmt; + int ret; + + WARN_ON(priv != file->private_data); + + fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!fmt) { + dev_dbg(&icd->dev, "invalid format 0x%08x\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { + field = V4L2_FIELD_NONE; + } else if (V4L2_FIELD_NONE != field) { + dev_err(&icd->dev, "Field type invalid.\n"); + return -EINVAL; + } + + /* limit to host capabilities */ + ret = ici->try_fmt_cap(ici, f); + + /* limit to sensor capabilities */ + if (!ret) + ret = icd->ops->try_fmt_cap(icd, f); + + /* calculate missing fields */ + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return ret; +} + +static int soc_camera_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Camera"); + + return 0; +} + +static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) +{ + return 0; +} + +static int soc_camera_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int ret; + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s: %d\n", __FUNCTION__, p->memory); + + ret = videobuf_reqbufs(&icf->vb_vidq, p); + if (ret < 0) + return ret; + + return ici->reqbufs(icf, p); + + return ret; +} + +static int soc_camera_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_querybuf(&icf->vb_vidq, p); +} + +static int soc_camera_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_qbuf(&icf->vb_vidq, p); +} + +static int soc_camera_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); +} + +static int soc_camera_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct soc_camera_device *icd = container_of(vdev->dev, + struct soc_camera_device, dev); + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct soc_camera_file *icf; + int ret; + + icf = vmalloc(sizeof(*icf)); + if (!icf) + return -ENOMEM; + + icf->icd = icd; + + if (!try_module_get(icd->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); + ret = -EINVAL; + goto emgd; + } + + if (!try_module_get(ici->owner)) { + dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + ret = -EINVAL; + goto emgi; + } + + file->private_data = icf; + dev_dbg(&icd->dev, "camera device open\n"); + + /* We must pass NULL as dev pointer, then all pci_* dma operations + * transform to normal dma_* ones. Do we need an irqlock? */ + videobuf_queue_pci_init(&icf->vb_vidq, ici->vbq_ops, NULL, NULL, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, + ici->msize, icd); + + return 0; + +emgi: + module_put(icd->ops->owner); +emgd: + vfree(icf); + return ret; +} + +static int soc_camera_close(struct inode *inode, struct file *file) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct video_device *vdev = icd->vdev; + + module_put(icd->ops->owner); + module_put(ici->owner); + vfree(file->private_data); + + dev_dbg(vdev->dev, "camera device close\n"); + + return 0; +} + +static int soc_camera_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct video_device *vdev = icd->vdev; + int err = -EINVAL; + + dev_err(vdev->dev, "camera device read not implemented\n"); + + return err; +} + +static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int err; + + dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + err = videobuf_mmap_mapper(&icf->vb_vidq, vma); + + dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, + err); + + return err; +} + +static unsigned int soc_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + if (list_empty(&icf->vb_vidq.stream)) { + dev_err(&icd->dev, "Trying to poll with no queued buffers!\n"); + return POLLERR; + } + + return ici->poll(file, pt); +} + + +static struct file_operations soc_camera_fops = { + .owner = THIS_MODULE, + .open = soc_camera_open, + .release = soc_camera_close, + .ioctl = video_ioctl2, + .read = soc_camera_read, + .mmap = soc_camera_mmap, + .poll = soc_camera_poll, + .llseek = no_llseek, +}; + + +static int soc_camera_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + struct v4l2_rect rect; + const static struct soc_camera_data_format *data_fmt; + + WARN_ON(priv != file->private_data); + + data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!data_fmt) + return -EINVAL; + + /* cached_datawidth may be further adjusted by the ici */ + icd->cached_datawidth = data_fmt->depth; + + ret = soc_camera_try_fmt_cap(file, icf, f); + if (ret < 0) + return ret; + + rect.left = icd->x_current; + rect.top = icd->y_current; + rect.width = f->fmt.pix.width; + rect.height = f->fmt.pix.height; + ret = ici->set_capture_format(icd, f->fmt.pix.pixelformat, &rect); + + if (!ret) { + icd->current_fmt = data_fmt; + icd->width = rect.width; + icd->height = rect.height; + icf->vb_vidq.field = f->fmt.pix.field; + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", + f->type); + + dev_dbg(&icd->dev, "set width: %d height: %d\n", + icd->width, icd->height); + } + + return ret; +} + +static int soc_camera_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + const struct soc_camera_data_format *format; + + WARN_ON(priv != file->private_data); + + if (f->index >= icd->ops->num_formats) + return -EINVAL; + + format = &icd->ops->formats[f->index]; + + strlcpy(f->description, format->name, sizeof(f->description)); + f->pixelformat = format->fourcc; + return 0; +} + +static int soc_camera_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + f->fmt.pix.width = icd->width; + f->fmt.pix.height = icd->height; + f->fmt.pix.field = icf->vb_vidq.field; + f->fmt.pix.pixelformat = icd->current_fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * icd->current_fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", + icd->current_fmt->fourcc); + return 0; +} + +static int soc_camera_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + WARN_ON(priv != file->private_data); + + strlcpy(cap->driver, ici->drv_name, sizeof(cap->driver)); + return ici->querycap(ici, cap); +} + +static int soc_camera_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s\n", __FUNCTION__); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + icd->ops->start_capture(icd); + + /* This calls buf_queue from host driver's videobuf_queue_ops */ + return videobuf_streamon(&icf->vb_vidq); +} + +static int soc_camera_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s\n", __FUNCTION__); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* This calls buf_release from host driver's videobuf_queue_ops for all + * remaining buffers. When the last buffer is freed, stop capture */ + videobuf_streamoff(&icf->vb_vidq); + + icd->ops->stop_capture(icd); + + return 0; +} + +static int soc_camera_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int i; + + WARN_ON(priv != file->private_data); + + if (!qc->id) + return -EINVAL; + + for (i = 0; i < icd->ops->num_controls; i++) + if (qc->id == icd->ops->controls[i].id) { + memcpy(qc, &(icd->ops->controls[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +static int soc_camera_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + if (icd->gain == (unsigned short)~0) + return -EINVAL; + ctrl->value = icd->gain; + return 0; + case V4L2_CID_EXPOSURE: + if (icd->exposure == (unsigned short)~0) + return -EINVAL; + ctrl->value = icd->exposure; + return 0; + } + + if (icd->ops->get_control) + return icd->ops->get_control(icd, ctrl); + return -EINVAL; +} + +static int soc_camera_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + if (icd->ops->set_control) + return icd->ops->set_control(icd, ctrl); + return -EINVAL; +} + +static int soc_camera_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->bounds.left = icd->x_min; + a->bounds.top = icd->y_min; + a->bounds.width = icd->width_max; + a->bounds.height = icd->height_max; + a->defrect.left = icd->x_min; + a->defrect.top = icd->y_min; + a->defrect.width = 640; + a->defrect.height = 480; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int soc_camera_g_crop(struct file *file, void *fh, + struct v4l2_crop *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c.left = icd->x_current; + a->c.top = icd->y_current; + a->c.width = icd->width; + a->c.height = icd->height; + + return 0; +} + +static int soc_camera_s_crop(struct file *file, void *fh, + struct v4l2_crop *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ret = ici->set_capture_format(icd, 0, &a->c); + if (!ret) { + icd->width = a->c.width; + icd->height = a->c.height; + icd->x_current = a->c.left; + icd->y_current = a->c.top; + } + + return ret; +} + +static int soc_camera_g_chip_ident(struct file *file, void *fh, + struct v4l2_chip_ident *id) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->get_chip_id) + return -EINVAL; + + return icd->ops->get_chip_id(icd, id); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int soc_camera_g_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->get_register) + return -EINVAL; + + return icd->ops->get_register(icd, reg); +} + +static int soc_camera_s_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->set_register) + return -EINVAL; + + return icd->ops->set_register(icd, reg); +} +#endif + +static int device_register_link(struct soc_camera_device *icd) +{ + int ret = device_register(&icd->dev); + + if (ret < 0) { + /* Prevent calling device_unregister() */ + icd->dev.parent = NULL; + dev_err(&icd->dev, "Cannot register device: %d\n", ret); + /* Even if probe() was unsuccessful for all registered drivers, + * device_register() returns 0, and we add the link, just to + * document this camera's control device */ + } else if (icd->control) + /* Have to sysfs_remove_link() before device_unregister()? */ + if (sysfs_create_link(&icd->dev.kobj, &icd->control->kobj, + "control")) + dev_warn(&icd->dev, + "Failed creating the control symlink\n"); + return ret; +} + +/* So far this function cannot fail */ +static void scan_add_host(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_for_each_entry(icd, &devices, list) { + if (icd->iface == ici->nr) { + icd->dev.parent = &ici->dev; + device_register_link(icd); + } + } + + mutex_unlock(&list_lock); +} + +/* return: 0 if no match found or a match found and + * device_register() successful, error code otherwise */ +static int scan_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici; + int ret = 0; + + mutex_lock(&list_lock); + + list_add_tail(&icd->list, &devices); + + /* Watch out for class_for_each_device / class_find_device API by + * Dave Young <hidave.darkstar@gmail.com> */ + list_for_each_entry(ici, &hosts, list) { + if (icd->iface == ici->nr) { + ret = 1; + icd->dev.parent = &ici->dev; + break; + } + } + + mutex_unlock(&list_lock); + + if (ret) + ret = device_register_link(icd); + + return ret; +} + +static int soc_camera_probe(struct device *dev) +{ + struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + + if (!icd->probe) + return -ENODEV; + + ret = ici->add(icd); + if (ret < 0) + return ret; + + ret = icd->probe(icd); + if (ret < 0) + ici->remove(icd); + else { + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); + icd->gain = qctrl ? qctrl->default_value : (unsigned short)~0; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = qctrl ? qctrl->default_value : + (unsigned short)~0; + } + + return ret; +} + +/* This is called on device_unregister, which only means we have to disconnect + * from the host, but not remove ourselves from the device list */ +static int soc_camera_remove(struct device *dev) +{ + struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + if (icd->remove) + icd->remove(icd); + + ici->remove(icd); + + return 0; +} + +static struct bus_type soc_camera_bus_type = { + .name = "soc-camera", + .probe = soc_camera_probe, + .remove = soc_camera_remove, +}; + +static struct device_driver ic_drv = { + .name = "camera", + .bus = &soc_camera_bus_type, + .owner = THIS_MODULE, +}; + +/* + * Image capture host - this is a host device, not a bus device, so, + * no bus reference, no probing. + */ +static struct class soc_camera_host_class = { + .owner = THIS_MODULE, + .name = "camera_host", +}; + +static void dummy_release(struct device *dev) +{ +} + +int soc_camera_host_register(struct soc_camera_host *ici, struct module *owner) +{ + int ret; + struct soc_camera_host *ix; + + if (!ici->vbq_ops || !ici->add || !ici->remove || !owner) + return -EINVAL; + + /* Number might be equal to the platform device ID */ + sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); + ici->dev.class = &soc_camera_host_class; + + mutex_lock(&list_lock); + list_for_each_entry(ix, &hosts, list) { + if (ix->nr == ici->nr) { + mutex_unlock(&list_lock); + return -EBUSY; + } + } + + list_add_tail(&ici->list, &hosts); + mutex_unlock(&list_lock); + + ici->owner = owner; + ici->dev.release = dummy_release; + + ret = device_register(&ici->dev); + + if (ret) + goto edevr; + + scan_add_host(ici); + + return 0; + +edevr: + mutex_lock(&list_lock); + list_del(&ici->list); + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(soc_camera_host_register); + +/* Unregister all clients! */ +void soc_camera_host_unregister(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_del(&ici->list); + + list_for_each_entry(icd, &devices, list) { + if (icd->dev.parent == &ici->dev) { + device_unregister(&icd->dev); + /* Not before device_unregister(), .remove + * needs parent to call ici->remove() */ + icd->dev.parent = NULL; + memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj)); + } + } + + mutex_unlock(&list_lock); + + device_unregister(&ici->dev); +} +EXPORT_SYMBOL(soc_camera_host_unregister); + +/* Image capture device */ +int soc_camera_device_register(struct soc_camera_device *icd) +{ + struct soc_camera_device *ix; + int num = -1, i; + + if (!icd) + return -EINVAL; + + for (i = 0; i < 256 && num < 0; i++) { + num = i; + list_for_each_entry(ix, &devices, list) { + if (ix->iface == icd->iface && ix->devnum == i) { + num = -1; + break; + } + } + } + + if (num < 0) + /* ok, we have 256 cameras on this host... + * man, stay reasonable... */ + return -ENOMEM; + + icd->devnum = num; + icd->dev.bus = &soc_camera_bus_type; + snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id), + "%u-%u", icd->iface, icd->devnum); + + icd->dev.release = dummy_release; + + if (icd->ops->get_datawidth) + icd->cached_datawidth = icd->ops->get_datawidth(icd); + + return scan_add_device(icd); +} +EXPORT_SYMBOL(soc_camera_device_register); + +void soc_camera_device_unregister(struct soc_camera_device *icd) +{ + mutex_lock(&list_lock); + list_del(&icd->list); + + /* The bus->remove will be eventually called */ + if (icd->dev.parent) + device_unregister(&icd->dev); + mutex_unlock(&list_lock); +} +EXPORT_SYMBOL(soc_camera_device_unregister); + +int soc_camera_video_start(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int err = -ENOMEM; + struct video_device *vdev; + + if (!icd->dev.parent) + return -ENODEV; + + vdev = video_device_alloc(); + if (!vdev) + goto evidallocd; + dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); + + strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); + /* Maybe better &ici->dev */ + vdev->dev = &icd->dev; + vdev->type = VID_TYPE_CAPTURE; + vdev->current_norm = V4L2_STD_UNKNOWN; + vdev->fops = &soc_camera_fops; + vdev->release = video_device_release; + vdev->minor = -1; + vdev->tvnorms = V4L2_STD_UNKNOWN, + vdev->vidioc_querycap = soc_camera_querycap; + vdev->vidioc_g_fmt_cap = soc_camera_g_fmt_cap; + vdev->vidioc_enum_fmt_cap = soc_camera_enum_fmt_cap; + vdev->vidioc_s_fmt_cap = soc_camera_s_fmt_cap; + vdev->vidioc_enum_input = soc_camera_enum_input; + vdev->vidioc_g_input = soc_camera_g_input; + vdev->vidioc_s_input = soc_camera_s_input; + vdev->vidioc_s_std = soc_camera_s_std; + vdev->vidioc_reqbufs = soc_camera_reqbufs; + vdev->vidioc_try_fmt_cap = soc_camera_try_fmt_cap; + vdev->vidioc_querybuf = soc_camera_querybuf; + vdev->vidioc_qbuf = soc_camera_qbuf; + vdev->vidioc_dqbuf = soc_camera_dqbuf; + vdev->vidioc_streamon = soc_camera_streamon; + vdev->vidioc_streamoff = soc_camera_streamoff; + vdev->vidioc_queryctrl = soc_camera_queryctrl; + vdev->vidioc_g_ctrl = soc_camera_g_ctrl; + vdev->vidioc_s_ctrl = soc_camera_s_ctrl; + vdev->vidioc_cropcap = soc_camera_cropcap; + vdev->vidioc_g_crop = soc_camera_g_crop; + vdev->vidioc_s_crop = soc_camera_s_crop; + vdev->vidioc_g_chip_ident = soc_camera_g_chip_ident; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vdev->vidioc_g_register = soc_camera_g_register; + vdev->vidioc_s_register = soc_camera_s_register; +#endif + + icd->current_fmt = &icd->ops->formats[0]; + + err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); + if (err < 0) { + dev_err(vdev->dev, "video_register_device failed\n"); + goto evidregd; + } + icd->vdev = vdev; + + return 0; + +evidregd: + video_device_release(vdev); +evidallocd: + return err; +} +EXPORT_SYMBOL(soc_camera_video_start); + +void soc_camera_video_stop(struct soc_camera_device *icd) +{ + struct video_device *vdev = icd->vdev; + + dev_dbg(&icd->dev, "%s\n", __FUNCTION__); + + if (!icd->dev.parent || !vdev) + return; + + mutex_lock(&video_lock); + video_unregister_device(vdev); + icd->vdev = NULL; + mutex_unlock(&video_lock); +} +EXPORT_SYMBOL(soc_camera_video_stop); + +static int __init soc_camera_init(void) +{ + int ret = bus_register(&soc_camera_bus_type); + if (ret) + return ret; + ret = driver_register(&ic_drv); + if (ret) + goto edrvr; + ret = class_register(&soc_camera_host_class); + if (ret) + goto eclr; + + return 0; + +eclr: + driver_unregister(&ic_drv); +edrvr: + bus_unregister(&soc_camera_bus_type); + return ret; +} + +static void __exit soc_camera_exit(void) +{ + class_unregister(&soc_camera_host_class); + driver_unregister(&ic_drv); + bus_unregister(&soc_camera_bus_type); +} + +module_init(soc_camera_init); +module_exit(soc_camera_exit); + +MODULE_DESCRIPTION("Image capture bus driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 916794e60..261675012 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -66,7 +66,7 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) dprintk("adr:0x%02x, i:%d, o:%d, g:%d\n", client->addr, i, o, g); - /* check if the paramters are valid */ + /* check if the parameters are valid */ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) return -1; diff --git a/linux/drivers/media/video/tuner-i2c.h b/linux/drivers/media/video/tuner-i2c.h index c53c327a4..60ba79480 100644 --- a/linux/drivers/media/video/tuner-i2c.h +++ b/linux/drivers/media/video/tuner-i2c.h @@ -83,7 +83,8 @@ static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, #define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ - i2c_adapter_id(i2cprops.adap), \ + i2cprops.adap ? \ + i2c_adapter_id(i2cprops.adap) : -1, \ i2cprops.addr, ##arg); \ } while (0) @@ -128,9 +129,10 @@ static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, ({ \ int __ret = 0; \ list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ - if ((i2c_adapter_id(state->i2c_props.adap) == \ - i2c_adapter_id(i2cadap)) && \ - (state->i2c_props.addr == i2caddr)) { \ + if ((state->i2c_props.addr == i2caddr) && \ + ((state->i2c_props.adap ? \ + i2c_adapter_id(state->i2c_props.adap) : -1) == \ + (i2cadap ? i2c_adapter_id(i2cadap) : -1))) { \ __tuner_info(state->i2c_props, \ "attaching existing instance\n"); \ state->i2c_props.count++; \ diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/video/tuner-xc2028.c index 95cc51bb7..89bba71d9 100644 --- a/linux/drivers/media/video/tuner-xc2028.c +++ b/linux/drivers/media/video/tuner-xc2028.c @@ -767,6 +767,9 @@ skip_std_specific: goto check_device; } + if (new_fw.type & FM) + goto check_device; + /* Load SCODE firmware, if exists */ tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr); diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index 30b3ba468..9b95d2f28 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -246,7 +246,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "TCL M2523_3DBH_E"}, { TUNER_ABSENT, "TCL M2523_3DIH_E"}, { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_ABSENT, "Philips FMD1216MEX"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216MEX"}, { TUNER_ABSENT, "Philips FRH2036B"}, { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, { TUNER_ABSENT, "MaxLinear MXL5005"}, diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.c b/linux/drivers/media/video/usbvideo/quickcam_messenger.c index e2af2d132..4b4e4d9f2 100644 --- a/linux/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.c @@ -275,7 +275,7 @@ static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) unsigned int p; /* - the registers controling gain are 8 bit of which + the registers controlling gain are 8 bit of which we affect only the last 4 bits with our gain. we know that if saturation is 0, (unsaturated) then we're grayscale (center axis of the colour cone) so diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index 5dc67e90d..e64547d12 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -137,7 +137,7 @@ static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { /* Function prototypes */ static void usbvision_release(struct usb_usbvision *usbvision); -/* Default initalization of device driver parameters */ +/* Default initialization of device driver parameters */ /* Set the default format for ISOC endpoint */ static int isocMode = ISOC_MODE_COMPRESS; /* Set the default Debug Mode of the device driver */ diff --git a/linux/drivers/media/video/videobuf-vmalloc.c b/linux/drivers/media/video/videobuf-vmalloc.c index 18e070204..4af0d329f 100644 --- a/linux/drivers/media/video/videobuf-vmalloc.c +++ b/linux/drivers/media/video/videobuf-vmalloc.c @@ -108,7 +108,7 @@ static struct vm_operations_struct videobuf_vm_ops = static void *__videobuf_alloc(size_t size) { - struct videbuf_vmalloc_memory *mem; + struct videobuf_vmalloc_memory *mem; struct videobuf_buffer *vb; vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); @@ -128,9 +128,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, struct v4l2_framebuffer *fbuf) { int pages; - - struct videbuf_vmalloc_memory *mem=vb->priv; - + struct videobuf_vmalloc_memory *mem=vb->priv; BUG_ON(!mem); @@ -196,7 +194,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) { - struct videbuf_vmalloc_memory *mem; + struct videobuf_vmalloc_memory *mem; struct videobuf_mapping *map; unsigned int first; int retval; @@ -268,7 +266,7 @@ static int __videobuf_copy_to_user ( struct videobuf_queue *q, char __user *data, size_t count, int nonblocking ) { - struct videbuf_vmalloc_memory *mem=q->read_buf->priv; + struct videobuf_vmalloc_memory *mem=q->read_buf->priv; BUG_ON (!mem); MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); @@ -289,7 +287,7 @@ static int __videobuf_copy_stream ( struct videobuf_queue *q, int vbihack, int nonblocking ) { unsigned int *fc; - struct videbuf_vmalloc_memory *mem=q->read_buf->priv; + struct videobuf_vmalloc_memory *mem=q->read_buf->priv; BUG_ON (!mem); MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); @@ -342,7 +340,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init); void *videobuf_to_vmalloc (struct videobuf_buffer *buf) { - struct videbuf_vmalloc_memory *mem=buf->priv; + struct videobuf_vmalloc_memory *mem=buf->priv; BUG_ON (!mem); MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); @@ -352,7 +350,7 @@ EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); void videobuf_vmalloc_free (struct videobuf_buffer *buf) { - struct videbuf_vmalloc_memory *mem=buf->priv; + struct videobuf_vmalloc_memory *mem=buf->priv; BUG_ON (!mem); MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 776edf222..b867e7e8c 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -41,7 +41,10 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> #include <linux/video_decoder.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) #include <linux/mutex.h> +#endif #include <asm/paccess.h> #include <asm/io.h> @@ -245,7 +248,11 @@ struct vino_framebuffer_queue { struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX]; spinlock_t queue_lock; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) struct mutex queue_mutex; +#else + struct semaphore queue_mutex; +#endif wait_queue_head_t frame_wait_queue; }; @@ -283,7 +290,12 @@ struct vino_channel_settings { /* the driver is currently processing the queue */ int capturing; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) struct mutex mutex; +#else + struct semaphore mutex; +#endif + spinlock_t capture_lock; unsigned int users; @@ -2593,11 +2605,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) /* First try D1 and then SAA7191 */ if (vino_drvdata->camera.driver && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { - if (i2c_use_client(vino_drvdata->camera.driver)) { - ret = -ENODEV; - goto out; - } - + i2c_use_client(vino_drvdata->camera.driver); vino_drvdata->camera.owner = vcs->channel; vcs->input = VINO_INPUT_D1; vcs->data_norm = VINO_DATA_NORM_D1; @@ -2606,11 +2614,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) int input, data_norm; int saa7191_input; - if (i2c_use_client(vino_drvdata->decoder.driver)) { - ret = -ENODEV; - goto out; - } - + i2c_use_client(vino_drvdata->decoder.driver); input = VINO_INPUT_COMPOSITE; saa7191_input = vino_get_saa7191_input(input); @@ -2692,10 +2696,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { - if (i2c_use_client(vino_drvdata->decoder.driver)) { - ret = -ENODEV; - goto out; - } + i2c_use_client(vino_drvdata->decoder.driver); vino_drvdata->decoder.owner = vcs->channel; } @@ -2763,10 +2764,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { - if (i2c_use_client(vino_drvdata->camera.driver)) { - ret = -ENODEV; - goto out; - } + i2c_use_client(vino_drvdata->camera.driver); vino_drvdata->camera.owner = vcs->channel; } diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c index 8a2dca0bb..989166eec 100644 --- a/linux/drivers/media/video/vpx3220.c +++ b/linux/drivers/media/video/vpx3220.c @@ -567,7 +567,7 @@ vpx3220_init_client (struct i2c_client *client) } /* ----------------------------------------------------------------------- - * Client managment code + * Client management code */ /* diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c index 1ad4ebaef..946dee4eb 100644 --- a/linux/drivers/media/video/zoran_card.c +++ b/linux/drivers/media/video/zoran_card.c @@ -1273,7 +1273,7 @@ zoran_setup_videocodec (struct zoran *zr, } /* - * Scan for a Buz card (actually for the PCI contoler ZR36057), + * Scan for a Buz card (actually for the PCI controller ZR36057), * request the irq and map the io memory */ static int __devinit diff --git a/linux/drivers/media/video/zr36050.c b/linux/drivers/media/video/zr36050.c index 44e68d7a8..cf8b271a1 100644 --- a/linux/drivers/media/video/zr36050.c +++ b/linux/drivers/media/video/zr36050.c @@ -160,7 +160,7 @@ zr36050_wait_end (struct zr36050 *ptr) udelay(1); if (i++ > 200000) { // 200ms, there is for sure something wrong!!! dprintk(1, - "%s: timout at wait_end (last status: 0x%02x)\n", + "%s: timeout at wait_end (last status: 0x%02x)\n", ptr->name, ptr->status1); break; } diff --git a/linux/drivers/media/video/zr36060.c b/linux/drivers/media/video/zr36060.c index 940055191..8e74054d5 100644 --- a/linux/drivers/media/video/zr36060.c +++ b/linux/drivers/media/video/zr36060.c @@ -163,7 +163,7 @@ zr36060_wait_end (struct zr36060 *ptr) udelay(1); if (i++ > 200000) { // 200ms, there is for sure something wrong!!! dprintk(1, - "%s: timout at wait_end (last status: 0x%02x)\n", + "%s: timeout at wait_end (last status: 0x%02x)\n", ptr->name, ptr->status); break; } |