diff options
Diffstat (limited to 'drivers/media')
191 files changed, 7067 insertions, 2694 deletions
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 1953ce559eca..0fcd3b5e60c8 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -338,8 +338,8 @@ int cec_register_adapter(struct cec_adapter *adap, res = cec_devnode_register(&adap->devnode, adap->owner); if (res) { #ifdef CONFIG_MEDIA_CEC_RC - /* Note: rc_unregister also calls rc_free */ rc_unregister_device(adap->rc); + rc_free_device(adap->rc); adap->rc = NULL; #endif return res; diff --git a/drivers/media/cec/usb/pulse8/pulse8-cec.c b/drivers/media/cec/usb/pulse8/pulse8-cec.c index 0df3af152762..fa5df1062753 100644 --- a/drivers/media/cec/usb/pulse8/pulse8-cec.c +++ b/drivers/media/cec/usb/pulse8/pulse8-cec.c @@ -235,6 +235,9 @@ static int pulse8_send_and_wait_once(struct pulse8 *pulse8, { int err; + if (!pulse8->serio) + return -ENODEV; + if (debug > 1) dev_info(pulse8->dev, "transmit %s: %*ph\n", pulse8_msgname(cmd[0]), cmd_len, cmd); @@ -655,6 +658,10 @@ static void pulse8_disconnect(struct serio *serio) { struct pulse8 *pulse8 = serio_get_drvdata(serio); + cancel_delayed_work_sync(&pulse8->ping_eeprom_work); + mutex_lock(&pulse8->lock); + pulse8->serio = NULL; + mutex_unlock(&pulse8->lock); cec_unregister_adapter(pulse8->adap); serio_set_drvdata(serio, NULL); serio_close(serio); diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c index abd3d21a18ca..f7766a9b31da 100644 --- a/drivers/media/common/b2c2/flexcop.c +++ b/drivers/media/common/b2c2/flexcop.c @@ -291,20 +291,6 @@ void flexcop_device_exit(struct flexcop_device *fc) } EXPORT_SYMBOL(flexcop_device_exit); -static int flexcop_module_init(void) -{ - info(DRIVER_NAME " loaded successfully"); - return 0; -} - -static void flexcop_module_cleanup(void) -{ - info(DRIVER_NAME " unloaded successfully"); -} - -module_init(flexcop_module_init); -module_exit(flexcop_module_cleanup); - MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_NAME); MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index fd12ef10409c..fe5c376d397a 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -417,19 +417,6 @@ int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev) } EXPORT_SYMBOL_GPL(saa7146_unregister_device); -static int __init saa7146_vv_init_module(void) -{ - return 0; -} - - -static void __exit saa7146_vv_cleanup_module(void) -{ -} - -module_init(saa7146_vv_init_module); -module_exit(saa7146_vv_cleanup_module); - MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c index af07fed21ae1..283770d583d5 100644 --- a/drivers/media/common/siano/smsir.c +++ b/drivers/media/common/siano/smsir.c @@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev) void sms_ir_exit(struct smscore_device_t *coredev) { rc_unregister_device(coredev->ir.dev); + rc_free_device(coredev->ir.dev); pr_debug("\n"); } diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c index 1ad4604474ac..0107cedd031e 100644 --- a/drivers/media/common/uvc.c +++ b/drivers/media/common/uvc.c @@ -41,6 +41,10 @@ static const struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_M420, }, { + .guid = UVC_GUID_FORMAT_P010, + .fcc = V4L2_PIX_FMT_P010, + }, + { .guid = UVC_GUID_FORMAT_UYVY, .fcc = V4L2_PIX_FMT_UYVY, }, diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 982021d547e5..b1d0695cda26 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -345,6 +345,7 @@ static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma) return err; } + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); /* * Use common vm_area operations to track buffer refcount. */ diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 58c4c489bf97..58b959b272c6 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -567,6 +567,7 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd, case AU8522_COMPOSITE_CH1: case AU8522_SVIDEO_CH13: case AU8522_COMPOSITE_CH4_SIF: + case AU8522_COMPOSITE_CH4: state->vid_input = input; break; default: diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index ebef27bcc989..d291113291e0 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2695,7 +2695,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff) static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) { - s16 unit_khz_dds_val; + s32 unit_khz_dds_val; u32 abs_offset_khz = abs(offset_khz); u32 dds = state->cfg.pll->ifreq & 0x1ffffff; u8 invert = !!(state->cfg.pll->ifreq & (1 << 25)); @@ -2716,7 +2716,7 @@ static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) dds = (1<<26) - dds; } else { ratio = 2; - unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal); + unit_khz_dds_val = 67108864 / state->cfg.pll->internal; if (offset_khz < 0) unit_khz_dds_val *= -1; diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index be156180e5ef..44bee1f3c5e9 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -78,7 +78,10 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data) .addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2 }; - m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x00); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); val = 0x11; ret = regmap_write(dev->regmap, 0x03, val); @@ -90,10 +93,18 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data) dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n", dev->dt_addr, ret, reg, data); - m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x10); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; } - m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x10); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", dev->dt_addr, reg, data); @@ -127,9 +138,14 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg) } }; - m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + if (dev->chip_id == M88DS3103C_CHIP_ID) { + m88ds3103_update_bits(dev, 0x04, 0x10, 0x00); + val = 0x11; + } else { + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + val = 0x12; + } - val = 0x12; ret = regmap_write(dev->regmap, 0x03, val); if (ret) dev_dbg(&client->dev, "fail=%d\n", ret); @@ -139,10 +155,18 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg) dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n", dev->dt_addr, ret, reg); - m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x10); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; } - m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x10); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", dev->dt_addr, reg, b1[0]); @@ -185,14 +209,25 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, switch (c->delivery_system) { case SYS_DVBS: - ret = regmap_read(dev->regmap, 0xd1, &utmp); - if (ret) - goto err; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + ret = regmap_read(dev->regmap, 0x0d, &utmp); + if (ret) + goto err; - if ((utmp & 0x07) == 0x07) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_LOCK; + if ((utmp & 0xf7) == 0xf7) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } else { + ret = regmap_read(dev->regmap, 0xd1, &utmp); + if (ret) + goto err; + + if ((utmp & 0x07) == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } break; case SYS_DVBS2: ret = regmap_read(dev->regmap, 0x0d, &utmp); @@ -201,8 +236,8 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, if ((utmp & 0x8f) == 0x8f) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_LOCK; + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; break; default: dev_dbg(&client->dev, "invalid delivery_system\n"); @@ -371,6 +406,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -428,8 +464,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev) reg15 = m88ds3103b_dt_read(dev, 0x15); - m88ds3103b_dt_write(dev, 0x05, 0x40); - m88ds3103b_dt_write(dev, 0x11, 0x08); + if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) { + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + } if (big_symbol) reg15 |= 0x02; @@ -441,8 +479,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev) usleep_range(5000, 5500); - m88ds3103b_dt_write(dev, 0x05, 0x00); - m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A)); + if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) { + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A)); + } usleep_range(5000, 5500); @@ -583,12 +623,6 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) sm = N - 1; - /* Write to registers */ - //reg15 &= 0x01; - //reg15 |= (pll_div_fb >> 8) & 0x01; - - //reg16 = pll_div_fb & 0xFF; - reg1D &= ~0x03; reg1D |= sm; reg1D |= 0x80; @@ -596,8 +630,11 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) reg1E = ((f3 << 4) + f2) & 0xFF; reg1F = ((f1 << 4) + f0) & 0xFF; - m88ds3103b_dt_write(dev, 0x05, 0x40); - m88ds3103b_dt_write(dev, 0x11, 0x08); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + } + m88ds3103b_dt_write(dev, 0x1D, reg1D); m88ds3103b_dt_write(dev, 0x1E, reg1E); m88ds3103b_dt_write(dev, 0x1F, reg1F); @@ -607,14 +644,88 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) usleep_range(5000, 5500); - m88ds3103b_dt_write(dev, 0x05, 0x00); - m88ds3103b_dt_write(dev, 0x11, 0x0A); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, 0x0A); + } usleep_range(5000, 5500); return 0; } +static int mt_fe_dmd_ds3103c_set_ts_out_mode(struct dvb_frontend *fe, enum m88ds3103_ts_mode mode) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + + unsigned int tmp, val = 0; + + regmap_read(dev->regmap, 0x0b, &val); + val &= ~0x01; + regmap_write(dev->regmap, 0x0b, val); + + regmap_read(dev->regmap, 0xfd, &tmp); + if (mode == M88DS3103_TS_PARALLEL) { + tmp &= ~0x01; + tmp &= ~0x04; + + regmap_write(dev->regmap, 0xfa, 0x01); + regmap_write(dev->regmap, 0xf1, 0x60); + regmap_write(dev->regmap, 0xfa, 0x00); + } else if (mode == M88DS3103_TS_SERIAL) { + tmp &= ~0x01; + tmp |= 0x04; + } else { + tmp |= 0x01; + tmp &= ~0x04; + + regmap_write(dev->regmap, 0xfa, 0x01); + regmap_write(dev->regmap, 0xf1, 0x60); + regmap_write(dev->regmap, 0xfa, 0x00); + } + + if (dev->cfg->ts_clk_pol) { + tmp &= ~0xf8; + tmp |= 0x02; + } else { + tmp &= ~0xb8; + tmp |= 0x42; + } + + tmp |= 0x80; + regmap_write(dev->regmap, 0xfd, tmp); + + val = 0; + if (mode != M88DS3103_TS_SERIAL) { + tmp = M88DS3103_TS_CI; + + val |= tmp & 0x03; + val |= (tmp << 2) & 0x0C; + val |= (tmp << 4) & 0x30; + val |= (tmp << 6) & 0xC0; + } else { + val = 0x00; + } + + regmap_write(dev->regmap, 0x0a, val); + + regmap_read(dev->regmap, 0x0b, &tmp); + + tmp &= ~0x20; + tmp |= 0x01; + + regmap_write(dev->regmap, 0x0b, tmp); + + regmap_read(dev->regmap, 0x0c, &tmp); + + regmap_write(dev->regmap, 0xf4, 0x01); + + tmp &= ~0x80; + regmap_write(dev->regmap, 0x0c, tmp); + + return 0; +} + static int m88ds3103_set_frontend(struct dvb_frontend *fe) { struct m88ds3103_dev *dev = fe->demodulator_priv; @@ -627,6 +738,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u16 u16tmp; u32 tuner_frequency_khz, target_mclk, u32tmp; s32 s32tmp; + unsigned int utmp; static const struct reg_sequence reset_buf[] = { {0x07, 0x80}, {0x07, 0x00} }; @@ -646,9 +758,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + /* Clear TS */ + ret = regmap_write(dev->regmap, 0xf5, 0x00); + /* Disable demod clock path */ - if (dev->chip_id == M88RS6000_CHIP_ID) { - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + if (dev->chip_id == M88RS6000_CHIP_ID || + dev->chip_id == M88DS3103C_CHIP_ID) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) { ret = regmap_read(dev->regmap, 0xb2, &u32tmp); if (ret) goto err; @@ -688,7 +805,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */ - if (dev->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID || + dev->chip_id == M88DS3103C_CHIP_ID) { if (c->symbol_rate > 45010000) dev->mclk = 110250000; else @@ -699,7 +817,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) else target_mclk = 144000000; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) { m88ds3103b_select_mclk(dev); m88ds3103b_set_mclk(dev, target_mclk / 1000); } @@ -772,6 +891,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (dev->chip_id == M88RS6000_CHIP_ID) { len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); init = m88rs6000_dvbs_init_reg_vals; + } else if (dev->chip_id == M88DS3103C_CHIP_ID) { + len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals); + init = m88ds3103c_dvbs_init_reg_vals; } else { len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); init = m88ds3103_dvbs_init_reg_vals; @@ -781,6 +903,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (dev->chip_id == M88RS6000_CHIP_ID) { len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); init = m88rs6000_dvbs2_init_reg_vals; + } else if (dev->chip_id == M88DS3103C_CHIP_ID) { + len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals); + init = m88ds3103c_dvbs_init_reg_vals; } else { len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); init = m88ds3103_dvbs2_init_reg_vals; @@ -799,7 +924,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; } - if (dev->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID || + dev->chip_id == M88DS3103C_CHIP_ID) { if (c->delivery_system == SYS_DVBS2 && c->symbol_rate <= 5000000) { ret = regmap_write(dev->regmap, 0xc0, 0x04); @@ -812,11 +938,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; } - ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); - if (ret) - goto err; + if (dev->chip_id != M88DS3103C_CHIP_ID) { + ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); + if (ret) + goto err; + } - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) { buf[0] = m88ds3103b_dt_read(dev, 0x15); buf[1] = m88ds3103b_dt_read(dev, 0x16); @@ -838,15 +967,22 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) m88ds3103b_dt_write(dev, 0x16, buf[1]); regmap_read(dev->regmap, 0x30, &u32tmp); - u32tmp &= ~0x80; - regmap_write(dev->regmap, 0x30, u32tmp & 0xff); + if (dev->chip_id == M88DS3103C_CHIP_ID) { + regmap_write(dev->regmap, 0x30, dev->config.agc_inv ? 0x18 : 0x08); + } else { + u32tmp &= ~0x80; + regmap_write(dev->regmap, 0x30, u32tmp & 0xff); + } } - ret = regmap_write(dev->regmap, 0xf1, 0x01); - if (ret) - goto err; + if (dev->chip_id != M88DS3103C_CHIP_ID) { + ret = regmap_write(dev->regmap, 0xf1, 0x01); + if (ret) + goto err; + } - if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) { + if (dev->chiptype != M88DS3103_CHIPTYPE_3103B && + dev->chiptype != M88DS3103_CHIPTYPE_3103C) { ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); if (ret) goto err; @@ -864,7 +1000,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) break; case M88DS3103_TS_PARALLEL: u8tmp = 0x02; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) { u8tmp = 0x01; u8tmp1 = 0x01; } @@ -881,10 +1018,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (dev->cfg->ts_clk_pol) u8tmp |= 0x40; - /* TS mode */ - ret = regmap_write(dev->regmap, 0xfd, u8tmp); - if (ret) - goto err; + if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) { + /* TS mode */ + ret = regmap_write(dev->regmap, 0xfd, u8tmp); + if (ret) + goto err; + } switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: @@ -918,7 +1057,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp); if (ret) goto err; - u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) + u8tmp = 0xcb; + else + u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; + ret = regmap_write(dev->regmap, 0xea, u8tmp); if (ret) goto err; @@ -930,7 +1074,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) else u8tmp = 0x06; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) m88ds3103b_set_mclk(dev, target_mclk / 1000); ret = regmap_write(dev->regmap, 0xc3, 0x08); @@ -949,9 +1094,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) + u16tmp = DIV_ROUND_CLOSEST_ULL((((u64)c->symbol_rate << 15) + dev->mclk / 4), + (dev->mclk / 2)); + else + u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); + buf[0] = (u16tmp >> 0) & 0xff; buf[1] = (u16tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); if (ret) goto err; @@ -960,7 +1111,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x30, 0x08, dev->cfg->agc_inv << 3); + } else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + ret = m88ds3103_update_bits(dev, 0x08, 0x43, 0x43); + + ret = m88ds3103_update_bits(dev, 0x30, 0x18, dev->cfg->agc_inv ? 0x18 : 0x08); + } else { + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); + } if (ret) goto err; @@ -974,10 +1133,37 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0); if (ret) goto err; + } + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + ret = m88ds3103_update_bits(dev, 0x76, 0x80, 0x00); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x22, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x23, 0x01, 0x00); + ret = m88ds3103_update_bits(dev, 0x24, 0x01, 0x00); ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); if (ret) goto err; + + ret = regmap_read(dev->regmap, 0x08, &utmp); + if (ret) + goto err; + + if (c->delivery_system == SYS_DVBS) { + utmp = (utmp & 0xfb) | 0x40; + regmap_write(dev->regmap, 0x08, utmp); + regmap_write(dev->regmap, 0xe0, 0xf8); + } else if (c->delivery_system == SYS_DVBS2) { + utmp = utmp | 0x44; + regmap_write(dev->regmap, 0x08, utmp); + } else { + utmp = utmp & 0xbb; + regmap_write(dev->regmap, 0x08, utmp); + regmap_write(dev->regmap, 0xe0, 0xf8); + } } dev_dbg(&client->dev, "carrier offset=%d\n", @@ -986,8 +1172,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */ s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency); s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000); + + usleep_range(1000, 1200); + buf[0] = (s32tmp >> 0) & 0xff; buf[1] = (s32tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); if (ret) goto err; @@ -1012,6 +1202,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1031,13 +1222,65 @@ static int m88ds3103_init(struct dvb_frontend *fe) dev->warm = false; /* wake up device from sleep */ - ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); - if (ret) - goto err; - ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + ret = m88ds3103_update_bits(dev, 0x0b, 0x90, 0x80); /* set dt_addr */ + + m88ds3103b_dt_write(dev, 0x04, 0x01); /* reset */ + m88ds3103b_dt_write(dev, 0x04, 0x00); + usleep_range(800, 1200); + + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); + if (ret) + goto err; + + m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */ + m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */ + + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x0b, 0x01, 0x01); + if (ret) + goto err; + /* global reset, global diseqc reset, global fec reset */ + ret = regmap_write(dev->regmap, 0x07, 0x80); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x07, 0x00); + if (ret) + goto err; + usleep_range(800, 1200); + + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); + if (ret) + goto err; + } else { + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); + if (ret) + goto err; + } + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */ + m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */ + m88ds3103b_dt_write(dev, 0x24, 0x04); + m88ds3103b_dt_write(dev, 0x84, 0x04); + m88ds3103b_dt_write(dev, 0x15, 0x6c); + usleep_range(800, 1200); + } + + /* global reset, global diseqc reset, global fec reset */ + ret = regmap_write(dev->regmap, 0x07, 0xe0); if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); + ret = regmap_write(dev->regmap, 0x07, 0x00); if (ret) goto err; @@ -1051,20 +1294,14 @@ static int m88ds3103_init(struct dvb_frontend *fe) if (utmp) goto warm; - /* global reset, global diseqc reset, global fec reset */ - ret = regmap_write(dev->regmap, 0x07, 0xe0); - if (ret) - goto err; - ret = regmap_write(dev->regmap, 0x07, 0x00); - if (ret) - goto err; - /* cold state - try to download firmware */ dev_info(&client->dev, "found a '%s' in cold state\n", dev->fe.ops.info.name); if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) name = M88DS3103B_FIRMWARE; + else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) + name = M88DS3103C_FIRMWARE; else if (dev->chip_id == M88RS6000_CHIP_ID) name = M88RS6000_FIRMWARE; else @@ -1116,6 +1353,18 @@ static int m88ds3103_init(struct dvb_frontend *fe) dev_info(&client->dev, "firmware version: %X.%X\n", (utmp >> 4) & 0xf, (utmp >> 0 & 0xf)); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) { + mt_fe_dmd_ds3103c_set_ts_out_mode(fe, dev->cfg->ts_mode); + + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv ? 0x10 : 0x00); + if (ret) + goto err; + } + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { m88ds3103b_dt_write(dev, 0x21, 0x92); m88ds3103b_dt_write(dev, 0x15, 0x6C); @@ -1139,6 +1388,7 @@ err_release_firmware: release_firmware(firmware); err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1157,6 +1407,8 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) /* TS Hi-Z */ if (dev->chip_id == M88RS6000_CHIP_ID) utmp = 0x29; + else if (dev->chip_id == M88DS3103C_CHIP_ID) + utmp = 0x0b; else utmp = 0x27; ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00); @@ -1167,6 +1419,17 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); if (ret) goto err; + + /* Internal tuner sleep */ + if (dev->chip_id == M88DS3103C_CHIP_ID) { + ret = m88ds3103b_dt_write(dev, 0x10, 0x00); + if (ret) + goto err; + ret = m88ds3103b_dt_write(dev, 0x11, 0x00); + if (ret) + goto err; + } + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); if (ret) goto err; @@ -1177,6 +1440,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1341,11 +1605,13 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe, if (ret) goto err; +// dev_dbg(&client->dev, "%s() 0x%X | 0x%X\n", __func__, buf[0], buf[1]); c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000); return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1378,7 +1644,8 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, int ret; unsigned int utmp, tone, reg_a1_mask; - dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode); + dev_dbg(&client->dev, "fe_sec_tone_mode=%s\n", + fe_sec_tone_mode == SEC_TONE_ON ? "ON" : "OFF"); if (!dev->warm) { ret = -EAGAIN; @@ -1413,6 +1680,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1463,6 +1731,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe, return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1542,6 +1811,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1621,6 +1891,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } @@ -1818,6 +2089,7 @@ static int m88ds3103_probe(struct i2c_client *client) switch (dev->chip_id) { case M88RS6000_CHIP_ID: case M88DS3103_CHIP_ID: + case M88DS3103C_CHIP_ID: break; default: ret = -ENODEV; @@ -1847,7 +2119,8 @@ static int m88ds3103_probe(struct i2c_client *client) /* 0x29 register is defined differently for m88rs6000. */ /* set internal tuner address to 0x21 */ - if (dev->chip_id == M88RS6000_CHIP_ID) + if (dev->chip_id == M88RS6000_CHIP_ID || + dev->chip_id == M88DS3103C_CHIP_ID) utmp = 0x00; ret = regmap_write(dev->regmap, 0x29, utmp); @@ -1882,6 +2155,9 @@ static int m88ds3103_probe(struct i2c_client *client) if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B", sizeof(dev->fe.ops.info.name)); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) + strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103C", + sizeof(dev->fe.ops.info.name)); else if (dev->chip_id == M88RS6000_CHIP_ID) strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000", sizeof(dev->fe.ops.info.name)); @@ -1894,15 +2170,22 @@ static int m88ds3103_probe(struct i2c_client *client) pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend; pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B || + dev->chiptype == M88DS3103_CHIPTYPE_3103C) { /* enable i2c repeater for tuner */ - m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + if (dev->chip_id == M88DS3103C_CHIP_ID) + m88ds3103_update_bits(dev, 0x04, 0x10, 0x10); + else + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); /* get frontend address */ ret = regmap_read(dev->regmap, 0x29, &utmp); if (ret) goto err_del_adapters; + dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) + dev->dt_addr = 0x5c >> 1; dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr); dev->dt_client = i2c_new_dummy_device(client->adapter, @@ -1941,6 +2224,7 @@ static const struct i2c_device_id m88ds3103_id_table[] = { {"m88ds3103", M88DS3103_CHIPTYPE_3103}, {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, + {"m88ds3103c", M88DS3103_CHIPTYPE_3103C}, {} }; MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index 594ad9cbc2cc..d7d16e7904da 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -17,15 +17,18 @@ #include <linux/math64.h> #define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw" +#define M88DS3103C_FIRMWARE "dvb-demod-m88ds3103c.fw" #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" #define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" -#define M88RS6000_CHIP_ID 0x74 -#define M88DS3103_CHIP_ID 0x70 +#define M88DS3103_CHIP_ID 0x70 +#define M88RS6000_CHIP_ID 0x74 +#define M88DS3103C_CHIP_ID 0x71 #define M88DS3103_CHIPTYPE_3103 0 #define M88DS3103_CHIPTYPE_RS6000 1 #define M88DS3103_CHIPTYPE_3103B 2 +#define M88DS3103_CHIPTYPE_3103C 3 struct m88ds3103_dev { struct i2c_client *client; @@ -399,4 +402,43 @@ static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = { {0xb8, 0x00}, {0x29, 0x01}, }; + +static const struct m88ds3103_reg_val m88ds3103c_dvbs_init_reg_vals[] = { + {0x04, 0x10}, + {0x8a, 0x01}, + {0x16, 0xa7}, + {0x30, 0x08}, + {0x32, 0x32}, + {0x33, 0x35}, + {0x35, 0xff}, + {0x4a, 0x80}, + {0x4d, 0x93}, + {0xae, 0x09}, + {0x22, 0x01}, + {0x23, 0x00}, + {0x24, 0x00}, + {0x27, 0x07}, + {0x9c, 0x31}, + {0x9d, 0xc1}, + {0xcb, 0xf4}, + {0xca, 0x00}, + {0x7f, 0x04}, + {0x78, 0x0c}, + {0x85, 0x08}, + {0x08, 0x47}, + {0xf0, 0x03}, + {0xfa, 0x01}, + {0xf2, 0x00}, + {0xfa, 0x00}, + {0xe6, 0x00}, + {0xe7, 0xf3}, + {0x08, 0x43}, + {0xe0, 0xf8}, + {0x00, 0x00}, + {0xbd, 0x82}, + {0x80, 0xa8}, + {0x81, 0xea}, + {0xbe, 0xa1} +}; + #endif diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index c4bbcd127cac..9c5bac8cda47 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -40,7 +40,7 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) if (cmd->rlen) { /* wait cmd execution terminate */ - #define TIMEOUT 70 + #define TIMEOUT 140 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { ret = i2c_master_recv(client, cmd->args, cmd->rlen); @@ -54,6 +54,8 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) /* firmware ready? */ if ((cmd->args[0] >> 7) & 0x01) break; + + usleep_range(2500, 3500); } dev_dbg(&client->dev, "cmd execution took %d ms\n", @@ -572,8 +574,8 @@ static int si2168_sleep(struct dvb_frontend *fe) if (ret) goto err; - /* Firmware later than B 4.0-11 loses warm state during sleep */ - if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) + /* Firmware B 4.0-11 and later lose warm state during sleep */ + if (dev->version >= ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) dev->warm = false; cmd_init(&cmd, "\x13", 1, 0); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5eb1e0e0a87a..8f2ba4121586 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -355,6 +355,7 @@ config VIDEO_MT9V111 config VIDEO_OG01A1B tristate "OmniVision OG01A1B sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OG01A1B camera. @@ -489,6 +490,19 @@ config VIDEO_OV2685 To compile this driver as a module, choose M here: the module will be called ov2685. +config VIDEO_OV2732 + tristate "OmniVision OV2732 sensor support" + depends on OF + select MEDIA_CONTROLLER + select V4L2_CCI_I2C + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor driver for the OmniVision + OV2732 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2732. + config VIDEO_OV2735 tristate "OmniVision OV2735 sensor support" select V4L2_CCI_I2C @@ -690,6 +704,7 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" depends on OF_GPIO + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV9282 camera sensor. @@ -788,6 +803,18 @@ config VIDEO_S5KJN1 To compile this driver as a module, choose M here: the module will be called s5kjn1. +config VIDEO_T4KA3 + tristate "Toshiba T4KA3 sensor support" + depends on ACPI || COMPILE_TEST + depends on GPIOLIB + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Toshiba T4KA3 8 MP + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called t4ka3. + config VIDEO_VD55G1 tristate "ST VD55G1 sensor support" select V4L2_CCI_I2C @@ -1724,8 +1751,8 @@ config VIDEO_DS90UB960 select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API help - Device driver for the Texas Instruments DS90UB960 - FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. + Device driver for the Texas Instruments DS90UB954, DS90UB960 + FPD-Link III Deserializers and DS90UB9702 FPD-Link IV Deserializer. config VIDEO_MAX96714 tristate "Maxim MAX96714 GMSL2 deserializer" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a3a6396df3c4..90b276a7417a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_VIDEO_OV2640) += ov2640.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o +obj-$(CONFIG_VIDEO_OV2732) += ov2732.o obj-$(CONFIG_VIDEO_OV2735) += ov2735.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_OV4689) += ov4689.o @@ -139,6 +140,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o +obj-$(CONFIG_VIDEO_T4KA3) += t4ka3.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_TC358746) += tc358746.o obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index e6fa2f961177..955b7072a560 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -2541,6 +2541,6 @@ module_i2c_driver(alvium_i2c_driver); MODULE_DESCRIPTION("Allied Vision's Alvium Camera Driver"); MODULE_AUTHOR("Tommaso Merciai <tomm.merciai@gmail.com>"); -MODULE_AUTHOR("Martin Hecht <martin.hecht@avnet.eu>"); +MODULE_AUTHOR("Martin Hecht <mhecht73@gmail.com>"); MODULE_AUTHOR("Avnet Silica Software & Services EMEA"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index f156058500e3..ed324c2d87aa 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -1094,6 +1094,9 @@ static int ar0521_probe(struct i2c_client *client) /* Request optional reset pin (usually active low) and assert it */ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "failed to get reset gpio\n"); v4l2_i2c_subdev_init(&sensor->sd, client, &ar0521_subdev_ops); diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 4eb83636e102..1605cfa5db19 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -824,9 +824,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, op_lim_fr->min_pll_ip_clk_freq_hz)); min_op_pre_pll_clk_div = max_t(u16, op_lim_fr->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(pll->ext_clk_freq_hz, - op_lim_fr->max_pll_ip_clk_freq_hz))); + DIV_ROUND_UP(pll->ext_clk_freq_hz, + op_lim_fr->max_pll_ip_clk_freq_hz)); dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n", min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index a86306304330..69d5cc648c0f 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -1652,10 +1652,14 @@ static int set_v4lstd(struct i2c_client *client) struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; + u8 pal_n = 0; + u8 ntsc_j = 0; + u8 tmp_reg = 0; /* First tests should be against specific std */ if (state->std == V4L2_STD_NTSC_M_JP) { fmt = 0x2; + ntsc_j = 0x80; } else if (state->std == V4L2_STD_NTSC_443) { fmt = 0x3; } else if (state->std == V4L2_STD_PAL_M) { @@ -1663,6 +1667,7 @@ static int set_v4lstd(struct i2c_client *client) fmt = 0x5; } else if (state->std == V4L2_STD_PAL_N) { fmt = 0x6; + pal_n = 0x40; } else if (state->std == V4L2_STD_PAL_Nc) { fmt = 0x7; } else if (state->std == V4L2_STD_PAL_60) { @@ -1689,10 +1694,30 @@ static int set_v4lstd(struct i2c_client *client) /* Set format to NTSC-M */ cx25840_and_or(client, 0x400, ~0xf, 1); /* Turn off LCOMB */ - cx25840_and_or(client, 0x47b, ~6, 0); + cx25840_and_or(client, 0x47b, ~0x6, 0); + } else if (fmt == 0xc) { /* SECAM - Step 9c - toggle CKILLEN */ + tmp_reg = cx25840_read(client, 0x401); + cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20); + cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00); } + cx25840_and_or(client, 0x400, ~0xf, fmt); - cx25840_and_or(client, 0x403, ~0x3, pal_m); + + if (fmt >= 4 && fmt < 8) { + tmp_reg = cx25840_read(client, 0x401); + cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x00 : 0x40); /* CAGCEN */ + cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x40 : 0x00); + cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20); /* CKILLEN */ + cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00); + } + + if (pal_m) + cx25840_and_or(client, 0x403, ~0x3, pal_m); + else if (pal_n) /* cx25840 datasheet table 3-19 */ + cx25840_and_or(client, 0x403, ~0x40, pal_n); + else if (ntsc_j) /* cx25840 datasheet table 3-19 */ + cx25840_and_or(client, 0x403, ~0x80, ntsc_j); + if (is_cx23888(state)) cx23888_std_setup(client); else diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index e97e499b04e6..49aa5f4a172c 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -372,63 +372,6 @@ static int ub913_set_routing(struct v4l2_subdev *sd, return _ub913_set_routing(sd, state, routing); } -static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - struct v4l2_mbus_frame_desc *fd) -{ - struct ub913_data *priv = sd_to_ub913(sd); - const struct v4l2_subdev_krouting *routing; - struct v4l2_mbus_frame_desc source_fd; - struct v4l2_subdev_route *route; - struct v4l2_subdev_state *state; - int ret; - - if (pad != UB913_PAD_SOURCE) - return -EINVAL; - - ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, - priv->source_sd_pad, &source_fd); - if (ret) - return ret; - - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; - - state = v4l2_subdev_lock_and_get_active_state(sd); - - routing = &state->routing; - - for_each_active_route(routing, route) { - unsigned int i; - - if (route->source_pad != pad) - continue; - - for (i = 0; i < source_fd.num_entries; i++) { - if (source_fd.entry[i].stream == route->sink_stream) - break; - } - - if (i == source_fd.num_entries) { - dev_err(&priv->client->dev, - "Failed to find stream from source frame desc\n"); - ret = -EPIPE; - goto out_unlock; - } - - fd->entry[fd->num_entries].stream = route->source_stream; - fd->entry[fd->num_entries].flags = source_fd.entry[i].flags; - fd->entry[fd->num_entries].length = source_fd.entry[i].length; - fd->entry[fd->num_entries].pixelcode = - source_fd.entry[i].pixelcode; - - fd->num_entries++; - } - -out_unlock: - v4l2_subdev_unlock_state(state); - - return ret; -} - static int ub913_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) @@ -544,7 +487,7 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = { .enable_streams = ub913_enable_streams, .disable_streams = ub913_disable_streams, .set_routing = ub913_set_routing, - .get_frame_desc = ub913_get_frame_desc, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub913_set_fmt, }; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index daefdb108fbf..a8ab67f4137f 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -424,65 +424,6 @@ static int ub953_set_routing(struct v4l2_subdev *sd, return _ub953_set_routing(sd, state, routing); } -static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - struct v4l2_mbus_frame_desc *fd) -{ - struct ub953_data *priv = sd_to_ub953(sd); - struct v4l2_mbus_frame_desc source_fd; - struct v4l2_subdev_route *route; - struct v4l2_subdev_state *state; - int ret; - - if (pad != UB953_PAD_SOURCE) - return -EINVAL; - - ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, - priv->source_sd_pad, &source_fd); - if (ret) - return ret; - - fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; - - state = v4l2_subdev_lock_and_get_active_state(sd); - - for_each_active_route(&state->routing, route) { - struct v4l2_mbus_frame_desc_entry *source_entry = NULL; - unsigned int i; - - if (route->source_pad != pad) - continue; - - for (i = 0; i < source_fd.num_entries; i++) { - if (source_fd.entry[i].stream == route->sink_stream) { - source_entry = &source_fd.entry[i]; - break; - } - } - - if (!source_entry) { - dev_err(&priv->client->dev, - "Failed to find stream from source frame desc\n"); - ret = -EPIPE; - goto out_unlock; - } - - fd->entry[fd->num_entries].stream = route->source_stream; - fd->entry[fd->num_entries].flags = source_entry->flags; - fd->entry[fd->num_entries].length = source_entry->length; - fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; - fd->entry[fd->num_entries].bus.csi2.vc = - source_entry->bus.csi2.vc; - fd->entry[fd->num_entries].bus.csi2.dt = - source_entry->bus.csi2.dt; - - fd->num_entries++; - } - -out_unlock: - v4l2_subdev_unlock_state(state); - - return ret; -} static int ub953_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, @@ -694,7 +635,7 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = { .enable_streams = ub953_enable_streams, .disable_streams = ub953_disable_streams, .set_routing = ub953_set_routing, - .get_frame_desc = ub953_get_frame_desc, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub953_set_fmt, }; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index e133127397f4..d50e977cf6ce 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -396,6 +396,13 @@ #define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3) #define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0) +#define UB954_IR_RX_ANA_STROBE_SET_CLK_DATA 0x08 +#define UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY BIT(3) +#define UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(7) +#define UB954_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK GENMASK(2, 0) +#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(4, 6) +#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT 4 + /* UB9702 Registers */ #define UB9702_SR_CSI_EXCLUSIVE_FWD2 0x3c @@ -454,12 +461,23 @@ #define UB960_MAX_EQ_LEVEL 14 #define UB960_NUM_EQ_LEVELS (UB960_MAX_EQ_LEVEL - UB960_MIN_EQ_LEVEL + 1) +enum chip_type { + UB954, + UB960, + UB9702, +}; + +enum chip_family { + FAMILY_FPD3, + FAMILY_FPD4, +}; + struct ub960_hw_data { const char *model; + enum chip_type chip_type; + enum chip_family chip_family; u8 num_rxports; u8 num_txports; - bool is_ub9702; - bool is_fpdlink4; }; enum ub960_rxport_mode { @@ -982,6 +1000,10 @@ static int ub960_txport_select(struct ub960_data *priv, u8 nport) lockdep_assert_held(&priv->reg_lock); + /* UB954 has only 1 CSI TX. Hence, no need to select */ + if (priv->hw_data->chip_type == UB954) + return 0; + if (priv->reg_current.txport == nport) return 0; @@ -1406,10 +1428,11 @@ static int ub960_parse_dt_txport(struct ub960_data *priv, priv->tx_link_freq[0] = vep.link_frequencies[0]; priv->tx_data_rate = priv->tx_link_freq[0] * 2; - if (priv->tx_data_rate != MHZ(1600) && - priv->tx_data_rate != MHZ(1200) && - priv->tx_data_rate != MHZ(800) && - priv->tx_data_rate != MHZ(400)) { + if ((priv->tx_data_rate != MHZ(1600) && + priv->tx_data_rate != MHZ(1200) && + priv->tx_data_rate != MHZ(800) && + priv->tx_data_rate != MHZ(400)) || + (priv->hw_data->chip_type == UB954 && priv->tx_data_rate == MHZ(1200))) { dev_err(dev, "tx%u: invalid 'link-frequencies' value\n", nport); ret = -EINVAL; goto err_free_vep; @@ -1533,22 +1556,35 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, u8 clk_delay, data_delay; int ret; - ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); - if (ret) - return ret; + if (priv->hw_data->chip_type == UB954) { + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, &v, NULL); + if (ret) + return ret; - clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? - 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + clk_delay = (v & UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; - ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); - if (ret) - return ret; + data_delay = (v & UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + } else { + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); + if (ret) + return ret; - data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? + clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); + if (ret) + return ret; + + data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + } + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v, NULL); if (ret) return ret; @@ -1569,26 +1605,49 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, static int ub960_rxport_set_strobe_pos(struct ub960_data *priv, unsigned int nport, s8 strobe_pos) { - u8 clk_delay, data_delay; int ret = 0; - clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; - data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; - - if (strobe_pos < UB960_MIN_AEQ_STROBE_POS) - clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY; - else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS) - data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY; - else if (strobe_pos < 0) - clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; - else if (strobe_pos > 0) - data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; - - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret); - - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret); + if (priv->hw_data->chip_type == UB954) { + u8 clk_data_delay; + + clk_data_delay = UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY | + UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + if (strobe_pos < UB960_MIN_AEQ_STROBE_POS) + clk_data_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS) + clk_data_delay = (strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY) << + UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT; + else if (strobe_pos < 0) + clk_data_delay = abs(strobe_pos) | + UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + else if (strobe_pos > 0) + clk_data_delay = (strobe_pos | + UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) << + UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT; + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, clk_data_delay, &ret); + } else { + u8 clk_delay, data_delay; + + clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + if (strobe_pos < UB960_MIN_AEQ_STROBE_POS) + clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS) + data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos < 0) + clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + else if (strobe_pos > 0) + data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret); + } return ret; } @@ -1924,7 +1983,7 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (ret) return ret; - if (priv->hw_data->is_ub9702) { + if (priv->hw_data->chip_type == UB9702) { dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", nport, ((u64)v * HZ_PER_MHZ) >> 8); } else { @@ -2186,7 +2245,7 @@ static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) ser_pdata->port = nport; ser_pdata->atr = priv->atr; - if (priv->hw_data->is_ub9702) + if (priv->hw_data->chip_type == UB9702) ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub9702(priv, rxport); else ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub960(priv, rxport); @@ -2352,7 +2411,7 @@ static int ub960_init_tx_ports(struct ub960_data *priv) { int ret; - if (priv->hw_data->is_ub9702) + if (priv->hw_data->chip_type == UB9702) ret = ub960_init_tx_ports_ub9702(priv); else ret = ub960_init_tx_ports_ub960(priv); @@ -3624,7 +3683,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, case RXPORT_MODE_CSI2_SYNC: case RXPORT_MODE_CSI2_NONSYNC: - if (!priv->hw_data->is_ub9702) { + if (priv->hw_data->chip_type == UB960 || + priv->hw_data->chip_type == UB954) { /* Map all VCs from this port to the same VC */ ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP, (vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) | @@ -4158,33 +4218,40 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), v & (u8)BIT(0)); - ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), - &v16, NULL); - if (ret) - return ret; + /* + * Frame counter, frame error counter, line counter and line error counter + * registers are marked as reserved in the UB954 datasheet. Hence restrict + * the following register reads only for UB960 and UB9702. + */ + if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB9702) { + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tframe counter %u\n", v16); + dev_info(dev, "\tframe counter %u\n", v16); - ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), - &v16, NULL); - if (ret) - return ret; + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tframe error counter %u\n", v16); + dev_info(dev, "\tframe error counter %u\n", v16); - ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), - &v16, NULL); - if (ret) - return ret; + ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tline counter %u\n", v16); + dev_info(dev, "\tline counter %u\n", v16); - ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), - &v16, NULL); - if (ret) - return ret; + ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; - dev_info(dev, "\tline error counter %u\n", v16); + dev_info(dev, "\tline error counter %u\n", v16); + } } for_each_rxport(priv, it) { @@ -4250,7 +4317,7 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tcsi_err_counter %u\n", v); - if (!priv->hw_data->is_ub9702) { + if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB954) { ret = ub960_log_status_ub960_sp_eq(priv, nport); if (ret) return ret; @@ -4408,7 +4475,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, return -EINVAL; } - if (!priv->hw_data->is_fpdlink4 && cdr_mode == RXPORT_CDR_FPD4) { + if (priv->hw_data->chip_family != FAMILY_FPD4 && cdr_mode == RXPORT_CDR_FPD4) { dev_err(dev, "rx%u: FPD-Link 4 CDR not supported\n", nport); return -EINVAL; } @@ -5010,7 +5077,12 @@ static int ub960_enable_core_hw(struct ub960_data *priv) if (ret) goto err_pd_gpio; - if (priv->hw_data->is_ub9702) + /* + * UB954 REFCLK_FREQ is not synchronized, so multiple reads are recommended + * by the datasheet. However, a single read is practically seen to be + * sufficient and moreover it is only used for a debug print. + */ + if (priv->hw_data->chip_type == UB9702) ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq, NULL); else @@ -5029,7 +5101,7 @@ static int ub960_enable_core_hw(struct ub960_data *priv) goto err_pd_gpio; /* release GPIO lock */ - if (priv->hw_data->is_ub9702) { + if (priv->hw_data->chip_type == UB9702) { ret = ub960_update_bits(priv, UB960_SR_RESET, UB960_SR_RESET_GPIO_LOCK_RELEASE, UB960_SR_RESET_GPIO_LOCK_RELEASE, @@ -5102,7 +5174,7 @@ static int ub960_probe(struct i2c_client *client) if (ret) goto err_free_ports; - if (priv->hw_data->is_ub9702) + if (priv->hw_data->chip_type == UB9702) ret = ub960_init_rx_ports_ub9702(priv); else ret = ub960_init_rx_ports_ub960(priv); @@ -5169,21 +5241,32 @@ static void ub960_remove(struct i2c_client *client) mutex_destroy(&priv->reg_lock); } +static const struct ub960_hw_data ds90ub954_hw = { + .model = "ub954", + .chip_type = UB954, + .chip_family = FAMILY_FPD3, + .num_rxports = 2, + .num_txports = 1, +}; + static const struct ub960_hw_data ds90ub960_hw = { .model = "ub960", + .chip_type = UB960, + .chip_family = FAMILY_FPD3, .num_rxports = 4, .num_txports = 2, }; static const struct ub960_hw_data ds90ub9702_hw = { .model = "ub9702", + .chip_type = UB9702, + .chip_family = FAMILY_FPD4, .num_rxports = 4, .num_txports = 2, - .is_ub9702 = true, - .is_fpdlink4 = true, }; static const struct i2c_device_id ub960_id[] = { + { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw }, { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, {} @@ -5191,6 +5274,7 @@ static const struct i2c_device_id ub960_id[] = { MODULE_DEVICE_TABLE(i2c, ub960_id); static const struct of_device_id ub960_dt_ids[] = { + { .compatible = "ti,ds90ub954-q1", .data = &ds90ub954_hw }, { .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw }, { .compatible = "ti,ds90ub9702-q1", .data = &ds90ub9702_hw }, {} diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c index d434721ba8ed..d037d3009c9a 100644 --- a/drivers/media/i2c/dw9768.c +++ b/drivers/media/i2c/dw9768.c @@ -551,6 +551,6 @@ static struct i2c_driver dw9768_i2c_driver = { }; module_i2c_driver(dw9768_i2c_driver); -MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>"); +MODULE_AUTHOR("Dongchun Zhu"); MODULE_DESCRIPTION("DW9768 VCM driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index fee63bc106d9..7da02ce5da15 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -1218,6 +1218,9 @@ static int imx219_probe(struct i2c_client *client) /* Request optional enable pin */ imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(imx219->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(imx219->reset_gpio), + "failed to get reset gpio\n"); /* * The sensor must be powered for imx219_identify_module() diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index e50dcfd830f5..bc9ee449a87c 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -709,12 +709,16 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; + mutex_lock(&imx258->mutex); + /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; try_fmt->height = supported_modes[0].height; try_fmt->code = imx258_get_format_code(imx258); try_fmt->field = V4L2_FIELD_NONE; + mutex_unlock(&imx258->mutex); + /* Initialize try_crop */ try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->left = IMX258_PIXEL_ARRAY_LEFT; @@ -839,7 +843,9 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; + mutex_lock(&imx258->mutex); code->code = imx258_get_format_code(imx258); + mutex_unlock(&imx258->mutex); return 0; } @@ -849,10 +855,16 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum *fse) { struct imx258 *imx258 = to_imx258(sd); + u32 code; + if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != imx258_get_format_code(imx258)) + mutex_lock(&imx258->mutex); + code = imx258_get_format_code(imx258); + mutex_unlock(&imx258->mutex); + + if (fse->code != code) return -EINVAL; fse->min_width = supported_modes[fse->index].width; diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index 8ab63ad8f385..38ea1902f2c2 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -129,7 +129,8 @@ /* Master Mode Operation Control */ #define IMX283_REG_XMSTA CCI_REG8(0x3105) -#define IMX283_XMSTA BIT(0) +#define IMX283_XMSTA_START 0 +#define IMX283_XMSTA_STOP BIT(0) #define IMX283_REG_SYNCDRV CCI_REG8(0x3107) #define IMX283_SYNCDRV_XHS_XVS (0xa0 | 0x02) @@ -149,6 +150,9 @@ #define IMX283_REG_PLSTMG02 CCI_REG8(0x36aa) #define IMX283_PLSTMG02_VAL 0x00 +#define IMX283_REG_MIPI_CLK CCI_REG8(0x3a43) +#define IMX283_MIPI_CLK_NONCONTINUOUS BIT(0) + #define IMX283_REG_EBD_X_OUT_SIZE CCI_REG16_LE(0x3a54) /* Test pattern generator */ @@ -565,6 +569,7 @@ struct imx283 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *vflip; + bool mipi_clk_noncontinuous; unsigned long link_freq_bitmap; u16 hmax; @@ -988,6 +993,7 @@ static int imx283_set_pad_format(struct v4l2_subdev *sd, static int imx283_standby_cancel(struct imx283 *imx283) { unsigned int link_freq_idx; + u8 mipi_clk; int ret = 0; cci_write(imx283->cci, IMX283_REG_STANDBY, @@ -1007,6 +1013,10 @@ static int imx283_standby_cancel(struct imx283 *imx283) /* Enable PLL */ cci_write(imx283->cci, IMX283_REG_STBPL, IMX283_STBPL_NORMAL, &ret); + /* Configure MIPI clock mode */ + mipi_clk = imx283->mipi_clk_noncontinuous ? IMX283_MIPI_CLK_NONCONTINUOUS : 0; + cci_write(imx283->cci, IMX283_REG_MIPI_CLK, mipi_clk, &ret); + /* Configure the MIPI link speed */ link_freq_idx = __ffs(imx283->link_freq_bitmap); cci_multi_reg_write(imx283->cci, link_freq_reglist[link_freq_idx].regs, @@ -1023,8 +1033,6 @@ static int imx283_standby_cancel(struct imx283 *imx283) usleep_range(19000, 20000); cci_write(imx283->cci, IMX283_REG_CLAMP, IMX283_CLPSQRST, &ret); - cci_write(imx283->cci, IMX283_REG_XMSTA, 0, &ret); - cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret); return ret; } @@ -1117,6 +1125,10 @@ static int imx283_start_streaming(struct imx283 *imx283, /* Apply customized values from controls (HMAX/VMAX/SHR) */ ret = __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler); + /* Start master mode */ + cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_START, &ret); + cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret); + return ret; } @@ -1153,12 +1165,14 @@ static int imx283_disable_streams(struct v4l2_subdev *sd, u64 streams_mask) { struct imx283 *imx283 = to_imx283(sd); - int ret; + int ret = 0; if (pad != IMAGE_PAD) return -EINVAL; - ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL); + cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_STOP, &ret); + cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STANDBY, &ret); + if (ret) dev_err(imx283->dev, "Failed to stop stream\n"); @@ -1426,6 +1440,9 @@ static int imx283_parse_endpoint(struct imx283 *imx283) goto done_endpoint_free; } + imx283->mipi_clk_noncontinuous = + bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, link_frequencies, ARRAY_SIZE(link_frequencies), diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 776107efe386..27a5c212a527 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -3,9 +3,13 @@ #include <linux/acpi.h> #include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include <linux/unaligned.h> #include <media/v4l2-ctrls.h> @@ -62,6 +66,9 @@ #define IMX355_EXT_CLK 19200000 #define IMX355_LINK_FREQ_INDEX 0 +/* number of data lanes */ +#define IMX355_DATA_LANES 4 + struct imx355_reg { u16 address; u8 val; @@ -125,6 +132,15 @@ struct imx355 { * Protect access to sensor v4l2 controls. */ struct mutex mutex; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +static const struct regulator_bulk_data imx355_supplies[] = { + { .supply = "avdd" }, + { .supply = "dvdd" }, + { .supply = "dovdd" }, }; static const struct imx355_reg imx355_global_regs[] = { @@ -1515,6 +1531,52 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = { .open = imx355_open, }; +static int imx355_power_off(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + + gpiod_set_value_cansleep(imx355->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(imx355_supplies), imx355->supplies); + clk_disable_unprepare(imx355->clk); + + return 0; +} + +static int imx355_power_on(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + int ret; + + ret = clk_prepare_enable(imx355->clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clocks"); + + ret = regulator_bulk_enable(ARRAY_SIZE(imx355_supplies), + imx355->supplies); + if (ret) { + dev_err_probe(dev, ret, "failed to enable regulators"); + goto error_disable_clocks; + } + + usleep_range(1000, 2000); + gpiod_set_value_cansleep(imx355->reset_gpio, 0); + usleep_range(10000, 11000); + + return 0; + +error_disable_clocks: + clk_disable_unprepare(imx355->clk); + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx355_pm_ops, imx355_power_off, + imx355_power_on, NULL); + /* Initialize control handlers */ static int imx355_init_controls(struct imx355 *imx355) { @@ -1646,6 +1708,9 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) if (!cfg) goto out_err; + if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) + goto out_err; + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, link_freq_menu_items, @@ -1689,16 +1754,26 @@ static int imx355_probe(struct i2c_client *client) "external clock %lu is not supported\n", freq); - /* Initialize subdev */ - v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); - - /* Check module identity */ - ret = imx355_identify_module(imx355); + ret = devm_regulator_bulk_get_const(imx355->dev, + ARRAY_SIZE(imx355_supplies), + imx355_supplies, + &imx355->supplies); if (ret) { - dev_err(imx355->dev, "failed to find sensor: %d", ret); + dev_err_probe(imx355->dev, ret, "could not get regulators"); goto error_probe; } + imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(imx355->reset_gpio)) { + ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), + "failed to get gpios"); + goto error_probe; + } + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); + imx355->hwcfg = imx355_get_hwcfg(imx355->dev); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); @@ -1706,13 +1781,24 @@ static int imx355_probe(struct i2c_client *client) goto error_probe; } + ret = imx355_power_on(imx355->dev); + if (ret) + goto error_probe; + + /* Check module identity */ + ret = imx355_identify_module(imx355); + if (ret) { + dev_err(imx355->dev, "failed to find sensor: %d", ret); + goto error_power_off; + } + /* Set default mode to max resolution */ imx355->cur_mode = &supported_modes[0]; ret = imx355_init_controls(imx355); if (ret) { dev_err(imx355->dev, "failed to init controls: %d", ret); - goto error_probe; + goto error_power_off; } /* Initialize subdev */ @@ -1752,6 +1838,9 @@ error_media_entity_runtime_pm: error_handler_free: v4l2_ctrl_handler_free(imx355->sd.ctrl_handler); +error_power_off: + imx355_power_off(imx355->dev); + error_probe: mutex_destroy(&imx355->mutex); @@ -1768,7 +1857,11 @@ static void imx355_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(imx355->dev); - pm_runtime_set_suspended(imx355->dev); + + if (!pm_runtime_status_suspended(imx355->dev)) { + imx355_power_off(imx355->dev); + pm_runtime_set_suspended(imx355->dev); + } mutex_destroy(&imx355->mutex); } @@ -1779,10 +1872,18 @@ static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { }; MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids); +static const struct of_device_id imx355_match_table[] = { + { .compatible = "sony,imx355", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx355_match_table); + static struct i2c_driver imx355_i2c_driver = { .driver = { .name = "imx355", .acpi_match_table = ACPI_PTR(imx355_acpi_ids), + .of_match_table = imx355_match_table, + .pm = &imx355_pm_ops, }, .probe = imx355_probe, .remove = imx355_remove, diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index b3826f803547..e25e0a9ff65c 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -925,7 +925,7 @@ static int imx412_parse_hw_config(struct imx412 *imx412) /* Request optional reset pin */ imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset", - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(imx412->reset_gpio)) { dev_err(imx412->dev, "failed to get reset gpio %pe\n", imx412->reset_gpio); @@ -1037,7 +1037,11 @@ static int imx412_power_on(struct device *dev) goto error_reset; } - usleep_range(1000, 1200); + /* + * Certain Arducam IMX577 module variants require a longer reset settle + * time. Increasing the delay from 1ms to 10ms ensures reliable startup. + */ + usleep_range(10000, 12000); return 0; diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 5588cdd7ec20..604745317004 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work) mutex_unlock(&ir->lock); if (rc == -ENODEV) { rc_unregister_device(ir->rc); + rc_free_device(ir->rc); ir->rc = NULL; return; } @@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client) i2c_unregister_device(ir->tx_c); rc_unregister_device(ir->rc); + rc_free_device(ir->rc); } static const struct i2c_device_id ir_kbd_id[] = { diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index e6e214f8294b..ac0712ce1e65 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -1205,7 +1205,7 @@ static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset) { struct max9286_priv *priv = gpiochip_get_data(chip); - return priv->gpio_state & BIT(offset); + return !!(priv->gpio_state & BIT(offset)); } static int max9286_register_gpio(struct max9286_priv *priv) diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c index 16b0ace15813..e395e2d14e97 100644 --- a/drivers/media/i2c/mt9m114.c +++ b/drivers/media/i2c/mt9m114.c @@ -368,6 +368,10 @@ * Data Structures */ +struct mt9m114_model_info { + bool state_standby_polling; +}; + enum mt9m114_format_flag { MT9M114_FMT_FLAG_PARALLEL = BIT(0), MT9M114_FMT_FLAG_CSI2 = BIT(1), @@ -417,6 +421,8 @@ struct mt9m114 { struct v4l2_ctrl *tpg[4]; } ifp; + + const struct mt9m114_model_info *info; }; /* ----------------------------------------------------------------------------- @@ -2284,9 +2290,11 @@ static int mt9m114_power_on(struct mt9m114 *sensor) * reaches the standby mode (either initiated manually above in * parallel mode, or automatically after reset in MIPI mode). */ - ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY); - if (ret < 0) - goto error_clock; + if (sensor->info->state_standby_polling) { + ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY); + if (ret < 0) + goto error_clock; + } return 0; @@ -2532,6 +2540,10 @@ static int mt9m114_probe(struct i2c_client *client) if (ret < 0) return ret; + sensor->info = device_get_match_data(dev); + if (!sensor->info) + return -ENODEV; + /* Acquire clocks, GPIOs and regulators. */ sensor->clk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(sensor->clk)) { @@ -2646,15 +2658,24 @@ static void mt9m114_remove(struct i2c_client *client) pm_runtime_set_suspended(dev); } +static const struct mt9m114_model_info mt9m114_models_default = { + .state_standby_polling = true, +}; + +static const struct mt9m114_model_info mt9m114_models_aptina = { + .state_standby_polling = false, +}; + static const struct of_device_id mt9m114_of_ids[] = { - { .compatible = "onnn,mt9m114" }, - { /* sentinel */ }, + { .compatible = "onnn,mt9m114", .data = &mt9m114_models_default }, + { .compatible = "aptina,mi1040", .data = &mt9m114_models_aptina }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt9m114_of_ids); static const struct acpi_device_id mt9m114_acpi_ids[] = { - { "INT33F0" }, - { /* sentinel */ }, + { "INT33F0", (kernel_ulong_t)&mt9m114_models_default }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_ids); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 1500ee4db47e..ea5d43d925ff 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -1183,6 +1183,10 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(mt9p031->reset)) { + ret = PTR_ERR(mt9p031->reset); + goto done; + } ret = mt9p031_clk_setup(mt9p031); if (ret) diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index c7184de6251a..1675f0460969 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -9,65 +9,53 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> -#include <linux/unaligned.h> - +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> -#define OG01A1B_REG_VALUE_08BIT 1 -#define OG01A1B_REG_VALUE_16BIT 2 -#define OG01A1B_REG_VALUE_24BIT 3 - #define OG01A1B_LINK_FREQ_500MHZ 500000000ULL #define OG01A1B_SCLK 120000000LL #define OG01A1B_MCLK 19200000 #define OG01A1B_DATA_LANES 2 -#define OG01A1B_RGB_DEPTH 10 -#define OG01A1B_REG_CHIP_ID 0x300a +#define OG01A1B_REG_CHIP_ID CCI_REG24(0x300a) #define OG01A1B_CHIP_ID 0x470141 -#define OG01A1B_REG_MODE_SELECT 0x0100 +#define OG01A1B_REG_MODE_SELECT CCI_REG8(0x0100) #define OG01A1B_MODE_STANDBY 0x00 #define OG01A1B_MODE_STREAMING 0x01 /* vertical-timings from sensor */ -#define OG01A1B_REG_VTS 0x380e +#define OG01A1B_REG_VTS CCI_REG16(0x380e) #define OG01A1B_VTS_120FPS 0x0498 #define OG01A1B_VTS_120FPS_MIN 0x0498 #define OG01A1B_VTS_MAX 0x7fff /* horizontal-timings from sensor */ -#define OG01A1B_REG_HTS 0x380c +#define OG01A1B_REG_HTS CCI_REG16(0x380c) /* Exposure controls from sensor */ -#define OG01A1B_REG_EXPOSURE 0x3501 +#define OG01A1B_REG_EXPOSURE CCI_REG16(0x3501) #define OG01A1B_EXPOSURE_MIN 1 #define OG01A1B_EXPOSURE_MAX_MARGIN 14 #define OG01A1B_EXPOSURE_STEP 1 /* Analog gain controls from sensor */ -#define OG01A1B_REG_ANALOG_GAIN 0x3508 +#define OG01A1B_REG_ANALOG_GAIN CCI_REG16(0x3508) #define OG01A1B_ANAL_GAIN_MIN 16 #define OG01A1B_ANAL_GAIN_MAX 248 /* Max = 15.5x */ #define OG01A1B_ANAL_GAIN_STEP 1 /* Digital gain controls from sensor */ -#define OG01A1B_REG_DIG_GAIN 0x350a +#define OG01A1B_REG_DIG_GAIN CCI_REG24(0x350a) #define OG01A1B_DGTL_GAIN_MIN 1024 #define OG01A1B_DGTL_GAIN_MAX 16384 /* Max = 16x */ #define OG01A1B_DGTL_GAIN_STEP 1 #define OG01A1B_DGTL_GAIN_DEFAULT 1024 -/* Group Access */ -#define OG01A1B_REG_GROUP_ACCESS 0x3208 -#define OG01A1B_GROUP_HOLD_START 0x0 -#define OG01A1B_GROUP_HOLD_END 0x10 -#define OG01A1B_GROUP_HOLD_LAUNCH 0xa0 - /* Test Pattern Control */ -#define OG01A1B_REG_TEST_PATTERN 0x5100 +#define OG01A1B_REG_TEST_PATTERN CCI_REG8(0x5100) #define OG01A1B_TEST_PATTERN_ENABLE BIT(7) #define OG01A1B_TEST_PATTERN_BAR_SHIFT 2 @@ -77,14 +65,9 @@ enum { OG01A1B_LINK_FREQ_1000MBPS, }; -struct og01a1b_reg { - u16 address; - u8 val; -}; - struct og01a1b_reg_list { + const struct cci_reg_sequence *regs; u32 num_of_regs; - const struct og01a1b_reg *regs; }; struct og01a1b_link_freq_config { @@ -114,275 +97,279 @@ struct og01a1b_mode { const struct og01a1b_reg_list reg_list; }; -static const struct og01a1b_reg mipi_data_rate_1000mbps[] = { - {0x0103, 0x01}, - {0x0303, 0x02}, - {0x0304, 0x00}, - {0x0305, 0xd2}, - {0x0323, 0x02}, - {0x0324, 0x01}, - {0x0325, 0x77}, +static const u32 og01a1b_mbus_formats[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y8_1X8, }; -static const struct og01a1b_reg mode_1280x1024_regs[] = { - {0x0300, 0x0a}, - {0x0301, 0x29}, - {0x0302, 0x31}, - {0x0303, 0x02}, - {0x0304, 0x00}, - {0x0305, 0xd2}, - {0x0306, 0x00}, - {0x0307, 0x01}, - {0x0308, 0x02}, - {0x0309, 0x00}, - {0x0310, 0x00}, - {0x0311, 0x00}, - {0x0312, 0x07}, - {0x0313, 0x00}, - {0x0314, 0x00}, - {0x0315, 0x00}, - {0x0320, 0x02}, - {0x0321, 0x01}, - {0x0322, 0x01}, - {0x0323, 0x02}, - {0x0324, 0x01}, - {0x0325, 0x77}, - {0x0326, 0xce}, - {0x0327, 0x04}, - {0x0329, 0x02}, - {0x032a, 0x04}, - {0x032b, 0x04}, - {0x032c, 0x02}, - {0x032d, 0x01}, - {0x032e, 0x00}, - {0x300d, 0x02}, - {0x300e, 0x04}, - {0x3021, 0x08}, - {0x301e, 0x03}, - {0x3103, 0x00}, - {0x3106, 0x08}, - {0x3107, 0x40}, - {0x3216, 0x01}, - {0x3217, 0x00}, - {0x3218, 0xc0}, - {0x3219, 0x55}, - {0x3500, 0x00}, - {0x3501, 0x04}, - {0x3502, 0x8a}, - {0x3506, 0x01}, - {0x3507, 0x72}, - {0x3508, 0x01}, - {0x3509, 0x00}, - {0x350a, 0x01}, - {0x350b, 0x00}, - {0x350c, 0x00}, - {0x3541, 0x00}, - {0x3542, 0x40}, - {0x3605, 0xe0}, - {0x3606, 0x41}, - {0x3614, 0x20}, - {0x3620, 0x0b}, - {0x3630, 0x07}, - {0x3636, 0xa0}, - {0x3637, 0xf9}, - {0x3638, 0x09}, - {0x3639, 0x38}, - {0x363f, 0x09}, - {0x3640, 0x17}, - {0x3662, 0x04}, - {0x3665, 0x80}, - {0x3670, 0x68}, - {0x3674, 0x00}, - {0x3677, 0x3f}, - {0x3679, 0x00}, - {0x369f, 0x19}, - {0x36a0, 0x03}, - {0x36a2, 0x19}, - {0x36a3, 0x03}, - {0x370d, 0x66}, - {0x370f, 0x00}, - {0x3710, 0x03}, - {0x3715, 0x03}, - {0x3716, 0x03}, - {0x3717, 0x06}, - {0x3733, 0x00}, - {0x3778, 0x00}, - {0x37a8, 0x0f}, - {0x37a9, 0x01}, - {0x37aa, 0x07}, - {0x37bd, 0x1c}, - {0x37c1, 0x2f}, - {0x37c3, 0x09}, - {0x37c8, 0x1d}, - {0x37ca, 0x30}, - {0x37df, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x05}, - {0x3805, 0x0f}, - {0x3806, 0x04}, - {0x3807, 0x0f}, - {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x04}, - {0x380b, 0x00}, - {0x380c, 0x03}, - {0x380d, 0x50}, - {0x380e, 0x04}, - {0x380f, 0x98}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x08}, - {0x3814, 0x11}, - {0x3815, 0x11}, - {0x3820, 0x40}, - {0x3821, 0x04}, - {0x3826, 0x00}, - {0x3827, 0x00}, - {0x382a, 0x08}, - {0x382b, 0x52}, - {0x382d, 0xba}, - {0x383d, 0x14}, - {0x384a, 0xa2}, - {0x3866, 0x0e}, - {0x3867, 0x07}, - {0x3884, 0x00}, - {0x3885, 0x08}, - {0x3893, 0x68}, - {0x3894, 0x2a}, - {0x3898, 0x00}, - {0x3899, 0x31}, - {0x389a, 0x04}, - {0x389b, 0x00}, - {0x389c, 0x0b}, - {0x389d, 0xad}, - {0x389f, 0x08}, - {0x38a0, 0x00}, - {0x38a1, 0x00}, - {0x38a8, 0x70}, - {0x38ac, 0xea}, - {0x38b2, 0x00}, - {0x38b3, 0x08}, - {0x38bc, 0x20}, - {0x38c4, 0x0c}, - {0x38c5, 0x3a}, - {0x38c7, 0x3a}, - {0x38e1, 0xc0}, - {0x38ec, 0x3c}, - {0x38f0, 0x09}, - {0x38f1, 0x6f}, - {0x38fe, 0x3c}, - {0x391e, 0x00}, - {0x391f, 0x00}, - {0x3920, 0xa5}, - {0x3921, 0x00}, - {0x3922, 0x00}, - {0x3923, 0x00}, - {0x3924, 0x05}, - {0x3925, 0x00}, - {0x3926, 0x00}, - {0x3927, 0x00}, - {0x3928, 0x1a}, - {0x3929, 0x01}, - {0x392a, 0xb4}, - {0x392b, 0x00}, - {0x392c, 0x10}, - {0x392f, 0x40}, - {0x4000, 0xcf}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x07}, - {0x400a, 0x02}, - {0x400b, 0x54}, - {0x400c, 0x00}, - {0x400d, 0x07}, - {0x4010, 0xc0}, - {0x4012, 0x02}, - {0x4014, 0x04}, - {0x4015, 0x04}, - {0x4017, 0x02}, - {0x4042, 0x01}, - {0x4306, 0x04}, - {0x4307, 0x12}, - {0x4509, 0x00}, - {0x450b, 0x83}, - {0x4604, 0x68}, - {0x4608, 0x0a}, - {0x4700, 0x06}, - {0x4800, 0x64}, - {0x481b, 0x3c}, - {0x4825, 0x32}, - {0x4833, 0x18}, - {0x4837, 0x0f}, - {0x4850, 0x40}, - {0x4860, 0x00}, - {0x4861, 0xec}, - {0x4864, 0x00}, - {0x4883, 0x00}, - {0x4888, 0x90}, - {0x4889, 0x05}, - {0x488b, 0x04}, - {0x4f00, 0x04}, - {0x4f10, 0x04}, - {0x4f21, 0x01}, - {0x4f22, 0x40}, - {0x4f23, 0x44}, - {0x4f24, 0x51}, - {0x4f25, 0x41}, - {0x5000, 0x1f}, - {0x500a, 0x00}, - {0x5100, 0x00}, - {0x5111, 0x20}, - {0x3020, 0x20}, - {0x3613, 0x03}, - {0x38c9, 0x02}, - {0x5304, 0x01}, - {0x3620, 0x08}, - {0x3639, 0x58}, - {0x363a, 0x10}, - {0x3674, 0x04}, - {0x3780, 0xff}, - {0x3781, 0xff}, - {0x3782, 0x00}, - {0x3783, 0x01}, - {0x3798, 0xa3}, - {0x37aa, 0x10}, - {0x38a8, 0xf0}, - {0x38c4, 0x09}, - {0x38c5, 0xb0}, - {0x38df, 0x80}, - {0x38ff, 0x05}, - {0x4010, 0xf1}, - {0x4011, 0x70}, - {0x3667, 0x80}, - {0x4d00, 0x4a}, - {0x4d01, 0x18}, - {0x4d02, 0xbb}, - {0x4d03, 0xde}, - {0x4d04, 0x93}, - {0x4d05, 0xff}, - {0x4d09, 0x0a}, - {0x37aa, 0x16}, - {0x3606, 0x42}, - {0x3605, 0x00}, - {0x36a2, 0x17}, - {0x300d, 0x0a}, - {0x4d00, 0x4d}, - {0x4d01, 0x95}, - {0x3d8C, 0x70}, - {0x3d8d, 0xE9}, - {0x5300, 0x00}, - {0x5301, 0x10}, - {0x5302, 0x00}, - {0x5303, 0xE3}, - {0x3d88, 0x00}, - {0x3d89, 0x10}, - {0x3d8a, 0x00}, - {0x3d8b, 0xE3}, - {0x4f22, 0x00}, +static const struct cci_reg_sequence mipi_data_rate_1000mbps[] = { + { CCI_REG8(0x0103), 0x01 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0304), 0x00 }, + { CCI_REG8(0x0305), 0xd2 }, + { CCI_REG8(0x0323), 0x02 }, + { CCI_REG8(0x0324), 0x01 }, + { CCI_REG8(0x0325), 0x77 }, +}; + +static const struct cci_reg_sequence mode_1280x1024_regs[] = { + { CCI_REG8(0x0300), 0x0a }, + { CCI_REG8(0x0301), 0x29 }, + { CCI_REG8(0x0302), 0x31 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0304), 0x00 }, + { CCI_REG8(0x0305), 0xd2 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x0307), 0x01 }, + { CCI_REG8(0x0308), 0x02 }, + { CCI_REG8(0x0309), 0x00 }, + { CCI_REG8(0x0310), 0x00 }, + { CCI_REG8(0x0311), 0x00 }, + { CCI_REG8(0x0312), 0x07 }, + { CCI_REG8(0x0313), 0x00 }, + { CCI_REG8(0x0314), 0x00 }, + { CCI_REG8(0x0315), 0x00 }, + { CCI_REG8(0x0320), 0x02 }, + { CCI_REG8(0x0321), 0x01 }, + { CCI_REG8(0x0322), 0x01 }, + { CCI_REG8(0x0323), 0x02 }, + { CCI_REG8(0x0324), 0x01 }, + { CCI_REG8(0x0325), 0x77 }, + { CCI_REG8(0x0326), 0xce }, + { CCI_REG8(0x0327), 0x04 }, + { CCI_REG8(0x0329), 0x02 }, + { CCI_REG8(0x032a), 0x04 }, + { CCI_REG8(0x032b), 0x04 }, + { CCI_REG8(0x032c), 0x02 }, + { CCI_REG8(0x032d), 0x01 }, + { CCI_REG8(0x032e), 0x00 }, + { CCI_REG8(0x300d), 0x02 }, + { CCI_REG8(0x300e), 0x04 }, + { CCI_REG8(0x3021), 0x08 }, + { CCI_REG8(0x301e), 0x03 }, + { CCI_REG8(0x3103), 0x00 }, + { CCI_REG8(0x3106), 0x08 }, + { CCI_REG8(0x3107), 0x40 }, + { CCI_REG8(0x3216), 0x01 }, + { CCI_REG8(0x3217), 0x00 }, + { CCI_REG8(0x3218), 0xc0 }, + { CCI_REG8(0x3219), 0x55 }, + { CCI_REG8(0x3500), 0x00 }, + { CCI_REG8(0x3501), 0x04 }, + { CCI_REG8(0x3502), 0x8a }, + { CCI_REG8(0x3506), 0x01 }, + { CCI_REG8(0x3507), 0x72 }, + { CCI_REG8(0x3508), 0x01 }, + { CCI_REG8(0x3509), 0x00 }, + { CCI_REG8(0x350a), 0x01 }, + { CCI_REG8(0x350b), 0x00 }, + { CCI_REG8(0x350c), 0x00 }, + { CCI_REG8(0x3541), 0x00 }, + { CCI_REG8(0x3542), 0x40 }, + { CCI_REG8(0x3605), 0xe0 }, + { CCI_REG8(0x3606), 0x41 }, + { CCI_REG8(0x3614), 0x20 }, + { CCI_REG8(0x3620), 0x0b }, + { CCI_REG8(0x3630), 0x07 }, + { CCI_REG8(0x3636), 0xa0 }, + { CCI_REG8(0x3637), 0xf9 }, + { CCI_REG8(0x3638), 0x09 }, + { CCI_REG8(0x3639), 0x38 }, + { CCI_REG8(0x363f), 0x09 }, + { CCI_REG8(0x3640), 0x17 }, + { CCI_REG8(0x3665), 0x80 }, + { CCI_REG8(0x3670), 0x68 }, + { CCI_REG8(0x3674), 0x00 }, + { CCI_REG8(0x3677), 0x3f }, + { CCI_REG8(0x3679), 0x00 }, + { CCI_REG8(0x369f), 0x19 }, + { CCI_REG8(0x36a0), 0x03 }, + { CCI_REG8(0x36a2), 0x19 }, + { CCI_REG8(0x36a3), 0x03 }, + { CCI_REG8(0x370d), 0x66 }, + { CCI_REG8(0x370f), 0x00 }, + { CCI_REG8(0x3710), 0x03 }, + { CCI_REG8(0x3715), 0x03 }, + { CCI_REG8(0x3716), 0x03 }, + { CCI_REG8(0x3717), 0x06 }, + { CCI_REG8(0x3733), 0x00 }, + { CCI_REG8(0x3778), 0x00 }, + { CCI_REG8(0x37a8), 0x0f }, + { CCI_REG8(0x37a9), 0x01 }, + { CCI_REG8(0x37aa), 0x07 }, + { CCI_REG8(0x37bd), 0x1c }, + { CCI_REG8(0x37c1), 0x2f }, + { CCI_REG8(0x37c3), 0x09 }, + { CCI_REG8(0x37c8), 0x1d }, + { CCI_REG8(0x37ca), 0x30 }, + { CCI_REG8(0x37df), 0x00 }, + { CCI_REG8(0x3800), 0x00 }, + { CCI_REG8(0x3801), 0x00 }, + { CCI_REG8(0x3802), 0x00 }, + { CCI_REG8(0x3803), 0x00 }, + { CCI_REG8(0x3804), 0x05 }, + { CCI_REG8(0x3805), 0x0f }, + { CCI_REG8(0x3806), 0x04 }, + { CCI_REG8(0x3807), 0x0f }, + { CCI_REG8(0x3808), 0x05 }, + { CCI_REG8(0x3809), 0x00 }, + { CCI_REG8(0x380a), 0x04 }, + { CCI_REG8(0x380b), 0x00 }, + { CCI_REG8(0x380c), 0x03 }, + { CCI_REG8(0x380d), 0x50 }, + { CCI_REG8(0x380e), 0x04 }, + { CCI_REG8(0x380f), 0x98 }, + { CCI_REG8(0x3810), 0x00 }, + { CCI_REG8(0x3811), 0x08 }, + { CCI_REG8(0x3812), 0x00 }, + { CCI_REG8(0x3813), 0x08 }, + { CCI_REG8(0x3814), 0x11 }, + { CCI_REG8(0x3815), 0x11 }, + { CCI_REG8(0x3820), 0x40 }, + { CCI_REG8(0x3821), 0x04 }, + { CCI_REG8(0x3826), 0x00 }, + { CCI_REG8(0x3827), 0x00 }, + { CCI_REG8(0x382a), 0x08 }, + { CCI_REG8(0x382b), 0x52 }, + { CCI_REG8(0x382d), 0xba }, + { CCI_REG8(0x383d), 0x14 }, + { CCI_REG8(0x384a), 0xa2 }, + { CCI_REG8(0x3866), 0x0e }, + { CCI_REG8(0x3867), 0x07 }, + { CCI_REG8(0x3884), 0x00 }, + { CCI_REG8(0x3885), 0x08 }, + { CCI_REG8(0x3893), 0x68 }, + { CCI_REG8(0x3894), 0x2a }, + { CCI_REG8(0x3898), 0x00 }, + { CCI_REG8(0x3899), 0x31 }, + { CCI_REG8(0x389a), 0x04 }, + { CCI_REG8(0x389b), 0x00 }, + { CCI_REG8(0x389c), 0x0b }, + { CCI_REG8(0x389d), 0xad }, + { CCI_REG8(0x389f), 0x08 }, + { CCI_REG8(0x38a0), 0x00 }, + { CCI_REG8(0x38a1), 0x00 }, + { CCI_REG8(0x38a8), 0x70 }, + { CCI_REG8(0x38ac), 0xea }, + { CCI_REG8(0x38b2), 0x00 }, + { CCI_REG8(0x38b3), 0x08 }, + { CCI_REG8(0x38bc), 0x20 }, + { CCI_REG8(0x38c4), 0x0c }, + { CCI_REG8(0x38c5), 0x3a }, + { CCI_REG8(0x38c7), 0x3a }, + { CCI_REG8(0x38e1), 0xc0 }, + { CCI_REG8(0x38ec), 0x3c }, + { CCI_REG8(0x38f0), 0x09 }, + { CCI_REG8(0x38f1), 0x6f }, + { CCI_REG8(0x38fe), 0x3c }, + { CCI_REG8(0x391e), 0x00 }, + { CCI_REG8(0x391f), 0x00 }, + { CCI_REG8(0x3920), 0xa5 }, + { CCI_REG8(0x3921), 0x00 }, + { CCI_REG8(0x3922), 0x00 }, + { CCI_REG8(0x3923), 0x00 }, + { CCI_REG8(0x3924), 0x05 }, + { CCI_REG8(0x3925), 0x00 }, + { CCI_REG8(0x3926), 0x00 }, + { CCI_REG8(0x3927), 0x00 }, + { CCI_REG8(0x3928), 0x1a }, + { CCI_REG8(0x3929), 0x01 }, + { CCI_REG8(0x392a), 0xb4 }, + { CCI_REG8(0x392b), 0x00 }, + { CCI_REG8(0x392c), 0x10 }, + { CCI_REG8(0x392f), 0x40 }, + { CCI_REG8(0x4000), 0xcf }, + { CCI_REG8(0x4003), 0x40 }, + { CCI_REG8(0x4008), 0x00 }, + { CCI_REG8(0x4009), 0x07 }, + { CCI_REG8(0x400a), 0x02 }, + { CCI_REG8(0x400b), 0x54 }, + { CCI_REG8(0x400c), 0x00 }, + { CCI_REG8(0x400d), 0x07 }, + { CCI_REG8(0x4010), 0xc0 }, + { CCI_REG8(0x4012), 0x02 }, + { CCI_REG8(0x4014), 0x04 }, + { CCI_REG8(0x4015), 0x04 }, + { CCI_REG8(0x4017), 0x02 }, + { CCI_REG8(0x4042), 0x01 }, + { CCI_REG8(0x4306), 0x04 }, + { CCI_REG8(0x4307), 0x12 }, + { CCI_REG8(0x4509), 0x00 }, + { CCI_REG8(0x450b), 0x83 }, + { CCI_REG8(0x4604), 0x68 }, + { CCI_REG8(0x4608), 0x0a }, + { CCI_REG8(0x4700), 0x06 }, + { CCI_REG8(0x4800), 0x64 }, + { CCI_REG8(0x481b), 0x3c }, + { CCI_REG8(0x4825), 0x32 }, + { CCI_REG8(0x4833), 0x18 }, + { CCI_REG8(0x4837), 0x0f }, + { CCI_REG8(0x4850), 0x40 }, + { CCI_REG8(0x4860), 0x00 }, + { CCI_REG8(0x4861), 0xec }, + { CCI_REG8(0x4864), 0x00 }, + { CCI_REG8(0x4883), 0x00 }, + { CCI_REG8(0x4888), 0x90 }, + { CCI_REG8(0x4889), 0x05 }, + { CCI_REG8(0x488b), 0x04 }, + { CCI_REG8(0x4f00), 0x04 }, + { CCI_REG8(0x4f10), 0x04 }, + { CCI_REG8(0x4f21), 0x01 }, + { CCI_REG8(0x4f22), 0x40 }, + { CCI_REG8(0x4f23), 0x44 }, + { CCI_REG8(0x4f24), 0x51 }, + { CCI_REG8(0x4f25), 0x41 }, + { CCI_REG8(0x5000), 0x1f }, + { CCI_REG8(0x500a), 0x00 }, + { CCI_REG8(0x5100), 0x00 }, + { CCI_REG8(0x5111), 0x20 }, + { CCI_REG8(0x3020), 0x20 }, + { CCI_REG8(0x3613), 0x03 }, + { CCI_REG8(0x38c9), 0x02 }, + { CCI_REG8(0x5304), 0x01 }, + { CCI_REG8(0x3620), 0x08 }, + { CCI_REG8(0x3639), 0x58 }, + { CCI_REG8(0x363a), 0x10 }, + { CCI_REG8(0x3674), 0x04 }, + { CCI_REG8(0x3780), 0xff }, + { CCI_REG8(0x3781), 0xff }, + { CCI_REG8(0x3782), 0x00 }, + { CCI_REG8(0x3783), 0x01 }, + { CCI_REG8(0x3798), 0xa3 }, + { CCI_REG8(0x37aa), 0x10 }, + { CCI_REG8(0x38a8), 0xf0 }, + { CCI_REG8(0x38c4), 0x09 }, + { CCI_REG8(0x38c5), 0xb0 }, + { CCI_REG8(0x38df), 0x80 }, + { CCI_REG8(0x38ff), 0x05 }, + { CCI_REG8(0x4010), 0xf1 }, + { CCI_REG8(0x4011), 0x70 }, + { CCI_REG8(0x3667), 0x80 }, + { CCI_REG8(0x4d00), 0x4a }, + { CCI_REG8(0x4d01), 0x18 }, + { CCI_REG8(0x4d02), 0xbb }, + { CCI_REG8(0x4d03), 0xde }, + { CCI_REG8(0x4d04), 0x93 }, + { CCI_REG8(0x4d05), 0xff }, + { CCI_REG8(0x4d09), 0x0a }, + { CCI_REG8(0x37aa), 0x16 }, + { CCI_REG8(0x3606), 0x42 }, + { CCI_REG8(0x3605), 0x00 }, + { CCI_REG8(0x36a2), 0x17 }, + { CCI_REG8(0x300d), 0x0a }, + { CCI_REG8(0x4d00), 0x4d }, + { CCI_REG8(0x4d01), 0x95 }, + { CCI_REG8(0x3d8c), 0x70 }, + { CCI_REG8(0x3d8d), 0xe9 }, + { CCI_REG8(0x5300), 0x00 }, + { CCI_REG8(0x5301), 0x10 }, + { CCI_REG8(0x5302), 0x00 }, + { CCI_REG8(0x5303), 0xe3 }, + { CCI_REG8(0x3d88), 0x00 }, + { CCI_REG8(0x3d89), 0x10 }, + { CCI_REG8(0x3d8a), 0x00 }, + { CCI_REG8(0x3d8b), 0xe3 }, + { CCI_REG8(0x4f22), 0x00 }, }; static const char * const og01a1b_test_pattern_menu[] = { @@ -423,6 +410,7 @@ static const struct og01a1b_mode supported_modes[] = { struct og01a1b { struct device *dev; + struct regmap *regmap; struct clk *xvclk; struct gpio_desc *reset_gpio; struct regulator *avdd; @@ -443,102 +431,36 @@ struct og01a1b { /* Current mode */ const struct og01a1b_mode *cur_mode; - /* To serialize asynchronus callbacks */ - struct mutex mutex; + /* Selected media bus format output */ + u32 code; }; -static u64 to_pixel_rate(u32 f_index) +static u64 to_pixel_rate(u32 f_index, u32 bpp) { u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OG01A1B_DATA_LANES; - do_div(pixel_rate, OG01A1B_RGB_DEPTH); + do_div(pixel_rate, bpp); return pixel_rate; } -static u64 to_pixels_per_line(u32 hts, u32 f_index) +static u64 to_pixels_per_line(u32 hts, u32 f_index, u32 bpp) { - u64 ppl = hts * to_pixel_rate(f_index); + u64 ppl = hts * to_pixel_rate(f_index, bpp); do_div(ppl, OG01A1B_SCLK); return ppl; } -static int og01a1b_read_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(addr_buf); - msgs[0].buf = addr_buf; - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -static int og01a1b_write_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << 8 * (4 - len), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -static int og01a1b_write_reg_list(struct og01a1b *og01a1b, - const struct og01a1b_reg_list *r_list) -{ - unsigned int i; - int ret; - - for (i = 0; i < r_list->num_of_regs; i++) { - ret = og01a1b_write_reg(og01a1b, r_list->regs[i].address, 1, - r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(og01a1b->dev, - "failed to write reg 0x%4.4x. error = %d", - r_list->regs[i].address, ret); - return ret; - } - } - - return 0; -} - static int og01a1b_test_pattern(struct og01a1b *og01a1b, u32 pattern) { if (pattern) pattern = (pattern - 1) << OG01A1B_TEST_PATTERN_BAR_SHIFT | OG01A1B_TEST_PATTERN_ENABLE; - return og01a1b_write_reg(og01a1b, OG01A1B_REG_TEST_PATTERN, - OG01A1B_REG_VALUE_08BIT, pattern); + return cci_write(og01a1b->regmap, OG01A1B_REG_TEST_PATTERN, + pattern, NULL); } static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) @@ -565,26 +487,23 @@ static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_ANALOG_GAIN, - OG01A1B_REG_VALUE_16BIT, - ctrl->val << 4); + ret = cci_write(og01a1b->regmap, OG01A1B_REG_ANALOG_GAIN, + ctrl->val << 4, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_DIG_GAIN, - OG01A1B_REG_VALUE_24BIT, - ctrl->val << 6); + ret = cci_write(og01a1b->regmap, OG01A1B_REG_DIG_GAIN, + ctrl->val << 6, NULL); break; case V4L2_CID_EXPOSURE: - ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_EXPOSURE, - OG01A1B_REG_VALUE_16BIT, ctrl->val); + ret = cci_write(og01a1b->regmap, OG01A1B_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_VBLANK: - ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_VTS, - OG01A1B_REG_VALUE_16BIT, - og01a1b->cur_mode->height + ctrl->val); + ret = cci_write(og01a1b->regmap, OG01A1B_REG_VTS, + og01a1b->cur_mode->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: @@ -609,6 +528,7 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b) { struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max, h_blank; + u32 bpp; int ret; ctrl_hdlr = &og01a1b->ctrl_handler; @@ -616,7 +536,6 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b) if (ret) return ret; - ctrl_hdlr->lock = &og01a1b->mutex; og01a1b->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &og01a1b_ctrl_ops, V4L2_CID_LINK_FREQ, @@ -626,13 +545,12 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b) if (og01a1b->link_freq) og01a1b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + bpp = (og01a1b->code == MEDIA_BUS_FMT_Y10_1X10 ? 10 : 8); og01a1b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, - to_pixel_rate - (OG01A1B_LINK_FREQ_1000MBPS), - 1, - to_pixel_rate - (OG01A1B_LINK_FREQ_1000MBPS)); + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(OG01A1B_LINK_FREQ_1000MBPS, bpp), + 1, + to_pixel_rate(OG01A1B_LINK_FREQ_1000MBPS, bpp)); og01a1b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, V4L2_CID_VBLANK, og01a1b->cur_mode->vts_min - @@ -642,7 +560,7 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b) og01a1b->cur_mode->vts_def - og01a1b->cur_mode->height); h_blank = to_pixels_per_line(og01a1b->cur_mode->hts, - og01a1b->cur_mode->link_freq_index) - + og01a1b->cur_mode->link_freq_index, bpp) - og01a1b->cur_mode->width; og01a1b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, V4L2_CID_HBLANK, h_blank, h_blank, @@ -682,78 +600,78 @@ static void og01a1b_update_pad_format(const struct og01a1b_mode *mode, { fmt->width = mode->width; fmt->height = mode->height; - fmt->code = MEDIA_BUS_FMT_Y10_1X10; fmt->field = V4L2_FIELD_NONE; } -static int og01a1b_start_streaming(struct og01a1b *og01a1b) +static int og01a1b_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct og01a1b *og01a1b = to_og01a1b(sd); + unsigned int link_freq_index = og01a1b->cur_mode->link_freq_index; const struct og01a1b_reg_list *reg_list; - int link_freq_index, ret; + int ret; - link_freq_index = og01a1b->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = pm_runtime_resume_and_get(og01a1b->dev); + if (ret) + return ret; - ret = og01a1b_write_reg_list(og01a1b, reg_list); + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = cci_multi_reg_write(og01a1b->regmap, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { - dev_err(og01a1b->dev, "failed to set plls"); - return ret; + dev_err(og01a1b->dev, "failed to set plls: %d\n", ret); + goto error; } reg_list = &og01a1b->cur_mode->reg_list; - ret = og01a1b_write_reg_list(og01a1b, reg_list); + ret = cci_multi_reg_write(og01a1b->regmap, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { - dev_err(og01a1b->dev, "failed to set mode"); - return ret; + dev_err(og01a1b->dev, "failed to set mode: %d\n", ret); + goto error; + } + + ret = cci_write(og01a1b->regmap, CCI_REG8(0x3662), + (og01a1b->code == MEDIA_BUS_FMT_Y10_1X10 ? 0x4 : 0x6), + NULL); + if (ret) { + dev_err(og01a1b->dev, "failed to set output format: %d\n", ret); + goto error; } ret = __v4l2_ctrl_handler_setup(og01a1b->sd.ctrl_handler); if (ret) - return ret; + goto error; - ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT, - OG01A1B_REG_VALUE_08BIT, - OG01A1B_MODE_STREAMING); + ret = cci_write(og01a1b->regmap, OG01A1B_REG_MODE_SELECT, + OG01A1B_MODE_STREAMING, NULL); if (ret) { - dev_err(og01a1b->dev, "failed to set stream"); - return ret; + dev_err(og01a1b->dev, "failed to start streaming: %d\n", ret); + goto error; } return 0; -} -static void og01a1b_stop_streaming(struct og01a1b *og01a1b) -{ - if (og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT, - OG01A1B_REG_VALUE_08BIT, OG01A1B_MODE_STANDBY)) - dev_err(og01a1b->dev, "failed to set stream"); +error: + pm_runtime_put_autosuspend(og01a1b->dev); + + return ret; } -static int og01a1b_set_stream(struct v4l2_subdev *sd, int enable) +static int og01a1b_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct og01a1b *og01a1b = to_og01a1b(sd); - int ret = 0; - - mutex_lock(&og01a1b->mutex); - if (enable) { - ret = pm_runtime_resume_and_get(og01a1b->dev); - if (ret) { - mutex_unlock(&og01a1b->mutex); - return ret; - } + int ret; - ret = og01a1b_start_streaming(og01a1b); - if (ret) { - enable = 0; - og01a1b_stop_streaming(og01a1b); - pm_runtime_put(og01a1b->dev); - } - } else { - og01a1b_stop_streaming(og01a1b); - pm_runtime_put(og01a1b->dev); - } + ret = cci_write(og01a1b->regmap, OG01A1B_REG_MODE_SELECT, + OG01A1B_MODE_STANDBY, NULL); + if (ret) + dev_err(og01a1b->dev, "failed to stop streaming: %d\n", ret); - mutex_unlock(&og01a1b->mutex); + pm_runtime_put_autosuspend(og01a1b->dev); return ret; } @@ -764,55 +682,38 @@ static int og01a1b_set_format(struct v4l2_subdev *sd, { struct og01a1b *og01a1b = to_og01a1b(sd); const struct og01a1b_mode *mode; - s32 vblank_def, h_blank; + s32 vblank_def, h_blank, bpp; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; - } else { - og01a1b->cur_mode = mode; - __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(og01a1b->pixel_rate, - to_pixel_rate(mode->link_freq_index)); - - /* Update limits and set FPS to default */ - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(og01a1b->vblank, - mode->vts_min - mode->height, - OG01A1B_VTS_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(og01a1b->vblank, vblank_def); - h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - - mode->width; - __v4l2_ctrl_modify_range(og01a1b->hblank, h_blank, h_blank, 1, - h_blank); - } - mutex_unlock(&og01a1b->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + goto set_format; - return 0; -} + bpp = (fmt->format.code == MEDIA_BUS_FMT_Y10_1X10 ? 10 : 8); + __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(og01a1b->pixel_rate, + to_pixel_rate(mode->link_freq_index, bpp)); -static int og01a1b_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct og01a1b *og01a1b = to_og01a1b(sd); + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(og01a1b->vblank, + mode->vts_min - mode->height, + OG01A1B_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(og01a1b->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index, + bpp) - mode->width; + __v4l2_ctrl_modify_range(og01a1b->hblank, h_blank, h_blank, 1, h_blank); - mutex_lock(&og01a1b->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format); + og01a1b->cur_mode = mode; + og01a1b->code = fmt->format.code; - mutex_unlock(&og01a1b->mutex); +set_format: + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; return 0; } @@ -821,10 +722,10 @@ static int og01a1b_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + if (code->index > ARRAY_SIZE(og01a1b_mbus_formats) - 1) return -EINVAL; - code->code = MEDIA_BUS_FMT_Y10_1X10; + code->code = og01a1b_mbus_formats[code->index]; return 0; } @@ -836,7 +737,8 @@ static int og01a1b_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_Y10_1X10) + if (fse->code != MEDIA_BUS_FMT_Y10_1X10 && + fse->code != MEDIA_BUS_FMT_Y8_1X8) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -847,27 +749,36 @@ static int og01a1b_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int og01a1b_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct og01a1b *og01a1b = to_og01a1b(sd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .width = og01a1b->cur_mode->width, + .height = og01a1b->cur_mode->height, + .code = og01a1b->code, + }, + }; - mutex_lock(&og01a1b->mutex); - og01a1b_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); - mutex_unlock(&og01a1b->mutex); + og01a1b_set_format(sd, state, &fmt); return 0; } static const struct v4l2_subdev_video_ops og01a1b_video_ops = { - .s_stream = og01a1b_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops og01a1b_pad_ops = { .set_fmt = og01a1b_set_format, - .get_fmt = og01a1b_get_format, + .get_fmt = v4l2_subdev_get_fmt, .enum_mbus_code = og01a1b_enum_mbus_code, .enum_frame_size = og01a1b_enum_frame_size, + .enable_streams = og01a1b_enable_streams, + .disable_streams = og01a1b_disable_streams, }; static const struct v4l2_subdev_ops og01a1b_subdev_ops = { @@ -880,21 +791,20 @@ static const struct media_entity_operations og01a1b_subdev_entity_ops = { }; static const struct v4l2_subdev_internal_ops og01a1b_internal_ops = { - .open = og01a1b_open, + .init_state = og01a1b_init_state, }; static int og01a1b_identify_module(struct og01a1b *og01a1b) { int ret; - u32 val; + u64 val; - ret = og01a1b_read_reg(og01a1b, OG01A1B_REG_CHIP_ID, - OG01A1B_REG_VALUE_24BIT, &val); + ret = cci_read(og01a1b->regmap, OG01A1B_REG_CHIP_ID, &val, NULL); if (ret) return ret; if (val != OG01A1B_CHIP_ID) { - dev_err(og01a1b->dev, "chip id mismatch: %x!=%x", + dev_err(og01a1b->dev, "chip id mismatch: %x!=%llx", OG01A1B_CHIP_ID, val); return -ENXIO; } @@ -1042,10 +952,10 @@ static void og01a1b_remove(struct i2c_client *client) struct og01a1b *og01a1b = to_og01a1b(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&og01a1b->sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(og01a1b->dev); - mutex_destroy(&og01a1b->mutex); } static int og01a1b_probe(struct i2c_client *client) @@ -1062,6 +972,11 @@ static int og01a1b_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); + og01a1b->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(og01a1b->regmap)) + return dev_err_probe(og01a1b->dev, PTR_ERR(og01a1b->regmap), + "failed to init CCI\n"); + og01a1b->xvclk = devm_v4l2_sensor_clk_get(og01a1b->dev, NULL); if (IS_ERR(og01a1b->xvclk)) return dev_err_probe(og01a1b->dev, PTR_ERR(og01a1b->xvclk), @@ -1134,14 +1049,15 @@ static int og01a1b_probe(struct i2c_client *client) goto power_off; } - mutex_init(&og01a1b->mutex); og01a1b->cur_mode = &supported_modes[0]; + og01a1b->code = og01a1b_mbus_formats[0]; ret = og01a1b_init_controls(og01a1b); if (ret) { dev_err(og01a1b->dev, "failed to init controls: %d", ret); goto probe_error_v4l2_ctrl_handler_free; } + og01a1b->sd.state_lock = og01a1b->ctrl_handler.lock; og01a1b->sd.internal_ops = &og01a1b_internal_ops; og01a1b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; og01a1b->sd.entity.ops = &og01a1b_subdev_entity_ops; @@ -1153,11 +1069,18 @@ static int og01a1b_probe(struct i2c_client *client) goto probe_error_v4l2_ctrl_handler_free; } + ret = v4l2_subdev_init_finalize(&og01a1b->sd); + if (ret < 0) { + dev_err_probe(og01a1b->dev, ret, + "failed to finalize subdevice init\n"); + goto probe_error_media_entity_cleanup; + } + ret = v4l2_async_register_subdev_sensor(&og01a1b->sd); if (ret < 0) { dev_err(og01a1b->dev, "failed to register V4L2 subdev: %d", ret); - goto probe_error_media_entity_cleanup; + goto probe_error_v4l2_subdev_cleanup; } /* Enable runtime PM and turn off the device */ @@ -1167,12 +1090,14 @@ static int og01a1b_probe(struct i2c_client *client) return 0; +probe_error_v4l2_subdev_cleanup: + v4l2_subdev_cleanup(&og01a1b->sd); + probe_error_media_entity_cleanup: media_entity_cleanup(&og01a1b->sd.entity); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(og01a1b->sd.ctrl_handler); - mutex_destroy(&og01a1b->mutex); power_off: og01a1b_power_off(og01a1b->dev); diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 70d9d7c43f18..143dcfe10445 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -998,6 +998,6 @@ static struct i2c_driver ov02a10_i2c_driver = { }; module_i2c_driver(ov02a10_i2c_driver); -MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>"); +MODULE_AUTHOR("Dongchun Zhu"); MODULE_DESCRIPTION("OmniVision OV02A10 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c index 43ec2a1f2fcf..9adef5446a61 100644 --- a/drivers/media/i2c/ov08d10.c +++ b/drivers/media/i2c/ov08d10.c @@ -8,12 +8,12 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> #define OV08D10_SCLK 144000000ULL -#define OV08D10_XVCLK_19_2 19200000 #define OV08D10_ROWCLK 36000 #define OV08D10_DATA_LANES 2 #define OV08D10_RGB_DEPTH 10 @@ -77,8 +77,13 @@ struct ov08d10_reg_list { const struct ov08d10_reg *regs; }; +static const u32 ov08d10_xvclk_freqs[] = { + 19200000, + 24000000 +}; + struct ov08d10_link_freq_config { - const struct ov08d10_reg_list reg_list; + const struct ov08d10_reg_list reg_list[ARRAY_SIZE(ov08d10_xvclk_freqs)]; }; struct ov08d10_mode { @@ -88,13 +93,13 @@ struct ov08d10_mode { /* Frame height in pixels */ u32 height; - /* Horizontal timining size */ + /* Horizontal timing size */ u32 hts; - /* Default vertical timining size */ + /* Default vertical timing size */ u32 vts_def; - /* Min vertical timining size */ + /* Min vertical timing size */ u32 vts_min; /* Link frequency needed for this resolution */ @@ -107,8 +112,8 @@ struct ov08d10_mode { u8 data_lanes; }; -/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes */ -static const struct ov08d10_reg mipi_data_rate_720mbps[] = { +/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 19.2 MHz */ +static const struct ov08d10_reg mipi_data_rate_720mbps_19_2[] = { {0xfd, 0x00}, {0x11, 0x2a}, {0x14, 0x43}, @@ -118,8 +123,8 @@ static const struct ov08d10_reg mipi_data_rate_720mbps[] = { {0xb7, 0x02} }; -/* 1632x1224 needs 360Mbps/lane, 2 lanes */ -static const struct ov08d10_reg mipi_data_rate_360mbps[] = { +/* 1632x1224 needs 360Mbps/lane, 2 lanes - 19.2 MHz */ +static const struct ov08d10_reg mipi_data_rate_360mbps_19_2[] = { {0xfd, 0x00}, {0x1a, 0x04}, {0x1b, 0xe1}, @@ -131,6 +136,30 @@ static const struct ov08d10_reg mipi_data_rate_360mbps[] = { {0xb7, 0x02} }; +/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 24 MHz */ +static const struct ov08d10_reg mipi_data_rate_720mbps_24_0[] = { + {0xfd, 0x00}, + {0x11, 0x2a}, + {0x14, 0x43}, + {0x1a, 0x04}, + {0x1b, 0xb4}, + {0x1e, 0x13}, + {0xb7, 0x02} +}; + +/* 1632x1224 needs 360Mbps/lane, 2 lanes - 24 MHz */ +static const struct ov08d10_reg mipi_data_rate_360mbps_24_0[] = { + {0xfd, 0x00}, + {0x1a, 0x04}, + {0x1b, 0xb4}, + {0x1d, 0x00}, + {0x1c, 0x19}, + {0x11, 0x2a}, + {0x14, 0x54}, + {0x1e, 0x13}, + {0xb7, 0x02} +}; + static const struct ov08d10_reg lane_2_mode_3280x2460[] = { /* 3280x2460 resolution */ {0xfd, 0x01}, @@ -217,7 +246,7 @@ static const struct ov08d10_reg lane_2_mode_3280x2460[] = { {0x9a, 0x30}, {0xa8, 0x02}, {0xfd, 0x02}, - {0xa1, 0x01}, + {0xa1, 0x00}, {0xa2, 0x09}, {0xa3, 0x9c}, {0xa5, 0x00}, @@ -335,7 +364,7 @@ static const struct ov08d10_reg lane_2_mode_3264x2448[] = { {0x9a, 0x30}, {0xa8, 0x02}, {0xfd, 0x02}, - {0xa1, 0x09}, + {0xa1, 0x08}, {0xa2, 0x09}, {0xa3, 0x90}, {0xa5, 0x08}, @@ -381,7 +410,6 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = { {0x07, 0x05}, {0x21, 0x02}, {0x24, 0x30}, - {0x33, 0x03}, {0x31, 0x06}, {0x33, 0x03}, {0x01, 0x03}, @@ -467,7 +495,7 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = { {0xaa, 0xd0}, {0xab, 0x06}, {0xac, 0x68}, - {0xa1, 0x09}, + {0xa1, 0x04}, {0xa2, 0x04}, {0xa3, 0xc8}, {0xa5, 0x04}, @@ -514,9 +542,18 @@ static const char * const ov08d10_test_pattern_menu[] = { "Standard Color Bar", }; +static const char *const ov08d10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + struct ov08d10 { struct device *dev; struct clk *clk; + struct reset_control *reset; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov08d10_supply_names)]; + u8 xvclk_index; struct v4l2_subdev sd; struct media_pad pad; @@ -534,7 +571,7 @@ struct ov08d10 { /* Current mode */ const struct ov08d10_mode *cur_mode; - /* To serialize asynchronus callbacks */ + /* To serialize asynchronous callbacks */ struct mutex mutex; /* lanes index */ @@ -557,17 +594,29 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = { }, {{ .reg_list = { + { .num_of_regs = - ARRAY_SIZE(mipi_data_rate_720mbps), - .regs = mipi_data_rate_720mbps, - } + ARRAY_SIZE(mipi_data_rate_720mbps_19_2), + .regs = mipi_data_rate_720mbps_19_2, + }, + { + .num_of_regs = + ARRAY_SIZE(mipi_data_rate_720mbps_24_0), + .regs = mipi_data_rate_720mbps_24_0, + }} }, { .reg_list = { + { .num_of_regs = - ARRAY_SIZE(mipi_data_rate_360mbps), - .regs = mipi_data_rate_360mbps, - } + ARRAY_SIZE(mipi_data_rate_360mbps_19_2), + .regs = mipi_data_rate_360mbps_19_2, + }, + { + .num_of_regs = + ARRAY_SIZE(mipi_data_rate_360mbps_24_0), + .regs = mipi_data_rate_360mbps_24_0, + }} }}, {{ .width = 3280, @@ -613,8 +662,8 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = { static u32 ov08d10_get_format_code(struct ov08d10 *ov08d10) { static const u32 codes[2][2] = { - { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10}, - { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10}, + { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10 }, }; return codes[ov08d10->vflip->val][ov08d10->hflip->val]; @@ -665,7 +714,7 @@ static int ov08d10_write_reg_list(struct ov08d10 *ov08d10, r_list->regs[i].val); if (ret) { dev_err_ratelimited(ov08d10->dev, - "failed to write reg 0x%2.2x. error = %d", + "failed to write reg 0x%2.2x. error = %d\n", r_list->regs[i].address, ret); return ret; } @@ -864,7 +913,7 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl) exposure_max); } - /* V4L2 controls values will be applied only when power is already up */ + /* V4L2 control values will be applied only when power is already up */ if (!pm_runtime_get_if_in_use(ov08d10->dev)) return 0; @@ -1020,37 +1069,38 @@ static int ov08d10_start_streaming(struct ov08d10 *ov08d10) link_freq_index = ov08d10->cur_mode->link_freq_index; reg_list = - &ov08d10->priv_lane->link_freq_configs[link_freq_index].reg_list; + &ov08d10->priv_lane->link_freq_configs[link_freq_index] + .reg_list[ov08d10->xvclk_index]; /* soft reset */ ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); if (ret < 0) { - dev_err(ov08d10->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor\n"); return ret; } ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e); if (ret < 0) { - dev_err(ov08d10->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor\n"); return ret; } usleep_range(3000, 4000); ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b); if (ret < 0) { - dev_err(ov08d10->dev, "failed to reset sensor"); + dev_err(ov08d10->dev, "failed to reset sensor\n"); return ret; } /* update sensor setting */ ret = ov08d10_write_reg_list(ov08d10, reg_list); if (ret) { - dev_err(ov08d10->dev, "failed to set plls"); + dev_err(ov08d10->dev, "failed to set plls\n"); return ret; } reg_list = &ov08d10->cur_mode->reg_list; ret = ov08d10_write_reg_list(ov08d10, reg_list); if (ret) { - dev_err(ov08d10->dev, "failed to set mode"); + dev_err(ov08d10->dev, "failed to set mode\n"); return ret; } @@ -1077,19 +1127,19 @@ static void ov08d10_stop_streaming(struct ov08d10 *ov08d10) ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); if (ret < 0) { - dev_err(ov08d10->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming\n"); return; } ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT, OV08D10_MODE_STANDBY); if (ret < 0) { - dev_err(ov08d10->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming\n"); return; } ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); if (ret < 0) { - dev_err(ov08d10->dev, "failed to stop streaming"); + dev_err(ov08d10->dev, "failed to stop streaming\n"); return; } } @@ -1266,6 +1316,56 @@ static const struct v4l2_subdev_internal_ops ov08d10_internal_ops = { .open = ov08d10_open, }; +static int ov08d10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + reset_control_assert(ov08d10->reset); + + regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies), + ov08d10->supplies); + + clk_disable_unprepare(ov08d10->clk); + + return 0; +} + +static int ov08d10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov08d10 *ov08d10 = to_ov08d10(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ov08d10->supplies), + ov08d10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(ov08d10->clk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies), + ov08d10->supplies); + + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + if (ov08d10->reset) { + /* Delay from DVDD stable to sensor XSHUTDN pull up: 5ms */ + fsleep(5 * USEC_PER_MSEC); + + reset_control_deassert(ov08d10->reset); + + /* Delay from XSHUTDN pull up to SCCB start: 8ms */ + fsleep(8 * USEC_PER_MSEC); + } + + return 0; +} + static int ov08d10_identify_module(struct ov08d10 *ov08d10) { struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); @@ -1325,7 +1425,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10) /* Get number of data lanes */ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto check_hwcfg_error; @@ -1337,7 +1437,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10) ov08d10->modes_size = ov08d10_modes_num(ov08d10); if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); + dev_err(dev, "no link frequencies defined\n"); ret = -EINVAL; goto check_hwcfg_error; } @@ -1350,7 +1450,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10) } if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", + dev_err(dev, "no link frequency %lld supported\n", ov08d10->priv_lane->link_freq_menu[i]); ret = -EINVAL; goto check_hwcfg_error; @@ -1372,6 +1472,10 @@ static void ov08d10_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(ov08d10->dev); + if (!pm_runtime_status_suspended(ov08d10->dev)) { + ov08d10_power_off(ov08d10->dev); + pm_runtime_set_suspended(ov08d10->dev); + } mutex_destroy(&ov08d10->mutex); } @@ -1379,6 +1483,7 @@ static int ov08d10_probe(struct i2c_client *client) { struct ov08d10 *ov08d10; unsigned long freq; + unsigned int i; int ret; ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL); @@ -1393,30 +1498,56 @@ static int ov08d10_probe(struct i2c_client *client) "failed to get clock\n"); freq = clk_get_rate(ov08d10->clk); - if (freq != OV08D10_XVCLK_19_2) - dev_warn(ov08d10->dev, - "external clock rate %lu is not supported\n", freq); + for (i = 0; i < ARRAY_SIZE(ov08d10_xvclk_freqs); i++) { + if (freq == ov08d10_xvclk_freqs[i]) + break; + } + if (i >= ARRAY_SIZE(ov08d10_xvclk_freqs)) + return dev_err_probe(ov08d10->dev, -EINVAL, + "external clock rate %lu is not supported\n", + freq); + ov08d10->xvclk_index = i; ret = ov08d10_get_hwcfg(ov08d10); if (ret) { - dev_err(ov08d10->dev, "failed to get HW configuration: %d", + dev_err(ov08d10->dev, "failed to get HW configuration: %d\n", ret); return ret; } + ov08d10->reset = devm_reset_control_get_optional_exclusive(ov08d10->dev, NULL); + if (IS_ERR(ov08d10->reset)) + return dev_err_probe(ov08d10->dev, PTR_ERR(ov08d10->reset), + "failed to get reset\n"); + reset_control_assert(ov08d10->reset); + + for (i = 0; i < ARRAY_SIZE(ov08d10_supply_names); i++) + ov08d10->supplies[i].supply = ov08d10_supply_names[i]; + + ret = devm_regulator_bulk_get(ov08d10->dev, + ARRAY_SIZE(ov08d10->supplies), + ov08d10->supplies); + if (ret) + return dev_err_probe(ov08d10->dev, ret, + "failed to get regulators\n"); + v4l2_i2c_subdev_init(&ov08d10->sd, client, &ov08d10_subdev_ops); + ret = ov08d10_power_on(ov08d10->dev); + if (ret) + return dev_err_probe(ov08d10->dev, ret, "failed to power on\n"); + ret = ov08d10_identify_module(ov08d10); if (ret) { - dev_err(ov08d10->dev, "failed to find sensor: %d", ret); - return ret; + dev_err(ov08d10->dev, "failed to find sensor: %d\n", ret); + goto probe_error_power_off; } mutex_init(&ov08d10->mutex); ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0]; ret = ov08d10_init_controls(ov08d10); if (ret) { - dev_err(ov08d10->dev, "failed to init controls: %d", ret); + dev_err(ov08d10->dev, "failed to init controls: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1426,37 +1557,42 @@ static int ov08d10_probe(struct i2c_client *client) ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad); if (ret) { - dev_err(ov08d10->dev, "failed to init entity pads: %d", ret); + dev_err(ov08d10->dev, "failed to init entity pads: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } + pm_runtime_set_active(ov08d10->dev); + pm_runtime_enable(ov08d10->dev); + ret = v4l2_async_register_subdev_sensor(&ov08d10->sd); if (ret < 0) { - dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d", + dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d\n", ret); goto probe_error_media_entity_cleanup; } - /* - * Device is already turned on by i2c-core with ACPI domain PM. - * Enable runtime PM and turn off the device. - */ - pm_runtime_set_active(ov08d10->dev); - pm_runtime_enable(ov08d10->dev); pm_runtime_idle(ov08d10->dev); return 0; probe_error_media_entity_cleanup: + pm_runtime_disable(ov08d10->dev); + pm_runtime_set_suspended(ov08d10->dev); media_entity_cleanup(&ov08d10->sd.entity); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov08d10->sd.ctrl_handler); mutex_destroy(&ov08d10->mutex); +probe_error_power_off: + ov08d10_power_off(ov08d10->dev); + return ret; } +static DEFINE_RUNTIME_DEV_PM_OPS(ov08d10_pm_ops, + ov08d10_power_off, ov08d10_power_on, NULL); + #ifdef CONFIG_ACPI static const struct acpi_device_id ov08d10_acpi_ids[] = { { "OVTI08D1" }, @@ -1466,10 +1602,18 @@ static const struct acpi_device_id ov08d10_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ov08d10_acpi_ids); #endif +static const struct of_device_id ov08d10_of_match[] = { + { .compatible = "ovti,ov08d10" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov08d10_of_match); + static struct i2c_driver ov08d10_i2c_driver = { .driver = { .name = "ov08d10", + .pm = pm_ptr(&ov08d10_pm_ops), .acpi_match_table = ACPI_PTR(ov08d10_acpi_ids), + .of_match_table = ov08d10_of_match, }, .probe = ov08d10_probe, .remove = ov08d10_remove, diff --git a/drivers/media/i2c/ov2732.c b/drivers/media/i2c/ov2732.c new file mode 100644 index 000000000000..40035320fec6 --- /dev/null +++ b/drivers/media/i2c/ov2732.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov2732 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * Copyright (C) 2025-2026 Walter Werner Schneider <contact@schnwalter.eu> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV2732_LANES 2 +#define OV2732_BITS_PER_SAMPLE 10 +#define OV2732_LINK_FREQ_DEFAULT 360000000 +#define OV2732_XVCLK_FREQ 24000000 +#define OV2732_PIXEL_RATE \ + (OV2732_LINK_FREQ_DEFAULT * 2 * OV2732_LANES / OV2732_BITS_PER_SAMPLE) +#define OV2732_NATIVE_WIDTH 1920U +#define OV2732_NATIVE_HEIGHT 1080U + +/* Delay from power up to the first SCCB transaction. */ +#define OV2732_POWER_UP_DELAY_CYCLES 8192 +/* Delay from the last SCCB transaction to power down. */ +#define OV2732_POWER_DOWN_DELAY_CYCLES 512 +#define OV2732_DELAY_US(cycles) \ + (DIV_ROUND_UP((cycles), OV2732_XVCLK_FREQ / USEC_PER_SEC)) + +#define OV2732_REG_CHIP_ID CCI_REG24(0x300a) +#define OV2732_CHIP_ID 0x002732 + +#define OV2732_REG_MODE_SELECT CCI_REG8(0x0100) +#define OV2732_MODE_STANDBY 0x00 +#define OV2732_MODE_STREAMING 0x01 + +#define OV2732_REG_ANALOGUE_GAIN CCI_REG16(0x3508) +#define OV2732_REG_DIGITAL_GAIN CCI_REG16(0x350a) +#define OV2732_REG_EXPOSURE CCI_REG24(0x3500) +#define OV2732_REG_HTS CCI_REG16(0x380c) +#define OV2732_REG_VTS CCI_REG16(0x380e) +#define OV2732_ANALOGUE_GAIN_MIN 0x80 +/* Max analogue gain is documented as 0x3fff, but it overflows after 0x3ff. */ +#define OV2732_ANALOGUE_GAIN_MAX 0x3ff +#define OV2732_ANALOGUE_GAIN_DEFAULT 0x80 +#define OV2732_DIGITAL_GAIN_MIN 0x00 +#define OV2732_DIGITAL_GAIN_MAX 0x3fff +#define OV2732_DIGITAL_GAIN_DEFAULT 0x0400 +#define OV2732_EXPOSURE_DEFAULT 0x40 +#define OV2732_EXPOSURE_MIN 0x10 +#define OV2732_EXPOSURE_OFFSET 4 +#define OV2732_HBLANK_DEFAULT 0x0068 +#define OV2732_VTS_MAX 0x7fff + +#define OV2732_REG_TEST_PATTERN CCI_REG8(0x5080) +#define OV2732_TEST_PATTERN_DISABLE 0x00 +#define OV2732_TEST_PATTERN_BAR1 0x80 +#define OV2732_TEST_PATTERN_BAR2 0x84 +#define OV2732_TEST_PATTERN_BAR3 0x88 +#define OV2732_TEST_PATTERN_BAR4 0x8c +#define OV2732_TEST_PATTERN_BAR5 0xC0 +#define OV2732_TEST_PATTERN_RANDOM 0x81 +#define OV2732_TEST_PATTERN_SQUARES_C 0x82 +#define OV2732_TEST_PATTERN_SQUARES_BW 0x92 + +static const char * const ov2732_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +static const struct cci_reg_sequence ov2732_common_regs[] = { + /* PLL control, reset all registers. */ + { CCI_REG8(0x0103), 0x01 }, + + /* Analog control. */ + { CCI_REG8(0x3600), 0x55 }, + { CCI_REG8(0x3601), 0x52 }, + { CCI_REG8(0x3612), 0xb5 }, + { CCI_REG8(0x3613), 0xb3 }, + { CCI_REG8(0x3616), 0x83 }, + { CCI_REG8(0x3621), 0x00 }, + { CCI_REG8(0x3624), 0x06 }, + { CCI_REG8(0x3642), 0x88 }, + { CCI_REG8(0x3660), 0x00 }, + { CCI_REG8(0x3661), 0x00 }, + { CCI_REG8(0x366a), 0x64 }, + { CCI_REG8(0x366c), 0x00 }, + { CCI_REG8(0x366e), 0xff }, + { CCI_REG8(0x366f), 0xff }, + { CCI_REG8(0x3677), 0x11 }, + { CCI_REG8(0x3678), 0x11 }, + { CCI_REG8(0x3679), 0x0c }, + { CCI_REG8(0x3680), 0xff }, + { CCI_REG8(0x3681), 0x16 }, + { CCI_REG8(0x3682), 0x16 }, + { CCI_REG8(0x3683), 0x90 }, + { CCI_REG8(0x3684), 0x90 }, + + /* ADC sync control. */ + { CCI_REG8(0x4503), 0x00 }, + { CCI_REG8(0x4508), 0x14 }, + { CCI_REG8(0x450a), 0x00 }, + { CCI_REG8(0x450b), 0x40 }, + + /* ISP control, enable: WIN, DPC & ISP. */ + { CCI_REG8(0x5000), 0xa1 }, +}; + +struct ov2732_mode { + u32 width; + u32 height; + u32 vts; +}; + +static const struct ov2732_mode supported_modes[] = { + { + .width = 1920, + .height = 1080, + .vts = 1184, + }, +}; + +struct ov2732 { + struct device *dev; + struct regmap *regmap; + + struct media_pad pad; + struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + + struct clk *xvclk; + u32 xvclk_freq; + struct gpio_desc *powerdown_gpio; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov2732_supply_names)]; +}; + +#define to_ov2732(_sd) container_of(_sd, struct ov2732, sd) + +static const s64 link_freq_menu_items[] = { + OV2732_LINK_FREQ_DEFAULT, +}; + +static const char * const ov2732_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4", + "Vertical Color Bar Type 5", + "Random", + "Color Squares", + "Black and White Squares", +}; + +static const int ov2732_test_pattern_val[] = { + OV2732_TEST_PATTERN_DISABLE, + OV2732_TEST_PATTERN_BAR1, + OV2732_TEST_PATTERN_BAR2, + OV2732_TEST_PATTERN_BAR3, + OV2732_TEST_PATTERN_BAR4, + OV2732_TEST_PATTERN_BAR5, + OV2732_TEST_PATTERN_RANDOM, + OV2732_TEST_PATTERN_SQUARES_C, + OV2732_TEST_PATTERN_SQUARES_BW, +}; + +static int ov2732_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2732 *ov2732 = to_ov2732(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ov2732_supply_names), + ov2732->supplies); + if (ret) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(ov2732->xvclk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + goto reg_off; + } + + /* Wait 10ms before power up, as per datasheet. */ + fsleep(10 * USEC_PER_MSEC); + + gpiod_set_value_cansleep(ov2732->reset_gpio, 0); + gpiod_set_value_cansleep(ov2732->powerdown_gpio, 0); + + /* Datasheet requires an 8192 cycles wait, but that isn't enough. */ + fsleep(OV2732_DELAY_US(OV2732_POWER_UP_DELAY_CYCLES * 2)); + + return 0; + +reg_off: + regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names), + ov2732->supplies); + + return ret; +} + +static int ov2732_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2732 *ov2732 = to_ov2732(sd); + + clk_disable_unprepare(ov2732->xvclk); + + /* Wait for 512 cycles as per datasheet. */ + fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES)); + + gpiod_set_value_cansleep(ov2732->powerdown_gpio, 1); + gpiod_set_value_cansleep(ov2732->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names), + ov2732->supplies); + + return 0; +} + +static int ov2732_identify_chip(struct ov2732 *ov2732) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd); + int ret; + u64 val; + + ret = cci_read(ov2732->regmap, OV2732_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to read chip id\n"); + + if (val != OV2732_CHIP_ID) + return dev_err_probe(&client->dev, -ENODEV, + "chip id mismatch: %x!=%llx\n", + OV2732_CHIP_ID, val); + + return 0; +} + +static int ov2732_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov2732_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov2732_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct ov2732 *ov2732 = to_ov2732(sd); + const struct ov2732_mode *mode; + s64 vblank_def; + int ret; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + vblank_def = mode->vts - mode->height; + ret = __v4l2_ctrl_modify_range(ov2732->vblank, vblank_def, + OV2732_VTS_MAX - mode->height, 1, + vblank_def); + + return ret; +} + +static int ov2732_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV2732_NATIVE_WIDTH; + sel->r.height = OV2732_NATIVE_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int ov2732_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov2732 *ov2732 = to_ov2732(sd); + struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd); + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + /* Set stream off register for PLL changes. */ + ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT, + OV2732_MODE_STANDBY, NULL); + if (ret) + goto err_put_autosuspend; + + /* Send all registers that are common to all modes */ + ret = cci_multi_reg_write(ov2732->regmap, ov2732_common_regs, + ARRAY_SIZE(ov2732_common_regs), NULL); + if (ret) { + dev_err(&client->dev, "failed to init registers\n"); + goto err_put_autosuspend; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(ov2732->sd.ctrl_handler); + if (ret) + goto err_put_autosuspend; + + /* Set stream on register */ + ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT, + OV2732_MODE_STREAMING, NULL); + if (ret) + goto err_put_autosuspend; + + return 0; + +err_put_autosuspend: + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static int ov2732_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov2732 *ov2732 = to_ov2732(sd); + struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd); + int ret; + + /* set stream off register */ + ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT, + OV2732_MODE_STANDBY, NULL); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); + + /* Wait for 512 cycles as per datasheet. */ + fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES)); + + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov2732_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov2732_pad_ops = { + .enum_mbus_code = ov2732_enum_mbus_code, + .enum_frame_size = ov2732_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ov2732_set_fmt, + .get_selection = ov2732_get_selection, + .enable_streams = ov2732_enable_streams, + .disable_streams = ov2732_disable_streams, +}; + +static const struct v4l2_subdev_ops ov2732_subdev_ops = { + .video = &ov2732_video_ops, + .pad = &ov2732_pad_ops, +}; + +static int ov2732_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .field = V4L2_FIELD_NONE, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + } + }; + + return ov2732_set_fmt(sd, sd_state, &fmt); +} + +static const struct v4l2_subdev_internal_ops ov2732_internal_ops = { + .init_state = ov2732_init_state, +}; + +static int ov2732_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov2732 *ov2732 = + container_of(ctrl->handler, struct ov2732, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&ov2732->sd); + format = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + exposure_max = format->height + ctrl->val - + OV2732_EXPOSURE_OFFSET; + exposure_def = exposure_max; + ret = __v4l2_ctrl_modify_range(ov2732->exposure, + OV2732_EXPOSURE_MIN, + exposure_max, + ov2732->exposure->step, + exposure_def); + if (ret) + return ret; + } + + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(ov2732->regmap, OV2732_REG_ANALOGUE_GAIN, + ctrl->val, &ret); + break; + case V4L2_CID_DIGITAL_GAIN: + cci_write(ov2732->regmap, OV2732_REG_DIGITAL_GAIN, + ctrl->val, &ret); + break; + case V4L2_CID_EXPOSURE: + /* Lowest 4 bits are fraction bits. */ + cci_write(ov2732->regmap, OV2732_REG_EXPOSURE, + (u32)ctrl->val << 4, &ret); + break; + case V4L2_CID_VBLANK: + cci_write(ov2732->regmap, OV2732_REG_VTS, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN: + cci_write(ov2732->regmap, OV2732_REG_TEST_PATTERN, + ov2732_test_pattern_val[ctrl->val], &ret); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +}; + +static const struct v4l2_ctrl_ops ov2732_ctrl_ops = { + .s_ctrl = ov2732_set_ctrl, +}; + +static int ov2732_init_controls(struct ov2732 *ov2732) +{ + const struct ov2732_mode *mode = &supported_modes[0]; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, vblank_def, vblank_max; + int ret; + + handler = &ov2732->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 10); + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + OV2732_PIXEL_RATE, OV2732_PIXEL_RATE, + 1, OV2732_PIXEL_RATE); + + ctrl = v4l2_ctrl_new_int_menu(handler, &ov2732_ctrl_ops, + V4L2_CID_LINK_FREQ, 0, 0, + link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov2732->hblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, + V4L2_CID_HBLANK, + OV2732_HBLANK_DEFAULT, + OV2732_HBLANK_DEFAULT, + 1, OV2732_HBLANK_DEFAULT); + if (ov2732->hblank) + ov2732->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts - mode->height; + vblank_max = OV2732_VTS_MAX - mode->height; + ov2732->vblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, + V4L2_CID_VBLANK, + vblank_def, vblank_max, + 1, vblank_def); + + exposure_max = mode->vts - OV2732_EXPOSURE_OFFSET; + ov2732->exposure = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, + V4L2_CID_EXPOSURE, + OV2732_EXPOSURE_MIN, exposure_max, + 1, OV2732_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV2732_ANALOGUE_GAIN_MIN, OV2732_ANALOGUE_GAIN_MAX, + 1, OV2732_ANALOGUE_GAIN_DEFAULT); + + v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV2732_DIGITAL_GAIN_MIN, OV2732_DIGITAL_GAIN_MAX, + 1, OV2732_DIGITAL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(handler, &ov2732_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2732_test_pattern_menu) - 1, + 0, 0, ov2732_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err_probe(ov2732->dev, ret, "Control init failed\n"); + goto err_handler_free; + } + + ret = v4l2_fwnode_device_parse(ov2732->dev, &props); + if (ret) + goto err_handler_free; + + ret = v4l2_ctrl_new_fwnode_properties(handler, &ov2732_ctrl_ops, &props); + if (ret) + goto err_handler_free; + + ov2732->sd.ctrl_handler = handler; + + return 0; + +err_handler_free: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov2632_probe_dt(struct ov2732 *ov2732) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(ov2732->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) { + dev_err_probe(ov2732->dev, -EINVAL, "could not parse endpoint\n"); + goto err_probe_dt; + } + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV2732_LANES) { + dev_err(ov2732->dev, "only a 2-lane CSI2 config is supported\n"); + ret = -EINVAL; + } + +err_probe_dt: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov2732_get_regulators(struct ov2732 *ov2732) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ov2732_supply_names); i++) + ov2732->supplies[i].supply = ov2732_supply_names[i]; + + return devm_regulator_bulk_get(ov2732->dev, + ARRAY_SIZE(ov2732_supply_names), + ov2732->supplies); +} + +static int ov2732_probe(struct i2c_client *client) +{ + struct ov2732 *ov2732; + int ret; + + ov2732 = devm_kzalloc(&client->dev, sizeof(*ov2732), GFP_KERNEL); + if (!ov2732) + return -ENOMEM; + + ov2732->dev = &client->dev; + + ret = ov2632_probe_dt(ov2732); + if (ret) + return ret; + + ov2732->xvclk = devm_v4l2_sensor_clk_get(ov2732->dev, NULL); + if (IS_ERR(ov2732->xvclk)) + return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->xvclk), + "failed to get xvclk\n"); + + ov2732->xvclk_freq = clk_get_rate(ov2732->xvclk); + if (ov2732->xvclk_freq != OV2732_XVCLK_FREQ) + return dev_err_probe(ov2732->dev, -EINVAL, + "xvclk frequency not supported: %dHz\n", + ov2732->xvclk_freq); + + ov2732->powerdown_gpio = devm_gpiod_get_optional(ov2732->dev, + "powerdown", + GPIOD_OUT_HIGH); + + ov2732->reset_gpio = devm_gpiod_get_optional(ov2732->dev, "reset", + GPIOD_OUT_HIGH); + + ov2732->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov2732->regmap)) + return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->regmap), + "failed to init CCI\n"); + + ret = ov2732_get_regulators(ov2732); + if (ret) + return dev_err_probe(ov2732->dev, ret, + "failed to get regulators\n"); + + v4l2_i2c_subdev_init(&ov2732->sd, client, &ov2732_subdev_ops); + + /* Device must be powered on for ov2732_identify_chip(). */ + ret = ov2732_power_on(ov2732->dev); + if (ret) + return ret; + + pm_runtime_set_active(ov2732->dev); + pm_runtime_enable(ov2732->dev); + + ret = ov2732_identify_chip(ov2732); + if (ret) + goto err_power_off; + + ret = ov2732_init_controls(ov2732); + if (ret) + goto err_power_off; + + /* Initialize subdev */ + ov2732->sd.internal_ops = &ov2732_internal_ops; + ov2732->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov2732->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov2732->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&ov2732->sd.entity, 1, &ov2732->pad); + if (ret) { + dev_err_probe(ov2732->dev, ret, "failed to init entity pads\n"); + goto error_handler_free; + } + + ov2732->sd.state_lock = ov2732->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov2732->sd); + if (ret < 0) { + dev_err_probe(ov2732->dev, ret, "subdev init error\n"); + goto err_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&ov2732->sd); + if (ret < 0) { + dev_err_probe(ov2732->dev, ret, + "failed to register sensor sub-device\n"); + goto err_subdev_cleanup; + } + + pm_runtime_set_autosuspend_delay(ov2732->dev, 1000); + pm_runtime_use_autosuspend(ov2732->dev); + pm_runtime_idle(ov2732->dev); + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&ov2732->sd); + +err_media_entity: + media_entity_cleanup(&ov2732->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(&ov2732->ctrl_handler); + +err_power_off: + pm_runtime_disable(ov2732->dev); + pm_runtime_set_suspended(ov2732->dev); + ov2732_power_off(ov2732->dev); + + return ret; +} + +static void ov2732_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2732 *ov2732 = to_ov2732(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&ov2732->ctrl_handler); + + pm_runtime_disable(ov2732->dev); + if (!pm_runtime_status_suspended(ov2732->dev)) { + ov2732_power_off(ov2732->dev); + pm_runtime_set_suspended(ov2732->dev); + } +} + +static const struct of_device_id ov2732_of_match[] = { + { .compatible = "ovti,ov2732", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ov2732_of_match); + +static DEFINE_RUNTIME_DEV_PM_OPS(ov2732_pm_ops, ov2732_power_off, + ov2732_power_on, NULL); + +static struct i2c_driver ov2732_i2c_driver = { + .driver = { + .name = "ov2732", + .of_match_table = ov2732_of_match, + .pm = pm_sleep_ptr(&ov2732_pm_ops), + }, + .probe = ov2732_probe, + .remove = ov2732_remove, +}; +module_i2c_driver(ov2732_i2c_driver); + +MODULE_DESCRIPTION("OmniVision ov2732 sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Walter Werner Schneider <contact@schnwalter.eu>"); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac..db9bd2892140 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index ea26df328189..508149485248 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1181,17 +1181,26 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675) if (!fwnode) return -ENXIO; + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + ov5675->xvclk = devm_v4l2_sensor_clk_get(dev, NULL); - if (IS_ERR(ov5675->xvclk)) - return dev_err_probe(dev, PTR_ERR(ov5675->xvclk), - "failed to get xvclk: %pe\n", - ov5675->xvclk); + if (IS_ERR(ov5675->xvclk)) { + ret = dev_err_probe(dev, PTR_ERR(ov5675->xvclk), + "failed to get xvclk\n"); + goto check_hwcfg_error; + } xvclk_rate = clk_get_rate(ov5675->xvclk); if (xvclk_rate != OV5675_XVCLK_19_2) { dev_err(dev, "external clock rate %u is unsupported", xvclk_rate); - return -EINVAL; + ret = -EINVAL; + goto check_hwcfg_error; } ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset", @@ -1199,7 +1208,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675) if (IS_ERR(ov5675->reset_gpio)) { ret = PTR_ERR(ov5675->reset_gpio); dev_err(dev, "failed to get reset-gpios: %d\n", ret); - return ret; + goto check_hwcfg_error; } for (i = 0; i < OV5675_NUM_SUPPLIES; i++) @@ -1208,16 +1217,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675) ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES, ov5675->supplies); if (ret) - return ret; - - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; + goto check_hwcfg_error; if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV5675_DATA_LANES) { dev_err(dev, "number of CSI2 data lanes %d is not supported", diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index e2998cfa0d18..8bedb47cd7cf 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1887,12 +1887,14 @@ static const struct v4l2_ctrl_ops ov8856_ctrl_ops = { static int ov8856_init_controls(struct ov8856 *ov8856) { + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max, h_blank; int ret; ctrl_hdlr = &ov8856->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -1951,12 +1953,27 @@ static int ov8856_init_controls(struct ov8856 *ov8856) V4L2_CID_HFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - if (ctrl_hdlr->error) - return ctrl_hdlr->error; + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto err_ctrl_handler_free; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto err_ctrl_handler_free; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov8856_ctrl_ops, + &props); + if (ret) + goto err_ctrl_handler_free; ov8856->sd.ctrl_handler = ctrl_hdlr; return 0; + +err_ctrl_handler_free: + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; } static void ov8856_update_pad_format(struct ov8856 *ov8856, diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index ded9b2044ff8..2167fb73ea41 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -12,38 +12,40 @@ #include <linux/math.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> /* Streaming Mode */ -#define OV9282_REG_MODE_SELECT 0x0100 +#define OV9282_REG_MODE_SELECT CCI_REG8(0x0100) #define OV9282_MODE_STANDBY 0x00 #define OV9282_MODE_STREAMING 0x01 -#define OV9282_REG_PLL_CTRL_0D 0x030d +#define OV9282_REG_PLL_CTRL_0D CCI_REG8(0x030d) #define OV9282_PLL_CTRL_0D_RAW8 0x60 #define OV9282_PLL_CTRL_0D_RAW10 0x50 -#define OV9282_REG_TIMING_HTS 0x380c +#define OV9282_REG_TIMING_HTS CCI_REG16(0x380c) #define OV9282_TIMING_HTS_MAX 0x7fff /* Lines per frame */ -#define OV9282_REG_LPFR 0x380e +#define OV9282_REG_LPFR CCI_REG16(0x380e) /* Chip ID */ -#define OV9282_REG_ID 0x300a +#define OV9282_REG_ID CCI_REG16(0x300a) #define OV9282_ID 0x9281 /* Output enable registers */ -#define OV9282_REG_OUTPUT_ENABLE4 0x3004 +#define OV9282_REG_OUTPUT_ENABLE4 CCI_REG8(0x3004) #define OV9282_OUTPUT_ENABLE4_GPIO2 BIT(1) #define OV9282_OUTPUT_ENABLE4_D9 BIT(0) -#define OV9282_REG_OUTPUT_ENABLE5 0x3005 +#define OV9282_REG_OUTPUT_ENABLE5 CCI_REG8(0x3005) #define OV9282_OUTPUT_ENABLE5_D8 BIT(7) #define OV9282_OUTPUT_ENABLE5_D7 BIT(6) #define OV9282_OUTPUT_ENABLE5_D6 BIT(5) @@ -53,7 +55,7 @@ #define OV9282_OUTPUT_ENABLE5_D2 BIT(1) #define OV9282_OUTPUT_ENABLE5_D1 BIT(0) -#define OV9282_REG_OUTPUT_ENABLE6 0x3006 +#define OV9282_REG_OUTPUT_ENABLE6 CCI_REG8(0x3006) #define OV9282_OUTPUT_ENABLE6_D0 BIT(7) #define OV9282_OUTPUT_ENABLE6_PCLK BIT(6) #define OV9282_OUTPUT_ENABLE6_HREF BIT(5) @@ -62,14 +64,14 @@ #define OV9282_OUTPUT_ENABLE6_VSYNC BIT(1) /* Exposure control */ -#define OV9282_REG_EXPOSURE 0x3500 +#define OV9282_REG_EXPOSURE CCI_REG24(0x3500) #define OV9282_EXPOSURE_MIN 1 #define OV9282_EXPOSURE_OFFSET 25 #define OV9282_EXPOSURE_STEP 1 #define OV9282_EXPOSURE_DEFAULT 0x0282 /* AEC/AGC manual */ -#define OV9282_REG_AEC_MANUAL 0x3503 +#define OV9282_REG_AEC_MANUAL CCI_REG8(0x3503) #define OV9282_DIGFRAC_GAIN_DELAY BIT(6) #define OV9282_GAIN_CHANGE_DELAY BIT(5) #define OV9282_GAIN_DELAY BIT(4) @@ -78,28 +80,28 @@ #define OV9282_AEC_MANUAL_DEFAULT 0x00 /* Analog gain control */ -#define OV9282_REG_AGAIN 0x3509 +#define OV9282_REG_AGAIN CCI_REG8(0x3509) #define OV9282_AGAIN_MIN 0x10 #define OV9282_AGAIN_MAX 0xff #define OV9282_AGAIN_STEP 1 #define OV9282_AGAIN_DEFAULT 0x10 /* Group hold register */ -#define OV9282_REG_HOLD 0x3308 +#define OV9282_REG_HOLD CCI_REG8(0x3308) -#define OV9282_REG_ANA_CORE_2 0x3662 +#define OV9282_REG_ANA_CORE_2 CCI_REG8(0x3662) #define OV9282_ANA_CORE2_RAW8 0x07 #define OV9282_ANA_CORE2_RAW10 0x05 -#define OV9282_REG_TIMING_FORMAT_1 0x3820 -#define OV9282_REG_TIMING_FORMAT_2 0x3821 +#define OV9282_REG_TIMING_FORMAT_1 CCI_REG8(0x3820) +#define OV9282_REG_TIMING_FORMAT_2 CCI_REG8(0x3821) #define OV9282_FLIP_BIT BIT(2) -#define OV9282_REG_MIPI_CTRL00 0x4800 +#define OV9282_REG_MIPI_CTRL00 CCI_REG8(0x4800) #define OV9282_GATED_CLOCK BIT(5) /* Flash/Strobe control registers */ -#define OV9282_REG_STROBE_FRAME_SPAN 0x3925 +#define OV9282_REG_STROBE_FRAME_SPAN CCI_REG32(0x3925) #define OV9282_STROBE_FRAME_SPAN_DEFAULT 0x0000001a /* Input clock rate */ @@ -140,23 +142,13 @@ static const char * const ov9282_supply_names[] = { #define OV9282_NUM_SUPPLIES ARRAY_SIZE(ov9282_supply_names) /** - * struct ov9282_reg - ov9282 sensor register - * @address: Register address - * @val: Register value - */ -struct ov9282_reg { - u16 address; - u8 val; -}; - -/** * struct ov9282_reg_list - ov9282 sensor register list * @num_of_regs: Number of registers in the list * @regs: Pointer to register list */ struct ov9282_reg_list { u32 num_of_regs; - const struct ov9282_reg *regs; + const struct cci_reg_sequence *regs; }; /** @@ -188,6 +180,7 @@ struct ov9282_mode { * struct ov9282 - ov9282 sensor device structure * @dev: Pointer to generic device * @sd: V4L2 sub-device + * @regmap: Regmap for sensor register access * @pad: Media pad. Only one pad supported * @reset_gpio: Sensor reset gpio * @inclk: Sensor input clock @@ -209,6 +202,7 @@ struct ov9282_mode { struct ov9282 { struct device *dev; struct v4l2_subdev sd; + struct regmap *regmap; struct media_pad pad; struct gpio_desc *reset_gpio; struct clk *inclk; @@ -227,7 +221,6 @@ struct ov9282 { bool noncontinuous_clock; const struct ov9282_mode *cur_mode; u32 code; - struct mutex mutex; }; static const s64 link_freq[] = { @@ -241,73 +234,68 @@ static const s64 link_freq[] = { * register arrays as some settings are written as part of ov9282_power_on, * and the reset will clear them. */ -static const struct ov9282_reg common_regs[] = { - {0x0302, 0x32}, - {0x030e, 0x02}, - {0x3001, 0x00}, +static const struct cci_reg_sequence common_regs[] = { + {CCI_REG8(0x0302), 0x32}, + {CCI_REG8(0x030e), 0x02}, + {CCI_REG8(0x3001), 0x00}, {OV9282_REG_OUTPUT_ENABLE4, 0x00}, {OV9282_REG_OUTPUT_ENABLE5, 0x00}, {OV9282_REG_OUTPUT_ENABLE6, OV9282_OUTPUT_ENABLE6_ILPWM}, - {0x3011, 0x0a}, - {0x3013, 0x18}, - {0x301c, 0xf0}, - {0x3022, 0x01}, - {0x3030, 0x10}, - {0x3039, 0x32}, - {0x303a, 0x00}, + {CCI_REG8(0x3011), 0x0a}, + {CCI_REG8(0x3013), 0x18}, + {CCI_REG8(0x301c), 0xf0}, + {CCI_REG8(0x3022), 0x01}, + {CCI_REG8(0x3030), 0x10}, + {CCI_REG8(0x3039), 0x32}, + {CCI_REG8(0x303a), 0x00}, {OV9282_REG_AEC_MANUAL, OV9282_GAIN_PREC16_EN}, - {0x3505, 0x8c}, - {0x3507, 0x03}, - {0x3508, 0x00}, - {0x3610, 0x80}, - {0x3611, 0xa0}, - {0x3620, 0x6e}, - {0x3632, 0x56}, - {0x3633, 0x78}, - {0x3666, 0x00}, - {0x366f, 0x5a}, - {0x3680, 0x84}, - {0x3712, 0x80}, - {0x372d, 0x22}, - {0x3731, 0x80}, - {0x3732, 0x30}, - {0x377d, 0x22}, - {0x3788, 0x02}, - {0x3789, 0xa4}, - {0x378a, 0x00}, - {0x378b, 0x4a}, - {0x3799, 0x20}, - {0x3881, 0x42}, - {0x38a8, 0x02}, - {0x38a9, 0x80}, - {0x38b1, 0x00}, - {0x38c4, 0x00}, - {0x38c5, 0xc0}, - {0x38c6, 0x04}, - {0x38c7, 0x80}, - {0x3920, 0xff}, - {0x4010, 0x40}, - {0x4043, 0x40}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4501, 0x00}, - {0x450a, 0x08}, - {0x4601, 0x04}, - {0x470f, 0x00}, - {0x4f07, 0x00}, - {0x5000, 0x9f}, - {0x5001, 0x00}, - {0x5e00, 0x00}, - {0x5d00, 0x07}, - {0x5d01, 0x00}, - {0x0101, 0x01}, - {0x1000, 0x03}, - {0x5a08, 0x84}, -}; - -static struct ov9282_reg_list common_regs_list = { - .num_of_regs = ARRAY_SIZE(common_regs), - .regs = common_regs, + {CCI_REG8(0x3505), 0x8c}, + {CCI_REG8(0x3507), 0x03}, + {CCI_REG8(0x3508), 0x00}, + {CCI_REG8(0x3610), 0x80}, + {CCI_REG8(0x3611), 0xa0}, + {CCI_REG8(0x3620), 0x6e}, + {CCI_REG8(0x3632), 0x56}, + {CCI_REG8(0x3633), 0x78}, + {CCI_REG8(0x3666), 0x00}, + {CCI_REG8(0x366f), 0x5a}, + {CCI_REG8(0x3680), 0x84}, + {CCI_REG8(0x3712), 0x80}, + {CCI_REG8(0x372d), 0x22}, + {CCI_REG8(0x3731), 0x80}, + {CCI_REG8(0x3732), 0x30}, + {CCI_REG8(0x377d), 0x22}, + {CCI_REG8(0x3788), 0x02}, + {CCI_REG8(0x3789), 0xa4}, + {CCI_REG8(0x378a), 0x00}, + {CCI_REG8(0x378b), 0x4a}, + {CCI_REG8(0x3799), 0x20}, + {CCI_REG8(0x3881), 0x42}, + {CCI_REG8(0x38a8), 0x02}, + {CCI_REG8(0x38a9), 0x80}, + {CCI_REG8(0x38b1), 0x00}, + {CCI_REG8(0x38c4), 0x00}, + {CCI_REG8(0x38c5), 0xc0}, + {CCI_REG8(0x38c6), 0x04}, + {CCI_REG8(0x38c7), 0x80}, + {CCI_REG8(0x3920), 0xff}, + {CCI_REG8(0x4010), 0x40}, + {CCI_REG8(0x4043), 0x40}, + {CCI_REG8(0x4307), 0x30}, + {CCI_REG8(0x4317), 0x00}, + {CCI_REG8(0x4501), 0x00}, + {CCI_REG8(0x450a), 0x08}, + {CCI_REG8(0x4601), 0x04}, + {CCI_REG8(0x470f), 0x00}, + {CCI_REG8(0x4f07), 0x00}, + {CCI_REG8(0x5000), 0x9f}, + {CCI_REG8(0x5001), 0x00}, + {CCI_REG8(0x5e00), 0x00}, + {CCI_REG8(0x5d00), 0x07}, + {CCI_REG8(0x5d01), 0x00}, + {CCI_REG8(0x0101), 0x01}, + {CCI_REG8(0x1000), 0x03}, + {CCI_REG8(0x5a08), 0x84}, }; #define MODE_1280_800 0 @@ -317,96 +305,96 @@ static struct ov9282_reg_list common_regs_list = { #define DEFAULT_MODE MODE_1280_720 /* Sensor mode registers */ -static const struct ov9282_reg mode_1280x800_regs[] = { - {0x3778, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x05}, - {0x3805, 0x0f}, - {0x3806, 0x03}, - {0x3807, 0x2f}, - {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x03}, - {0x380b, 0x20}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x08}, - {0x3814, 0x11}, - {0x3815, 0x11}, +static const struct cci_reg_sequence mode_1280x800_regs[] = { + {CCI_REG8(0x3778), 0x00}, + {CCI_REG8(0x3800), 0x00}, + {CCI_REG8(0x3801), 0x00}, + {CCI_REG8(0x3802), 0x00}, + {CCI_REG8(0x3803), 0x00}, + {CCI_REG8(0x3804), 0x05}, + {CCI_REG8(0x3805), 0x0f}, + {CCI_REG8(0x3806), 0x03}, + {CCI_REG8(0x3807), 0x2f}, + {CCI_REG8(0x3808), 0x05}, + {CCI_REG8(0x3809), 0x00}, + {CCI_REG8(0x380a), 0x03}, + {CCI_REG8(0x380b), 0x20}, + {CCI_REG8(0x3810), 0x00}, + {CCI_REG8(0x3811), 0x08}, + {CCI_REG8(0x3812), 0x00}, + {CCI_REG8(0x3813), 0x08}, + {CCI_REG8(0x3814), 0x11}, + {CCI_REG8(0x3815), 0x11}, {OV9282_REG_TIMING_FORMAT_1, 0x40}, {OV9282_REG_TIMING_FORMAT_2, 0x00}, - {0x4003, 0x40}, - {0x4008, 0x04}, - {0x4009, 0x0b}, - {0x400c, 0x00}, - {0x400d, 0x07}, - {0x4507, 0x00}, - {0x4509, 0x00}, + {CCI_REG8(0x4003), 0x40}, + {CCI_REG8(0x4008), 0x04}, + {CCI_REG8(0x4009), 0x0b}, + {CCI_REG8(0x400c), 0x00}, + {CCI_REG8(0x400d), 0x07}, + {CCI_REG8(0x4507), 0x00}, + {CCI_REG8(0x4509), 0x00}, }; -static const struct ov9282_reg mode_1280x720_regs[] = { - {0x3778, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x05}, - {0x3805, 0x0f}, - {0x3806, 0x02}, - {0x3807, 0xdf}, - {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x02}, - {0x380b, 0xd0}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x08}, - {0x3814, 0x11}, - {0x3815, 0x11}, +static const struct cci_reg_sequence mode_1280x720_regs[] = { + {CCI_REG8(0x3778), 0x00}, + {CCI_REG8(0x3800), 0x00}, + {CCI_REG8(0x3801), 0x00}, + {CCI_REG8(0x3802), 0x00}, + {CCI_REG8(0x3803), 0x00}, + {CCI_REG8(0x3804), 0x05}, + {CCI_REG8(0x3805), 0x0f}, + {CCI_REG8(0x3806), 0x02}, + {CCI_REG8(0x3807), 0xdf}, + {CCI_REG8(0x3808), 0x05}, + {CCI_REG8(0x3809), 0x00}, + {CCI_REG8(0x380a), 0x02}, + {CCI_REG8(0x380b), 0xd0}, + {CCI_REG8(0x3810), 0x00}, + {CCI_REG8(0x3811), 0x08}, + {CCI_REG8(0x3812), 0x00}, + {CCI_REG8(0x3813), 0x08}, + {CCI_REG8(0x3814), 0x11}, + {CCI_REG8(0x3815), 0x11}, {OV9282_REG_TIMING_FORMAT_1, 0x3c}, {OV9282_REG_TIMING_FORMAT_2, 0x84}, - {0x4003, 0x40}, - {0x4008, 0x02}, - {0x4009, 0x05}, - {0x400c, 0x00}, - {0x400d, 0x03}, - {0x4507, 0x00}, - {0x4509, 0x80}, + {CCI_REG8(0x4003), 0x40}, + {CCI_REG8(0x4008), 0x02}, + {CCI_REG8(0x4009), 0x05}, + {CCI_REG8(0x400c), 0x00}, + {CCI_REG8(0x400d), 0x03}, + {CCI_REG8(0x4507), 0x00}, + {CCI_REG8(0x4509), 0x80}, }; -static const struct ov9282_reg mode_640x400_regs[] = { - {0x3778, 0x10}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x05}, - {0x3805, 0x0f}, - {0x3806, 0x03}, - {0x3807, 0x2f}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0x90}, - {0x3810, 0x00}, - {0x3811, 0x04}, - {0x3812, 0x00}, - {0x3813, 0x04}, - {0x3814, 0x31}, - {0x3815, 0x22}, +static const struct cci_reg_sequence mode_640x400_regs[] = { + {CCI_REG8(0x3778), 0x10}, + {CCI_REG8(0x3800), 0x00}, + {CCI_REG8(0x3801), 0x00}, + {CCI_REG8(0x3802), 0x00}, + {CCI_REG8(0x3803), 0x00}, + {CCI_REG8(0x3804), 0x05}, + {CCI_REG8(0x3805), 0x0f}, + {CCI_REG8(0x3806), 0x03}, + {CCI_REG8(0x3807), 0x2f}, + {CCI_REG8(0x3808), 0x02}, + {CCI_REG8(0x3809), 0x80}, + {CCI_REG8(0x380a), 0x01}, + {CCI_REG8(0x380b), 0x90}, + {CCI_REG8(0x3810), 0x00}, + {CCI_REG8(0x3811), 0x04}, + {CCI_REG8(0x3812), 0x00}, + {CCI_REG8(0x3813), 0x04}, + {CCI_REG8(0x3814), 0x31}, + {CCI_REG8(0x3815), 0x22}, {OV9282_REG_TIMING_FORMAT_1, 0x60}, {OV9282_REG_TIMING_FORMAT_2, 0x01}, - {0x4008, 0x02}, - {0x4009, 0x05}, - {0x400c, 0x00}, - {0x400d, 0x03}, - {0x4507, 0x03}, - {0x4509, 0x80}, + {CCI_REG8(0x4008), 0x02}, + {CCI_REG8(0x4009), 0x05}, + {CCI_REG8(0x400c), 0x00}, + {CCI_REG8(0x400d), 0x03}, + {CCI_REG8(0x4507), 0x03}, + {CCI_REG8(0x4509), 0x80}, }; /* Supported sensor mode configurations */ @@ -486,97 +474,6 @@ static inline struct ov9282 *to_ov9282(struct v4l2_subdev *subdev) } /** - * ov9282_read_reg() - Read registers. - * @ov9282: pointer to ov9282 device - * @reg: register address - * @len: length of bytes to read. Max supported bytes is 4 - * @val: pointer to register value to be filled. - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_read_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd); - struct i2c_msg msgs[2] = {0}; - u8 addr_buf[2] = {0}; - u8 data_buf[4] = {0}; - int ret; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/** - * ov9282_write_reg() - Write register - * @ov9282: pointer to ov9282 device - * @reg: register address - * @len: length of bytes. Max supported bytes is 4 - * @val: register value - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_write_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd); - u8 buf[6] = {0}; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/** - * ov9282_write_regs() - Write a list of registers - * @ov9282: pointer to ov9282 device - * @regs: list of registers to be written - * @len: length of registers array - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_write_regs(struct ov9282 *ov9282, - const struct ov9282_reg *regs, u32 len) -{ - unsigned int i; - int ret; - - for (i = 0; i < len; i++) { - ret = ov9282_write_reg(ov9282, regs[i].address, 1, regs[i].val); - if (ret) - return ret; - } - - return 0; -} - -/** * ov9282_update_controls() - Update control ranges based on streaming mode * @ov9282: pointer to ov9282 device * @mode: pointer to ov9282_mode sensor mode @@ -633,21 +530,21 @@ static u32 ov9282_exposure_to_us(struct ov9282 *ov9282, u32 exposure) */ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) { - int ret; u32 exposure_us = ov9282_exposure_to_us(ov9282, exposure); + int ret, ret_hold; dev_dbg(ov9282->dev, "Set exp %u (~%u us), analog gain %u", exposure, exposure_us, gain); - ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1); + ret = cci_write(ov9282->regmap, OV9282_REG_HOLD, 0x01, NULL); if (ret) return ret; - ret = ov9282_write_reg(ov9282, OV9282_REG_EXPOSURE, 3, exposure << 4); + ret = cci_write(ov9282->regmap, OV9282_REG_EXPOSURE, exposure << 4, NULL); if (ret) goto error_release_group_hold; - ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain); + ret = cci_write(ov9282->regmap, OV9282_REG_AGAIN, gain, NULL); if (ret) goto error_release_group_hold; @@ -656,60 +553,9 @@ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) OV9282_STROBE_FRAME_SPAN_DEFAULT); error_release_group_hold: - ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0); - - return ret; -} + ret_hold = cci_write(ov9282->regmap, OV9282_REG_HOLD, 0, NULL); -static int ov9282_set_ctrl_hflip(struct ov9282 *ov9282, int value) -{ - u32 current_val; - int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1, - ¤t_val); - if (ret) - return ret; - - if (value) - current_val |= OV9282_FLIP_BIT; - else - current_val &= ~OV9282_FLIP_BIT; - - return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1, - current_val); -} - -static int ov9282_set_ctrl_vflip(struct ov9282 *ov9282, int value) -{ - u32 current_val; - int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1, - ¤t_val); - if (ret) - return ret; - - if (value) - current_val |= OV9282_FLIP_BIT; - else - current_val &= ~OV9282_FLIP_BIT; - - return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1, - current_val); -} - -static int ov9282_set_ctrl_flash_strobe_oe(struct ov9282 *ov9282, bool enable) -{ - u32 current_val; - int ret; - - ret = ov9282_read_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, ¤t_val); - if (ret) - return ret; - - if (enable) - current_val |= OV9282_OUTPUT_ENABLE6_STROBE; - else - current_val &= ~OV9282_OUTPUT_ENABLE6_STROBE; - - return ov9282_write_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, current_val); + return ret ? ret : ret_hold; } static u32 ov9282_us_to_flash_duration(struct ov9282 *ov9282, u32 value) @@ -740,30 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value) return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR); } -static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value) -{ - u32 val = ov9282_us_to_flash_duration(ov9282, value); - int ret; - - ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN, 1, - (val >> 24) & 0xff); - if (ret) - return ret; - - ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 1, 1, - (val >> 16) & 0xff); - if (ret) - return ret; - - ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 2, 1, - (val >> 8) & 0xff); - if (ret) - return ret; - - return ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 3, 1, - val & 0xff); -} - /** * ov9282_set_ctrl() - Set subdevice control * @ctrl: pointer to v4l2_ctrl structure @@ -818,23 +640,27 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: lpfr = ov9282->vblank + ov9282->cur_mode->height; - ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr); + ret = cci_write(ov9282->regmap, OV9282_REG_LPFR, lpfr, NULL); break; case V4L2_CID_HFLIP: - ret = ov9282_set_ctrl_hflip(ov9282, ctrl->val); + ret = cci_update_bits(ov9282->regmap, OV9282_REG_TIMING_FORMAT_2, + OV9282_FLIP_BIT, ctrl->val ? OV9282_FLIP_BIT : 0, NULL); break; case V4L2_CID_VFLIP: - ret = ov9282_set_ctrl_vflip(ov9282, ctrl->val); + ret = cci_update_bits(ov9282->regmap, OV9282_REG_TIMING_FORMAT_1, + OV9282_FLIP_BIT, ctrl->val ? OV9282_FLIP_BIT : 0, NULL); break; case V4L2_CID_HBLANK: - ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_HTS, 2, - (ctrl->val + ov9282->cur_mode->width) >> 1); + ret = cci_write(ov9282->regmap, OV9282_REG_TIMING_HTS, + (ctrl->val + ov9282->cur_mode->width) >> 1, NULL); break; case V4L2_CID_FLASH_STROBE_OE: - ret = ov9282_set_ctrl_flash_strobe_oe(ov9282, ctrl->val); + ret = cci_update_bits(ov9282->regmap, OV9282_REG_OUTPUT_ENABLE6, + OV9282_OUTPUT_ENABLE6_STROBE, + ctrl->val ? OV9282_OUTPUT_ENABLE6_STROBE : 0, NULL); break; case V4L2_CID_FLASH_DURATION: - ret = ov9282_set_ctrl_flash_duration(ov9282, ctrl->val); + ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, ctrl->val, NULL); break; default: dev_err(ov9282->dev, "Invalid control %d", ctrl->id); @@ -968,8 +794,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, { struct ov9282 *ov9282 = to_ov9282(sd); - mutex_lock(&ov9282->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -980,8 +804,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, fmt); } - mutex_unlock(&ov9282->mutex); - return 0; } @@ -1002,8 +824,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, u32 code; int ret = 0; - mutex_lock(&ov9282->mutex); - mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, @@ -1029,8 +849,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, } } - mutex_unlock(&ov9282->mutex); - return ret; } @@ -1077,10 +895,8 @@ static int ov9282_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP: { struct ov9282 *ov9282 = to_ov9282(sd); - mutex_lock(&ov9282->mutex); sel->r = *__ov9282_get_pad_crop(ov9282, sd_state, sel->pad, sel->which); - mutex_unlock(&ov9282->mutex); return 0; } @@ -1106,15 +922,11 @@ static int ov9282_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -/** - * ov9282_start_streaming() - Start sensor stream - * @ov9282: pointer to ov9282 device - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_start_streaming(struct ov9282 *ov9282) +static int ov9282_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { - const struct ov9282_reg bitdepth_regs[2][2] = { + const struct cci_reg_sequence bitdepth_regs[2][2] = { { {OV9282_REG_PLL_CTRL_0D, OV9282_PLL_CTRL_0D_RAW10}, {OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW10}, @@ -1123,98 +935,74 @@ static int ov9282_start_streaming(struct ov9282 *ov9282) {OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW8}, } }; + struct ov9282 *ov9282 = to_ov9282(sd); const struct ov9282_reg_list *reg_list; int bitdepth_index; int ret; + ret = pm_runtime_resume_and_get(ov9282->dev); + if (ret) + return ret; + /* Write common registers */ - ret = ov9282_write_regs(ov9282, common_regs_list.regs, - common_regs_list.num_of_regs); + ret = cci_multi_reg_write(ov9282->regmap, common_regs, + ARRAY_SIZE(common_regs), NULL); if (ret) { dev_err(ov9282->dev, "fail to write common registers"); - return ret; + goto err_pm_put; } bitdepth_index = ov9282->code == MEDIA_BUS_FMT_Y10_1X10 ? 0 : 1; - ret = ov9282_write_regs(ov9282, bitdepth_regs[bitdepth_index], 2); + ret = cci_multi_reg_write(ov9282->regmap, + bitdepth_regs[bitdepth_index], 2, NULL); if (ret) { dev_err(ov9282->dev, "fail to write bitdepth regs"); - return ret; + goto err_pm_put; } /* Write sensor mode registers */ reg_list = &ov9282->cur_mode->reg_list; - ret = ov9282_write_regs(ov9282, reg_list->regs, reg_list->num_of_regs); + ret = cci_multi_reg_write(ov9282->regmap, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { dev_err(ov9282->dev, "fail to write initial registers"); - return ret; + goto err_pm_put; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler); if (ret) { dev_err(ov9282->dev, "fail to setup handler"); - return ret; + goto err_pm_put; } /* Start streaming */ - ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, - 1, OV9282_MODE_STREAMING); + ret = cci_write(ov9282->regmap, OV9282_REG_MODE_SELECT, + OV9282_MODE_STREAMING, NULL); if (ret) { dev_err(ov9282->dev, "fail to start streaming"); - return ret; + goto err_pm_put; } return 0; -} -/** - * ov9282_stop_streaming() - Stop sensor stream - * @ov9282: pointer to ov9282 device - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_stop_streaming(struct ov9282 *ov9282) -{ - return ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, - 1, OV9282_MODE_STANDBY); +err_pm_put: + pm_runtime_put(ov9282->dev); + + return ret; } -/** - * ov9282_set_stream() - Enable sensor streaming - * @sd: pointer to ov9282 subdevice - * @enable: set to enable sensor streaming - * - * Return: 0 if successful, error code otherwise. - */ -static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) +static int ov9282_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct ov9282 *ov9282 = to_ov9282(sd); int ret; - mutex_lock(&ov9282->mutex); - - if (enable) { - ret = pm_runtime_resume_and_get(ov9282->dev); - if (ret) - goto error_unlock; - - ret = ov9282_start_streaming(ov9282); - if (ret) - goto error_power_off; - } else { - ov9282_stop_streaming(ov9282); - pm_runtime_put(ov9282->dev); - } - - mutex_unlock(&ov9282->mutex); - - return 0; + ret = cci_write(ov9282->regmap, OV9282_REG_MODE_SELECT, + OV9282_MODE_STANDBY, NULL); -error_power_off: pm_runtime_put(ov9282->dev); -error_unlock: - mutex_unlock(&ov9282->mutex); return ret; } @@ -1228,14 +1016,14 @@ error_unlock: static int ov9282_detect(struct ov9282 *ov9282) { int ret; - u32 val; + u64 val; - ret = ov9282_read_reg(ov9282, OV9282_REG_ID, 2, &val); + ret = cci_read(ov9282->regmap, OV9282_REG_ID, &val, NULL); if (ret) return ret; if (val != OV9282_ID) { - dev_err(ov9282->dev, "chip id mismatch: %x!=%x", + dev_err(ov9282->dev, "chip id mismatch: %x!=%llx", OV9282_ID, val); return -ENXIO; } @@ -1346,7 +1134,7 @@ static const struct v4l2_subdev_core_ops ov9282_core_ops = { }; static const struct v4l2_subdev_video_ops ov9282_video_ops = { - .s_stream = ov9282_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { @@ -1355,6 +1143,8 @@ static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { .get_fmt = ov9282_get_pad_format, .set_fmt = ov9282_set_pad_format, .get_selection = ov9282_get_selection, + .enable_streams = ov9282_enable_streams, + .disable_streams = ov9282_disable_streams, }; static const struct v4l2_subdev_ops ov9282_subdev_ops = { @@ -1397,9 +1187,8 @@ static int ov9282_power_on(struct device *dev) usleep_range(400, 600); - ret = ov9282_write_reg(ov9282, OV9282_REG_MIPI_CTRL00, 1, - ov9282->noncontinuous_clock ? - OV9282_GATED_CLOCK : 0); + ret = cci_write(ov9282->regmap, OV9282_REG_MIPI_CTRL00, + ov9282->noncontinuous_clock ? OV9282_GATED_CLOCK : 0, NULL); if (ret) { dev_err(ov9282->dev, "fail to write MIPI_CTRL00"); goto error_clk; @@ -1457,9 +1246,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &ov9282->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; ov9282->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1576,13 +1362,15 @@ static int ov9282_probe(struct i2c_client *client) return ret; } - mutex_init(&ov9282->mutex); + ov9282->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov9282->regmap)) + return dev_err_probe(ov9282->dev, PTR_ERR(ov9282->regmap), + "Failed to init CCI\n"); ret = ov9282_power_on(ov9282->dev); - if (ret) { - dev_err(ov9282->dev, "failed to power-on the sensor"); - goto error_mutex_destroy; - } + if (ret) + return dev_err_probe(ov9282->dev, ret, + "failed to power-on the sensor"); /* Check module identity */ ret = ov9282_detect(ov9282); @@ -1615,27 +1403,34 @@ static int ov9282_probe(struct i2c_client *client) goto error_handler_free; } - ret = v4l2_async_register_subdev_sensor(&ov9282->sd); + ov9282->sd.state_lock = ov9282->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov9282->sd); if (ret < 0) { - dev_err(ov9282->dev, - "failed to register async subdev: %d", ret); + dev_err_probe(ov9282->dev, ret, "failed to init subdev\n"); goto error_media_entity; } pm_runtime_set_active(ov9282->dev); pm_runtime_enable(ov9282->dev); + + ret = v4l2_async_register_subdev_sensor(&ov9282->sd); + if (ret < 0) + goto v4l2_subdev_cleanup; + pm_runtime_idle(ov9282->dev); return 0; +v4l2_subdev_cleanup: + v4l2_subdev_cleanup(&ov9282->sd); + pm_runtime_disable(ov9282->dev); + pm_runtime_set_suspended(ov9282->dev); error_media_entity: media_entity_cleanup(&ov9282->sd.entity); error_handler_free: v4l2_ctrl_handler_free(ov9282->sd.ctrl_handler); error_power_off: ov9282_power_off(ov9282->dev); -error_mutex_destroy: - mutex_destroy(&ov9282->mutex); return ret; } @@ -1649,9 +1444,9 @@ error_mutex_destroy: static void ov9282_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9282 *ov9282 = to_ov9282(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1659,8 +1454,6 @@ static void ov9282_remove(struct i2c_client *client) if (!pm_runtime_status_suspended(&client->dev)) ov9282_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&ov9282->mutex); } static const struct dev_pm_ops ov9282_pm_ops = { diff --git a/drivers/media/i2c/t4ka3.c b/drivers/media/i2c/t4ka3.c new file mode 100644 index 000000000000..746548868bb0 --- /dev/null +++ b/drivers/media/i2c/t4ka3.c @@ -0,0 +1,1064 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for T4KA3 8M camera sensor. + * + * Copyright (C) 2015 Intel Corporation. All Rights Reserved. + * Copyright (C) 2016 XiaoMi, Inc. + * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> + * Copyright (C) 2026 Kate Hsuan <hpa@redhat.com> + */ + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define T4KA3_NATIVE_WIDTH 3280 +#define T4KA3_NATIVE_HEIGHT 2464 +#define T4KA3_NATIVE_START_LEFT 0 +#define T4KA3_NATIVE_START_TOP 0 +#define T4KA3_ACTIVE_WIDTH 3280 +#define T4KA3_ACTIVE_HEIGHT 2460 +#define T4KA3_ACTIVE_START_LEFT 0 +#define T4KA3_ACTIVE_START_TOP 2 +#define T4KA3_MIN_CROP_WIDTH 2 +#define T4KA3_MIN_CROP_HEIGHT 2 + +#define T4KA3_PIXELS_PER_LINE 3440 +#define T4KA3_LINES_PER_FRAME_30FPS 2492 +#define T4KA3_FPS 30 +#define T4KA3_PIXEL_RATE \ + (T4KA3_PIXELS_PER_LINE * T4KA3_LINES_PER_FRAME_30FPS * T4KA3_FPS) + +/* + * TODO this really should be derived from the 19.2 MHz xvclk combined + * with the PLL settings. But without a datasheet this is the closest + * approximation possible. + * + * link-freq = pixel_rate * bpp / (lanes * 2) + * (lanes * 2) because CSI lanes use double-data-rate (DDR) signalling. + * bpp = 10 and lanes = 4 + */ +#define T4KA3_LINK_FREQ ((u64)T4KA3_PIXEL_RATE * 10 / 8) + +/* For enum_frame_size() full-size + binned-/quarter-size */ +#define T4KA3_FRAME_SIZES 2 + +#define T4KA3_REG_PRODUCT_ID_HIGH CCI_REG8(0x0000) +#define T4KA3_REG_PRODUCT_ID_LOW CCI_REG8(0x0001) +#define T4KA3_PRODUCT_ID 0x1490 + +#define T4KA3_REG_STREAM CCI_REG8(0x0100) +#define T4KA3_REG_IMG_ORIENTATION CCI_REG8(0x0101) +#define T4KA3_HFLIP_BIT BIT(0) +#define T4KA3_VFLIP_BIT BIT(1) +#define T4KA3_REG_PARAM_HOLD CCI_REG8(0x0104) +#define T4KA3_REG_COARSE_INTEGRATION_TIME CCI_REG16(0x0202) +#define T4KA3_COARSE_INTEGRATION_TIME_MARGIN 6 +#define T4KA3_REG_DIGGAIN_GREEN_R CCI_REG16(0x020e) +#define T4KA3_REG_DIGGAIN_RED CCI_REG16(0x0210) +#define T4KA3_REG_DIGGAIN_BLUE CCI_REG16(0x0212) +#define T4KA3_REG_DIGGAIN_GREEN_B CCI_REG16(0x0214) +#define T4KA3_REG_GLOBAL_GAIN CCI_REG16(0x0234) +#define T4KA3_MIN_GLOBAL_GAIN_SUPPORTED 0x0080 +#define T4KA3_MAX_GLOBAL_GAIN_SUPPORTED 0x07ff +#define T4KA3_REG_FRAME_LENGTH_LINES CCI_REG16(0x0340) /* aka VTS */ +/* FIXME: need a datasheet to verify the min + max vblank values */ +#define T4KA3_MIN_VBLANK 4 +#define T4KA3_MAX_VBLANK 0xffff +#define T4KA3_REG_PIXELS_PER_LINE CCI_REG16(0x0342) /* aka HTS */ +/* These 2 being horz/vert start is a guess (no datasheet), always 0 */ +#define T4KA3_REG_HORZ_START CCI_REG16(0x0344) +#define T4KA3_REG_VERT_START CCI_REG16(0x0346) +/* Always 3279 (T4KA3_NATIVE_WIDTH - 1, window is used to crop */ +#define T4KA3_REG_HORZ_END CCI_REG16(0x0348) +/* Always 2463 (T4KA3_NATIVE_HEIGHT - 1, window is used to crop */ +#define T4KA3_REG_VERT_END CCI_REG16(0x034a) +/* Output size (after cropping/window) */ +#define T4KA3_REG_HORZ_OUTPUT_SIZE CCI_REG16(0x034c) +#define T4KA3_REG_VERT_OUTPUT_SIZE CCI_REG16(0x034e) +/* Window/crop start + size *after* binning */ +#define T4KA3_REG_WIN_START_X CCI_REG16(0x0408) +#define T4KA3_REG_WIN_START_Y CCI_REG16(0x040a) +#define T4KA3_REG_WIN_WIDTH CCI_REG16(0x040c) +#define T4KA3_REG_WIN_HEIGHT CCI_REG16(0x040e) +#define T4KA3_REG_TEST_PATTERN_MODE CCI_REG8(0x0601) +/* Unknown register at address 0x0900 */ +#define T4KA3_REG_0900 CCI_REG8(0x0900) +#define T4KA3_REG_BINNING CCI_REG8(0x0901) +#define T4KA3_BINNING_VAL(_bin) \ +({ \ + typeof(_bin) (b) = (_bin); \ + ((b) << 4) | (b); \ +}) + +#define to_t4ka3_sensor(_sd) container_of_const(_sd, \ + struct t4ka3_data, sd) +#define ctrl_to_t4ka3(_ctrl) container_of_const((_ctrl)->handler, \ + struct t4ka3_data, \ + ctrls.handler) + +struct t4ka3_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; +}; + +struct t4ka3_mode { + int binning; + u16 win_x; + u16 win_y; +}; + +struct t4ka3_data { + struct v4l2_subdev sd; + struct media_pad pad; + struct mutex lock; /* serialize sensor's ioctl */ + struct t4ka3_ctrls ctrls; + struct t4ka3_mode mode; + struct device *dev; + struct regmap *regmap; + struct gpio_desc *powerdown_gpio; + struct gpio_desc *reset_gpio; + int streaming; + + /* MIPI lane info */ + u32 link_freq_index; + u8 mipi_lanes; +}; + +/* init settings */ +static const struct cci_reg_sequence t4ka3_init_config[] = { + { CCI_REG8(0x4136), 0x13 }, + { CCI_REG8(0x4137), 0x33 }, + { CCI_REG8(0x3094), 0x01 }, + { CCI_REG8(0x0233), 0x01 }, + { CCI_REG8(0x4B06), 0x01 }, + { CCI_REG8(0x4B07), 0x01 }, + { CCI_REG8(0x3028), 0x01 }, + { CCI_REG8(0x3032), 0x14 }, + { CCI_REG8(0x305C), 0x0C }, + { CCI_REG8(0x306D), 0x0A }, + { CCI_REG8(0x3071), 0xFA }, + { CCI_REG8(0x307E), 0x0A }, + { CCI_REG8(0x307F), 0xFC }, + { CCI_REG8(0x3091), 0x04 }, + { CCI_REG8(0x3092), 0x60 }, + { CCI_REG8(0x3096), 0xC0 }, + { CCI_REG8(0x3100), 0x07 }, + { CCI_REG8(0x3101), 0x4C }, + { CCI_REG8(0x3118), 0xCC }, + { CCI_REG8(0x3139), 0x06 }, + { CCI_REG8(0x313A), 0x06 }, + { CCI_REG8(0x313B), 0x04 }, + { CCI_REG8(0x3143), 0x02 }, + { CCI_REG8(0x314F), 0x0E }, + { CCI_REG8(0x3169), 0x99 }, + { CCI_REG8(0x316A), 0x99 }, + { CCI_REG8(0x3171), 0x05 }, + { CCI_REG8(0x31A1), 0xA7 }, + { CCI_REG8(0x31A2), 0x9C }, + { CCI_REG8(0x31A3), 0x8F }, + { CCI_REG8(0x31A4), 0x75 }, + { CCI_REG8(0x31A5), 0xEE }, + { CCI_REG8(0x31A6), 0xEA }, + { CCI_REG8(0x31A7), 0xE4 }, + { CCI_REG8(0x31A8), 0xE4 }, + { CCI_REG8(0x31DF), 0x05 }, + { CCI_REG8(0x31EC), 0x1B }, + { CCI_REG8(0x31ED), 0x1B }, + { CCI_REG8(0x31EE), 0x1B }, + { CCI_REG8(0x31F0), 0x1B }, + { CCI_REG8(0x31F1), 0x1B }, + { CCI_REG8(0x31F2), 0x1B }, + { CCI_REG8(0x3204), 0x3F }, + { CCI_REG8(0x3205), 0x03 }, + { CCI_REG8(0x3210), 0x01 }, + { CCI_REG8(0x3216), 0x68 }, + { CCI_REG8(0x3217), 0x58 }, + { CCI_REG8(0x3218), 0x58 }, + { CCI_REG8(0x321A), 0x68 }, + { CCI_REG8(0x321B), 0x60 }, + { CCI_REG8(0x3238), 0x03 }, + { CCI_REG8(0x3239), 0x03 }, + { CCI_REG8(0x323A), 0x05 }, + { CCI_REG8(0x323B), 0x06 }, + { CCI_REG8(0x3243), 0x03 }, + { CCI_REG8(0x3244), 0x08 }, + { CCI_REG8(0x3245), 0x01 }, + { CCI_REG8(0x3307), 0x19 }, + { CCI_REG8(0x3308), 0x19 }, + { CCI_REG8(0x3320), 0x01 }, + { CCI_REG8(0x3326), 0x15 }, + { CCI_REG8(0x3327), 0x0D }, + { CCI_REG8(0x3328), 0x01 }, + { CCI_REG8(0x3380), 0x01 }, + { CCI_REG8(0x339E), 0x07 }, + { CCI_REG8(0x3424), 0x00 }, + { CCI_REG8(0x343C), 0x01 }, + { CCI_REG8(0x3398), 0x04 }, + { CCI_REG8(0x343A), 0x10 }, + { CCI_REG8(0x339A), 0x22 }, + { CCI_REG8(0x33B4), 0x00 }, + { CCI_REG8(0x3393), 0x01 }, + { CCI_REG8(0x33B3), 0x6E }, + { CCI_REG8(0x3433), 0x06 }, + { CCI_REG8(0x3433), 0x00 }, + { CCI_REG8(0x33B3), 0x00 }, + { CCI_REG8(0x3393), 0x03 }, + { CCI_REG8(0x33B4), 0x03 }, + { CCI_REG8(0x343A), 0x00 }, + { CCI_REG8(0x339A), 0x00 }, + { CCI_REG8(0x3398), 0x00 } +}; + +static const struct cci_reg_sequence t4ka3_pre_mode_set_regs[] = { + { CCI_REG8(0x0112), 0x0A }, + { CCI_REG8(0x0113), 0x0A }, + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x4136), 0x13 }, + { CCI_REG8(0x4137), 0x33 }, + { CCI_REG8(0x0820), 0x0A }, + { CCI_REG8(0x0821), 0x0D }, + { CCI_REG8(0x0822), 0x00 }, + { CCI_REG8(0x0823), 0x00 }, + { CCI_REG8(0x0301), 0x0A }, + { CCI_REG8(0x0303), 0x01 }, + { CCI_REG8(0x0305), 0x04 }, + { CCI_REG8(0x0306), 0x02 }, + { CCI_REG8(0x0307), 0x18 }, + { CCI_REG8(0x030B), 0x01 }, +}; + +static const struct cci_reg_sequence t4ka3_post_mode_set_regs[] = { + { CCI_REG8(0x0902), 0x00 }, + { CCI_REG8(0x4220), 0x00 }, + { CCI_REG8(0x4222), 0x01 }, + { CCI_REG8(0x3380), 0x01 }, + { CCI_REG8(0x3090), 0x88 }, + { CCI_REG8(0x3394), 0x20 }, + { CCI_REG8(0x3090), 0x08 }, + { CCI_REG8(0x3394), 0x10 } +}; + +static const s64 link_freq_menu_items[] = { + T4KA3_LINK_FREQ, +}; + +/* T4KA3 default GRBG */ +static const int t4ka3_hv_flip_bayer_order[] = { + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, +}; + +static const struct v4l2_rect t4ka3_default_crop = { + .left = T4KA3_ACTIVE_START_LEFT, + .top = T4KA3_ACTIVE_START_TOP, + .width = T4KA3_ACTIVE_WIDTH, + .height = T4KA3_ACTIVE_HEIGHT, +}; + +static void t4ka3_set_bayer_order(struct t4ka3_data *sensor, + struct v4l2_mbus_framefmt *fmt) +{ + unsigned int hv_flip = 0; + + if (sensor->ctrls.vflip && sensor->ctrls.vflip->val) + hv_flip += 1; + + if (sensor->ctrls.hflip && sensor->ctrls.hflip->val) + hv_flip += 2; + + fmt->code = t4ka3_hv_flip_bayer_order[hv_flip]; +} + +static int t4ka3_update_exposure_range(struct t4ka3_data *sensor, + struct v4l2_mbus_framefmt *fmt) +{ + int exp_max = fmt->height + sensor->ctrls.vblank->val - + T4KA3_COARSE_INTEGRATION_TIME_MARGIN; + + return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max, + 1, exp_max); +} + +static void t4ka3_fill_format(struct t4ka3_data *sensor, + struct v4l2_mbus_framefmt *fmt, + unsigned int width, unsigned int height) +{ + memset(fmt, 0, sizeof(*fmt)); + fmt->width = width; + fmt->height = height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + t4ka3_set_bayer_order(sensor, fmt); +} + +static void t4ka3_calc_mode(struct t4ka3_data *sensor, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_rect *crop) +{ + int width; + int height; + int binning; + + width = fmt->width; + height = fmt->height; + + if (width <= (crop->width / 2) && height <= (crop->height / 2)) + binning = 2; + else + binning = 1; + + width *= binning; + height *= binning; + + sensor->mode.binning = binning; + sensor->mode.win_x = (crop->left + (crop->width - width) / 2) & ~1; + sensor->mode.win_y = (crop->top + (crop->height - height) / 2) & ~1; + /* + * t4ka3's window is done after binning, but must still be a + * multiple of 2 ? + * Round up to avoid top 2 black lines in 1640x1230 (quarter res) case. + */ + sensor->mode.win_x = DIV_ROUND_UP(sensor->mode.win_x, binning); + sensor->mode.win_y = DIV_ROUND_UP(sensor->mode.win_y, binning); +} + +static void t4ka3_get_vblank_limits(struct t4ka3_data *sensor, + struct v4l2_subdev_state *state, + int *min, int *max, int *def) +{ + struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0); + + *min = T4KA3_MIN_VBLANK + (sensor->mode.binning - 1) * fmt->height; + *max = T4KA3_MAX_VBLANK - fmt->height; + *def = T4KA3_LINES_PER_FRAME_30FPS - fmt->height; +} + +static int t4ka3_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(sd_state, format->pad); + unsigned int width, height; + int min, max, def, ret = 0; + + /* Limit set_fmt max size to crop width / height */ + width = clamp_val(ALIGN(format->format.width, 2), + T4KA3_MIN_CROP_WIDTH, crop->width); + height = clamp_val(ALIGN(format->format.height, 2), + T4KA3_MIN_CROP_HEIGHT, crop->height); + t4ka3_fill_format(sensor, &format->format, width, height); + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && sensor->streaming) + return -EBUSY; + + *v4l2_subdev_state_get_format(sd_state, 0) = format->format; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + t4ka3_calc_mode(sensor, fmt, crop); + + /* vblank range is height dependent adjust and reset to default */ + t4ka3_get_vblank_limits(sensor, sd_state, &min, &max, &def); + ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, min, max, 1, def); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def); + if (ret) + return ret; + + def = T4KA3_PIXELS_PER_LINE - fmt->width; + ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def); + if (ret) + return ret; + + return __v4l2_ctrl_s_ctrl(sensor->ctrls.hblank, def); +} + +/* Horizontal or vertically flip the image */ +static int t4ka3_update_flip(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + int value, u8 flip_bit) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + int ret; + u64 val; + + if (sensor->streaming) + return -EBUSY; + + val = value ? flip_bit : 0; + + ret = cci_update_bits(sensor->regmap, T4KA3_REG_IMG_ORIENTATION, + flip_bit, val, NULL); + if (ret) + return ret; + + t4ka3_set_bayer_order(sensor, fmt); + + return 0; +} + +static int t4ka3_test_pattern(struct t4ka3_data *sensor, s32 value) +{ + return cci_write(sensor->regmap, T4KA3_REG_TEST_PATTERN_MODE, + value, NULL); +} + +static int t4ka3_detect(struct t4ka3_data *sensor, u16 *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + struct i2c_adapter *adapter = client->adapter; + u64 high, low; + int ret = 0; + + /* i2c check */ + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* check sensor chip ID */ + cci_read(sensor->regmap, T4KA3_REG_PRODUCT_ID_HIGH, &high, &ret); + cci_read(sensor->regmap, T4KA3_REG_PRODUCT_ID_LOW, &low, &ret); + if (ret) + return ret; + + *id = (((u8)high) << 8) | (u8)low; + if (*id != T4KA3_PRODUCT_ID) { + dev_err(sensor->dev, "main sensor t4ka3 ID error\n"); + return -ENODEV; + } + + return 0; +} + +static int t4ka3_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct t4ka3_data *sensor = ctrl_to_t4ka3(ctrl); + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, 0); + int ret; + + /* Update exposure range on vblank changes */ + if (ctrl->id == V4L2_CID_VBLANK) { + ret = t4ka3_update_exposure_range(sensor, fmt); + if (ret) + return ret; + } + + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(sensor->sd.dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ret = t4ka3_test_pattern(sensor, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = t4ka3_update_flip(&sensor->sd, fmt, + ctrl->val, T4KA3_VFLIP_BIT); + break; + case V4L2_CID_HFLIP: + ret = t4ka3_update_flip(&sensor->sd, fmt, + ctrl->val, T4KA3_HFLIP_BIT); + break; + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, T4KA3_REG_FRAME_LENGTH_LINES, + fmt->height + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(sensor->regmap, + T4KA3_REG_COARSE_INTEGRATION_TIME, + ctrl->val, NULL); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(sensor->regmap, T4KA3_REG_GLOBAL_GAIN, + ctrl->val, NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(sensor->sd.dev); + + return ret; +} + +static int t4ka3_set_mode(struct t4ka3_data *sensor, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0); + int ret = 0; + + cci_write(sensor->regmap, T4KA3_REG_HORZ_OUTPUT_SIZE, fmt->width, &ret); + /* Write mode-height - 2 otherwise things don't work, hw-bug ? */ + cci_write(sensor->regmap, T4KA3_REG_VERT_OUTPUT_SIZE, + fmt->height - 2, &ret); + + cci_write(sensor->regmap, T4KA3_REG_PIXELS_PER_LINE, + T4KA3_PIXELS_PER_LINE, &ret); + /* Always use the full sensor, using window to crop */ + cci_write(sensor->regmap, T4KA3_REG_HORZ_START, 0, &ret); + cci_write(sensor->regmap, T4KA3_REG_VERT_START, 0, &ret); + cci_write(sensor->regmap, T4KA3_REG_HORZ_END, + T4KA3_NATIVE_WIDTH - 1, &ret); + cci_write(sensor->regmap, T4KA3_REG_VERT_END, + T4KA3_NATIVE_HEIGHT - 1, &ret); + /* Set window */ + cci_write(sensor->regmap, T4KA3_REG_WIN_START_X, + sensor->mode.win_x, &ret); + cci_write(sensor->regmap, T4KA3_REG_WIN_START_Y, + sensor->mode.win_y, &ret); + cci_write(sensor->regmap, T4KA3_REG_WIN_WIDTH, fmt->width, &ret); + cci_write(sensor->regmap, T4KA3_REG_WIN_HEIGHT, fmt->height, &ret); + /* Write 1 to unknown register 0x0900 */ + cci_write(sensor->regmap, T4KA3_REG_0900, 1, &ret); + cci_write(sensor->regmap, T4KA3_REG_BINNING, + T4KA3_BINNING_VAL(sensor->mode.binning), &ret); + + return ret; +} + +static int t4ka3_enable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + int ret; + + ret = pm_runtime_get_sync(sensor->sd.dev); + if (ret < 0) { + dev_err(sensor->dev, "power-up err.\n"); + goto error_powerdown; + } + + cci_multi_reg_write(sensor->regmap, t4ka3_init_config, + ARRAY_SIZE(t4ka3_init_config), &ret); + /* enable group hold */ + cci_write(sensor->regmap, T4KA3_REG_PARAM_HOLD, 1, &ret); + cci_multi_reg_write(sensor->regmap, t4ka3_pre_mode_set_regs, + ARRAY_SIZE(t4ka3_pre_mode_set_regs), &ret); + if (ret) + goto error_powerdown; + + ret = t4ka3_set_mode(sensor, state); + if (ret) + goto error_powerdown; + + ret = cci_multi_reg_write(sensor->regmap, t4ka3_post_mode_set_regs, + ARRAY_SIZE(t4ka3_post_mode_set_regs), NULL); + if (ret) + goto error_powerdown; + + /* Restore value of all ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret) + goto error_powerdown; + + /* disable group hold */ + cci_write(sensor->regmap, T4KA3_REG_PARAM_HOLD, 0, &ret); + cci_write(sensor->regmap, T4KA3_REG_STREAM, 1, &ret); + if (ret) + goto error_powerdown; + + sensor->streaming = 1; + + return ret; + +error_powerdown: + pm_runtime_put(sensor->sd.dev); + + return ret; +} + +static int t4ka3_disable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + int ret; + + ret = cci_write(sensor->regmap, T4KA3_REG_STREAM, 0, NULL); + pm_runtime_put(sensor->sd.dev); + sensor->streaming = 0; + + if (ret) + dev_err(sensor->dev, + "failed to disable stream with return value: %d\n", + ret); + + return 0; +} + +static int t4ka3_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = T4KA3_NATIVE_WIDTH; + sel->r.height = T4KA3_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = t4ka3_default_crop; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int t4ka3_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * Clamp the boundaries of the crop rectangle to the size of the sensor + * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't + * disrupted. + */ + rect.left = clamp_val(ALIGN(sel->r.left, 2), + T4KA3_NATIVE_START_LEFT, T4KA3_NATIVE_WIDTH); + rect.top = clamp_val(ALIGN(sel->r.top, 2), + T4KA3_NATIVE_START_TOP, T4KA3_NATIVE_HEIGHT); + rect.width = clamp_val(ALIGN(sel->r.width, 2), T4KA3_MIN_CROP_WIDTH, + T4KA3_NATIVE_WIDTH - rect.left); + rect.height = clamp_val(ALIGN(sel->r.height, 2), T4KA3_MIN_CROP_HEIGHT, + T4KA3_NATIVE_HEIGHT - rect.top); + + crop = v4l2_subdev_state_get_crop(state, sel->pad); + + if (rect.width != crop->width || rect.height != crop->height) { + /* + * Reset the output image size if the crop rectangle size has + * been modified. + */ + format = v4l2_subdev_state_get_format(state, sel->pad); + format->width = rect.width; + format->height = rect.height; + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + t4ka3_calc_mode(sensor, format, crop); + } + + sel->r = *crop = rect; + + return 0; +} + +static int +t4ka3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int t4ka3_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_rect *crop; + + if (fse->index >= T4KA3_FRAME_SIZES) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, fse->pad); + + fse->min_width = crop->width / (fse->index + 1); + fse->min_height = crop->height / (fse->index + 1); + fse->max_width = fse->min_width; + fse->max_height = fse->min_height; + + return 0; +} + +static int t4ka3_check_hwcfg(struct t4ka3_data *sensor) +{ + struct fwnode_handle *fwnode = dev_fwnode(sensor->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *endpoint; + unsigned long link_freq_bitmap; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + + if (ret < 0) + goto out_free_bus_cfg; + + sensor->link_freq_index = ffs(link_freq_bitmap) - 1; + + /* 4 MIPI lanes */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(sensor->dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto out_free_bus_cfg; + } + + sensor->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int t4ka3_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + + *v4l2_subdev_state_get_crop(sd_state, 0) = t4ka3_default_crop; + + t4ka3_fill_format(sensor, v4l2_subdev_state_get_format(sd_state, 0), + T4KA3_ACTIVE_WIDTH, T4KA3_ACTIVE_HEIGHT); + return 0; +} + +static const struct v4l2_ctrl_ops t4ka3_ctrl_ops = { + .s_ctrl = t4ka3_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops t4ka3_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops t4ka3_pad_ops = { + .enum_mbus_code = t4ka3_enum_mbus_code, + .enum_frame_size = t4ka3_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = t4ka3_set_pad_format, + .get_selection = t4ka3_get_selection, + .set_selection = t4ka3_set_selection, + .enable_streams = t4ka3_enable_stream, + .disable_streams = t4ka3_disable_stream, +}; + +static const struct v4l2_subdev_ops t4ka3_ops = { + .video = &t4ka3_video_ops, + .pad = &t4ka3_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops t4ka3_internal_ops = { + .init_state = t4ka3_init_state, +}; + +static int t4ka3_init_controls(struct t4ka3_data *sensor) +{ + const struct v4l2_ctrl_ops *ops = &t4ka3_ctrl_ops; + struct t4ka3_ctrls *ctrls = &sensor->ctrls; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + int ret, min, max, def; + static const char * const test_pattern_menu[] = { + "Disabled", + "Solid White", + "Color Bars", + "Gradient", + "Random Data", + }; + + v4l2_ctrl_handler_init(hdl, 11); + + hdl->lock = &sensor->lock; + + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + + ctrls->test_pattern = + v4l2_ctrl_new_std_menu_items(hdl, ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, + V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, + 0, T4KA3_PIXEL_RATE, + 1, T4KA3_PIXEL_RATE); + + state = v4l2_subdev_lock_and_get_active_state(&sensor->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + + t4ka3_calc_mode(sensor, fmt, crop); + t4ka3_get_vblank_limits(sensor, state, &min, &max, &def); + + v4l2_subdev_unlock_state(state); + + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + min, max, 1, def); + + def = T4KA3_PIXELS_PER_LINE - T4KA3_ACTIVE_WIDTH; + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + def, def, 1, def); + + max = T4KA3_LINES_PER_FRAME_30FPS - + T4KA3_COARSE_INTEGRATION_TIME_MARGIN; + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, max, 1, max); + + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + T4KA3_MIN_GLOBAL_GAIN_SUPPORTED, + T4KA3_MAX_GLOBAL_GAIN_SUPPORTED, + 1, T4KA3_MIN_GLOBAL_GAIN_SUPPORTED); + + ret = v4l2_fwnode_device_parse(sensor->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + + if (hdl->error) + return hdl->error; + + ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + sensor->sd.ctrl_handler = hdl; + + return 0; +} + +static int t4ka3_pm_suspend(struct device *dev) +{ + struct t4ka3_data *sensor = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(sensor->powerdown_gpio, 1); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + + return 0; +} + +static int t4ka3_pm_resume(struct device *dev) +{ + struct t4ka3_data *sensor = dev_get_drvdata(dev); + u16 sensor_id; + int ret; + + usleep_range(5000, 6000); + + gpiod_set_value_cansleep(sensor->powerdown_gpio, 0); + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + + /* waiting for the sensor after powering up */ + fsleep(20000); + + ret = t4ka3_detect(sensor, &sensor_id); + if (ret) { + dev_err(sensor->dev, "sensor detect failed\n"); + gpiod_set_value_cansleep(sensor->powerdown_gpio, 1); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + + return ret; + } + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(t4ka3_pm_ops, t4ka3_pm_suspend, + t4ka3_pm_resume, NULL); + +static void t4ka3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct t4ka3_data *sensor = to_t4ka3_sensor(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sensor->sd.entity); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + t4ka3_pm_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static int t4ka3_probe(struct i2c_client *client) +{ + struct t4ka3_data *sensor; + int ret; + + /* allocate sensor device & init sub device */ + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->dev = &client->dev; + + ret = t4ka3_check_hwcfg(sensor); + if (ret) + return ret; + + mutex_init(&sensor->lock); + + v4l2_i2c_subdev_init(&sensor->sd, client, &t4ka3_ops); + sensor->sd.internal_ops = &t4ka3_internal_ops; + + sensor->powerdown_gpio = devm_gpiod_get(&client->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->powerdown_gpio)) + return dev_err_probe(&client->dev, + PTR_ERR(sensor->powerdown_gpio), + "getting powerdown GPIO\n"); + + sensor->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(sensor->reset_gpio), + "getting reset GPIO\n"); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return PTR_ERR(sensor->regmap); + + ret = t4ka3_pm_resume(sensor->dev); + if (ret) + return ret; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) + goto err_pm_disable; + + sensor->sd.state_lock = sensor->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to init subdev: %d", ret); + goto err_media_entity; + } + + ret = t4ka3_init_controls(sensor); + if (ret) + goto err_controls; + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret) + goto err_controls; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_idle(&client->dev); + + return 0; + +err_controls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + v4l2_subdev_cleanup(&sensor->sd); + +err_media_entity: + media_entity_cleanup(&sensor->sd.entity); + +err_pm_disable: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); + t4ka3_pm_suspend(&client->dev); + + return ret; +} + +static const struct acpi_device_id t4ka3_acpi_match[] = { + { "XMCC0003" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, t4ka3_acpi_match); + +static struct i2c_driver t4ka3_driver = { + .driver = { + .name = "t4ka3", + .acpi_match_table = ACPI_PTR(t4ka3_acpi_match), + .pm = pm_sleep_ptr(&t4ka3_pm_ops), + }, + .probe = t4ka3_probe, + .remove = t4ka3_remove, +}; +module_i2c_driver(t4ka3_driver) + +MODULE_DESCRIPTION("A low-level driver for T4KA3 sensor"); +MODULE_AUTHOR("HARVEY LV <harvey.lv@intel.com>"); +MODULE_AUTHOR("Kate Hsuan <hpa@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c index d64d0099e6fe..3fb2166c81ef 100644 --- a/drivers/media/i2c/vgxy61.c +++ b/drivers/media/i2c/vgxy61.c @@ -1802,6 +1802,9 @@ static int vgxy61_probe(struct i2c_client *client) sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "failed to get reset gpio\n"); ret = vgxy61_get_regulators(sensor); if (ret) { diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index 373b6c6817d7..f704476506e0 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv) if (btv->remote == NULL) return; - bttv_ir_stop(btv); rc_unregister_device(btv->remote->dev); + bttv_ir_stop(btv); + rc_free_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; } diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index da23e7dfeef5..95a84709d5a6 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -2443,6 +2443,9 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_VIEWCAST_460E: case CX23885_BOARD_AVERMEDIA_CE310B: case CX23885_BOARD_AVERMEDIA_H789C: + if (dev->disable_analog) + break; + dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 0892a5fd137d..4a8af8b88d84 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -48,6 +48,11 @@ static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); +static unsigned int disable_analog_video[8] = { 0, 0, 0, 0, 0, 0, 0, 0}; +static int disable_analog_argc; +module_param_array(disable_analog_video, int, &disable_analog_argc, 0644); +MODULE_PARM_DESC(disable_analog_video, "disable analog video for card type"); + static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); @@ -924,6 +929,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885; } + for (i = 0; i < disable_analog_argc; i++) { + if (disable_analog_video[i] == dev->board) { + pr_warn("Disabling analog for board %d\n", dev->board); + dev->disable_analog = 1; + } + } + /* If the user specific a clk freq override, apply it */ if (cx23885_boards[dev->board].clk_freq > 0) dev->clk_freq = cx23885_boards[dev->board].clk_freq; @@ -1043,7 +1055,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_gpio_enable(dev, 0x300, 0); } - if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { + if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO && + !dev->disable_analog) { if (cx23885_video_register(dev) < 0) { pr_err("%s() Failed to register analog video adapters on VID_A\n", __func__); diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 05a7859cbe57..f240ccda40ed 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2373,7 +2373,8 @@ static int dvb_register(struct cx23885_tsport *port) port->i2c_client_tuner = client_tuner; /* we only attach tuner for analog on the 888 version */ - if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) { + if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB && + !dev->disable_analog) { pr_info("%s(): QUADHD_DVB analog setup\n", __func__); dev->ts1.analog_fe.tuner_priv = client_tuner; @@ -2466,7 +2467,8 @@ static int dvb_register(struct cx23885_tsport *port) port->i2c_client_tuner = client_tuner; /* we only attach tuner for analog on the 888 version */ - if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) { + if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC && + !dev->disable_analog) { pr_info("%s(): QUADHD_ATSC analog setup\n", __func__); dev->ts1.analog_fe.tuner_priv = client_tuner; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index ffbbeca8a8e5..554767b8ef2b 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev) if (dev->kernel_ir == NULL) return; rc_unregister_device(dev->kernel_ir->rc); + rc_free_device(dev->kernel_ir->rc); kfree(dev->kernel_ir->phys); kfree(dev->kernel_ir->name); kfree(dev->kernel_ir); diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 8ba1f306238c..8653ee1d9ba6 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -404,6 +404,7 @@ struct cx23885_dev { unsigned char radio_addr; struct v4l2_subdev *sd_cx25840; struct work_struct cx25840_work; + unsigned int disable_analog; /* Infrared */ struct v4l2_subdev *sd_ir; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 3f73391f9098..4fdc59aaed8e 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -397,7 +397,7 @@ static int dsp_buffer_free(struct cx25821_audio_dev *chip) { struct cx25821_riscmem *risc = &chip->buf->risc; - BUG_ON(!chip->dma_size); + WARN_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); cx25821_alsa_dma_unmap(chip); @@ -509,8 +509,8 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, chip->num_periods = params_periods(hw_params); chip->dma_size = chip->period_size * params_periods(hw_params); - BUG_ON(!chip->dma_size); - BUG_ON(chip->num_periods & (chip->num_periods - 1)); + WARN_ON(!chip->dma_size); + WARN_ON(chip->num_periods & (chip->num_periods - 1)); buf = kzalloc_obj(*buf); if (NULL == buf) diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index e958eecb29c5..5d9ce4f9af01 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -509,8 +509,9 @@ int cx88_ir_fini(struct cx88_core *core) if (!ir) return 0; - cx88_ir_stop(core); rc_unregister_device(ir->dev); + cx88_ir_stop(core); + rc_free_device(ir->dev); kfree(ir); /* done */ diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index de05d8b0f9dc..bbd24769ae56 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105) static void dm1105_ir_exit(struct dm1105_dev *dm1105) { rc_unregister_device(dm1105->ir.dev); + rc_free_device(dm1105->ir.dev); } static int dm1105_hw_init(struct dm1105_dev *dev) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 32cc95a766b7..fc6608e33de4 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -91,6 +91,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), /* Omnivision OV2680 */ IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), + /* Omnivision OV5675 */ + IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000), /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Sony IMX471 */ @@ -107,6 +109,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9340"), + }, + .driver_data = "OVTI02C1", + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9350"), }, .driver_data = "OVTI02C1", @@ -114,6 +123,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 9440"), + }, + .driver_data = "OVTI02C1", + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 9640"), }, .driver_data = "OVTI02C1", diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index 34f67f4f1bb5..d033d4618169 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -686,7 +686,7 @@ out_free_irq: out_ipu6_rpm_put: pm_runtime_put_sync(&isp->psys->auxdev.dev); out_ipu6_bus_del_devices: - if (isp->psys) { + if (!IS_ERR_OR_NULL(isp->psys)) { ipu6_cpd_free_pkg_dir(isp->psys); ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt); } diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c index 34c0d979240f..edb4cacf55d2 100644 --- a/drivers/media/pci/mantis/mantis_input.c +++ b/drivers/media/pci/mantis/mantis_input.c @@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init); void mantis_input_exit(struct mantis_pci *mantis) { rc_unregister_device(mantis->rc); + rc_free_device(mantis->rc); } EXPORT_SYMBOL_GPL(mantis_input_exit); diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index a7351a469386..d23d854581c5 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -84,7 +84,8 @@ static int temp_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct mgb4_dev *mgbdev = dev_get_drvdata(dev); - u32 val10, raw; + u32 raw; + int val10; if (type != hwmon_temp || attr != hwmon_temp_input) return -EOPNOTSUPP; diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_in.c b/drivers/media/pci/mgb4/mgb4_sysfs_in.c index 4cd4addcd0a5..80ff05450e98 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_in.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_in.c @@ -128,7 +128,16 @@ static ssize_t color_mapping_show(struct device *dev, u32 config = mgb4_read_reg(&vindev->mgbdev->video, vindev->config->regs.config); - return sprintf(buf, "%s\n", config & (1U << 8) ? "0" : "1"); + switch ((config >> 7) & 3) { + case 0: /* SPWG/VESA */ + return sprintf(buf, "1\n"); + case 1: /* ZDML */ + return sprintf(buf, "2\n"); + case 2: /* OLDI/JEIDA */ + return sprintf(buf, "0\n"); + default: + return -EIO; + } } /* @@ -151,17 +160,20 @@ static ssize_t color_mapping_store(struct device *dev, switch (val) { case 0: /* OLDI/JEIDA */ - fpga_data = (1U << 8); + fpga_data = 2; break; case 1: /* SPWG/VESA */ fpga_data = 0; break; + case 2: /* ZDML */ + fpga_data = 1; + break; default: return -EINVAL; } mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.config, - 1U << 8, fpga_data); + 3U << 7, fpga_data << 7); return count; } diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c index 5769f3ca6c2f..d807218e28ca 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c @@ -143,6 +143,64 @@ end: return ret; } +static ssize_t color_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(dev); + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, + voutdev->config->regs.config); + + switch ((config >> 6) & 3) { + case 0: /* SPWG/VESA */ + return sprintf(buf, "1\n"); + case 1: /* ZDML */ + return sprintf(buf, "2\n"); + case 2: /* OLDI/JEIDA */ + return sprintf(buf, "0\n"); + default: + return -EIO; + } +} + +/* + * Color mapping change is expected to be called on live streams. Video device + * locking/queue check is not needed. + */ +static ssize_t color_mapping_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct video_device *vdev = to_video_device(dev); + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); + u32 fpga_data; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + switch (val) { + case 0: /* OLDI/JEIDA */ + fpga_data = 2; + break; + case 1: /* SPWG/VESA */ + fpga_data = 0; + break; + case 2: /* ZDML */ + fpga_data = 1; + break; + default: + return -EINVAL; + } + + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config, + 3U << 6, fpga_data << 6); + + return count; +} + static ssize_t display_width_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -711,6 +769,7 @@ static DEVICE_ATTR_RW(hback_porch); static DEVICE_ATTR_RW(hfront_porch); static DEVICE_ATTR_RW(vback_porch); static DEVICE_ATTR_RW(vfront_porch); +static DEVICE_ATTR_RW(color_mapping); static DEVICE_ATTR_RW(fpdl3_output_width); @@ -731,6 +790,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = { &dev_attr_vback_porch.attr, &dev_attr_vfront_porch.attr, &dev_attr_fpdl3_output_width.attr, + &dev_attr_color_mapping.attr, NULL }; @@ -740,6 +800,7 @@ struct attribute *mgb4_gmsl3_out_attrs[] = { &dev_attr_display_width.attr, &dev_attr_display_height.attr, &dev_attr_frame_rate.attr, + &dev_attr_color_mapping.attr, NULL }; @@ -759,5 +820,6 @@ struct attribute *mgb4_gmsl1_out_attrs[] = { &dev_attr_hfront_porch.attr, &dev_attr_vback_porch.attr, &dev_attr_vfront_porch.attr, + &dev_attr_color_mapping.attr, NULL }; diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c index 04e85765373e..1164e91cbb7b 100644 --- a/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -28,7 +28,7 @@ static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); -#define i2c_dbg(level, fmt, arg...) do { \ +#define saa7134_i2c_dbg(level, fmt, arg...) do { \ if (i2c_debug == level) \ printk(KERN_DEBUG pr_fmt("i2c: " fmt), ## arg); \ } while (0) @@ -84,20 +84,20 @@ static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) enum i2c_status status; status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; - i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]); + saa7134_i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]); return status; } static inline void i2c_set_status(struct saa7134_dev *dev, enum i2c_status status) { - i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]); + saa7134_i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]); saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); } static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) { - i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]); + saa7134_i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]); saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); } @@ -160,7 +160,7 @@ static int i2c_reset(struct saa7134_dev *dev) enum i2c_status status; int count; - i2c_dbg(2, "i2c reset\n"); + saa7134_i2c_dbg(2, "i2c reset\n"); status = i2c_get_status(dev); if (!i2c_is_error(status)) return true; @@ -198,7 +198,7 @@ static inline int i2c_send_byte(struct saa7134_dev *dev, // dword |= 0x40 << 16; /* 400 kHz */ dword |= 0xf0 << 24; saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); - i2c_dbg(2, "i2c data => 0x%x\n", data); + saa7134_i2c_dbg(2, "i2c data => 0x%x\n", data); if (!i2c_is_busy_wait(dev)) return -EIO; @@ -220,7 +220,7 @@ static inline int i2c_recv_byte(struct saa7134_dev *dev) if (i2c_is_error(status)) return -EIO; data = saa_readb(SAA7134_I2C_DATA); - i2c_dbg(2, "i2c data <= 0x%x\n", data); + saa7134_i2c_dbg(2, "i2c data <= 0x%x\n", data); return data; } @@ -237,12 +237,12 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, if (!i2c_reset(dev)) return -EIO; - i2c_dbg(2, "start xfer\n"); - i2c_dbg(1, "i2c xfer:"); + saa7134_i2c_dbg(2, "start xfer\n"); + saa7134_i2c_dbg(1, "i2c xfer:"); for (i = 0; i < num; i++) { if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { /* send address */ - i2c_dbg(2, "send address\n"); + saa7134_i2c_dbg(2, "send address\n"); addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) addr |= 1; @@ -265,7 +265,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, } if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - i2c_dbg(2, "read bytes\n"); + saa7134_i2c_dbg(2, "read bytes\n"); for (byte = 0; byte < msgs[i].len; byte++) { i2c_cont(1, " ="); rc = i2c_recv_byte(dev); @@ -286,7 +286,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, } } else { /* write bytes */ - i2c_dbg(2, "write bytes\n"); + saa7134_i2c_dbg(2, "write bytes\n"); for (byte = 0; byte < msgs[i].len; byte++) { data = msgs[i].buf[byte]; i2c_cont(1, " %02x", data); @@ -296,7 +296,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, } } } - i2c_dbg(2, "xfer done\n"); + saa7134_i2c_dbg(2, "xfer done\n"); i2c_cont(1, " >"); i2c_set_attr(dev,STOP); rc = -EIO; diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 5b7101415780..7f6680de3156 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; rc_unregister_device(dev->remote->dev); + rc_free_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; } diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 74406d5ea0a5..6bcde506adf5 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -888,6 +888,15 @@ static int get_resources(struct saa7164_dev *dev) return -EBUSY; } +static void release_resources(struct saa7164_dev *dev) +{ + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + + release_mem_region(pci_resource_start(dev->pci, 2), + pci_resource_len(dev->pci, 2)); +} + static int saa7164_port_init(struct saa7164_dev *dev, int portnr) { struct saa7164_port *port = NULL; @@ -947,9 +956,9 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr); - mutex_lock(&devlist); - list_add_tail(&dev->devlist, &saa7164_devlist); - mutex_unlock(&devlist); + scoped_guard(mutex, &devlist) { + list_add_tail(&dev->devlist, &saa7164_devlist); + } /* board config */ dev->board = UNSET; @@ -996,11 +1005,17 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) } /* PCI/e allocations */ - dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); + dev->lmmio = pci_ioremap_bar(dev->pci, 0); + if (!dev->lmmio) { + dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 0\n"); + goto err_ioremap_bar0; + } - dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), - pci_resource_len(dev->pci, 2)); + dev->lmmio2 = pci_ioremap_bar(dev->pci, 2); + if (!dev->lmmio2) { + dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 2\n"); + goto err_ioremap_bar2; + } dev->bmmio = (u8 __iomem *)dev->lmmio; dev->bmmio2 = (u8 __iomem *)dev->lmmio2; @@ -1019,17 +1034,25 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) saa7164_pci_quirks(dev); return 0; + +err_ioremap_bar2: + iounmap(dev->lmmio); +err_ioremap_bar0: + release_resources(dev); + + scoped_guard(mutex, &devlist) { + list_del(&dev->devlist); + } + saa7164_devcount--; + + return -ENODEV; } static void saa7164_dev_unregister(struct saa7164_dev *dev) { dprintk(1, "%s()\n", __func__); - release_mem_region(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); - - release_mem_region(pci_resource_start(dev->pci, 2), - pci_resource_len(dev->pci, 2)); + release_resources(dev); if (!atomic_dec_and_test(&dev->refcount)) return; diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c index cc9f384f7f1e..341cef62452f 100644 --- a/drivers/media/pci/saa7164/saa7164-fw.c +++ b/drivers/media/pci/saa7164/saa7164-fw.c @@ -10,8 +10,8 @@ #include "saa7164.h" -#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw" -#define SAA7164_REV2_FIRMWARE_SIZE 4019072 +#define SAA7164_REV2_FIRMWARE "v4l-saa7164-1.0.2-3.fw" +#define SAA7164_REV2_FIRMWARE_SIZE 4038864 #define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw" #define SAA7164_REV3_FIRMWARE_SIZE 4019072 diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index c0604d9c7011..0bbe4fa2d5a8 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev) rc_unregister_device(rc_dev); smi_ir_stop(ir); + rc_free_device(rc_dev); ir->rc_dev = NULL; } diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 3709c0fb23b0..8b496b959d7e 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work); rc_unregister_device(budget_ci->ir.dev); + rc_free_device(budget_ci->ir.dev); } static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index d81facf735d9..f707bdc1fb0f 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -1373,7 +1373,7 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (zr->codec->type != zr->card.video_codec) { pci_err(pdev, "%s - wrong codec\n", __func__); - goto zr_unreg_videocodec; + goto zr_detach_codec; } } if (zr->card.video_vfe != 0) { diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 64fc88d89ccc..7cccc994fc50 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -447,17 +447,14 @@ static void vpu_m2m_device_run(void *priv) { } -static void vpu_m2m_job_abort(void *priv) +static int vpu_m2m_job_ready(void *priv) { - struct vpu_inst *inst = priv; - struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx; - - v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx); + return 0; } static const struct v4l2_m2m_ops vpu_m2m_ops = { .device_run = vpu_m2m_device_run, - .job_abort = vpu_m2m_job_abort + .job_ready = vpu_m2m_job_ready, }; static int vpu_vb2_queue_setup(struct vb2_queue *vq, diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-common.h b/drivers/media/platform/arm/mali-c55/mali-c55-common.h index 31c1deaca146..13a3e9dc4243 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-common.h +++ b/drivers/media/platform/arm/mali-c55/mali-c55-common.h @@ -306,5 +306,7 @@ bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55); void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55, enum mali_c55_config_spaces cfg_space); void mali_c55_params_write_config(struct mali_c55 *mali_c55); +void mali_c55_params_init_isp_config(struct mali_c55 *mali_c55, + const struct v4l2_subdev_state *state); #endif /* _MALI_C55_COMMON_H */ diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 43b834459ccf..c1a562cd214e 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -663,41 +663,6 @@ static int mali_c55_init_context(struct mali_c55 *mali_c55, mali_c55->base + config_space_addrs[MALI_C55_CONFIG_PING], MALI_C55_CONFIG_SPACE_SIZE); - /* - * Some features of the ISP need to be disabled by default and only - * enabled at the same time as they're configured by a parameters buffer - */ - - /* Bypass the sqrt and square compression and expansion modules */ - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1, - MALI_C55_REG_BYPASS_1_FE_SQRT, - MALI_C55_REG_BYPASS_1_FE_SQRT); - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, - MALI_C55_REG_BYPASS_3_SQUARE_BE, - MALI_C55_REG_BYPASS_3_SQUARE_BE); - - /* Bypass the temper module */ - mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2, - MALI_C55_REG_BYPASS_2_TEMPER); - - /* Disable the temper module's DMA read/write */ - mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0); - - /* Bypass the colour noise reduction */ - mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4, - MALI_C55_REG_BYPASS_4_CNR); - - /* Disable the sinter module */ - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG, - MALI_C55_SINTER_ENABLE_MASK, 0); - - /* Disable the RGB Gamma module for each output */ - mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0); - mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0); - - /* Disable the colour correction matrix */ - mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0); - return 0; } diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index 497f25fbdd13..4c0fd1ec741c 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -112,9 +112,6 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55, const struct v4l2_subdev_state *state) { struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); - const struct mali_c55_isp_format_info *cfg; - const struct v4l2_mbus_framefmt *format; - const struct v4l2_rect *crop; u32 val; int ret; @@ -122,35 +119,11 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55, MALI_C55_REG_MCU_CONFIG_WRITE_MASK, MALI_C55_REG_MCU_CONFIG_WRITE_PING); - /* Apply input windowing */ - crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); - format = v4l2_subdev_state_get_format(state, - MALI_C55_ISP_PAD_SINK_VIDEO); - cfg = mali_c55_isp_get_mbus_config_by_code(format->code); - - mali_c55_write(mali_c55, MALI_C55_REG_HC_START, - MALI_C55_HC_START(crop->left)); - mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE, - MALI_C55_HC_SIZE(crop->width)); - mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE, - MALI_C55_VC_START(crop->top) | - MALI_C55_VC_SIZE(crop->height)); - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, - MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width); - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, - MALI_C55_REG_ACTIVE_HEIGHT_MASK, - format->height << 16); - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER, - MALI_C55_BAYER_ORDER_MASK, cfg->order); - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH, - MALI_C55_INPUT_WIDTH_MASK, - MALI_C55_INPUT_WIDTH_20BIT); - - mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS, - MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK, - cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK : - 0x00); - + /* + * Apply default ISP configuration and the apply configurations from + * the first available parameters buffer. + */ + mali_c55_params_init_isp_config(mali_c55, state); mali_c55_params_write_config(mali_c55); ret = mali_c55_config_write(ctx, MALI_C55_CONFIG_PING, true); if (ret) { diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index be0e909bcf29..de0e9d898db7 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -43,9 +43,9 @@ * @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN * @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and * header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP - * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG - * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION - * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS + * @awb_config: For header->type == MALI_C55_PARAM_BLOCK_AWB_CONFIG + * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG + * @shading_selection: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION * @data: Allows easy initialisation of a union variable with a * pointer into a __u8 array. */ @@ -732,6 +732,134 @@ void mali_c55_params_write_config(struct mali_c55 *mali_c55) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } +void mali_c55_params_init_isp_config(struct mali_c55 *mali_c55, + const struct v4l2_subdev_state *state) +{ + const struct mali_c55_isp_format_info *cfg; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + + /* Apply input windowing */ + crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); + format = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SINK_VIDEO); + cfg = mali_c55_isp_get_mbus_config_by_code(format->code); + + mali_c55_write(mali_c55, MALI_C55_REG_HC_START, + MALI_C55_HC_START(crop->left)); + mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE, + MALI_C55_HC_SIZE(crop->width)); + mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE, + MALI_C55_VC_START(crop->top) | + MALI_C55_VC_SIZE(crop->height)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, + MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, + MALI_C55_REG_ACTIVE_HEIGHT_MASK, + format->height << 16); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER, + MALI_C55_BAYER_ORDER_MASK, cfg->order); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH, + MALI_C55_INPUT_WIDTH_MASK, + MALI_C55_INPUT_WIDTH_20BIT); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS, + MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK, + cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK : + 0x00); + + /* + * Some features of the ISP need to be disabled by default and only + * enabled at the same time as they're configured by a parameters buffer + */ + + /* Bypass the sqrt and square compression and expansion modules */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1, + MALI_C55_REG_BYPASS_1_FE_SQRT, + MALI_C55_REG_BYPASS_1_FE_SQRT); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_SQUARE_BE, + MALI_C55_REG_BYPASS_3_SQUARE_BE); + + /* Bypass the sensor offset correction (BLS) module */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, + MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH); + + /* Configure 1x digital gain. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN, + MALI_C55_DIGITAL_GAIN_MASK, 256); + + /* Set all AWB gains to 1x. at both AWB configuration points*/ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1, + MALI_C55_AWB_GAIN00_MASK, 256); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1, + MALI_C55_AWB_GAIN01_MASK, + MALI_C55_AWB_GAIN01(256)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2, + MALI_C55_AWB_GAIN10_MASK, 256); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2, + MALI_C55_AWB_GAIN11_MASK, + MALI_C55_AWB_GAIN11(256)); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1_AEXP, + MALI_C55_AWB_GAIN00_MASK, 256); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1_AEXP, + MALI_C55_AWB_GAIN01_MASK, + MALI_C55_AWB_GAIN01(256)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2_AEXP, + MALI_C55_AWB_GAIN10_MASK, 256); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2_AEXP, + MALI_C55_AWB_GAIN11_MASK, + MALI_C55_AWB_GAIN11(256)); + + /* Bypass mesh shading corrections (LSC). */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_ENABLE_MASK, + false); + + /* Bypass the temper module */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2, + MALI_C55_REG_BYPASS_2_TEMPER); + + /* Disable the temper module's DMA read/write */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0); + + /* Disable IRIDIX module. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_IRIDIX, + MALI_C55_REG_BYPASS_3_IRIDIX); + + /* Bypass the colour noise reduction and the PF modules */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4, + MALI_C55_REG_BYPASS_4_CNR | + MALI_C55_REG_BYPASS_4_PF_CORRECTION); + + /* Disable the sinter module */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG, + MALI_C55_SINTER_ENABLE_MASK, 0); + + /* Disable the RGB Gamma module for each output */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0); + + /* Disable the colour correction matrix */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0); + + /* Disable AWB stats. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AWB_DISABLE_MASK, + MALI_C55_AWB_DISABLE_MASK); + + /* Disable auto-exposure 1024-bin histograms at both tap points. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AEXP_HIST_DISABLE_MASK, + MALI_C55_AEXP_HIST_DISABLE); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AEXP_IHIST_DISABLE_MASK, + MALI_C55_AEXP_IHIST_DISABLE); +} + void mali_c55_unregister_params(struct mali_c55 *mali_c55) { struct mali_c55_params *params = &mali_c55->params; diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h index f5a148add1c8..f098effde7b4 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h +++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h @@ -128,8 +128,8 @@ enum mali_c55_interrupts { #define MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH BIT(1) #define MALI_C55_REG_BYPASS_3_MESH_SHADING BIT(3) #define MALI_C55_REG_BYPASS_3_WHITE_BALANCE BIT(4) -#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(5) -#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(6) +#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(5) +#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(6) #define MALI_C55_REG_BYPASS_4 0x18ec0 #define MALI_C55_REG_BYPASS_4_DEMOSAIC_RGB BIT(1) #define MALI_C55_REG_BYPASS_4_PF_CORRECTION BIT(3) diff --git a/drivers/media/platform/broadcom/Kconfig b/drivers/media/platform/broadcom/Kconfig index 32b76ebfcd9a..93ba565d9a02 100644 --- a/drivers/media/platform/broadcom/Kconfig +++ b/drivers/media/platform/broadcom/Kconfig @@ -14,10 +14,5 @@ config VIDEO_BCM2835_UNICAM This is a V4L2 driver that controls the CSI-2 receiver directly, independently from the VC4 firmware. - This driver is mutually exclusive with the use of bcm2835-camera. The - firmware will disable all access to the peripheral from within the - firmware if it finds a DT node using it, and bcm2835-camera will - therefore fail to probe. - To compile this driver as a module, choose M here. The module will be called bcm2835-unicam. diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 53a342853be6..8d28ba0b59a3 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -14,10 +14,6 @@ * and TI CAL camera interface driver by * Benoit Parrot <bparrot@ti.com> * - * - * There are two camera drivers in the kernel for BCM283x - this one and - * bcm2835-camera (currently in staging). - * * This driver directly controls the Unicam peripheral - there is no * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data * and writes it into SDRAM. The only potential processing options are to @@ -342,12 +338,12 @@ static const struct unicam_format_info unicam_image_formats[] = { .csi_dt = MIPI_CSI2_DT_RGB565, }, { .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_1X24, + .code = MEDIA_BUS_FMT_BGR888_1X24, .depth = 24, .csi_dt = MIPI_CSI2_DT_RGB888, }, { .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_BGR888_1X24, + .code = MEDIA_BUS_FMT_RGB888_1X24, .depth = 24, .csi_dt = MIPI_CSI2_DT_RGB888, }, { @@ -2148,22 +2144,45 @@ static int unicam_video_link_validate(struct media_link *link) const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix; const struct unicam_format_info *fmtinfo; - fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat, - UNICAM_SD_PAD_SOURCE_IMAGE); + fmtinfo = unicam_find_format_by_code(format->code, + UNICAM_SD_PAD_SOURCE_IMAGE); if (WARN_ON(!fmtinfo)) { ret = -EPIPE; goto out; } - if (fmtinfo->code != format->code || - fmt->height != format->height || + /* + * Unicam initially associated BGR24 to BGR888_1X24 and RGB24 to + * RGB888_1X24. + * + * In order to allow the applications using the old behaviour to + * run, let's accept the old combination, but warn about it. + */ + if (fmtinfo->fourcc != fmt->pixelformat) { + if ((fmt->pixelformat == V4L2_PIX_FMT_BGR24 && + format->code == MEDIA_BUS_FMT_BGR888_1X24) || + (fmt->pixelformat == V4L2_PIX_FMT_RGB24 && + format->code == MEDIA_BUS_FMT_RGB888_1X24)) { + dev_warn_once(node->dev->dev, + "Incorrect pixel format %p4cc for 0x%04x. Fix your application to use %p4cc.\n", + &fmt->pixelformat, format->code, &fmtinfo->fourcc); + } else { + dev_dbg(node->dev->dev, + "image: format mismatch: 0x%04x <=> %p4cc\n", + format->code, &fmt->pixelformat); + ret = -EPIPE; + goto out; + } + } + + if (fmt->height != format->height || fmt->width != format->width || fmt->field != format->field) { dev_dbg(node->dev->dev, - "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n", - fmt->width, fmt->height, fmtinfo->code, + "image: (%u x %u) %s != (%u x %u) %s\n", + fmt->width, fmt->height, v4l2_field_names[fmt->field], - format->width, format->height, format->code, + format->width, format->height, v4l2_field_names[format->field]); ret = -EPIPE; } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c index bb13267ced38..8f71920a8a35 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -49,6 +49,7 @@ int wave5_vdi_init(struct device *dev) if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) { WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code); + wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem); return -EOPNOTSUPP; } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 80e1831a42e0..d419076d7052 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1303,13 +1303,17 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) { unsigned int i; + unsigned long flags; for (i = 0; i < vb->num_planes; i++) vb2_set_plane_payload(vb, i, 0); vbuf->field = V4L2_FIELD_NONE; + spin_lock_irqsave(&inst->state_spinlock, flags); send_eos_event(inst); + spin_unlock_irqrestore(&inst->state_spinlock, flags); + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf); } else { v4l2_m2m_buf_queue(m2m_ctx, vbuf); @@ -1462,8 +1466,13 @@ static int streamoff_output(struct vb2_queue *q) inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr; inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr; - if (v4l2_m2m_has_stopped(m2m_ctx)) + if (v4l2_m2m_has_stopped(m2m_ctx)) { + unsigned long flags; + + spin_lock_irqsave(&inst->state_spinlock, flags); send_eos_event(inst); + spin_unlock_irqrestore(&inst->state_spinlock, flags); + } /* streamoff on output cancels any draining operation */ inst->eos = false; @@ -1584,6 +1593,7 @@ static int initialize_sequence(struct vpu_instance *inst) { struct dec_initial_info initial_info; int ret = 0; + unsigned long flags; memset(&initial_info, 0, sizeof(struct dec_initial_info)); @@ -1605,7 +1615,9 @@ static int initialize_sequence(struct vpu_instance *inst) return ret; } + spin_lock_irqsave(&inst->state_spinlock, flags); handle_dynamic_resolution_change(inst); + spin_unlock_irqrestore(&inst->state_spinlock, flags); return 0; } diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index c01124a349f6..8c684756d5fc 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1202,6 +1202,7 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c index fae3e1ad2df7..67f3001153ae 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c @@ -163,7 +163,7 @@ void mdp_vpu_unregister(struct mdp_dev *mdp) } static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id, - void *buf, unsigned int len) + const void *buf, unsigned int len) { struct mdp_dev *mdp = vpu_to_mdp(vpu); unsigned int t = MDP_VPU_MESSAGE_TIMEOUT; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c index c977ed0c09b6..4e4541b2fc8e 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c @@ -215,6 +215,15 @@ static int fops_vcodec_release(struct file *file) v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + /* + * Cancel any pending encode work before freeing the context. + * Although v4l2_m2m_ctx_release() waits for m2m job completion, + * the workqueue handler (mtk_venc_worker) may still be accessing + * the context after v4l2_m2m_job_finish() returns. Without this, + * a use-after-free occurs when the worker accesses ctx after kfree. + */ + cancel_work_sync(&ctx->encode_work); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_del_init(&ctx->list); spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index bdebbe3f4198..7787fdcdc67c 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -11,6 +11,7 @@ #include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/irqreturn.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/of.h> @@ -74,6 +75,7 @@ struct dw100_device { struct clk_bulk_data *clks; int num_clks; struct dentry *debugfs_root; + bool frame_failed; }; struct dw100_q_data { @@ -98,6 +100,7 @@ struct dw100_ctx { unsigned int map_width; unsigned int map_height; bool user_map_is_set; + bool user_map_is_dirty; /* Source and destination queue data */ struct dw100_q_data q_data[2]; @@ -293,11 +296,15 @@ static u32 dw100_map_format_coordinates(u16 xq, u16 yq) return (u32)((yq << 16) | xq); } -static u32 *dw100_get_user_map(struct dw100_ctx *ctx) +static void dw100_update_mapping(struct dw100_ctx *ctx) { struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP]; - return ctrl->p_cur.p_u32; + if (!ctx->user_map_is_dirty) + return; + + memcpy(ctx->map, ctrl->p_cur.p_u32, ctx->map_size); + ctx->user_map_is_dirty = false; } /* @@ -306,8 +313,6 @@ static u32 *dw100_get_user_map(struct dw100_ctx *ctx) */ static int dw100_create_mapping(struct dw100_ctx *ctx) { - u32 *user_map; - if (ctx->map) dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, ctx->map, ctx->map_dma); @@ -318,8 +323,8 @@ static int dw100_create_mapping(struct dw100_ctx *ctx) if (!ctx->map) return -ENOMEM; - user_map = dw100_get_user_map(ctx); - memcpy(ctx->map, user_map, ctx->map_size); + ctx->user_map_is_dirty = true; + dw100_update_mapping(ctx); dev_dbg(&ctx->dw_dev->pdev->dev, "%ux%u %s mapping created (d:%pad-c:%p) for stream %ux%u->%ux%u\n", @@ -351,6 +356,7 @@ static int dw100_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP: ctx->user_map_is_set = true; + ctx->user_map_is_dirty = true; break; } @@ -405,6 +411,7 @@ static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl, } ctx->user_map_is_set = false; + ctx->user_map_is_dirty = true; } static const struct v4l2_ctrl_type_ops dw100_ctrl_type_ops = { @@ -459,6 +466,15 @@ static int dw100_queue_setup(struct vb2_queue *vq, return 0; } +static int dw100_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + static int dw100_buf_prepare(struct vb2_buffer *vb) { unsigned int i; @@ -500,6 +516,13 @@ static void dw100_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static void dw100_buf_request_complete(struct vb2_buffer *vb) +{ + struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); +} + static void dw100_return_all_buffers(struct vb2_queue *q, enum vb2_buffer_state state) { @@ -553,11 +576,13 @@ static void dw100_stop_streaming(struct vb2_queue *q) } static const struct vb2_ops dw100_qops = { - .queue_setup = dw100_queue_setup, - .buf_prepare = dw100_buf_prepare, - .buf_queue = dw100_buf_queue, - .start_streaming = dw100_start_streaming, - .stop_streaming = dw100_stop_streaming, + .queue_setup = dw100_queue_setup, + .buf_out_validate = dw100_buf_out_validate, + .buf_prepare = dw100_buf_prepare, + .buf_queue = dw100_buf_queue, + .start_streaming = dw100_start_streaming, + .stop_streaming = dw100_stop_streaming, + .buf_request_complete = dw100_buf_request_complete, }; static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq, @@ -575,6 +600,7 @@ static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->vq_mutex; src_vq->dev = ctx->dw_dev->v4l2_dev.dev; + src_vq->supports_requests = true; ret = vb2_queue_init(src_vq); if (ret) @@ -1058,7 +1084,6 @@ static const struct v4l2_ioctl_ops dw100_ioctl_ops = { static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error) { struct dw100_ctx *curr_ctx; - struct vb2_v4l2_buffer *src_vb, *dst_vb; enum vb2_buffer_state buf_state; curr_ctx = v4l2_m2m_get_curr_priv(dw_dev->m2m_dev); @@ -1069,21 +1094,16 @@ static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error) return; } - src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); - if (likely(!with_error)) buf_state = VB2_BUF_STATE_DONE; else buf_state = VB2_BUF_STATE_ERROR; - v4l2_m2m_buf_done(src_vb, buf_state); - v4l2_m2m_buf_done(dst_vb, buf_state); - dev_dbg(&dw_dev->pdev->dev, "Finishing transaction with%s error(s)\n", with_error ? "" : "out"); - v4l2_m2m_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx); + v4l2_m2m_buf_done_and_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx, + buf_state); } static void dw100_hw_reset(struct dw100_device *dw_dev) @@ -1386,7 +1406,8 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id) { struct dw100_device *dw_dev = dev_id; u32 pending_irqs, err_irqs, frame_done_irq; - bool with_error = true; + + dw_dev->frame_failed = true; pending_irqs = dw_hw_get_pending_irqs(dw_dev); frame_done_irq = pending_irqs & DW100_INTERRUPT_STATUS_INT_FRAME_DONE; @@ -1394,7 +1415,7 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id) if (frame_done_irq) { dev_dbg(&dw_dev->pdev->dev, "Frame done interrupt\n"); - with_error = false; + dw_dev->frame_failed = false; err_irqs &= ~DW100_INTERRUPT_STATUS_INT_ERR_STATUS (DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE); } @@ -1407,30 +1428,57 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id) dw100_hw_clear_irq(dw_dev, pending_irqs | DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT); - dw100_job_finish(dw_dev, with_error); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t dw100_irq_thread_fn(int irq, void *dev_id) +{ + struct dw100_device *dw_dev = dev_id; + + dw100_job_finish(dw_dev, dw_dev->frame_failed); return IRQ_HANDLED; } -static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, - struct vb2_v4l2_buffer *out_vb) +static void dw100_device_run(void *priv) { + struct dw100_ctx *ctx = priv; struct dw100_device *dw_dev = ctx->dw_dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; - out_vb->sequence = - dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++; - in_vb->sequence = + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, + &ctx->hdl); + + if (src_buf->vb2_buf.req_obj.req) + dw100_update_mapping(ctx); + else if (ctx->user_map_is_dirty) + dev_warn_once(&dw_dev->pdev->dev, + "V4L2 requests are required to update the vertex map dynamically\n"); + + /* + * As the hardware does not update any volatile controls, we can + * complete control handling before starting the dewarper. + */ + v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, + &ctx->hdl); + + src_buf->sequence = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)->sequence++; + dst_buf->sequence = + dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++; - dev_dbg(&ctx->dw_dev->pdev->dev, + dev_dbg(&dw_dev->pdev->dev, "Starting queues %p->%p, sequence %u->%u\n", v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE), v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), - in_vb->sequence, out_vb->sequence); + src_buf->sequence, dst_buf->sequence); - v4l2_m2m_buf_copy_metadata(in_vb, out_vb); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); /* Now, let's deal with hardware ... */ dw100_hw_master_bus_disable(dw_dev); @@ -1439,10 +1487,10 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, dw100_hw_set_src_crop(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], &ctx->q_data[DW100_QUEUE_DST]); dw100_hw_set_source(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], - &in_vb->vb2_buf); + &src_buf->vb2_buf); dw100_hw_set_destination(dw_dev, &ctx->q_data[DW100_QUEUE_DST], ctx->q_data[DW100_QUEUE_SRC].fmt, - &out_vb->vb2_buf); + &dst_buf->vb2_buf); dw100_hw_set_mapping(dw_dev, ctx->map_dma, ctx->map_width, ctx->map_height); dw100_hw_enable_irq(dw_dev); @@ -1452,21 +1500,15 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, dw100_hw_master_bus_enable(dw_dev); } -static void dw100_device_run(void *priv) -{ - struct dw100_ctx *ctx = priv; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - dw100_start(ctx, src_buf, dst_buf); -} - static const struct v4l2_m2m_ops dw100_m2m_ops = { .device_run = dw100_device_run, }; +static const struct media_device_ops dw100_m2m_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + static struct video_device *dw100_init_video_device(struct dw100_device *dw_dev) { struct video_device *vfd = &dw_dev->vfd; @@ -1555,8 +1597,9 @@ static int dw100_probe(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); - ret = devm_request_irq(&pdev->dev, irq, dw100_irq_handler, IRQF_ONESHOT, - dev_name(&pdev->dev), dw_dev); + ret = devm_request_threaded_irq(&pdev->dev, irq, dw100_irq_handler, + dw100_irq_thread_fn, 0, + dev_name(&pdev->dev), dw_dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); goto err_pm; @@ -1578,6 +1621,7 @@ static int dw100_probe(struct platform_device *pdev) dw_dev->mdev.dev = &pdev->dev; strscpy(dw_dev->mdev.model, "dw100", sizeof(dw_dev->mdev.model)); media_device_init(&dw_dev->mdev); + dw_dev->mdev.ops = &dw100_m2m_media_ops; dw_dev->v4l2_dev.mdev = &dw_dev->mdev; ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h index adb93e977be9..e8ddfc61b4f2 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h @@ -5,6 +5,8 @@ * Copyright 2018-2019 NXP */ +#include <linux/bitfield.h> + #ifndef _MXC_JPEG_HW_H #define _MXC_JPEG_HW_H @@ -73,6 +75,7 @@ #define GLB_CTRL_DEC_GO (0x1 << 2) #define GLB_CTRL_L_ENDIAN(le) ((le) << 3) #define GLB_CTRL_SLOT_EN(slot) (0x1 << ((slot) + 4)) +#define GLB_CTRL_CUR_VERSION(r) FIELD_GET(GENMASK_U32(19, 16), r) /* COM_STAUS fields */ #define COM_STATUS_DEC_ONGOING(r) (((r) & (1 << 31)) >> 31) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index b442dcba02e7..725e94152884 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -64,6 +64,12 @@ #include "mxc-jpeg-hw.h" #include "mxc-jpeg.h" +#define call_void_jpeg_enc_ops(jpeg, op, args...) \ + do { \ + if ((jpeg)->enc_cfg_ops && (jpeg)->enc_cfg_ops->op) \ + (jpeg)->enc_cfg_ops->op(args); \ + } while (0) + static const struct mxc_jpeg_fmt mxc_formats[] = { { .name = "JPEG", @@ -1030,11 +1036,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) if (jpeg->mode == MXC_JPEG_ENCODE && ctx->enc_state == MXC_JPEG_ENC_CONF) { - q_data = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ctx->enc_state = MXC_JPEG_ENCODING; - dev_dbg(dev, "Encoder config finished. Start encoding...\n"); - mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality); - mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt)); + call_void_jpeg_enc_ops(jpeg, exit_config_mode, ctx); goto job_unlock; } if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed && @@ -1272,6 +1274,7 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf, jpeg_src_buf = vb2_to_mxc_buf(src_buf); + ctx->extseq = mxc_jpeg_is_extended_sequential(jpeg_src_buf->fmt); /* setup the decoding descriptor */ desc->next_descpt_ptr = 0; /* end of chain */ q_data_cap = mxc_jpeg_get_q_data(ctx, cap_type); @@ -1335,9 +1338,15 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, struct mxc_jpeg_q_data *q_data; enum mxc_jpeg_image_format img_fmt; int w, h; + bool extseq; q_data = mxc_jpeg_get_q_data(ctx, src_buf->vb2_queue->type); + extseq = mxc_jpeg_is_extended_sequential(q_data->fmt); + + ctx->extseq = extseq; + memset(desc, 0, sizeof(struct mxc_jpeg_desc)); + memset(cfg_desc, 0, sizeof(struct mxc_jpeg_desc)); jpeg->slot_data.cfg_stream_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr, q_data->fmt->fourcc, @@ -1348,11 +1357,6 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN; cfg_desc->buf_base0 = jpeg->slot_data.cfg_stream_handle; - cfg_desc->buf_base1 = 0; - cfg_desc->line_pitch = 0; - cfg_desc->stm_bufbase = 0; /* no output expected */ - cfg_desc->stm_bufsize = 0x0; - cfg_desc->imgsize = 0; cfg_desc->stm_ctrl = STM_CTRL_CONFIG_MOD(1); cfg_desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1); @@ -1372,11 +1376,14 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, desc->stm_ctrl = STM_CTRL_CONFIG_MOD(0) | STM_CTRL_IMAGE_FORMAT(img_fmt); desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1); - if (mxc_jpeg_is_extended_sequential(q_data->fmt)) + if (extseq) desc->stm_ctrl |= STM_CTRL_PIXEL_PRECISION; else desc->stm_ctrl &= ~STM_CTRL_PIXEL_PRECISION; mxc_jpeg_addrs(desc, src_buf, dst_buf, 0); + + call_void_jpeg_enc_ops(jpeg, setup_desc, ctx); + dev_dbg(jpeg->dev, "cfg_desc:\n"); print_descriptor_info(jpeg->dev, cfg_desc); dev_dbg(jpeg->dev, "enc desc:\n"); @@ -1388,6 +1395,54 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, mxc_jpeg_set_desc(cfg_desc_handle, reg, slot); } +static void mxc_jpeg_enc_start_config_manually(struct mxc_jpeg_ctx *ctx) +{ + struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg; + void __iomem *reg = jpeg->base_reg; + struct device *dev = jpeg->dev; + + ctx->enc_state = MXC_JPEG_ENC_CONF; + mxc_jpeg_enc_mode_conf(dev, reg, ctx->extseq); +} + +static void mxc_jpeg_enc_finish_config_manually(struct mxc_jpeg_ctx *ctx) +{ + struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg; + void __iomem *reg = jpeg->base_reg; + struct device *dev = jpeg->dev; + + ctx->enc_state = MXC_JPEG_ENCODING; + dev_dbg(dev, "Encoder config finished. Start encoding...\n"); + mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality); + mxc_jpeg_enc_mode_go(dev, reg, ctx->extseq); +} + +static void mxc_jpeg_enc_configure_desc(struct mxc_jpeg_ctx *ctx) +{ + struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg; + struct mxc_jpeg_desc *desc = jpeg->slot_data.desc; + struct mxc_jpeg_desc *cfg_desc = jpeg->slot_data.cfg_desc; + + ctx->enc_state = MXC_JPEG_ENCODING; + cfg_desc->mode = (ctx->extseq) ? 0xb0 : 0xa0; + cfg_desc->cfg_mode = 0x3ff; + + desc->mode = (ctx->extseq) ? 0x150 : 0x140; + desc->cfg_mode = 0x3ff; + desc->quality = ctx->jpeg_quality; + desc->lumth = 0xffff; + desc->chrth = 0xffff; +} + +static const struct mxc_jpeg_enc_ops mxc_jpeg_enc_cfg_ops_v0 = { + .enter_config_mode = mxc_jpeg_enc_start_config_manually, + .exit_config_mode = mxc_jpeg_enc_finish_config_manually +}; + +static const struct mxc_jpeg_enc_ops mxc_jpeg_enc_cfg_ops_v1 = { + .setup_desc = mxc_jpeg_enc_configure_desc +}; + static const struct mxc_jpeg_fmt *mxc_jpeg_get_sibling_format(const struct mxc_jpeg_fmt *fmt) { int i; @@ -1593,12 +1648,10 @@ static void mxc_jpeg_device_run(void *priv) if (jpeg->mode == MXC_JPEG_ENCODE) { dev_dbg(dev, "Encoding on slot %d\n", ctx->slot); - ctx->enc_state = MXC_JPEG_ENC_CONF; mxc_jpeg_config_enc_desc(&dst_buf->vb2_buf, ctx, &src_buf->vb2_buf, &dst_buf->vb2_buf); /* start config phase */ - mxc_jpeg_enc_mode_conf(dev, reg, - mxc_jpeg_is_extended_sequential(q_data_out->fmt)); + call_void_jpeg_enc_ops(jpeg, enter_config_mode, ctx); } else { dev_dbg(dev, "Decoding on slot %d\n", ctx->slot); print_mxc_buf(jpeg, &src_buf->vb2_buf, 0); @@ -2842,6 +2895,14 @@ fail: return ret; } +static int mxc_jpeg_get_version(void __iomem *reg) +{ + u32 regval; + + regval = readl(reg + GLB_CTRL); + return GLB_CTRL_CUR_VERSION(regval); +} + static int mxc_jpeg_probe(struct platform_device *pdev) { struct mxc_jpeg_dev *jpeg; @@ -2974,12 +3035,35 @@ static int mxc_jpeg_probe(struct platform_device *pdev) jpeg->dec_vdev->minor); platform_set_drvdata(pdev, jpeg); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) { + dev_err(dev, "Failed to enable runtime PM: %d\n", ret); + goto err_pm; + } + + if (mode == MXC_JPEG_ENCODE) { + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err_pm; + + if (mxc_jpeg_get_version(jpeg->base_reg) == 0) + jpeg->enc_cfg_ops = &mxc_jpeg_enc_cfg_ops_v0; + else + jpeg->enc_cfg_ops = &mxc_jpeg_enc_cfg_ops_v1; + + pm_runtime_put_sync(dev); + } return 0; +err_pm: + video_unregister_device(jpeg->dec_vdev); + /* set NULL to prevent double-free */ + jpeg->dec_vdev = NULL; err_vdev_register: - video_device_release(jpeg->dec_vdev); + /* Only release if allocation succeeded but registration failed */ + if (jpeg->dec_vdev) + video_device_release(jpeg->dec_vdev); err_vdev_alloc: v4l2_m2m_release(jpeg->m2m_dev); @@ -3050,7 +3134,6 @@ static void mxc_jpeg_remove(struct platform_device *pdev) mxc_jpeg_free_slot_data(jpeg); - pm_runtime_disable(&pdev->dev); video_unregister_device(jpeg->dec_vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index 9c5b4f053ded..c00c13549746 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -81,6 +81,17 @@ struct mxc_jpeg_desc { u32 stm_bufsize; u32 imgsize; u32 stm_ctrl; + /* below parameters are valid for v1 */ + u32 mode; + u32 cfg_mode; + u32 quality; + u32 rc_regs_sel; + u32 lumth; + u32 chrth; + u32 nomfrsize_lo; + u32 nomfrsize_hi; + u32 ofbsize_lo; + u32 ofbsize_hi; } __packed; struct mxc_jpeg_q_data { @@ -105,6 +116,7 @@ struct mxc_jpeg_ctx { unsigned int source_change; bool need_initial_source_change_evt; bool header_parsed; + bool extseq; struct v4l2_ctrl_handler ctrl_handler; u8 jpeg_quality; struct delayed_work task_timer; @@ -125,6 +137,15 @@ struct mxc_jpeg_slot_data { dma_addr_t cfg_dec_daddr; }; +struct mxc_jpeg_enc_ops { + /* Manual configuration (v0 hardware) - two-phase process */ + void (*enter_config_mode)(struct mxc_jpeg_ctx *ctx); + void (*exit_config_mode)(struct mxc_jpeg_ctx *ctx); + + /* Descriptor-based configuration (v1 hardware) - single-phase */ + void (*setup_desc)(struct mxc_jpeg_ctx *ctx); +}; + struct mxc_jpeg_dev { spinlock_t hw_lock; /* hardware access lock */ unsigned int mode; @@ -142,6 +163,7 @@ struct mxc_jpeg_dev { struct device **pd_dev; struct device_link **pd_link; struct gen_pool *sram_pool; + const struct mxc_jpeg_enc_ops *enc_cfg_ops; }; /** diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 9a43fd1eb0bc..5f9691d76434 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1359,18 +1359,18 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) - return -ENOTCONN; + return dev_err_probe(csis->dev, -ENOTCONN, + "failed to get local endpoint\n"); ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) - return ret; + return dev_err_probe(csis->dev, ret, + "failed to parse endpoint\n"); for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { - if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { - dev_err(csis->dev, - "data lanes reordering is not supported"); - return -EINVAL; - } + if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) + return dev_err_probe(csis->dev, -EINVAL, + "data lanes reordering is not supported\n"); } csis->bus = vep.bus.mipi_csi2; @@ -1382,15 +1382,22 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep, struct v4l2_async_connection); if (IS_ERR(asd)) - return PTR_ERR(asd); + return dev_err_probe(csis->dev, PTR_ERR(asd), + "failed to add remote fwnode to notifier\n"); csis->notifier.ops = &mipi_csis_notify_ops; ret = v4l2_async_nf_register(&csis->notifier); if (ret) - return ret; + return dev_err_probe(csis->dev, ret, + "failed to register notifier\n"); - return v4l2_async_register_subdev(&csis->sd); + ret = v4l2_async_register_subdev(&csis->sd); + if (ret) + return dev_err_probe(csis->dev, ret, + "failed to register subdev\n"); + + return 0; } /* ----------------------------------------------------------------------------- @@ -1549,10 +1556,8 @@ static int mipi_csis_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &csis->sd); ret = mipi_csis_async_register(csis); - if (ret < 0) { - dev_err(dev, "async register failed: %d\n", ret); + if (ret < 0) goto err_cleanup; - } /* Initialize debugfs. */ mipi_csis_debugfs_init(csis); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 16392420903a..4bf8570e1b9e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -314,6 +314,17 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .has_36bit_dma = true, }; +static const struct mxc_isi_plat_data mxc_imx95_data = { + .model = MXC_ISI_IMX95, + .num_ports = 4, + .num_channels = 8, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = true, +}; + static const struct mxc_isi_plat_data mxc_imx8qm_data = { .model = MXC_ISI_IMX8QM, .num_ports = 5, @@ -557,6 +568,7 @@ static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, { .compatible = "fsl,imx91-isi", .data = &mxc_imx91_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, + { .compatible = "fsl,imx95-isi", .data = &mxc_imx95_data }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, mxc_isi_of_match); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 3cbd35305af0..14d63ec36416 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -162,6 +162,7 @@ enum model { MXC_ISI_IMX8ULP, MXC_ISI_IMX91, MXC_ISI_IMX93, + MXC_ISI_IMX95, }; struct mxc_isi_plat_data { diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 9225a7ac1c3e..0187d4ab97e8 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -309,8 +309,8 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, val = mxc_isi_read(pipe, CHNL_CTRL); val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK | - CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK | - CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK); + CHNL_CTRL_SRC_TYPE_MASK | CHNL_CTRL_MIPI_VC_ID_MASK | + CHNL_CTRL_SRC_INPUT_MASK); /* * If no scaling or color space conversion is needed, bypass the @@ -323,8 +323,6 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, if (pipe->chained) val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN); - val |= CHNL_CTRL_BLANK_PXL(0xff); - /* Input source (including VC configuration for CSI-2) */ if (input == MXC_ISI_INPUT_MEM) { /* diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 13682bf6e9f8..1be3a728f32f 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -1410,7 +1410,7 @@ int mxc_isi_video_register(struct mxc_isi_pipe *pipe, q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mxc_isi_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = 2; + q->min_queued_buffers = 0; q->lock = &video->lock; q->dev = pipe->isi->dev; diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index e111b1debd69..04ebed8a0493 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -72,21 +72,6 @@ enum { ST_SUSPENDED = 4, }; -enum imx8mq_mipi_csi_clk { - CSI2_CLK_CORE, - CSI2_CLK_ESC, - CSI2_CLK_UI, - CSI2_NUM_CLKS, -}; - -static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = { - [CSI2_CLK_CORE] = "core", - [CSI2_CLK_ESC] = "esc", - [CSI2_CLK_UI] = "ui", -}; - -#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id) - struct imx8mq_plat_data { int (*enable)(struct csi_state *state, u32 hs_settle); void (*disable)(struct csi_state *state); @@ -112,7 +97,9 @@ struct csi_state { struct device *dev; const struct imx8mq_plat_data *pdata; void __iomem *regs; - struct clk_bulk_data clks[CSI2_NUM_CLKS]; + struct clk_bulk_data *clks; + struct clk *esc_clk; + u32 num_clks; struct reset_control *rst; struct regulator *mipi_phy_regulator; @@ -352,18 +339,14 @@ static int imx8mq_mipi_csi_sw_reset(struct csi_state *state) { int ret; - /* - * these are most likely self-clearing reset bits. to make it - * more clear, the reset-imx7 driver should implement the - * .reset() operation. - */ ret = reset_control_assert(state->rst); if (ret < 0) { dev_err(state->dev, "Failed to assert resets: %d\n", ret); return ret; } - return 0; + /* Explicitly release reset to make sure reset bits are cleared. */ + return reset_control_deassert(state->rst); } static void imx8mq_mipi_csi_set_params(struct csi_state *state) @@ -385,24 +368,16 @@ static void imx8mq_mipi_csi_set_params(struct csi_state *state) CSI2RX_SEND_LEVEL); } -static int imx8mq_mipi_csi_clk_enable(struct csi_state *state) -{ - return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks); -} - -static void imx8mq_mipi_csi_clk_disable(struct csi_state *state) -{ - clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks); -} - -static int imx8mq_mipi_csi_clk_get(struct csi_state *state) +static struct clk *imx8mq_mipi_csi_find_esc_clk(struct csi_state *state) { unsigned int i; - for (i = 0; i < CSI2_NUM_CLKS; i++) - state->clks[i].id = imx8mq_mipi_csi_clk_id[i]; + for (i = 0; i < state->num_clks; i++) { + if (!strcmp(state->clks[i].id, "esc")) + return state->clks[i].clk; + } - return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks); + return ERR_PTR(-ENODEV); } static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, @@ -457,7 +432,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, * documentation recommends picking a value away from the boundaries. * Let's pick the average. */ - esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk); + esc_clk_rate = clk_get_rate(state->esc_clk); if (!esc_clk_rate) { dev_err(state->dev, "Could not get esc clock rate.\n"); return -EINVAL; @@ -727,18 +702,18 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) - return -ENOTCONN; + return dev_err_probe(state->dev, -ENOTCONN, + "failed to get local endpoint fwnode\n"); ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) - return ret; + return dev_err_probe(state->dev, ret, + "failed to parse endpoint\n"); for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { - if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { - dev_err(state->dev, - "data lanes reordering is not supported"); - return -EINVAL; - } + if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) + return dev_err_probe(state->dev, -EINVAL, + "data lanes reordering is not supported"); } state->bus = vep.bus.mipi_csi2; @@ -750,15 +725,22 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep, struct v4l2_async_connection); if (IS_ERR(asd)) - return PTR_ERR(asd); + return dev_err_probe(state->dev, PTR_ERR(asd), + "failed to add fwnode to notifier\n"); state->notifier.ops = &imx8mq_mipi_csi_notify_ops; ret = v4l2_async_nf_register(&state->notifier); if (ret) - return ret; + return dev_err_probe(state->dev, ret, + "failed to register notifier\n"); - return v4l2_async_register_subdev(&state->sd); + ret = v4l2_async_register_subdev(&state->sd); + if (ret) + return dev_err_probe(state->dev, ret, + "failed to register subdev\n"); + + return 0; } /* ----------------------------------------------------------------------------- @@ -774,7 +756,7 @@ static void imx8mq_mipi_csi_pm_suspend(struct device *dev) if (state->state & ST_POWERED) { imx8mq_mipi_csi_stop_stream(state); - imx8mq_mipi_csi_clk_disable(state); + clk_bulk_disable_unprepare(state->num_clks, state->clks); state->state &= ~ST_POWERED; } @@ -792,7 +774,7 @@ static int imx8mq_mipi_csi_pm_resume(struct device *dev) if (!(state->state & ST_POWERED)) { state->state |= ST_POWERED; - ret = imx8mq_mipi_csi_clk_enable(state); + ret = clk_bulk_prepare_enable(state->num_clks, state->clks); } if (state->state & ST_STREAMING) { sd_state = v4l2_subdev_lock_and_get_active_state(sd); @@ -1013,9 +995,16 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev) if (IS_ERR(state->regs)) return PTR_ERR(state->regs); - ret = imx8mq_mipi_csi_clk_get(state); + ret = devm_clk_bulk_get_all(dev, &state->clks); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Failed to get clocks\n"); + + state->num_clks = ret; + + state->esc_clk = imx8mq_mipi_csi_find_esc_clk(state); + if (IS_ERR(state->esc_clk)) + return dev_err_probe(dev, PTR_ERR(state->esc_clk), + "Couldn't find esc clock\n"); platform_set_drvdata(pdev, &state->sd); @@ -1081,6 +1070,7 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev) static const struct of_device_id imx8mq_mipi_csi_of_match[] = { { .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data }, { .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data }, + { .compatible = "fsl,imx8ulp-mipi-csi2", .data = &imx8qxp_data }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match); diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index d1b31ab8b8c4..7305cc4a04cb 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -71,10 +71,7 @@ struct rcar_csi2; #define FLD_REG 0x1c #define FLD_FLD_NUM(n) (((n) & 0xff) << 16) #define FLD_DET_SEL(n) (((n) & 0x3) << 4) -#define FLD_FLD_EN4 BIT(3) -#define FLD_FLD_EN3 BIT(2) -#define FLD_FLD_EN2 BIT(1) -#define FLD_FLD_EN BIT(0) +#define FLD_FLD_EN(ch) BIT(ch) /* Automatic Standby Control */ #define ASTBY_REG 0x20 @@ -959,35 +956,6 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) return 0; } -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, - unsigned int lanes) -{ - struct media_pad *remote_pad; - struct v4l2_subdev *source; - s64 freq; - u64 mbps; - - if (!priv->remote) - return -ENODEV; - - source = priv->remote; - remote_pad = &source->entity.pads[priv->remote_pad]; - - freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes); - if (freq < 0) { - int ret = (int)freq; - - dev_err(priv->dev, "failed to get link freq for %s: %d\n", - source->name, ret); - - return ret; - } - - mbps = div_u64(freq * 2, MEGA); - - return mbps; -} - static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, unsigned int *lanes) { @@ -1035,6 +1003,62 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return 0; } +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) +{ + struct media_pad *remote_pad; + struct v4l2_subdev *source; + s64 freq; + u64 mbps; + + if (!priv->remote) + return -ENODEV; + + source = priv->remote; + remote_pad = &source->entity.pads[priv->remote_pad]; + + /* + * First try to get the real link freq. If that fails, try the heuristic + * method with bpp and lanes (but that only works for one route). + */ + freq = v4l2_get_link_freq(remote_pad, 0, 0); + if (freq < 0) { + const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; + unsigned int lanes; + unsigned int bpp; + int ret; + + ret = rcsi2_get_active_lanes(priv, &lanes); + if (ret) + return ret; + + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + if (!fmt) + return -EINVAL; + + format = rcsi2_code_to_fmt(fmt->code); + if (!format) + return -EINVAL; + + bpp = format->bpp; + + freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes); + if (freq < 0) { + int ret = (int)freq; + + dev_err(priv->dev, "failed to get link freq for %s: %d\n", + source->name, ret); + + return ret; + } + } + + mbps = div_u64(freq * 2, MEGA); + + return mbps; +} + static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { @@ -1082,8 +1106,8 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, } if (fmt->field == V4L2_FIELD_ALTERNATE) - fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 - | FLD_FLD_EN; + fld = FLD_DET_SEL(1) | FLD_FLD_EN(3) | FLD_FLD_EN(2) | + FLD_FLD_EN(1) | FLD_FLD_EN(0); /* * Get the number of active data lanes inspecting the remote mbus @@ -1096,7 +1120,7 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, phycnt = PHYCNT_ENABLECLK; phycnt |= (1 << lanes) - 1; - mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + mbps = rcsi2_calc_mbps(priv, state); if (mbps < 0) return mbps; @@ -1478,23 +1502,15 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { const struct rcsi2_cphy_setting *cphy = NULL; - const struct rcar_csi2_format *format; - const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int mbps; int ret; - /* Use the format on the sink pad to compute the receiver config. */ - fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); - format = rcsi2_code_to_fmt(fmt->code); - if (!format) - return -EINVAL; - ret = rcsi2_get_active_lanes(priv, &lanes); if (ret) return ret; - mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + mbps = rcsi2_calc_mbps(priv, state); if (mbps < 0) return mbps; @@ -1735,23 +1751,15 @@ static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { - const struct rcar_csi2_format *format; - const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int mbps; int ret; - /* Calculate parameters */ - fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); - format = rcsi2_code_to_fmt(fmt->code); - if (!format) - return -EINVAL; - ret = rcsi2_get_active_lanes(priv, &lanes); if (ret) return ret; - mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + mbps = rcsi2_calc_mbps(priv, state); if (mbps < 0) return mbps; diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c index 1eb29a0b774a..8fb2cc3b5650 100644 --- a/drivers/media/platform/renesas/rcar-isp/csisp.c +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c @@ -28,10 +28,7 @@ #define ISPSTART_STOP 0x0000 #define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n))) -#define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24) -#define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16) -#define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8) -#define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f) +#define ISPPROCMODE_DT_PROC_MODE_VCn(vc, pm) (((pm) & 0x3f) << (8 * (vc))) #define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n))) @@ -263,10 +260,10 @@ static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state) /* Setup processing method. */ risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype), - ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); + ISPPROCMODE_DT_PROC_MODE_VCn(3, format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VCn(2, format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VCn(1, format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VCn(0, format->procmode)); /* Start ISP. */ risp_write_cs(isp, ISPSTART_REG, ISPSTART_START); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index b619d1436a41..f9af9177e02f 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -676,8 +676,30 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) if (vin->scaler) vin->scaler(vin); + /* + * VNIS_REG has four lowest bits always 0, i.e. the stride has to be + * aligned to 16 bytes. This is done in rvin_format_bytesperline(). + */ + fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; + + /* + * RAW8 format bpp is 1, but the hardware process RAW8 format in 2 pixel + * units, so we need to divide the stride by 2. + */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_GREY: + stride /= 2; + break; + default: + break; + } + rvin_write(vin, stride, VNIS_REG); } diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 079dbaf016c2..9d45e11898c1 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -155,6 +155,18 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin, case V4L2_PIX_FMT_NV16: align = 0x20; break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_GREY: + /* + * RAW8 format bpp is 1, but the hardware process RAW8 format in + * 2 pixel units, and we need to align to 32 bytes. See + * rvin_crop_scale_comp(). + */ + align = 0x20; + break; default: align = 0x10; break; diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c index e9857eb5b51a..355842abb24b 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c @@ -119,7 +119,7 @@ static irqreturn_t rzv2h_ivc_isr(int irq, void *context) * The second interrupt indicates that the post-frame transfer VBLANK * has completed, we can now schedule a new frame transfer, if any. */ - queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + rzv2h_ivc_transfer_buffer(ivc); return IRQ_HANDLED; } diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c index 799453250b85..b167f1bab7ef 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c @@ -7,6 +7,7 @@ #include "rzv2h-ivc.h" +#include <linux/bitfield.h> #include <linux/cleanup.h> #include <linux/iopoll.h> #include <linux/lockdep.h> @@ -24,7 +25,7 @@ #include <media/videobuf2-dma-contig.h> #define RZV2H_IVC_FIXED_HBLANK 0x20 -#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 15 + (120501 / (hts))) +#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 70100 / (hts)) struct rzv2h_ivc_buf { struct vb2_v4l2_buffer vb; @@ -142,30 +143,30 @@ void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } -static void rzv2h_ivc_transfer_buffer(struct work_struct *work) +void rzv2h_ivc_transfer_buffer(struct rzv2h_ivc *ivc) { - struct rzv2h_ivc *ivc = container_of(work, struct rzv2h_ivc, - buffers.work); struct rzv2h_ivc_buf *buf; + lockdep_assert_held(&ivc->spinlock); + + if (ivc->vvalid_ifp) + return; + /* Setup buffers */ scoped_guard(spinlock_irqsave, &ivc->buffers.lock) { buf = list_first_entry_or_null(&ivc->buffers.queue, struct rzv2h_ivc_buf, queue); - } - - if (!buf) - return; + if (!buf) + return; - list_del(&buf->queue); + list_del(&buf->queue); + ivc->buffers.curr = buf; + } - ivc->buffers.curr = buf; buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_SADDL_P0, buf->addr); - scoped_guard(spinlock_irqsave, &ivc->spinlock) { - ivc->vvalid_ifp = 2; - } + ivc->vvalid_ifp = 2; rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_FRCON, 0x1); } @@ -200,8 +201,8 @@ static void rzv2h_ivc_buf_queue(struct vb2_buffer *vb) } scoped_guard(spinlock_irq, &ivc->spinlock) { - if (vb2_is_streaming(vb->vb2_queue) && !ivc->vvalid_ifp) - queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + if (vb2_is_streaming(vb->vb2_queue)) + rzv2h_ivc_transfer_buffer(ivc); } } @@ -214,10 +215,10 @@ static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc) /* Currently only CRU packed pixel formats are supported */ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, - RZV2H_IVC_INPUT_FMT_CRU_PACKED); - - rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, - RZV2H_IVC_PXFMT_DTYPE, fmt->dtype); + FIELD_PREP(RZV2H_IVC_AXIRX_PXFMT_FIELD_DTYPE, + fmt->dtype) | + FIELD_PREP(RZV2H_IVC_AXIRX_PXFMT_FIELD_CLFMT, + RZV2H_IVC_CLFMT_CRU_PACKED)); rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_HSIZE, pix->width); rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_VSIZE, pix->height); @@ -235,8 +236,10 @@ static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc) hts = pix->width + RZV2H_IVC_FIXED_HBLANK; vblank = RZV2H_IVC_MIN_VBLANK(hts); - rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_BLANK, - RZV2H_IVC_VBLANK(vblank)); + rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_BLANK, + RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK, + FIELD_PREP(RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK, + vblank)); } static void rzv2h_ivc_return_buffers(struct rzv2h_ivc *ivc, @@ -277,7 +280,9 @@ static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count) rzv2h_ivc_format_configure(ivc); - queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + scoped_guard(spinlock_irq, &ivc->spinlock) { + rzv2h_ivc_transfer_buffer(ivc); + } return 0; @@ -294,9 +299,10 @@ static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); u32 val = 0; - rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, 0x1); + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP); readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, - val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP), + 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); video_device_pipeline_stop(&ivc->vdev.dev); @@ -443,11 +449,6 @@ int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev) spin_lock_init(&ivc->buffers.lock); INIT_LIST_HEAD(&ivc->buffers.queue); - INIT_WORK(&ivc->buffers.work, rzv2h_ivc_transfer_buffer); - - ivc->buffers.async_wq = alloc_workqueue("rzv2h-ivc", 0, 0); - if (!ivc->buffers.async_wq) - return -EINVAL; /* Initialise vb2 queue */ vb2q = &ivc->vdev.vb2q; @@ -465,7 +466,7 @@ int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev) ret = vb2_queue_init(vb2q); if (ret) { dev_err(ivc->dev, "vb2 queue init failed\n"); - goto err_destroy_workqueue; + return ret; } /* Initialise Video Device */ @@ -514,8 +515,6 @@ err_cleanup_vdev_entity: media_entity_cleanup(&vdev->entity); err_release_vb2q: vb2_queue_release(vb2q); -err_destroy_workqueue: - destroy_workqueue(ivc->buffers.async_wq); return ret; } diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h index 3bcaab990b0f..6f644ba796a9 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h @@ -24,9 +24,10 @@ #define RZV2H_IVC_ONE_EXPOSURE 0x00 #define RZV2H_IVC_TWO_EXPOSURE 0x01 #define RZV2H_IVC_REG_AXIRX_PXFMT 0x0004 -#define RZV2H_IVC_INPUT_FMT_MIPI (0 << 16) -#define RZV2H_IVC_INPUT_FMT_CRU_PACKED BIT(16) -#define RZV2H_IVC_PXFMT_DTYPE GENMASK(7, 0) +#define RZV2H_IVC_AXIRX_PXFMT_FIELD_CLFMT GENMASK(17, 16) +#define RZV2H_IVC_CLFMT_MIPI 0 +#define RZV2H_IVC_CLFMT_CRU_PACKED 1 +#define RZV2H_IVC_AXIRX_PXFMT_FIELD_DTYPE GENMASK(7, 0) #define RZV2H_IVC_REG_AXIRX_SADDL_P0 0x0010 #define RZV2H_IVC_REG_AXIRX_SADDH_P0 0x0014 #define RZV2H_IVC_REG_AXIRX_SADDL_P1 0x0018 @@ -34,7 +35,7 @@ #define RZV2H_IVC_REG_AXIRX_HSIZE 0x0020 #define RZV2H_IVC_REG_AXIRX_VSIZE 0x0024 #define RZV2H_IVC_REG_AXIRX_BLANK 0x0028 -#define RZV2H_IVC_VBLANK(x) ((x) << 16) +#define RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK GENMASK(25, 16) #define RZV2H_IVC_REG_AXIRX_STRD 0x0030 #define RZV2H_IVC_REG_AXIRX_ISSU 0x0040 #define RZV2H_IVC_REG_AXIRX_ERACT 0x0048 @@ -45,6 +46,7 @@ #define RZV2H_IVC_REG_FM_MCON 0x0104 #define RZV2H_IVC_REG_FM_FRCON 0x0108 #define RZV2H_IVC_REG_FM_STOP 0x010c +#define RZV2H_IVC_REG_FM_STOP_FSTOP BIT(20) #define RZV2H_IVC_REG_FM_INT_EN 0x0120 #define RZV2H_IVC_VVAL_IFPE BIT(0) #define RZV2H_IVC_REG_FM_INT_STA 0x0124 @@ -102,8 +104,6 @@ struct rzv2h_ivc { struct { /* Spinlock to guard buffer queue */ spinlock_t lock; - struct workqueue_struct *async_wq; - struct work_struct work; struct list_head queue; struct rzv2h_ivc_buf *curr; unsigned int sequence; @@ -128,3 +128,4 @@ void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc); void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val); void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr, u32 mask, u32 val); +void rzv2h_ivc_transfer_buffer(struct rzv2h_ivc *ivc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 5fc2e5a3bb30..b1a2c68e9944 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -59,25 +59,17 @@ static const struct v4l2_ctrl_ops brx_ctrl_ops = { * V4L2 Subdevice Operations */ +static const unsigned int brx_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + /* * The BRx can't perform format conversion, all sink and source formats must be * identical. We pick the format on the first sink pad (pad 0) and propagate it * to all other pads. */ -static int brx_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, - ARRAY_SIZE(codes)); -} - static int brx_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) @@ -164,14 +156,20 @@ static int brx_set_format(struct v4l2_subdev *subdev, compose->height = format->height; } - /* Propagate the format code to all pads. */ + /* + * Propagate the format code to all pads, and the whole format to the + * source pad. + */ if (fmt->pad == BRX_PAD_SINK(0)) { unsigned int i; - for (i = 0; i <= brx->entity.source_pad; ++i) { + for (i = 0; i < brx->entity.source_pad; ++i) { format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } + + format = v4l2_subdev_state_get_format(state, i); + *format = fmt->format; } done: @@ -262,7 +260,7 @@ done: } static const struct v4l2_subdev_pad_ops brx_pad_ops = { - .enum_mbus_code = brx_enum_mbus_code, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = brx_set_format, @@ -271,6 +269,7 @@ static const struct v4l2_subdev_pad_ops brx_pad_ops = { }; static const struct v4l2_subdev_ops brx_ops = { + .core = &vsp1_entity_core_ops, .pad = &brx_pad_ops, }; @@ -416,6 +415,12 @@ struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; brx->entity.ops = &brx_entity_ops; brx->entity.type = type; + brx->entity.codes = brx_codes; + brx->entity.num_codes = ARRAY_SIZE(brx_codes); + brx->entity.min_width = BRX_MIN_SIZE; + brx->entity.max_width = BRX_MAX_SIZE; + brx->entity.min_height = BRX_MIN_SIZE; + brx->entity.max_height = BRX_MAX_SIZE; if (type == VSP1_ENTITY_BRU) { num_pads = vsp1->info->num_bru_inputs + 1; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 98645bd2a983..04c466c4da81 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -113,7 +113,7 @@ static const struct v4l2_ctrl_config clu_mode_control = { }; /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ static const unsigned int clu_codes[] = { @@ -122,46 +122,15 @@ static const unsigned int clu_codes[] = { MEDIA_BUS_FMT_AYUV8_1X32, }; -static int clu_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, clu_codes, - ARRAY_SIZE(clu_codes)); -} - -static int clu_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - CLU_MIN_SIZE, - CLU_MIN_SIZE, CLU_MAX_SIZE, - CLU_MAX_SIZE); -} - -static int clu_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, clu_codes, - ARRAY_SIZE(clu_codes), - CLU_MIN_SIZE, CLU_MIN_SIZE, - CLU_MAX_SIZE, CLU_MAX_SIZE); -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - static const struct v4l2_subdev_pad_ops clu_pad_ops = { - .enum_mbus_code = clu_enum_mbus_code, - .enum_frame_size = clu_enum_frame_size, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, + .enum_frame_size = vsp1_subdev_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = clu_set_format, + .set_fmt = vsp1_subdev_set_pad_format, }; static const struct v4l2_subdev_ops clu_ops = { + .core = &vsp1_entity_core_ops, .pad = &clu_pad_ops, }; @@ -247,6 +216,12 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) clu->entity.ops = &clu_entity_ops; clu->entity.type = VSP1_ENTITY_CLU; + clu->entity.codes = clu_codes; + clu->entity.num_codes = ARRAY_SIZE(clu_codes); + clu->entity.min_width = CLU_MIN_SIZE; + clu->entity.min_height = CLU_MIN_SIZE; + clu->entity.max_width = CLU_MAX_SIZE; + clu->entity.max_height = CLU_MAX_SIZE; ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops, MEDIA_ENT_F_PROC_VIDEO_LUT); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 2de515c497eb..627b5046fa80 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -240,8 +240,12 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) media_device_unregister(&vsp1->media_dev); media_device_cleanup(&vsp1->media_dev); - if (!vsp1->info->uapi) - vsp1_drm_cleanup(vsp1); + if (!vsp1->info->uapi) { + if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4) + vsp1_vspx_cleanup(vsp1); + else + vsp1_drm_cleanup(vsp1); + } } static int vsp1_create_entities(struct vsp1_device *vsp1) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index a6680d531872..1dad9589768c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -7,11 +7,14 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/gfp.h> +#include <linux/mutex.h> #include <media/media-entity.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/v4l2-subdev.h> #include "vsp1.h" @@ -181,8 +184,6 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, * @subdev: V4L2 subdevice * @sd_state: V4L2 subdev state * @code: Media bus code enumeration - * @codes: Array of supported media bus codes - * @ncodes: Number of supported media bus codes * * This function implements the subdev enum_mbus_code pad operation for entities * that do not support format conversion. It enumerates the given supported @@ -191,16 +192,15 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, */ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code, - const unsigned int *codes, unsigned int ncodes) + struct v4l2_subdev_mbus_code_enum *code) { struct vsp1_entity *entity = to_vsp1_entity(subdev); if (code->pad == 0) { - if (code->index >= ncodes) + if (code->index >= entity->num_codes) return -EINVAL; - code->code = codes[code->index]; + code->code = entity->codes[code->index]; } else { struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; @@ -230,10 +230,6 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, * @subdev: V4L2 subdevice * @sd_state: V4L2 subdev state * @fse: Frame size enumeration - * @min_width: Minimum image width - * @min_height: Minimum image height - * @max_width: Maximum image width - * @max_height: Maximum image height * * This function implements the subdev enum_frame_size pad operation for * entities that do not support scaling or cropping. It reports the given @@ -242,47 +238,54 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, */ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse, - unsigned int min_width, unsigned int min_height, - unsigned int max_width, unsigned int max_height) + struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *format; - int ret = 0; - state = vsp1_entity_get_state(entity, sd_state, fse->which); - if (!state) + if (fse->index) return -EINVAL; - format = v4l2_subdev_state_get_format(state, fse->pad); + if (fse->pad == 0) { + unsigned int i; - mutex_lock(&entity->lock); + for (i = 0; i < entity->num_codes; ++i) { + if (fse->code == entity->codes[i]) + break; + } - if (fse->index || fse->code != format->code) { - ret = -EINVAL; - goto done; - } + if (i == entity->num_codes) + return -EINVAL; - if (fse->pad == 0) { - fse->min_width = min_width; - fse->max_width = max_width; - fse->min_height = min_height; - fse->max_height = max_height; + fse->min_width = entity->min_width; + fse->max_width = entity->max_width; + fse->min_height = entity->min_height; + fse->max_height = entity->max_height; } else { + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; + + state = vsp1_entity_get_state(entity, sd_state, fse->which); + if (!state) + return -EINVAL; + /* - * The size on the source pad are fixed and always identical to - * the size on the sink pad. + * The media bus code and size on the source pad are fixed and + * always identical to the sink pad. */ + format = v4l2_subdev_state_get_format(state, 0); + + guard(mutex)(&entity->lock); + + if (fse->code != format->code) + return -EINVAL; + fse->min_width = format->width; fse->max_width = format->width; fse->min_height = format->height; fse->max_height = format->height; } -done: - mutex_unlock(&entity->lock); - return ret; + return 0; } /* @@ -290,25 +293,15 @@ done: * @subdev: V4L2 subdevice * @sd_state: V4L2 subdev state * @fmt: V4L2 subdev format - * @codes: Array of supported media bus codes - * @ncodes: Number of supported media bus codes - * @min_width: Minimum image width - * @min_height: Minimum image height - * @max_width: Maximum image width - * @max_height: Maximum image height * * This function implements the subdev set_fmt pad operation for entities that - * do not support scaling or cropping. It defaults to the first supplied media + * do not support scaling or cropping. It defaults to the first supported media * bus code if the requested code isn't supported, clamps the size to the - * supplied minimum and maximum, and propagates the sink pad format to the - * source pad. + * entity's limits, and propagates the sink pad format to the source pad. */ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt, - const unsigned int *codes, unsigned int ncodes, - unsigned int min_width, unsigned int min_height, - unsigned int max_width, unsigned int max_height) + struct v4l2_subdev_format *fmt) { struct vsp1_entity *entity = to_vsp1_entity(subdev); struct v4l2_subdev_state *state; @@ -337,16 +330,17 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, * Default to the first media bus code if the requested format is not * supported. */ - for (i = 0; i < ncodes; ++i) { - if (fmt->format.code == codes[i]) + for (i = 0; i < entity->num_codes; ++i) { + if (fmt->format.code == entity->codes[i]) break; } - format->code = i < ncodes ? codes[i] : codes[0]; + format->code = i < entity->num_codes + ? entity->codes[i] : entity->codes[0]; format->width = clamp_t(unsigned int, fmt->format.width, - min_width, max_width); + entity->min_width, entity->max_width); format->height = clamp_t(unsigned int, fmt->format.height, - min_height, max_height); + entity->min_height, entity->max_height); format->field = V4L2_FIELD_NONE; format->colorspace = fmt->format.colorspace; @@ -386,7 +380,7 @@ static int vsp1_entity_init_state(struct v4l2_subdev *subdev, unsigned int pad; /* Initialize all pad formats with default values. */ - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + for (pad = 0; pad < subdev->entity.num_pads; ++pad) { struct v4l2_subdev_format format = { .pad = pad, .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -403,6 +397,11 @@ static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { .init_state = vsp1_entity_init_state, }; +const struct v4l2_subdev_core_ops vsp1_entity_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -646,6 +645,9 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, subdev->entity.ops = &vsp1->media_ops; subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (ops->core == &vsp1_entity_core_ops) + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index b7c72d0b7f8e..c0c1fe7d3e40 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -113,6 +113,13 @@ struct vsp1_entity { unsigned int index; const struct vsp1_route *route; + const u32 *codes; + unsigned int num_codes; + unsigned int min_width; + unsigned int min_height; + unsigned int max_width; + unsigned int max_height; + struct vsp1_pipeline *pipe; struct list_head list_dev; @@ -136,6 +143,8 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) return container_of(subdev, struct vsp1_entity, subdev); } +extern const struct v4l2_subdev_core_ops vsp1_entity_core_ops; + int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, const struct v4l2_subdev_ops *ops, u32 function); @@ -180,18 +189,12 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt); int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt, - const unsigned int *codes, unsigned int ncodes, - unsigned int min_width, unsigned int min_height, - unsigned int max_width, unsigned int max_height); + struct v4l2_subdev_format *fmt); int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code, - const unsigned int *codes, unsigned int ncodes); + struct v4l2_subdev_mbus_code_enum *code); int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse, - unsigned int min_w, unsigned int min_h, - unsigned int max_w, unsigned int max_h); + struct v4l2_subdev_frame_size_enum *fse); #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 390ea50f1595..3f87a2c9df0e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -167,16 +167,15 @@ static int histo_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); - if (code->pad == HISTO_PAD_SOURCE) { - code->code = MEDIA_BUS_FMT_FIXED; + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_METADATA_FIXED; return 0; } - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, - histo->formats, - histo->num_formats); + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code); } static int histo_enum_frame_size(struct v4l2_subdev *subdev, @@ -184,12 +183,9 @@ static int histo_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad != HISTO_PAD_SINK) - return -EINVAL; + return -ENOTTY; - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - HISTO_MIN_SIZE, - HISTO_MIN_SIZE, HISTO_MAX_SIZE, - HISTO_MAX_SIZE); + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse); } static int histo_get_selection(struct v4l2_subdev *subdev, @@ -354,22 +350,68 @@ static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *selection; + unsigned int i; - if (fmt->pad == HISTO_PAD_SOURCE) { - fmt->format.code = MEDIA_BUS_FMT_FIXED; - fmt->format.width = 0; - fmt->format.height = 0; - fmt->format.field = V4L2_FIELD_NONE; - fmt->format.colorspace = V4L2_COLORSPACE_RAW; + state = vsp1_entity_get_state(entity, sd_state, fmt->which); + if (!state) + return -EINVAL; - return 0; + format = v4l2_subdev_state_get_format(state, fmt->pad); + + guard(mutex)(&entity->lock); + + if (fmt->pad == HISTO_PAD_SINK) { + /* + * Default to the first media bus code if the requested format + * is not supported. + */ + for (i = 0; i < entity->num_codes; ++i) { + if (fmt->format.code == entity->codes[i]) + break; + } + + format->code = i < entity->num_codes + ? entity->codes[i] : entity->codes[0]; + format->width = clamp_t(unsigned int, fmt->format.width, + entity->min_width, entity->max_width); + format->height = clamp_t(unsigned int, fmt->format.height, + entity->min_height, entity->max_height); + format->field = V4L2_FIELD_NONE; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); + + /* Reset the crop and compose rectangles. */ + selection = v4l2_subdev_state_get_crop(state, fmt->pad); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + + selection = v4l2_subdev_state_get_compose(state, fmt->pad); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + } else { + format->code = MEDIA_BUS_FMT_METADATA_FIXED; + format->width = 0; + format->height = 0; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; } - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, - histo->formats, histo->num_formats, - HISTO_MIN_SIZE, HISTO_MIN_SIZE, - HISTO_MAX_SIZE, HISTO_MAX_SIZE); + fmt->format = *format; + + return 0; } static const struct v4l2_subdev_pad_ops histo_pad_ops = { @@ -382,6 +424,7 @@ static const struct v4l2_subdev_pad_ops histo_pad_ops = { }; static const struct v4l2_subdev_ops histo_ops = { + .core = &vsp1_entity_core_ops, .pad = &histo_pad_ops, }; @@ -490,8 +533,6 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, { int ret; - histo->formats = formats; - histo->num_formats = num_formats; histo->data_size = data_size; histo->meta_format = meta_format; @@ -506,6 +547,12 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, /* Initialize the VSP entity... */ histo->entity.ops = ops; histo->entity.type = type; + histo->entity.codes = formats; + histo->entity.num_codes = num_formats; + histo->entity.min_width = HISTO_MIN_SIZE; + histo->entity.min_height = HISTO_MIN_SIZE; + histo->entity.max_width = HISTO_MAX_SIZE; + histo->entity.max_height = HISTO_MAX_SIZE; ret = vsp1_entity_init(vsp1, &histo->entity, name, 2, &histo_ops, MEDIA_ENT_F_PROC_VIDEO_STATISTICS); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.h b/drivers/media/platform/renesas/vsp1/vsp1_histo.h index 06f029846244..227db810da52 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.h @@ -36,8 +36,6 @@ struct vsp1_histogram { struct video_device video; struct media_pad pad; - const u32 *formats; - unsigned int num_formats; size_t data_size; u32 meta_format; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 1fcd1967d3b2..830e124beb7b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/gfp.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> @@ -34,6 +35,11 @@ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, * V4L2 Subdevice Operations */ +static const unsigned int hsit_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, +}; + static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -56,10 +62,50 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - HSIT_MIN_SIZE, - HSIT_MIN_SIZE, HSIT_MAX_SIZE, - HSIT_MAX_SIZE); + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct vsp1_hsit *hsit = to_hsit(subdev); + u32 code; + + if (fse->index) + return -EINVAL; + + if ((fse->pad == HSIT_PAD_SINK && !hsit->inverse) | + (fse->pad == HSIT_PAD_SOURCE && hsit->inverse)) + code = MEDIA_BUS_FMT_ARGB8888_1X32; + else + code = MEDIA_BUS_FMT_AHSV8888_1X32; + + if (fse->code != code) + return -EINVAL; + + if (fse->pad == 0) { + fse->min_width = entity->min_width; + fse->max_width = entity->max_width; + fse->min_height = entity->min_height; + fse->max_height = entity->max_height; + } else { + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; + + state = vsp1_entity_get_state(entity, sd_state, fse->which); + if (!state) + return -EINVAL; + + /* + * The size on the source pad is fixed and always identical to + * the sink pad. + */ + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SINK); + + guard(mutex)(&entity->lock); + + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; } static int hsit_set_format(struct v4l2_subdev *subdev, @@ -175,6 +221,13 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) else hsit->entity.type = VSP1_ENTITY_HST; + hsit->entity.codes = hsit_codes; + hsit->entity.num_codes = ARRAY_SIZE(hsit_codes); + hsit->entity.min_width = HSIT_MIN_SIZE; + hsit->entity.min_height = HSIT_MIN_SIZE; + hsit->entity.max_width = HSIT_MAX_SIZE; + hsit->entity.max_height = HSIT_MAX_SIZE; + ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2, &hsit_ops, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c index 5dd62bebbe8c..d44c04e140bc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_iif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c @@ -36,38 +36,11 @@ static const unsigned int iif_codes[] = { MEDIA_BUS_FMT_METADATA_FIXED }; -static int iif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes, - ARRAY_SIZE(iif_codes)); -} - -static int iif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - IIF_MIN_WIDTH, IIF_MIN_HEIGHT, - IIF_MAX_WIDTH, IIF_MAX_HEIGHT); -} - -static int iif_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes, - ARRAY_SIZE(iif_codes), - IIF_MIN_WIDTH, IIF_MIN_HEIGHT, - IIF_MAX_WIDTH, IIF_MAX_HEIGHT); -} - static const struct v4l2_subdev_pad_ops iif_pad_ops = { - .enum_mbus_code = iif_enum_mbus_code, - .enum_frame_size = iif_enum_frame_size, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, + .enum_frame_size = vsp1_subdev_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = iif_set_format, + .set_fmt = vsp1_subdev_set_pad_format, }; static const struct v4l2_subdev_ops iif_ops = { @@ -106,6 +79,12 @@ struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1) iif->entity.ops = &iif_entity_ops; iif->entity.type = VSP1_ENTITY_IIF; + iif->entity.codes = iif_codes; + iif->entity.num_codes = ARRAY_SIZE(iif_codes); + iif->entity.min_width = IIF_MIN_WIDTH; + iif->entity.min_height = IIF_MIN_HEIGHT; + iif->entity.max_width = IIF_MAX_WIDTH; + iif->entity.max_height = IIF_MAX_HEIGHT; /* * The IIF is never exposed to userspace, but media entity registration diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b3d83d1c5306..1ebb88b3e4c9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -39,39 +39,11 @@ static const unsigned int lif_codes[] = { MEDIA_BUS_FMT_AYUV8_1X32, }; -static int lif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lif_codes, - ARRAY_SIZE(lif_codes)); -} - -static int lif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - LIF_MIN_SIZE, - LIF_MIN_SIZE, LIF_MAX_SIZE, - LIF_MAX_SIZE); -} - -static int lif_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lif_codes, - ARRAY_SIZE(lif_codes), - LIF_MIN_SIZE, LIF_MIN_SIZE, - LIF_MAX_SIZE, LIF_MAX_SIZE); -} - static const struct v4l2_subdev_pad_ops lif_pad_ops = { - .enum_mbus_code = lif_enum_mbus_code, - .enum_frame_size = lif_enum_frame_size, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, + .enum_frame_size = vsp1_subdev_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = lif_set_format, + .set_fmt = vsp1_subdev_set_pad_format, }; static const struct v4l2_subdev_ops lif_ops = { @@ -162,6 +134,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1, unsigned int index) lif->entity.ops = &lif_entity_ops; lif->entity.type = VSP1_ENTITY_LIF; lif->entity.index = index; + lif->entity.codes = lif_codes; + lif->entity.num_codes = ARRAY_SIZE(lif_codes); + lif->entity.min_width = LIF_MIN_SIZE; + lif->entity.min_height = LIF_MIN_SIZE; + lif->entity.max_width = LIF_MAX_SIZE; + lif->entity.max_height = LIF_MAX_SIZE; /* * The LIF is never exposed to userspace, but media entity registration diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index dd264e6532e0..94bdedcc5c92 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -89,7 +89,7 @@ static const struct v4l2_ctrl_config lut_table_control = { }; /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ static const unsigned int lut_codes[] = { @@ -98,46 +98,15 @@ static const unsigned int lut_codes[] = { MEDIA_BUS_FMT_AYUV8_1X32, }; -static int lut_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lut_codes, - ARRAY_SIZE(lut_codes)); -} - -static int lut_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - LUT_MIN_SIZE, - LUT_MIN_SIZE, LUT_MAX_SIZE, - LUT_MAX_SIZE); -} - -static int lut_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lut_codes, - ARRAY_SIZE(lut_codes), - LUT_MIN_SIZE, LUT_MIN_SIZE, - LUT_MAX_SIZE, LUT_MAX_SIZE); -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - static const struct v4l2_subdev_pad_ops lut_pad_ops = { - .enum_mbus_code = lut_enum_mbus_code, - .enum_frame_size = lut_enum_frame_size, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, + .enum_frame_size = vsp1_subdev_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = lut_set_format, + .set_fmt = vsp1_subdev_set_pad_format, }; static const struct v4l2_subdev_ops lut_ops = { + .core = &vsp1_entity_core_ops, .pad = &lut_pad_ops, }; @@ -208,6 +177,12 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; + lut->entity.codes = lut_codes; + lut->entity.num_codes = ARRAY_SIZE(lut_codes); + lut->entity.min_width = LUT_MIN_SIZE; + lut->entity.min_height = LUT_MIN_SIZE; + lut->entity.max_width = LUT_MAX_SIZE; + lut->entity.max_height = LUT_MAX_SIZE; ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops, MEDIA_ENT_F_PROC_VIDEO_LUT); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 811f2b7c5cc5..34b9095c9011 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -425,12 +425,13 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) if (rpf == NULL) return ERR_PTR(-ENOMEM); - rpf->max_width = RPF_MAX_WIDTH; - rpf->max_height = RPF_MAX_HEIGHT; - rpf->entity.ops = &rpf_entity_ops; rpf->entity.type = VSP1_ENTITY_RPF; rpf->entity.index = index; + rpf->entity.min_width = RWPF_MIN_WIDTH; + rpf->entity.min_height = RWPF_MIN_HEIGHT; + rpf->entity.max_width = RPF_MAX_WIDTH; + rpf->entity.max_height = RPF_MAX_HEIGHT; sprintf(name, "rpf.%u", index); ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &vsp1_rwpf_subdev_ops, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 9c8085d5d306..c72518b29f84 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -14,30 +14,53 @@ #include "vsp1_rwpf.h" #include "vsp1_video.h" -#define RWPF_MIN_WIDTH 1 -#define RWPF_MIN_HEIGHT 1 - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ +/* Keep HSV last. */ +static const unsigned int rwpf_codes[] = { + MEDIA_BUS_FMT_AYUV8_1X32, + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, +}; + static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AHSV8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; - if (code->index >= ARRAY_SIZE(codes)) + if (code->pad == RWPF_PAD_SINK) + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code); + + state = vsp1_entity_get_state(entity, sd_state, code->which); + if (!state) return -EINVAL; - code->code = codes[code->index]; + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + + guard(mutex)(&entity->lock); - if (code->pad == RWPF_PAD_SOURCE && - code->code == MEDIA_BUS_FMT_AYUV8_1X32) + /* + * The RWPF supports conversion between RGB and YUV formats, but HSV + * formats can't be converted. + */ + if (format->code == MEDIA_BUS_FMT_AHSV8888_1X32) { + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_AHSV8888_1X32; + } else { + if (code->index >= ARRAY_SIZE(rwpf_codes) - 1) + return -EINVAL; + + code->code = rwpf_codes[code->index]; + } + + if (code->code == MEDIA_BUS_FMT_AYUV8_1X32) code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION; @@ -48,12 +71,42 @@ static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - RWPF_MIN_WIDTH, - RWPF_MIN_HEIGHT, rwpf->max_width, - rwpf->max_height); + if (fse->pad == RWPF_PAD_SINK) + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse); + + if (fse->index) + return -EINVAL; + + state = vsp1_entity_get_state(entity, sd_state, fse->which); + if (!state) + return -EINVAL; + + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + + guard(mutex)(&entity->lock); + + /* + * The RWPF supports conversion between RGB and YUV formats, but + * HSV formats can't be converted. + */ + if ((format->code == MEDIA_BUS_FMT_AHSV8888_1X32) != + (fse->code == MEDIA_BUS_FMT_AHSV8888_1X32)) + return -EINVAL; + + /* + * The size on the source pad is fixed and always identical to + * the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + + return 0; } static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, @@ -126,9 +179,9 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->code = fmt->format.code; format->width = clamp_t(unsigned int, fmt->format.width, - RWPF_MIN_WIDTH, rwpf->max_width); + RWPF_MIN_WIDTH, rwpf->entity.max_width); format->height = clamp_t(unsigned int, fmt->format.height, - RWPF_MIN_HEIGHT, rwpf->max_height); + RWPF_MIN_HEIGHT, rwpf->entity.max_height); format->field = V4L2_FIELD_NONE; format->colorspace = fmt->format.colorspace; @@ -216,6 +269,8 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { + unsigned int min_width = RWPF_MIN_WIDTH; + unsigned int min_height = RWPF_MIN_HEIGHT; struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; @@ -244,21 +299,39 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* - * Restrict the crop rectangle coordinates to multiples of 2 to avoid - * shifting the color plane. + * For YUV formats, restrict the crop rectangle coordinates to multiples + * of 2 to avoid shifting the color plane. */ if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { sel->r.left = ALIGN(sel->r.left, 2); sel->r.top = ALIGN(sel->r.top, 2); sel->r.width = round_down(sel->r.width, 2); sel->r.height = round_down(sel->r.height, 2); + + /* + * The RPF doesn't enforces the alignment constraint on the sink + * pad format, which could have an odd size, possibly down to + * 1x1. In that case, the minimum width and height would be + * smaller than the sink pad format, leading to a negative upper + * bound in the left and top clamping. Clamp the minimum width + * and height to the format width and height to avoid this. + * + * In such a situation, odd values for the crop rectangle size + * would be accepted when clamping the width and height below. + * While that would create an invalid hardware configuration, + * the video device enforces proper alignment of the pixel + * format, and the mismatch will then result in link validation + * failure. Incorrect operation of the hardware is not possible. + */ + min_width = min(ALIGN(min_width, 2), format->width); + min_height = min(ALIGN(min_height, 2), format->height); } - sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); - sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); - sel->r.width = min_t(unsigned int, sel->r.width, + sel->r.left = clamp_t(int, sel->r.left, 0, format->width - min_width); + sel->r.top = clamp_t(int, sel->r.top, 0, format->height - min_height); + sel->r.width = clamp(sel->r.width, min_width, format->width - sel->r.left); - sel->r.height = min_t(unsigned int, sel->r.height, + sel->r.height = clamp(sel->r.height, min_height, format->height - sel->r.top); crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); @@ -284,6 +357,7 @@ static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { }; const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = { + .core = &vsp1_entity_core_ops, .pad = &vsp1_rwpf_pad_ops, }; @@ -311,6 +385,9 @@ static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) { + rwpf->entity.codes = rwpf_codes; + rwpf->entity.num_codes = ARRAY_SIZE(rwpf_codes); + v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index 5ac9f0a6fafc..89c1c8e8bb6d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -21,6 +21,9 @@ #define RWPF_PAD_SINK 0 #define RWPF_PAD_SOURCE 1 +#define RWPF_MIN_WIDTH 1 +#define RWPF_MIN_HEIGHT 1 + struct v4l2_ctrl; struct vsp1_dl_manager; struct vsp1_rwpf; @@ -36,9 +39,6 @@ struct vsp1_rwpf { struct vsp1_video *video; - unsigned int max_width; - unsigned int max_height; - struct v4l2_pix_format_mplane format; const struct vsp1_format_info *fmtinfo; unsigned int brx_input; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index bba2872afaf2..94149da0c900 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -7,8 +7,10 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/gfp.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> @@ -106,47 +108,35 @@ static const struct v4l2_ctrl_config sru_intensity_control = { * V4L2 Subdevice Operations */ -static int sru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, - ARRAY_SIZE(codes)); -} +static const unsigned int sru_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *format; - int ret = 0; + int ret; - state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which); - if (!state) - return -EINVAL; + ret = vsp1_subdev_enum_frame_size(subdev, sd_state, fse); + if (ret) + return ret; - format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + if (fse->pad == SRU_PAD_SOURCE) { + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; - mutex_lock(&sru->entity.lock); + state = vsp1_entity_get_state(&sru->entity, sd_state, + fse->which); + if (!state) + return -EINVAL; - if (fse->index || fse->code != format->code) { - ret = -EINVAL; - goto done; - } + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + + guard(mutex)(&sru->entity.lock); - if (fse->pad == SRU_PAD_SINK) { - fse->min_width = SRU_MIN_SIZE; - fse->max_width = SRU_MAX_SIZE; - fse->min_height = SRU_MIN_SIZE; - fse->max_height = SRU_MAX_SIZE; - } else { fse->min_width = format->width; fse->min_height = format->height; if (format->width <= SRU_MAX_SIZE / 2 && @@ -159,9 +149,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, } } -done: - mutex_unlock(&sru->entity.lock); - return ret; + return 0; } static void sru_try_format(struct vsp1_sru *sru, @@ -257,13 +245,14 @@ done: } static const struct v4l2_subdev_pad_ops sru_pad_ops = { - .enum_mbus_code = sru_enum_mbus_code, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = sru_set_format, }; static const struct v4l2_subdev_ops sru_ops = { + .core = &vsp1_entity_core_ops, .pad = &sru_pad_ops, }; @@ -370,6 +359,12 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) sru->entity.ops = &sru_entity_ops; sru->entity.type = VSP1_ENTITY_SRU; + sru->entity.codes = sru_codes; + sru->entity.num_codes = ARRAY_SIZE(sru_codes); + sru->entity.min_width = SRU_MIN_SIZE; + sru->entity.max_width = SRU_MAX_SIZE; + sru->entity.min_height = SRU_MIN_SIZE; + sru->entity.max_height = SRU_MAX_SIZE; ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops, MEDIA_ENT_F_PROC_VIDEO_SCALER); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 2db473b6f83c..dd4722315c56 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -7,8 +7,10 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/gfp.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> @@ -111,56 +113,42 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) * V4L2 Subdevice Pad Operations */ -static int uds_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, - ARRAY_SIZE(codes)); -} +static const unsigned int uds_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *format; - int ret = 0; + int ret; - state = vsp1_entity_get_state(&uds->entity, sd_state, fse->which); - if (!state) - return -EINVAL; + ret = vsp1_subdev_enum_frame_size(subdev, sd_state, fse); + if (ret) + return ret; - format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + if (fse->pad == UDS_PAD_SOURCE) { + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *format; - mutex_lock(&uds->entity.lock); + state = vsp1_entity_get_state(&uds->entity, sd_state, + fse->which); + if (!state) + return -EINVAL; - if (fse->index || fse->code != format->code) { - ret = -EINVAL; - goto done; - } + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + + guard(mutex)(&uds->entity.lock); - if (fse->pad == UDS_PAD_SINK) { - fse->min_width = UDS_MIN_SIZE; - fse->max_width = UDS_MAX_SIZE; - fse->min_height = UDS_MIN_SIZE; - fse->max_height = UDS_MAX_SIZE; - } else { uds_output_limits(format->width, &fse->min_width, &fse->max_width); uds_output_limits(format->height, &fse->min_height, &fse->max_height); } -done: - mutex_unlock(&uds->entity.lock); - return ret; + return 0; } static void uds_try_format(struct vsp1_uds *uds, @@ -244,7 +232,7 @@ done: */ static const struct v4l2_subdev_pad_ops uds_pad_ops = { - .enum_mbus_code = uds_enum_mbus_code, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = uds_set_format, @@ -410,6 +398,12 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) uds->entity.ops = &uds_entity_ops; uds->entity.type = VSP1_ENTITY_UDS; uds->entity.index = index; + uds->entity.codes = uds_codes; + uds->entity.num_codes = ARRAY_SIZE(uds_codes); + uds->entity.min_width = UDS_MIN_SIZE; + uds->entity.max_width = UDS_MAX_SIZE; + uds->entity.min_height = UDS_MIN_SIZE; + uds->entity.max_height = UDS_MAX_SIZE; sprintf(name, "uds.%u", index); ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index edaf28b544d2..3aefe5c9d421 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -53,34 +53,6 @@ static const unsigned int uif_codes[] = { MEDIA_BUS_FMT_AYUV8_1X32, }; -static int uif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, uif_codes, - ARRAY_SIZE(uif_codes)); -} - -static int uif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, - UIF_MIN_SIZE, - UIF_MIN_SIZE, UIF_MAX_SIZE, - UIF_MAX_SIZE); -} - -static int uif_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, uif_codes, - ARRAY_SIZE(uif_codes), - UIF_MIN_SIZE, UIF_MIN_SIZE, - UIF_MAX_SIZE, UIF_MAX_SIZE); -} - static int uif_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -171,10 +143,10 @@ done: */ static const struct v4l2_subdev_pad_ops uif_pad_ops = { - .enum_mbus_code = uif_enum_mbus_code, - .enum_frame_size = uif_enum_frame_size, + .enum_mbus_code = vsp1_subdev_enum_mbus_code, + .enum_frame_size = vsp1_subdev_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = uif_set_format, + .set_fmt = vsp1_subdev_set_pad_format, .get_selection = uif_get_selection, .set_selection = uif_set_selection, }; @@ -250,6 +222,12 @@ struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index) uif->entity.ops = &uif_entity_ops; uif->entity.type = VSP1_ENTITY_UIF; uif->entity.index = index; + uif->entity.codes = uif_codes; + uif->entity.num_codes = ARRAY_SIZE(uif_codes); + uif->entity.min_width = UIF_MIN_SIZE; + uif->entity.min_height = UIF_MIN_SIZE; + uif->entity.max_width = UIF_MAX_SIZE; + uif->entity.max_height = UIF_MAX_SIZE; /* The datasheet names the two UIF instances UIF4 and UIF5. */ sprintf(name, "uif.%u", index + 4); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 30662cfdf837..cd6c5592221b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -531,7 +531,7 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); - return wpf->flip.rotate ? 256 : wpf->max_width; + return wpf->flip.rotate ? 256 : wpf->entity.max_width; } static void wpf_partition(struct vsp1_entity *entity, @@ -567,12 +567,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (wpf == NULL) return ERR_PTR(-ENOMEM); + wpf->entity.min_width = RWPF_MIN_WIDTH; + wpf->entity.min_height = RWPF_MIN_HEIGHT; + if (vsp1->info->gen == 2) { - wpf->max_width = WPF_GEN2_MAX_WIDTH; - wpf->max_height = WPF_GEN2_MAX_HEIGHT; + wpf->entity.max_width = WPF_GEN2_MAX_WIDTH; + wpf->entity.max_height = WPF_GEN2_MAX_HEIGHT; } else { - wpf->max_width = WPF_GEN3_MAX_WIDTH; - wpf->max_height = WPF_GEN3_MAX_HEIGHT; + wpf->entity.max_width = WPF_GEN3_MAX_WIDTH; + wpf->entity.max_height = WPF_GEN3_MAX_HEIGHT; } wpf->entity.ops = &wpf_entity_ops; diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c index 1b81bcc067ef..9e67160a16e4 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c @@ -489,8 +489,8 @@ static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface, block = interface->index - RKCIF_MIPI_BASE; - if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) || - WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX)) + if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) || + WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs))) return RKCIF_REGISTER_NOTSUPPORTED; offset = rkcif->match_data->mipi->blocks[block].offset; @@ -510,9 +510,9 @@ static inline unsigned int rkcif_mipi_id_get_reg(struct rkcif_stream *stream, block = stream->interface->index - RKCIF_MIPI_BASE; id = stream->id; - if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) || - WARN_ON_ONCE(id > RKCIF_ID_MAX) || - WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX)) + if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) || + WARN_ON_ONCE(id >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id)) || + WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id[id]))) return RKCIF_REGISTER_NOTSUPPORTED; offset = rkcif->match_data->mipi->blocks[block].offset; diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c index 523103872b7a..414a9980cf2e 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c @@ -378,7 +378,8 @@ int rkcif_interface_register(struct rkcif_device *rkcif, snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d", interface->index - RKCIF_MIPI_BASE); - pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads); if (ret) diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c index e00010a91e8b..3130d420ad55 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c @@ -106,42 +106,42 @@ static int rkcif_stream_init_buffers(struct rkcif_stream *stream) { struct v4l2_pix_format_mplane *pix = &stream->pix; - stream->buffers[0] = rkcif_stream_pop_buffer(stream); - if (!stream->buffers[0]) - goto err_buff_0; - - stream->buffers[1] = rkcif_stream_pop_buffer(stream); - if (!stream->buffers[1]) - goto err_buff_1; - - if (stream->queue_buffer) { - stream->queue_buffer(stream, 0); - stream->queue_buffer(stream, 1); - } - stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage; stream->dummy.vaddr = dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size, &stream->dummy.buffer.buff_addr[0], GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); if (!stream->dummy.vaddr) - goto err_dummy; + return -ENOMEM; for (unsigned int i = 1; i < pix->num_planes; i++) stream->dummy.buffer.buff_addr[i] = stream->dummy.buffer.buff_addr[i - 1] + pix->plane_fmt[i - 1].bytesperline * pix->height; - return 0; + stream->buffers[0] = rkcif_stream_pop_buffer(stream); + if (!stream->buffers[0]) + goto err_dummy_free; + + stream->buffers[1] = rkcif_stream_pop_buffer(stream); + if (!stream->buffers[1]) { + stream->buffers[1] = &stream->dummy.buffer; + stream->buffers[1]->is_dummy = true; + } -err_dummy: - rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED); - stream->buffers[1] = NULL; + if (stream->queue_buffer) { + stream->queue_buffer(stream, 0); + stream->queue_buffer(stream, 1); + } + + return 0; -err_buff_1: - rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED); - stream->buffers[0] = NULL; -err_buff_0: +err_dummy_free: + dma_free_attrs(stream->rkcif->dev, stream->dummy.size, + stream->dummy.vaddr, + stream->dummy.buffer.buff_addr[0], + DMA_ATTR_NO_KERNEL_MAPPING); + stream->dummy.vaddr = NULL; return -EINVAL; } @@ -555,7 +555,7 @@ int rkcif_stream_register(struct rkcif_device *rkcif, vdev->vfl_dir = VFL_DIR_RX; video_set_drvdata(vdev, stream); - stream->pad.flags = MEDIA_PAD_FL_SINK; + stream->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; stream->pix.height = CIF_MIN_HEIGHT; stream->pix.width = CIF_MIN_WIDTH; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 867cdddf9f89..20d88865081c 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -1380,6 +1380,9 @@ static int rkisp1_enum_framesizes(struct file *file, void *fh, }; struct rkisp1_capture *cap = video_drvdata(file); + if (!rkisp1_find_fmt_cfg(cap, fsize->pixel_format)) + return -EINVAL; + if (fsize->index != 0) return -EINVAL; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index 2c9edd0a559b..57e573949e54 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -1020,11 +1020,15 @@ err_sclk: static int fimc_runtime_resume(struct device *dev) { struct fimc_dev *fimc = dev_get_drvdata(dev); + int ret; dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); /* Enable clocks and perform basic initialization */ - clk_enable(fimc->clock[CLK_GATE]); + ret = clk_enable(fimc->clock[CLK_GATE]); + if (ret) + return ret; + fimc_hw_reset(fimc); /* Resume the capture or mem-to-mem device */ diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index 0827fdaf455a..b7d9bfedd590 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -200,22 +200,19 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, static int fimc_is_register_subdevs(struct fimc_is *is) { - struct device_node *i2c_bus, *child; int ret, index = 0; ret = fimc_isp_subdev_create(&is->isp); if (ret < 0) return ret; - for_each_compatible_node(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") { - for_each_available_child_of_node(i2c_bus, child) { + for_each_compatible_node_scoped(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") { + for_each_available_child_of_node_scoped(i2c_bus, child) { ret = fimc_is_parse_sensor_config(is, index, child); - if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { - of_node_put(child); - of_node_put(i2c_bus); + if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) return ret; - } + index++; } } diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 13762861b769..e5663fbe6422 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -15,6 +15,7 @@ #include <linux/completion.h> #include <linux/delay.h> #include <linux/dmaengine.h> +#include <linux/genalloc.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -113,6 +114,9 @@ struct dcmi_buf { struct vb2_v4l2_buffer vb; bool prepared; struct sg_table sgt; + struct sg_table sgt_mdma; + struct dma_async_tx_descriptor *dma_desc; + struct dma_async_tx_descriptor *mdma_desc; size_t size; struct list_head list; }; @@ -158,14 +162,20 @@ struct stm32_dcmi { struct dma_chan *dma_chan; dma_cookie_t dma_cookie; u32 dma_max_burst; + + /* Elements for the MDMA - DMA chaining */ + struct gen_pool *sram_pool; + struct dma_chan *mdma_chan; + void *sram_buf; + u32 sram_buf_size; + dma_addr_t sram_dma_buf; + dma_cookie_t mdma_cookie; + u32 misr; int errors_count; int overrun_count; int buffers_count; - /* Ensure DMA operations atomicity */ - struct mutex dma_lock; - struct media_device mdev; struct media_pad vid_cap_pad; struct media_pipeline pipeline; @@ -229,127 +239,42 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) { struct dcmi_buf *buf; - spin_lock_irq(&dcmi->irqlock); - - if (dcmi->state != RUNNING) { - spin_unlock_irq(&dcmi->irqlock); - return -EINVAL; - } + /* Nothing to do if we are not running */ + if (dcmi->state != RUNNING) + return 0; /* Restart a new DMA transfer with next buffer */ if (list_empty(&dcmi->buffers)) { dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); dcmi->state = WAIT_FOR_BUFFER; - spin_unlock_irq(&dcmi->irqlock); return 0; } buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); dcmi->active = buf; - spin_unlock_irq(&dcmi->irqlock); - return dcmi_start_capture(dcmi, buf); } -static void dcmi_dma_callback(void *param) -{ - struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; - struct dma_tx_state state; - enum dma_status status; - struct dcmi_buf *buf = dcmi->active; - - spin_lock_irq(&dcmi->irqlock); - - /* Check DMA status */ - status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); - - switch (status) { - case DMA_IN_PROGRESS: - dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__); - break; - case DMA_PAUSED: - dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__); - break; - case DMA_ERROR: - dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__); - - /* Return buffer to V4L2 in error state */ - dcmi_buffer_done(dcmi, buf, 0, -EIO); - break; - case DMA_COMPLETE: - dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); - - /* Return buffer to V4L2 */ - dcmi_buffer_done(dcmi, buf, buf->size, 0); - - spin_unlock_irq(&dcmi->irqlock); - - /* Restart capture */ - if (dcmi_restart_capture(dcmi)) - dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", - __func__); - return; - default: - dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); - break; - } - - spin_unlock_irq(&dcmi->irqlock); -} - static int dcmi_start_dma(struct stm32_dcmi *dcmi, struct dcmi_buf *buf) { - struct dma_async_tx_descriptor *desc = NULL; - struct dma_slave_config config; - int ret; - - memset(&config, 0, sizeof(config)); - - config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR; - config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_maxburst = 4; - - /* Configure DMA channel */ - ret = dmaengine_slave_config(dcmi->dma_chan, &config); - if (ret < 0) { - dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n", - __func__, ret); - return ret; - } - - /* - * Avoid call of dmaengine_terminate_sync() between - * dmaengine_prep_slave_single() and dmaengine_submit() - * by locking the whole DMA submission sequence - */ - mutex_lock(&dcmi->dma_lock); - - /* Prepare a DMA transaction */ - desc = dmaengine_prep_slave_sg(dcmi->dma_chan, buf->sgt.sgl, buf->sgt.nents, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!desc) { - dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__); - mutex_unlock(&dcmi->dma_lock); - return -EINVAL; + /* Push current DMA transaction in the pending queue */ + if (dcmi->mdma_chan) { + dcmi->mdma_cookie = dmaengine_submit(buf->mdma_desc); + if (dma_submit_error(dcmi->mdma_cookie)) { + dev_err(dcmi->dev, "%s: MDMA submission failed\n", __func__); + return -ENXIO; + } } - /* Set completion callback routine for notification */ - desc->callback = dcmi_dma_callback; - desc->callback_param = dcmi; - - /* Push current DMA transaction in the pending queue */ - dcmi->dma_cookie = dmaengine_submit(desc); + dcmi->dma_cookie = dmaengine_submit(buf->dma_desc); if (dma_submit_error(dcmi->dma_cookie)) { dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__); - mutex_unlock(&dcmi->dma_lock); return -ENXIO; } - mutex_unlock(&dcmi->dma_lock); - + if (dcmi->mdma_chan) + dma_async_issue_pending(dcmi->mdma_chan); dma_async_issue_pending(dcmi->dma_chan); return 0; @@ -396,9 +321,11 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi) reg_set(dcmi->regs, DCMI_CR, CR_CROP); } -static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) +static void dcmi_process_frame(struct stm32_dcmi *dcmi) { - struct dma_tx_state state; + struct dma_tx_state state, state_dma; + size_t bytes_used; + enum dma_status status; struct dcmi_buf *buf = dcmi->active; @@ -406,38 +333,48 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) return; /* - * Because of variable JPEG buffer size sent by sensor, - * DMA transfer never completes due to transfer size never reached. - * In order to ensure that all the JPEG data are transferred - * in active buffer memory, DMA is drained. - * Then DMA tx status gives the amount of data transferred - * to memory, which is then returned to V4L2 through the active - * buffer payload. + * Pause the DMA transfer, leading to trigger of the MDMA channel read while + * keeping a valid residue value on the dma channel */ + if (dcmi->mdma_chan) { + spin_unlock_irq(&dcmi->irqlock); + dmaengine_pause(dcmi->dma_chan); + spin_lock_irq(&dcmi->irqlock); + + do { + status = dmaengine_tx_status(dcmi->mdma_chan, dcmi->mdma_cookie, &state); + cpu_relax(); + } while (status != DMA_ERROR && status != DMA_COMPLETE && + state.residue && state.in_flight_bytes); + } else { + status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); + } - /* Drain DMA */ - dmaengine_synchronize(dcmi->dma_chan); - - /* Get DMA residue to get JPEG size */ - status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); if (status != DMA_ERROR && state.residue < buf->size) { - /* Return JPEG buffer to V4L2 with received JPEG buffer size */ - dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); + bytes_used = buf->size - state.residue; + + if (state.residue && dcmi->mdma_chan) { + dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state_dma); + /* Getting full size residue equal to no residue */ + if (state_dma.residue == dcmi->sram_buf_size) + state_dma.residue = 0; + bytes_used -= state_dma.residue; + } + + /* Return buffer to V4L2 with received data size */ + dcmi_buffer_done(dcmi, buf, bytes_used, 0); } else { dcmi->errors_count++; - dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n", - __func__); - /* Return JPEG buffer to V4L2 in ERROR state */ + dev_err(dcmi->dev, "%s: DMA error. status: 0x%x, residue: %d\n", + __func__, status, state.residue); + /* Return buffer to V4L2 in ERROR state */ dcmi_buffer_done(dcmi, buf, 0, -EIO); } /* Abort DMA operation */ - dmaengine_terminate_sync(dcmi->dma_chan); - - /* Restart capture */ - if (dcmi_restart_capture(dcmi)) - dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n", - __func__); + dmaengine_terminate_async(dcmi->dma_chan); + if (dcmi->mdma_chan) + dmaengine_terminate_async(dcmi->mdma_chan); } static irqreturn_t dcmi_irq_thread(int irq, void *arg) @@ -447,19 +384,31 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) spin_lock_irq(&dcmi->irqlock); if (dcmi->misr & IT_OVR) { + /* Disable capture */ + reg_clear(dcmi->regs, DCMI_CR, CR_CAPTURE); + dcmi->overrun_count++; + if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD) dcmi->errors_count++; + + dmaengine_terminate_async(dcmi->dma_chan); + if (dcmi->mdma_chan) + dmaengine_terminate_async(dcmi->mdma_chan); + + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__); + spin_unlock_irq(&dcmi->irqlock); + return IRQ_HANDLED; } + if (dcmi->misr & IT_ERR) dcmi->errors_count++; - if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG && - dcmi->misr & IT_FRAME) { - /* JPEG received */ - spin_unlock_irq(&dcmi->irqlock); - dcmi_process_jpeg(dcmi); - return IRQ_HANDLED; + if (dcmi->misr & IT_FRAME) { + dcmi_process_frame(dcmi); + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__); } spin_unlock_irq(&dcmi->irqlock); @@ -539,31 +488,134 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) vb2_set_plane_payload(vb, 0, size); if (!buf->prepared) { + u32 max_size = dcmi->dma_max_burst; + unsigned int dma_nents; + /* Get memory addresses */ buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); - if (buf->size > dcmi->dma_max_burst) - num_sgs = DIV_ROUND_UP(buf->size, dcmi->dma_max_burst); + if (dcmi->mdma_chan) + max_size = dcmi->sram_buf_size / 2; + + if (buf->size > max_size) + num_sgs = DIV_ROUND_UP(buf->size, max_size); + + /* + * If we use MDMA chaining, DMA is used in cyclic mode and the scatterlist + * maximum size is thus 2 + */ + dma_nents = num_sgs; + if (dcmi->mdma_chan) + dma_nents = min_t(unsigned int, num_sgs, 2); - ret = sg_alloc_table(&buf->sgt, num_sgs, GFP_ATOMIC); + ret = sg_alloc_table(&buf->sgt, dma_nents, GFP_ATOMIC); if (ret) { - dev_err(dcmi->dev, "sg table alloc failed\n"); + dev_err(dcmi->dev, "sg table alloc failed for DMA\n"); return ret; } + if (dcmi->mdma_chan) { + ret = sg_alloc_table(&buf->sgt_mdma, num_sgs, GFP_ATOMIC); + if (ret) { + dev_err(dcmi->dev, "sg table alloc failed for MDMA\n"); + return ret; + } + } + dma_buf = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n", vb->index, &dma_buf, buf->size); - for_each_sg(buf->sgt.sgl, sg, num_sgs, i) { - size_t bytes = min_t(size_t, size, dcmi->dma_max_burst); + for_each_sg(buf->sgt.sgl, sg, dma_nents, i) { + size_t bytes = min_t(size_t, size, max_size); + + if (!dcmi->mdma_chan) { + sg_dma_address(sg) = dma_buf; + dma_buf += bytes; + } else { + /* Targets the beginning = first half of the sram_buf */ + sg_dma_address(sg) = dcmi->sram_dma_buf; + /* + * Targets the second half of the sram_bubf + * for odd indexes of the item of the sg_list + */ + if (i & 1) + sg->dma_address += (dcmi->sram_buf_size / 2); + } + /* + * In case of DMA-MDMA chaining, since the DMA is working in cyclic mode, + * we need to provide two linked-list node of same size in order to have + * a correct residue value computed. + */ + if (!dcmi->mdma_chan) + sg_dma_len(sg) = bytes; + else + sg_dma_len(sg) = dcmi->sram_buf_size / 2; - sg_dma_address(sg) = dma_buf; - sg_dma_len(sg) = bytes; - dma_buf += bytes; size -= bytes; } + /* Prepare a DMA transaction */ + buf->dma_desc = dmaengine_prep_slave_sg(dcmi->dma_chan, + buf->sgt.sgl, + buf->sgt.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!buf->dma_desc) { + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__); + if (dcmi->mdma_chan) + sg_free_table(&buf->sgt_mdma); + sg_free_table(&buf->sgt); + return -EIO; + } + + /* Mark the descriptor as reusable to avoid having to prepare it */ + ret = dmaengine_desc_set_reuse(buf->dma_desc); + if (ret) { + dev_err(dcmi->dev, "%s: DMA dmaengine_desc_set_reuse failed\n", __func__); + dmaengine_desc_free(buf->dma_desc); + if (dcmi->mdma_chan) + sg_free_table(&buf->sgt_mdma); + sg_free_table(&buf->sgt); + return -EIO; + } + + if (dcmi->mdma_chan) { + size = dcmi->fmt.fmt.pix.sizeimage; + for_each_sg(buf->sgt_mdma.sgl, sg, num_sgs, i) { + size_t bytes = min_t(size_t, size, max_size); + + sg_dma_address(sg) = dma_buf; + sg_dma_len(sg) = bytes; + dma_buf += bytes; + size -= bytes; + } + + buf->mdma_desc = dmaengine_prep_slave_sg(dcmi->mdma_chan, buf->sgt_mdma.sgl, + buf->sgt_mdma.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!buf->mdma_desc) { + dev_err(dcmi->dev, "%s: failed dmaengine_prep_slave_sg for MDMA\n", + __func__); + dmaengine_desc_free(buf->dma_desc); + sg_free_table(&buf->sgt_mdma); + sg_free_table(&buf->sgt); + return -EIO; + } + + ret = dmaengine_desc_set_reuse(buf->mdma_desc); + if (ret) { + dev_err(dcmi->dev, "%s: failed to set reuse for mdma desc\n", + __func__); + dmaengine_desc_free(buf->mdma_desc); + dmaengine_desc_free(buf->dma_desc); + sg_free_table(&buf->sgt_mdma); + sg_free_table(&buf->sgt); + return -EIO; + } + } + buf->prepared = true; vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); @@ -572,6 +624,31 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) return 0; } +static void dcmi_buf_cleanup(struct vb2_buffer *vb) +{ + struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); + int ret; + + if (!buf->prepared) + return; + + if (dcmi->mdma_chan) { + ret = dmaengine_desc_free(buf->mdma_desc); + if (ret) + dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n", + __func__, ret); + sg_free_table(&buf->sgt_mdma); + } + + ret = dmaengine_desc_free(buf->dma_desc); + if (ret) + dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n", + __func__, ret); + sg_free_table(&buf->sgt); +} + static void dcmi_buf_queue(struct vb2_buffer *vb) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); @@ -590,11 +667,9 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); - spin_unlock_irq(&dcmi->irqlock); if (dcmi_start_capture(dcmi, buf)) dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", __func__); - return; } spin_unlock_irq(&dcmi->irqlock); @@ -773,10 +848,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (dcmi->do_crop) dcmi_set_crop(dcmi); - /* Enable jpeg capture */ - if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) - reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ - /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); @@ -803,19 +874,18 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); - spin_unlock_irq(&dcmi->irqlock); ret = dcmi_start_capture(dcmi, buf); if (ret) { dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); + spin_unlock_irq(&dcmi->irqlock); goto err_pipeline_stop; } /* Enable interruptions */ - if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) - reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); - else - reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR); + reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); + + spin_unlock_irq(&dcmi->irqlock); return 0; @@ -876,9 +946,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) spin_unlock_irq(&dcmi->irqlock); /* Stop all pending DMA operations */ - mutex_lock(&dcmi->dma_lock); dmaengine_terminate_sync(dcmi->dma_chan); - mutex_unlock(&dcmi->dma_lock); + if (dcmi->mdma_chan) + dmaengine_terminate_sync(dcmi->mdma_chan); pm_runtime_put(dcmi->dev); @@ -895,6 +965,7 @@ static const struct vb2_ops dcmi_video_qops = { .queue_setup = dcmi_queue_setup, .buf_init = dcmi_buf_init, .buf_prepare = dcmi_buf_prepare, + .buf_cleanup = dcmi_buf_cleanup, .buf_queue = dcmi_buf_queue, .start_streaming = dcmi_start_streaming, .stop_streaming = dcmi_stop_streaming, @@ -1886,8 +1957,9 @@ static int dcmi_probe(struct platform_device *pdev) struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; struct stm32_dcmi *dcmi; struct vb2_queue *q; - struct dma_chan *chan; + struct dma_chan *chan, *mdma_chan; struct dma_slave_caps caps; + struct dma_slave_config dma_config, mdma_config; struct clk *mclk; int ret = 0; @@ -1949,14 +2021,73 @@ static int dcmi_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(chan), "Failed to request DMA channel\n"); + mdma_chan = dma_request_chan(&pdev->dev, "mdma_tx"); + if (IS_ERR(mdma_chan)) { + ret = PTR_ERR(mdma_chan); + if (ret != -ENODEV) + return dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n"); + mdma_chan = NULL; + } + + /* Configure the DMA channel */ + memset(&dma_config, 0, sizeof(dma_config)); + + dma_config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + if (mdma_chan) + dma_config.peripheral_size = 1; /* Indicates chaining */ + + /* Configure DMA channel */ + ret = dmaengine_slave_config(chan, &dma_config); + if (ret < 0) { + dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n", + __func__, ret); + goto err_dma_slave_config; + } + + /* If we want MDMA, we also need a sram pool */ + if (mdma_chan) { + dcmi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (!dcmi->sram_pool) { + dev_info(&pdev->dev, "No SRAM pool, can't use MDMA chaining\n"); + goto err_dma_slave_config; + } + + dev_info(&pdev->dev, "SRAM pool: %zu KiB for DMA/MDMA chaining\n", + gen_pool_size(dcmi->sram_pool) / 1024); + + dcmi->sram_buf_size = gen_pool_size(dcmi->sram_pool); + dcmi->sram_buf = gen_pool_dma_zalloc(dcmi->sram_pool, dcmi->sram_buf_size, + &dcmi->sram_dma_buf); + if (!dcmi->sram_buf) { + dev_err(dcmi->dev, "Failed to allocate from SRAM\n"); + goto err_dma_slave_config; + } + + /* Configure the MDMA Channel */ + memset(&mdma_config, 0, sizeof(mdma_config)); + mdma_config.direction = DMA_DEV_TO_MEM; + mdma_config.src_addr = dcmi->sram_dma_buf; + mdma_config.peripheral_size = dma_config.peripheral_size; + mdma_config.peripheral_config = dma_config.peripheral_config; + ret = dmaengine_slave_config(mdma_chan, &mdma_config); + if (ret < 0) { + dev_err(dcmi->dev, "%s: MDMA channel config failed (%d)\n", + __func__, ret); + goto err_mdma_slave_config; + } + } + dcmi->dma_max_burst = UINT_MAX; - ret = dma_get_slave_caps(chan, &caps); - if (!ret && caps.max_sg_burst) - dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES; + /* In case of using DMA-MDMA chaining we consider the maximum infini */ + if (!mdma_chan) { + ret = dma_get_slave_caps(chan, &caps); + if (!ret && caps.max_sg_burst) + dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES; + } spin_lock_init(&dcmi->irqlock); mutex_init(&dcmi->lock); - mutex_init(&dcmi->dma_lock); init_completion(&dcmi->complete); INIT_LIST_HEAD(&dcmi->buffers); @@ -1964,6 +2095,7 @@ static int dcmi_probe(struct platform_device *pdev) dcmi->mclk = mclk; dcmi->state = STOPPED; dcmi->dma_chan = chan; + dcmi->mdma_chan = mdma_chan; q = &dcmi->queue; @@ -2072,7 +2204,13 @@ err_device_unregister: v4l2_device_unregister(&dcmi->v4l2_dev); err_media_device_cleanup: media_device_cleanup(&dcmi->mdev); +err_mdma_slave_config: + if (dcmi->mdma_chan) + gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size); +err_dma_slave_config: dma_release_channel(dcmi->dma_chan); + if (dcmi->mdma_chan) + dma_release_channel(mdma_chan); return ret; } @@ -2090,9 +2228,13 @@ static void dcmi_remove(struct platform_device *pdev) media_device_cleanup(&dcmi->mdev); dma_release_channel(dcmi->dma_chan); + if (dcmi->mdma_chan) { + gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size); + dma_release_channel(dcmi->mdma_chan); + } } -static __maybe_unused int dcmi_runtime_suspend(struct device *dev) +static int dcmi_runtime_suspend(struct device *dev) { struct stm32_dcmi *dcmi = dev_get_drvdata(dev); @@ -2101,7 +2243,7 @@ static __maybe_unused int dcmi_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int dcmi_runtime_resume(struct device *dev) +static int dcmi_runtime_resume(struct device *dev) { struct stm32_dcmi *dcmi = dev_get_drvdata(dev); int ret; @@ -2113,7 +2255,7 @@ static __maybe_unused int dcmi_runtime_resume(struct device *dev) return ret; } -static __maybe_unused int dcmi_suspend(struct device *dev) +static int dcmi_suspend(struct device *dev) { /* disable clock */ pm_runtime_force_suspend(dev); @@ -2124,7 +2266,7 @@ static __maybe_unused int dcmi_suspend(struct device *dev) return 0; } -static __maybe_unused int dcmi_resume(struct device *dev) +static int dcmi_resume(struct device *dev) { /* restore pinctl default state */ pinctrl_pm_select_default_state(dev); @@ -2136,9 +2278,8 @@ static __maybe_unused int dcmi_resume(struct device *dev) } static const struct dev_pm_ops dcmi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) - SET_RUNTIME_PM_OPS(dcmi_runtime_suspend, - dcmi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) + RUNTIME_PM_OPS(dcmi_runtime_suspend, dcmi_runtime_resume, NULL) }; static struct platform_driver stm32_dcmi_driver = { @@ -2147,7 +2288,7 @@ static struct platform_driver stm32_dcmi_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), - .pm = &dcmi_pm_ops, + .pm = pm_ptr(&dcmi_pm_ops), }, }; diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig index bf2ac092fbb3..b109de2c8111 100644 --- a/drivers/media/platform/synopsys/Kconfig +++ b/drivers/media/platform/synopsys/Kconfig @@ -4,6 +4,7 @@ source "drivers/media/platform/synopsys/hdmirx/Kconfig" config VIDEO_DW_MIPI_CSI2RX tristate "Synopsys DesignWare MIPI CSI-2 Receiver" + depends on ARCH_ROCKCHIP || COMPILE_TEST depends on VIDEO_DEV depends on V4L_PLATFORM_DRIVERS depends on PM && COMMON_CLK diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 4d96171a650b..02eb4a6cafad 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -7,6 +7,7 @@ * Copyright (C) 2026 Collabora, Ltd. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> @@ -24,15 +25,6 @@ #include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> -#define DW_MIPI_CSI2RX_N_LANES 0x04 -#define DW_MIPI_CSI2RX_RESETN 0x10 -#define DW_MIPI_CSI2RX_PHY_STATE 0x14 -#define DW_MIPI_CSI2RX_ERR1 0x20 -#define DW_MIPI_CSI2RX_ERR2 0x24 -#define DW_MIPI_CSI2RX_MSK1 0x28 -#define DW_MIPI_CSI2RX_MSK2 0x2c -#define DW_MIPI_CSI2RX_CONTROL 0x40 - #define SW_CPHY_EN(x) ((x) << 0) #define SW_DSI_EN(x) ((x) << 4) #define SW_DATATYPE_FS(x) ((x) << 8) @@ -40,7 +32,47 @@ #define SW_DATATYPE_LS(x) ((x) << 20) #define SW_DATATYPE_LE(x) ((x) << 26) -#define DW_MIPI_CSI2RX_CLKS_MAX 1 +#define DW_REG_EXIST BIT(31) +#define DW_REG(x) (DW_REG_EXIST | (x)) + +#define DPHY_TEST_CTRL0_TEST_CLR BIT(0) + +#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x)) +#define IPI_VCID_VC_0_1(x) FIELD_PREP(GENMASK(3, 2), (x)) +#define IPI_VCID_VC_2 BIT(4) + +#define IPI_DATA_TYPE_DT(x) FIELD_PREP(GENMASK(5, 0), (x)) +#define IPI_DATA_TYPE_EMB_DATA_EN BIT(8) + +#define IPI_MODE_CONTROLLER BIT(1) +#define IPI_MODE_COLOR_MODE16 BIT(8) +#define IPI_MODE_CUT_THROUGH BIT(16) +#define IPI_MODE_ENABLE BIT(24) + +#define IPI_MEM_FLUSH_AUTO BIT(8) + +enum dw_mipi_csi2rx_regs_index { + DW_MIPI_CSI2RX_N_LANES, + DW_MIPI_CSI2RX_RESETN, + DW_MIPI_CSI2RX_PHY_STATE, + DW_MIPI_CSI2RX_ERR1, + DW_MIPI_CSI2RX_ERR2, + DW_MIPI_CSI2RX_MSK1, + DW_MIPI_CSI2RX_MSK2, + DW_MIPI_CSI2RX_CONTROL, + /* imx93 (v150) new register */ + DW_MIPI_CSI2RX_DPHY_RSTZ, + DW_MIPI_CSI2RX_PHY_TST_CTRL0, + DW_MIPI_CSI2RX_PHY_TST_CTRL1, + DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, + DW_MIPI_CSI2RX_IPI_DATATYPE, + DW_MIPI_CSI2RX_IPI_MEM_FLUSH, + DW_MIPI_CSI2RX_IPI_MODE, + DW_MIPI_CSI2RX_IPI_SOFTRSTN, + DW_MIPI_CSI2RX_IPI_VCID, + + DW_MIPI_CSI2RX_MAX, +}; enum { DW_MIPI_CSI2RX_PAD_SINK, @@ -48,6 +80,15 @@ enum { DW_MIPI_CSI2RX_PAD_MAX, }; +struct dw_mipi_csi2rx_device; + +struct dw_mipi_csi2rx_drvdata { + const u32 *regs; + void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2); + void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2); + void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2); +}; + struct dw_mipi_csi2rx_format { u32 code; u8 depth; @@ -72,6 +113,38 @@ struct dw_mipi_csi2rx_device { enum v4l2_mbus_type bus_type; u32 lanes_num; + + const struct dw_mipi_csi2rx_drvdata *drvdata; +}; + +static const u32 rk3568_regs[DW_MIPI_CSI2RX_MAX] = { + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4), + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x10), + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x14), + [DW_MIPI_CSI2RX_ERR1] = DW_REG(0x20), + [DW_MIPI_CSI2RX_ERR2] = DW_REG(0x24), + [DW_MIPI_CSI2RX_MSK1] = DW_REG(0x28), + [DW_MIPI_CSI2RX_MSK2] = DW_REG(0x2c), + [DW_MIPI_CSI2RX_CONTROL] = DW_REG(0x40), +}; + +static const struct dw_mipi_csi2rx_drvdata rk3568_drvdata = { + .regs = rk3568_regs, +}; + +static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4), + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8), + [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), + [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), + [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), + [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80), + [DW_MIPI_CSI2RX_IPI_VCID] = DW_REG(0x84), + [DW_MIPI_CSI2RX_IPI_DATATYPE] = DW_REG(0x88), + [DW_MIPI_CSI2RX_IPI_MEM_FLUSH] = DW_REG(0x8c), + [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0), }; static const struct v4l2_mbus_framefmt default_format = { @@ -186,16 +259,50 @@ static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) return container_of(sd, struct dw_mipi_csi2rx_device, sd); } +static bool dw_mipi_csi2rx_has_reg(struct dw_mipi_csi2rx_device *csi2, + enum dw_mipi_csi2rx_regs_index index) +{ + if (index < DW_MIPI_CSI2RX_MAX && + (csi2->drvdata->regs[index] & DW_REG_EXIST)) + return true; + + return false; +} + +static void __iomem * +dw_mipi_csi2rx_get_regaddr(struct dw_mipi_csi2rx_device *csi2, + enum dw_mipi_csi2rx_regs_index index) +{ + u32 off = (~DW_REG_EXIST) & csi2->drvdata->regs[index]; + + return csi2->base_addr + off; +} + static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2, - unsigned int addr, u32 val) + enum dw_mipi_csi2rx_regs_index index, + u32 val) { - writel(val, csi2->base_addr + addr); + if (!dw_mipi_csi2rx_has_reg(csi2, index)) { + dev_err_once(csi2->dev, + "write to non-existent register index: %d\n", + index); + return; + } + + writel(val, dw_mipi_csi2rx_get_regaddr(csi2, index)); } static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2, - unsigned int addr) + enum dw_mipi_csi2rx_regs_index index) { - return readl(csi2->base_addr + addr); + if (!dw_mipi_csi2rx_has_reg(csi2, index)) { + dev_err_once(csi2->dev, + "read non-existent register index: %d\n", index); + /* return 0 for non-existent registers */ + return 0; + } + + return readl(dw_mipi_csi2rx_get_regaddr(csi2, index)); } static const struct dw_mipi_csi2rx_format * @@ -260,14 +367,32 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2) return -EINVAL; } + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0); + + if (csi2->drvdata->dphy_assert_reset) + csi2->drvdata->dphy_assert_reset(csi2); + control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) | SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03); dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1); - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control); + + if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_CONTROL)) + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control); + + ret = phy_power_on(csi2->phy); + if (ret) + return ret; + + if (csi2->drvdata->dphy_deassert_reset) + csi2->drvdata->dphy_deassert_reset(csi2); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1); - return phy_power_on(csi2->phy); + if (csi2->drvdata->ipi_enable) + csi2->drvdata->ipi_enable(csi2); + + return 0; } static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2) @@ -275,8 +400,12 @@ static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2) phy_power_off(csi2->phy); dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0); - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0); - dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0); + + if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK1)) + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0); + + if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK2)) + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0); } static const struct media_entity_operations dw_mipi_csi2rx_media_ops = { @@ -431,10 +560,31 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, return ret; } +static int +dw_mipi_csi2rx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_unique(&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]); + if (IS_ERR(remote_pad)) { + dev_err(csi2->dev, "can't get remote source pad\n"); + return PTR_ERR(remote_pad); + } + + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + return v4l2_subdev_call(remote_sd, pad, get_frame_desc, + remote_pad->index, fd); +} + static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = { .enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = dw_mipi_csi2rx_set_fmt, + .get_frame_desc = dw_mipi_csi2rx_get_frame_desc, .set_routing = dw_mipi_csi2rx_set_routing, .enable_streams = dw_mipi_csi2rx_enable_streams, .disable_streams = dw_mipi_csi2rx_disable_streams, @@ -605,9 +755,93 @@ static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2) v4l2_async_nf_cleanup(&csi2->notifier); } +static void imx93_csi2rx_dphy_assert_reset(struct dw_mipi_csi2rx_device *csi2) +{ + u32 val; + + /* Release Synopsys DPHY test codes from reset */ + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0); + + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0); + val &= ~DPHY_TEST_CTRL0_TEST_CLR; + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val); + + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0); + /* Wait for at least 15ns */ + ndelay(15); + val |= DPHY_TEST_CTRL0_TEST_CLR; + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val); +} + +static void imx93_csi2rx_dphy_deassert_reset(struct dw_mipi_csi2rx_device *csi2) +{ + /* Release PHY from reset */ + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0x1); + /* + * ndelay() is not necessary have MMIO operation, need dummy read to + * ensure that the write operation above reaches its target. + */ + dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ); + ndelay(5); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0x1); + + dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ); + ndelay(5); +} + +static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2) +{ + int dt = csi2->formats->csi_dt; + u32 val; + + /* Do IPI soft reset */ + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x0); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x1); + + /* Select virtual channel and data type to be processed by IPI */ + val = IPI_DATA_TYPE_DT(dt); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_DATATYPE, val); + + /* Set virtual channel 0 as default */ + val = IPI_VCID_VC(0); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_VCID, val); + + /* + * Select IPI camera timing mode and allow the pixel stream + * to be non-continuous when pixel interface FIFO is empty + */ + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE); + val &= ~IPI_MODE_CONTROLLER; + val &= ~IPI_MODE_COLOR_MODE16; + val |= IPI_MODE_CUT_THROUGH; + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val); + + /* Memory is automatically flushed at each Frame Start */ + val = IPI_MEM_FLUSH_AUTO; + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, val); + + /* Enable IPI */ + val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE); + val |= IPI_MODE_ENABLE; + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val); +} + +static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { + .regs = imx93_regs, + .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, + .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, + .ipi_enable = imx93_csi2rx_dphy_ipi_enable, +}; + static const struct of_device_id dw_mipi_csi2rx_of_match[] = { { + .compatible = "fsl,imx93-mipi-csi2", + .data = &imx93_drvdata, + }, + { .compatible = "rockchip,rk3568-mipi-csi2", + .data = &rk3568_drvdata, }, {} }; @@ -629,8 +863,13 @@ static int dw_mipi_csi2rx_probe(struct platform_device *pdev) if (IS_ERR(csi2->base_addr)) return PTR_ERR(csi2->base_addr); + csi2->drvdata = device_get_match_data(dev); + if (!csi2->drvdata) + return dev_err_probe(dev, -EINVAL, + "failed to get driver data\n"); + ret = devm_clk_bulk_get_all(dev, &csi2->clks); - if (ret != DW_MIPI_CSI2RX_CLKS_MAX) + if (ret < 0) return dev_err_probe(dev, -ENODEV, "failed to get clocks\n"); csi2->clks_num = ret; @@ -639,7 +878,7 @@ static int dw_mipi_csi2rx_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(csi2->phy), "failed to get MIPI CSI-2 PHY\n"); - csi2->reset = devm_reset_control_get_exclusive(dev, NULL); + csi2->reset = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(csi2->reset)) return dev_err_probe(dev, PTR_ERR(csi2->reset), "failed to get reset\n"); diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 9cceffa4ce25..61ad20b18b8d 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -232,7 +232,7 @@ static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev) for (i = 0; i < 10; i++) { usleep_range(1000, 1100); - val = gpiod_get_value(hdmirx_dev->detect_5v_gpio); + val = gpiod_get_value_cansleep(hdmirx_dev->detect_5v_gpio); if (val > 0) cnt++; if (cnt >= detection_threshold) @@ -2252,10 +2252,6 @@ static void hdmirx_delayed_work_res_change(struct work_struct *work) static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id) { struct snps_hdmirx_dev *hdmirx_dev = dev_id; - u32 val; - - val = gpiod_get_value(hdmirx_dev->detect_5v_gpio); - v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val); queue_delayed_work(system_unbound_wq, &hdmirx_dev->delayed_work_hotplug, diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index 64e76e3576a8..b946c8087c77 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -1403,6 +1403,7 @@ static int isp_video_open(struct file *file) ret = vb2_queue_init(&handle->queue); if (ret < 0) { + v4l2_pipeline_pm_put(&video->video.entity); omap3isp_put(video->isp); goto done; } diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index a4b616a5ece7..0e91e87bda9b 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -3641,6 +3641,7 @@ static void vip_remove(struct platform_device *pdev) } v4l2_ctrl_handler_free(&shared->ctrl_handler); + v4l2_device_unregister(&shared->v4l2_dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 78abe810a88e..51d85de24fae 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface, input_free_device(input_dev); exit_unregister_device: rc_unregister_device(rc_dev); - rc_dev = NULL; exit_kill_urbs: usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); @@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface) struct ati_remote *ati_remote; ati_remote = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); if (!ati_remote) { dev_warn(&interface->dev, "%s - null device?\n", __func__); return; } + rc_unregister_device(ati_remote->rdev); + usb_set_intfdata(interface, NULL); usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); if (ati_remote->idev) input_unregister_device(ati_remote->idev); - rc_unregister_device(ati_remote->rdev); ati_remote_free_buffers(ati_remote); + rc_free_device(ati_remote->rdev); kfree(ati_remote); } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index f8120605501a..6f7dccc965e7 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1090,7 +1090,6 @@ exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); exit_unregister_device: rc_unregister_device(rdev); - rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); @@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev) ene_rx_restore_hw_buffer(dev); spin_unlock_irqrestore(&dev->hw_lock, flags); + rc_free_device(dev->rdev); free_irq(dev->irq, dev); release_region(dev->hw_io, ENE_IO_SIZE); kfree(dev); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index f7cfa8a073eb..5055dfc3f465 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev) struct fintek_dev *fintek = pnp_get_drvdata(pdev); unsigned long flags; + rc_unregister_device(fintek->rdev); spin_lock_irqsave(&fintek->fintek_lock, flags); /* disable CIR */ fintek_disable_cir(fintek); @@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev) free_irq(fintek->cir_irq, fintek); release_region(fintek->cir_addr, fintek->cir_port_len); - rc_unregister_device(fintek->rdev); + rc_free_device(fintek->rdev); kfree(fintek); } diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c index e185ead40464..d15fb17fa0fa 100644 --- a/drivers/media/rc/gpio-ir-tx.c +++ b/drivers/media/rc/gpio-ir-tx.c @@ -51,7 +51,7 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) static void delay_until(ktime_t until) { /* - * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on + * delta should never exceed 1 second (IR_MAX_DURATION) and on * m68k ndelay(s64) does not compile; so use s32 rather than s64. */ s32 delta; @@ -95,7 +95,7 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, { ktime_t edge; /* - * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on + * delta should never exceed 1 second (IR_MAX_DURATION) and on * m68k ndelay(s64) does not compile; so use s32 rather than s64. */ s32 delta; diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index e034c93d57cf..3e10f6fe89f8 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -34,7 +34,7 @@ struct igorplugusb { struct device *dev; struct urb *urb; - struct usb_ctrlrequest request; + struct usb_ctrlrequest *request; struct timer_list timer; @@ -122,7 +122,7 @@ static void igorplugusb_cmd(struct igorplugusb *ir, int cmd) { int ret; - ir->request.bRequest = cmd; + ir->request->bRequest = cmd; ir->urb->transfer_flags = 0; ret = usb_submit_urb(ir->urb, GFP_ATOMIC); if (ret && ret != -EPERM) @@ -164,13 +164,17 @@ static int igorplugusb_probe(struct usb_interface *intf, if (!ir) return -ENOMEM; + ir->request = kzalloc_obj(*ir->request, GFP_KERNEL); + if (!ir->request) + goto fail; + ir->dev = &intf->dev; timer_setup(&ir->timer, igorplugusb_timer, 0); - ir->request.bRequest = GET_INFRACODE; - ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; - ir->request.wLength = cpu_to_le16(MAX_PACKET); + ir->request->bRequest = GET_INFRACODE; + ir->request->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; + ir->request->wLength = cpu_to_le16(MAX_PACKET); ir->urb = usb_alloc_urb(0, GFP_KERNEL); if (!ir->urb) @@ -228,6 +232,7 @@ fail: usb_free_urb(ir->urb); rc_free_device(ir->rc); kfree(ir->buf_in); + kfree(ir->request); return ret; } @@ -242,7 +247,9 @@ static void igorplugusb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_unpoison_urb(ir->urb); usb_free_urb(ir->urb); + rc_free_device(ir->rc); kfree(ir->buf_in); + kfree(ir->request); } static const struct usb_device_id igorplugusb_table[] = { diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index c508f2536243..0c5b8befb0af 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(ir->urb_in); usb_kill_urb(ir->urb_out); + rc_free_device(ir->rc); usb_free_urb(ir->urb_in); usb_free_urb(ir->urb_out); usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 63f6f5b36838..f30adf4d8444 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -1118,9 +1118,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv) struct rc_dev *rdev = hw->rdev; if (!rdev) return; + rc_unregister_device(rdev); img_ir_set_decoder(priv, NULL, 0); hw->rdev = NULL; - rc_unregister_device(rdev); + rc_free_device(rdev); #ifdef CONFIG_COMMON_CLK if (!IS_ERR(priv->clk)) clk_notifier_unregister(priv->clk, &hw->clk_nb); diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c index 92fb7b555a0f..f1460d4acf3e 100644 --- a/drivers/media/rc/img-ir/img-ir-raw.c +++ b/drivers/media/rc/img-ir/img-ir-raw.c @@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) if (!rdev) return; + rc_unregister_device(rdev); /* switch off and disable raw (edge) interrupts */ spin_lock_irq(&priv->lock); raw->rdev = NULL; @@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); spin_unlock_irq(&priv->lock); - rc_unregister_device(rdev); + rc_free_device(rdev); timer_delete_sync(&raw->timer); } diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 7e92161105d5..9bb27ba8240f 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -2250,7 +2250,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf, mutex_lock(&ictx->lock); ictx->dev = dev; - ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf)); + ictx->usbdev_intf0 = interface_to_usbdev(intf); ictx->rx_urb_intf0 = rx_urb; ictx->tx_urb = tx_urb; ictx->rf_device = false; @@ -2308,7 +2308,6 @@ idev_setup_failed: usb_kill_urb(ictx->rx_urb_intf0); urb_submit_failed: find_endpoint_failed: - usb_put_dev(ictx->usbdev_intf0); mutex_unlock(&ictx->lock); usb_free_urb(tx_urb); tx_urb_alloc_failed: @@ -2338,7 +2337,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf, timer_setup(&ictx->ttimer, imon_touch_display_timeout, 0); } - ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf)); + ictx->usbdev_intf1 = interface_to_usbdev(intf); ictx->rx_urb_intf1 = rx_urb; ret = -ENODEV; @@ -2377,7 +2376,6 @@ urb_submit_failed: input_unregister_device(ictx->touch); touch_setup_failed: find_endpoint_failed: - usb_put_dev(ictx->usbdev_intf1); ictx->usbdev_intf1 = NULL; mutex_unlock(&ictx->lock); usb_free_urb(rx_urb); @@ -2426,7 +2424,7 @@ static int imon_probe(struct usb_interface *interface, struct imon_context *ictx = NULL; u16 vendor, product; - usbdev = usb_get_dev(interface_to_usbdev(interface)); + usbdev = interface_to_usbdev(interface); iface_desc = interface->cur_altsetting; ifnum = iface_desc->desc.bInterfaceNumber; vendor = le16_to_cpu(usbdev->descriptor.idVendor); @@ -2495,12 +2493,9 @@ static int imon_probe(struct usb_interface *interface, vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum); - usb_put_dev(usbdev); - return 0; fail: - usb_put_dev(usbdev); dev_err(dev, "unable to register, err %d\n", ret); return ret; @@ -2541,16 +2536,16 @@ static void imon_disconnect(struct usb_interface *interface) if (ifnum == 0) { ictx->dev_present_intf0 = false; + rc_unregister_device(ictx->rdev); usb_kill_urb(ictx->rx_urb_intf0); input_unregister_device(ictx->idev); - rc_unregister_device(ictx->rdev); + rc_free_device(ictx->rdev); if (ictx->display_supported) { if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) usb_deregister_dev(interface, &imon_lcd_class); else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD) usb_deregister_dev(interface, &imon_vfd_class); } - usb_put_dev(ictx->usbdev_intf0); } else { ictx->dev_present_intf1 = false; usb_kill_urb(ictx->rx_urb_intf1); @@ -2558,7 +2553,6 @@ static void imon_disconnect(struct usb_interface *interface) timer_delete_sync(&ictx->ttimer); input_unregister_device(ictx->touch); } - usb_put_dev(ictx->usbdev_intf1); } if (refcount_dec_and_test(&ictx->users)) diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index edc46828509c..1b061e4a3dcf 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) regerr: rc_unregister_device(rdev); - rdev = NULL; clkerr: clk_disable_unprepare(priv->clock); err: @@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clock); rc_unregister_device(priv->rdev); + rc_free_device(priv->rdev); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index d6472de5da87..089833e41178 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf) usb_free_urb(ir->urb_out); usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); + rc_free_device(ir->rc); kfree(ir->in); kfree(ir->out); kfree(ir); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index bf544517c67a..bde2a7051231 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1414,7 +1414,6 @@ exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params->io_region_size); exit_unregister_device: rc_unregister_device(rdev); - rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); @@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev) release_region(dev->cir_addr, dev->params->io_region_size); rc_unregister_device(dev->rdev); + rc_free_device(dev->rdev); kfree(dev); } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index ed55e9ec3c57..6a9e4382a224 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1729,7 +1729,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, goto urb_in_alloc_fail; ir->usbintf = intf; - ir->usbdev = usb_get_dev(dev); + ir->usbdev = dev; ir->dev = &intf->dev; ir->len_in = maxp; ir->flags.microsoft_gen1 = is_microsoft_gen1; @@ -1817,7 +1817,6 @@ static int mceusb_dev_probe(struct usb_interface *intf, /* Error-handling path */ rc_dev_fail: cancel_work_sync(&ir->kevent); - usb_put_dev(ir->usbdev); usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); urb_in_alloc_fail: @@ -1849,7 +1848,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); - usb_put_dev(dev); + rc_free_device(ir->rc); kfree(ir); } diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 2e269ef5e26b..ba24c2f22d39 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -648,9 +648,6 @@ int ir_raw_event_register(struct rc_dev *dev) void ir_raw_event_free(struct rc_dev *dev) { - if (!dev) - return; - kfree(dev->raw); dev->raw = NULL; } @@ -674,8 +671,6 @@ void ir_raw_event_unregister(struct rc_dev *dev) lirc_bpf_free(dev); - ir_raw_event_free(dev); - /* * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so * ensure that the raw member is null on unlock; this is how diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 78ac09b3cbd3..53d0540717b3 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -263,6 +263,7 @@ static int __init loop_init(void) static void __exit loop_exit(void) { rc_unregister_device(loopdev.dev); + rc_free_device(loopdev.dev); } module_init(loop_init); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 821607504008..dda3479ea3ad 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device) { struct rc_dev *dev = to_rc_dev(device); + ir_raw_event_free(dev); kfree(dev); } @@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev, } rc->dev.parent = dev; - rc->managed_alloc = true; *dr = rc; devres_add(dev, dr); @@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev) device_del(&dev->dev); ida_free(&rc_ida, dev->minor); - - if (!dev->managed_alloc) - rc_free_device(dev); } - EXPORT_SYMBOL_GPL(rc_unregister_device); /* diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 3b917a2a8918..3f828a564e19 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -1131,11 +1131,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct redrat3_dev *rr3 = usb_get_intfdata(intf); + struct rc_dev *rc = rr3->rc; usb_set_intfdata(intf, NULL); - rc_unregister_device(rr3->rc); + rc_unregister_device(rc); led_classdev_unregister(&rr3->led); redrat3_delete(rr3, udev); + rc_free_device(rc); } static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 6b70bac5f45d..0ba06bfc9e14 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); clk_disable_unprepare(rc_dev->sys_clock); rc_unregister_device(rc_dev->rdev); + rc_free_device(rc_dev->rdev); } static int st_rc_open(struct rc_dev *rdev) @@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev) return ret; rcerr: rc_unregister_device(rdev); - rdev = NULL; clkerr: clk_disable_unprepare(rc_dev->sys_clock); err: diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 5a18603f9a95..307985d74fe8 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -219,9 +219,8 @@ static void streamzap_callback(struct urb *urb) case -ESHUTDOWN: /* * this urb is terminated, clean up. - * sz might already be invalid at this point */ - dev_err(sz->dev, "urb terminated, status: %d\n", urb->status); + dev_dbg(sz->dev, "urb terminated, status: %d\n", urb->status); return; default: break; @@ -358,11 +357,16 @@ static int streamzap_probe(struct usb_interface *intf, usb_set_intfdata(intf, sz); - if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) + retval = usb_submit_urb(sz->urb_in, GFP_ATOMIC); + if (retval < 0) { dev_err(sz->dev, "urb submit failed\n"); + goto rc_submit_fail; + } return 0; - +rc_submit_fail: + rc_free_device(sz->rdev); + usb_set_intfdata(intf, NULL); rc_dev_fail: usb_free_urb(sz->urb_in); free_buf_in: @@ -388,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface) struct streamzap_ir *sz = usb_get_intfdata(interface); struct usb_device *usbdev = interface_to_usbdev(interface); - usb_set_intfdata(interface, NULL); - if (!sz) return; - usb_kill_urb(sz->urb_in); rc_unregister_device(sz->rdev); + usb_set_intfdata(interface, NULL); + + usb_kill_urb(sz->urb_in); usb_free_urb(sz->urb_in); usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in); + rc_free_device(sz->rdev); kfree(sz); } diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 92ef4e7c6f69..cb4c56bf0752 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -371,6 +371,7 @@ static void sunxi_ir_remove(struct platform_device *pdev) struct sunxi_ir *ir = platform_get_drvdata(pdev); rc_unregister_device(ir->rc); + rc_free_device(ir->rc); sunxi_ir_hw_exit(&pdev->dev); } diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 110a46900114..3848ad3a6b85 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -32,7 +32,7 @@ struct ttusbir { struct led_classdev led; struct urb *bulk_urb; - uint8_t bulk_buffer[5]; + u8 *bulk_buffer; int bulk_out_endp, iso_in_endp; bool led_on, is_led_on; atomic_t led_complete; @@ -186,13 +186,16 @@ static int ttusbir_probe(struct usb_interface *intf, struct rc_dev *rc; int i, j, ret; int altsetting = -1; + u8 *buffer; tt = kzalloc_obj(*tt); + buffer = kzalloc(5, GFP_KERNEL); rc = rc_allocate_device(RC_DRIVER_IR_RAW); - if (!tt || !rc) { + if (!tt || !rc || buffer) { ret = -ENOMEM; goto out; } + tt->bulk_buffer = buffer; /* find the correct alt setting */ for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) { @@ -281,8 +284,8 @@ static int ttusbir_probe(struct usb_interface *intf, tt->bulk_buffer[3] = 0x01; usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev, - tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer), - ttusbir_bulk_complete, tt); + tt->bulk_out_endp), tt->bulk_buffer, 5, + ttusbir_bulk_complete, tt); tt->led.name = "ttusbir:green:power"; tt->led.default_trigger = "rc-feedback"; @@ -333,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf, return 0; out3: rc_unregister_device(rc); - rc = NULL; out2: led_classdev_unregister(&tt->led); out: @@ -351,6 +353,7 @@ out: kfree(tt); } rc_free_device(rc); + kfree(buffer); return ret; } @@ -373,6 +376,8 @@ static void ttusbir_disconnect(struct usb_interface *intf) } usb_kill_urb(tt->bulk_urb); usb_free_urb(tt->bulk_urb); + rc_free_device(tt->rc); + kfree(tt->bulk_buffer); usb_set_intfdata(intf, NULL); kfree(tt); } diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 515469dd82d4..8e804661a621 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1132,7 +1132,6 @@ exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); exit_unregister_device: rc_unregister_device(data->dev); - data->dev = NULL; exit_free_rc: rc_free_device(data->dev); exit_unregister_led: @@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device) wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); rc_unregister_device(data->dev); + rc_free_device(data->dev); led_classdev_unregister(&data->led); diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c index 3e3da70cf8da..d2cb88b8f1ca 100644 --- a/drivers/media/rc/xbox_remote.c +++ b/drivers/media/rc/xbox_remote.c @@ -55,7 +55,7 @@ struct xbox_remote { struct usb_interface *interface; struct urb *irq_urb; - unsigned char inbuf[DATA_BUFSIZE] __aligned(sizeof(u16)); + u8 *inbuf; char rc_name[NAME_BUFSIZE]; char rc_phys[NAME_BUFSIZE]; @@ -218,6 +218,10 @@ static int xbox_remote_probe(struct usb_interface *interface, if (!xbox_remote || !rc_dev) goto exit_free_dev_rdev; + xbox_remote->inbuf = kzalloc(DATA_BUFSIZE, GFP_KERNEL); + if (!xbox_remote->inbuf) + goto exit_free_inbuf; + /* Allocate URB buffer */ xbox_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); if (!xbox_remote->irq_urb) @@ -262,6 +266,8 @@ exit_kill_urbs: usb_kill_urb(xbox_remote->irq_urb); exit_free_buffers: usb_free_urb(xbox_remote->irq_urb); +exit_free_inbuf: + kfree(xbox_remote->inbuf); exit_free_dev_rdev: rc_free_device(rc_dev); kfree(xbox_remote); @@ -277,15 +283,17 @@ static void xbox_remote_disconnect(struct usb_interface *interface) struct xbox_remote *xbox_remote; xbox_remote = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); if (!xbox_remote) { dev_warn(&interface->dev, "%s - null device?\n", __func__); return; } - usb_kill_urb(xbox_remote->irq_urb); rc_unregister_device(xbox_remote->rdev); + usb_set_intfdata(interface, NULL); + usb_kill_urb(xbox_remote->irq_urb); + rc_free_device(xbox_remote->rdev); usb_free_urb(xbox_remote->irq_urb); + kfree(xbox_remote->inbuf); kfree(xbox_remote); } diff --git a/drivers/media/test-drivers/vidtv/Kconfig b/drivers/media/test-drivers/vidtv/Kconfig index e511e51c0b5b..8cb10b75603b 100644 --- a/drivers/media/test-drivers/vidtv/Kconfig +++ b/drivers/media/test-drivers/vidtv/Kconfig @@ -2,6 +2,7 @@ config DVB_VIDTV tristate "Virtual DVB Driver (vidtv)" depends on DVB_CORE && MEDIA_SUPPORT && I2C + select CRC32 help The virtual DVB test driver serves as a reference DVB driver and helps validate the existing APIs in the media subsystem. It can also aid developers diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index b6203e10e37a..a8a76434989c 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -237,8 +237,10 @@ static int vidtv_start_feed(struct dvb_demux_feed *feed) if (dvb->nfeeds == 1) { ret = vidtv_start_streaming(dvb); - if (ret < 0) + if (ret < 0) { + dvb->nfeeds--; rc = ret; + } } mutex_unlock(&dvb->feed_lock); diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c index da20657adc74..5f8c3af87171 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_channel.c +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c @@ -341,6 +341,10 @@ vidtv_channel_pmt_match_sections(struct vidtv_channel *channels, tail = vidtv_psi_pmt_stream_init(tail, s->type, e_pid); + if (!tail) { + vidtv_psi_pmt_stream_destroy(head); + return; + } if (!head) head = tail; diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c index 403fbedb8663..f0134e38a1fb 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_mux.c +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -233,7 +233,7 @@ static u32 vidtv_mux_push_pcr(struct vidtv_mux *m) /* the 27Mhz clock will feed both parts of the PCR bitfield */ args.pcr = m->timing.clk; - nbytes += vidtv_ts_pcr_write_into(args); + nbytes += vidtv_ts_pcr_write_into(&args); m->mux_buf_offset += nbytes; m->num_streamed_pcr++; @@ -363,7 +363,7 @@ static u32 vidtv_mux_pad_with_nulls(struct vidtv_mux *m, u32 npkts) args.continuity_counter = &ctx->cc; for (i = 0; i < npkts; ++i) { - m->mux_buf_offset += vidtv_ts_null_write_into(args); + m->mux_buf_offset += vidtv_ts_null_write_into(&args); args.dest_offset = m->mux_buf_offset; } diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c index 685a1f7b2fb1..1b6225d65ef3 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_psi.c +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c @@ -31,61 +31,6 @@ #define INITIAL_CRC 0xffffffff #define ISO_LANGUAGE_CODE_LEN 3 -static const u32 CRC_LUT[256] = { - /* from libdvbv5 */ - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 -}; - -static u32 dvb_crc32(u32 crc, u8 *data, u32 len) -{ - /* from libdvbv5 */ - while (len--) - crc = (crc << 8) ^ CRC_LUT[((crc >> 24) ^ *data++) & 0xff]; - return crc; -} - static void vidtv_psi_update_version_num(struct vidtv_psi_table_header *h) { h->version++; @@ -175,7 +120,7 @@ static u32 vidtv_psi_ts_psi_write_into(struct psi_write_args *args) pr_warn_ratelimited("Missing CRC for chunk\n"); if (args->crc) - *args->crc = dvb_crc32(*args->crc, args->from, args->len); + *args->crc = crc32_be(*args->crc, args->from, args->len); if (args->new_psi_section && !aligned) { pr_warn_ratelimited("Cannot write a new PSI section in a misaligned buffer\n"); diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.c b/drivers/media/test-drivers/vidtv/vidtv_s302m.c index 581497644940..3c08e442b2d1 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_s302m.c +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.c @@ -45,7 +45,7 @@ #define FF_S302M_DEFAULT_PTS_OFFSET 100000 /* Used by the tone generator: number of samples for PI */ -#define PI 180 +#define PI_SAMPLES 180 static const u8 reverse[256] = { /* from ffmpeg */ @@ -259,10 +259,10 @@ static u16 vidtv_s302m_get_sample(struct vidtv_encoder *e) if (!ctx->last_tone) return 0x8000; - pos = (2 * PI * ctx->note_offset * ctx->last_tone) / S302M_SAMPLING_RATE_HZ; + pos = (2 * PI_SAMPLES * ctx->note_offset * ctx->last_tone) / S302M_SAMPLING_RATE_HZ; ctx->note_offset++; - return (fixp_sin32(pos % (2 * PI)) >> 16) + 0x8000; + return (fixp_sin32(pos % (2 * PI_SAMPLES)) >> 16) + 0x8000; } /* bug somewhere */ diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.c b/drivers/media/test-drivers/vidtv/vidtv_ts.c index ca4bb9c40b78..cbe9aff9ffb5 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_ts.c +++ b/drivers/media/test-drivers/vidtv/vidtv_ts.c @@ -48,7 +48,7 @@ void vidtv_ts_inc_cc(u8 *continuity_counter) *continuity_counter = 0; } -u32 vidtv_ts_null_write_into(struct null_packet_write_args args) +u32 vidtv_ts_null_write_into(const struct null_packet_write_args *args) { u32 nbytes = 0; struct vidtv_mpeg_ts ts_header = {}; @@ -56,21 +56,21 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args) ts_header.sync_byte = TS_SYNC_BYTE; ts_header.bitfield = cpu_to_be16(TS_NULL_PACKET_PID); ts_header.payload = 1; - ts_header.continuity_counter = *args.continuity_counter; + ts_header.continuity_counter = *args->continuity_counter; /* copy TS header */ - nbytes += vidtv_memcpy(args.dest_buf, - args.dest_offset + nbytes, - args.buf_sz, + nbytes += vidtv_memcpy(args->dest_buf, + args->dest_offset + nbytes, + args->buf_sz, &ts_header, sizeof(ts_header)); - vidtv_ts_inc_cc(args.continuity_counter); + vidtv_ts_inc_cc(args->continuity_counter); /* fill the rest with empty data */ - nbytes += vidtv_memset(args.dest_buf, - args.dest_offset + nbytes, - args.buf_sz, + nbytes += vidtv_memset(args->dest_buf, + args->dest_offset + nbytes, + args->buf_sz, TS_FILL_BYTE, TS_PACKET_LEN - nbytes); @@ -83,17 +83,17 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args) return nbytes; } -u32 vidtv_ts_pcr_write_into(struct pcr_write_args args) +u32 vidtv_ts_pcr_write_into(const struct pcr_write_args *args) { u32 nbytes = 0; struct vidtv_mpeg_ts ts_header = {}; struct vidtv_mpeg_ts_adaption ts_adap = {}; ts_header.sync_byte = TS_SYNC_BYTE; - ts_header.bitfield = cpu_to_be16(args.pid); + ts_header.bitfield = cpu_to_be16(args->pid); ts_header.scrambling = 0; /* cc is not incremented, but it is needed. see 13818-1 clause 2.4.3.3 */ - ts_header.continuity_counter = *args.continuity_counter; + ts_header.continuity_counter = *args->continuity_counter; ts_header.payload = 0; ts_header.adaptation_field = 1; @@ -102,27 +102,27 @@ u32 vidtv_ts_pcr_write_into(struct pcr_write_args args) ts_adap.PCR = 1; /* copy TS header */ - nbytes += vidtv_memcpy(args.dest_buf, - args.dest_offset + nbytes, - args.buf_sz, + nbytes += vidtv_memcpy(args->dest_buf, + args->dest_offset + nbytes, + args->buf_sz, &ts_header, sizeof(ts_header)); /* write the adap after the TS header */ - nbytes += vidtv_memcpy(args.dest_buf, - args.dest_offset + nbytes, - args.buf_sz, + nbytes += vidtv_memcpy(args->dest_buf, + args->dest_offset + nbytes, + args->buf_sz, &ts_adap, sizeof(ts_adap)); /* write the PCR optional */ - nbytes += vidtv_ts_write_pcr_bits(args.dest_buf, - args.dest_offset + nbytes, - args.pcr); + nbytes += vidtv_ts_write_pcr_bits(args->dest_buf, + args->dest_offset + nbytes, + args->pcr); - nbytes += vidtv_memset(args.dest_buf, - args.dest_offset + nbytes, - args.buf_sz, + nbytes += vidtv_memset(args->dest_buf, + args->dest_offset + nbytes, + args->buf_sz, TS_FILL_BYTE, TS_PACKET_LEN - nbytes); diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.h b/drivers/media/test-drivers/vidtv/vidtv_ts.h index 09b4ffd02829..3606398e160d 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_ts.h +++ b/drivers/media/test-drivers/vidtv/vidtv_ts.h @@ -90,7 +90,7 @@ void vidtv_ts_inc_cc(u8 *continuity_counter); * * Return: The number of bytes written into the buffer. */ -u32 vidtv_ts_null_write_into(struct null_packet_write_args args); +u32 vidtv_ts_null_write_into(const struct null_packet_write_args *args); /** * vidtv_ts_pcr_write_into - Write a PCR packet into a buffer. @@ -101,6 +101,6 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args); * * Return: The number of bytes written into the buffer. */ -u32 vidtv_ts_pcr_write_into(struct pcr_write_args args); +u32 vidtv_ts_pcr_write_into(const struct pcr_write_args *args); #endif //VIDTV_TS_H diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 7a45a2117748..861b334ffc65 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -12,6 +12,8 @@ #include <linux/slab.h> #include <media/media-device.h> #include <media/v4l2-device.h> +#include <media/tpg/v4l2-tpg.h> +#include <media/v4l2-ctrls.h> #define VIMC_PDEV_NAME "vimc" @@ -27,6 +29,15 @@ #define VIMC_FRAME_MIN_WIDTH 16 #define VIMC_FRAME_MIN_HEIGHT 16 +#define VIMC_PIXEL_RATE_FIXED 160000000 /* 160 MHz */ +#define VIMC_HBLANK_FIXED 800 +/* VBLANK - vertical blanking (primary FPS control) */ +#define VIMC_VBLANK_MIN 4 +#define VIMC_VBLANK_MAX 65535 +#define VIMC_VBLANK_STEP 1 +#define VIMC_VBLANK_DEFAULT 3223 /* 30fps vga */ +#define VIMC_PIXELS_THRESHOLD_30FPS (1920 * 1080) /* 2073600 pixels */ + #define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp) /* Source and sink pad checks */ @@ -159,6 +170,36 @@ struct vimc_ent_config { const struct vimc_ent_type *type; }; +enum vimc_sensor_osd_mode { + VIMC_SENSOR_OSD_SHOW_ALL = 0, + VIMC_SENSOR_OSD_SHOW_COUNTERS = 1, + VIMC_SENSOR_OSD_SHOW_NONE = 2 +}; + +struct vimc_sensor_device { + struct vimc_ent_device ved; + struct v4l2_subdev sd; + struct tpg_data tpg; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + + u8 *frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_area size; + enum vimc_sensor_osd_mode osd_value; + u64 start_stream_ts; + unsigned long fps_jiffies; + } hw; +}; + /** * vimc_is_source - returns true if the entity has only source pads * diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 69e714d4f228..5deebcc78a33 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -14,32 +14,6 @@ #include "vimc-common.h" -enum vimc_sensor_osd_mode { - VIMC_SENSOR_OSD_SHOW_ALL = 0, - VIMC_SENSOR_OSD_SHOW_COUNTERS = 1, - VIMC_SENSOR_OSD_SHOW_NONE = 2 -}; - -struct vimc_sensor_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - struct tpg_data tpg; - struct v4l2_ctrl_handler hdl; - struct media_pad pad; - - u8 *frame; - - /* - * Virtual "hardware" configuration, filled when the stream starts or - * when controls are set. - */ - struct { - struct v4l2_area size; - enum vimc_sensor_osd_mode osd_value; - u64 start_stream_ts; - } hw; -}; - static const struct v4l2_mbus_framefmt fmt_default = { .width = 640, .height = 480, @@ -51,10 +25,15 @@ static const struct v4l2_mbus_framefmt fmt_default = { static int vimc_sensor_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { + struct vimc_sensor_device *vsensor = + container_of(sd, struct vimc_sensor_device, sd); + struct v4l2_mbus_framefmt *mf; mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = fmt_default; + vsensor->hw.size.width = fmt_default.width; + vsensor->hw.size.height = fmt_default.height; return 0; } @@ -113,6 +92,26 @@ static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor, tpg_s_xfer_func(&vsensor->tpg, format->xfer_func); } +static int vimc_sensor_update_frame_timing(struct v4l2_subdev *sd, + u32 width, u32 height) +{ + struct vimc_sensor_device *vsensor = + container_of(sd, struct vimc_sensor_device, sd); + u64 pixel_rate = vsensor->pixel_rate->val; + u32 hts = width + vsensor->hblank->val; + u32 vts = height + vsensor->vblank->val; + u64 total_pixels = (u64)hts * vts; + u64 frame_interval_ns; + + frame_interval_ns = total_pixels * NSEC_PER_SEC; + do_div(frame_interval_ns, pixel_rate); + vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns); + if (vsensor->hw.fps_jiffies == 0) + vsensor->hw.fps_jiffies = 1; + + return 0; +} + static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) { const struct vimc_pix_map *vpix; @@ -134,6 +133,24 @@ static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) vimc_colorimetry_clamp(fmt); } +static u32 vimc_calc_vblank(u32 width, u32 height, + s64 pixel_rate, s32 hblank) +{ + u32 hts = width + hblank; + u32 target_fps; + u64 vts; + + target_fps = (width * height <= VIMC_PIXELS_THRESHOLD_30FPS) ? 30 : 10; + + vts = (u64)pixel_rate; + do_div(vts, target_fps * hts); + + if (vts > height) + return clamp((u32)(vts - height), VIMC_VBLANK_MIN, VIMC_VBLANK_MAX); + + return VIMC_VBLANK_MIN; +} + static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -163,6 +180,20 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, fmt->format.xfer_func, fmt->format.ycbcr_enc); *mf = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + u32 vblank_def = vimc_calc_vblank(fmt->format.width, + fmt->format.height, + vsensor->pixel_rate->val, + vsensor->hblank->val); + vsensor->hw.size.width = fmt->format.width; + vsensor->hw.size.height = fmt->format.height; + __v4l2_ctrl_modify_range(vsensor->vblank, + VIMC_VBLANK_MIN, + VIMC_VBLANK_MAX, + VIMC_VBLANK_STEP, + vblank_def); + __v4l2_ctrl_s_ctrl(vsensor->vblank, vblank_def); + } return 0; } @@ -248,6 +279,8 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) vsensor->hw.size.width = format->width; vsensor->hw.size.height = format->height; + vimc_sensor_update_frame_timing(sd, format->width, + format->height); v4l2_subdev_unlock_state(state); @@ -319,6 +352,15 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) case VIMC_CID_OSD_TEXT_MODE: vsensor->hw.osd_value = ctrl->val; break; + case V4L2_CID_PIXEL_RATE: + break; + case V4L2_CID_HBLANK: + break; + case V4L2_CID_VBLANK: + vimc_sensor_update_frame_timing(&vsensor->sd, + vsensor->hw.size.width, + vsensor->hw.size.height); + break; default: return -EINVAL; } @@ -403,6 +445,26 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, V4L2_CID_HUE, -128, 127, 1, 0); v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_SATURATION, 0, 255, 1, 128); + /* Timing controls for frame interval configuration */ + vsensor->pixel_rate = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, + V4L2_CID_PIXEL_RATE, + VIMC_PIXEL_RATE_FIXED, VIMC_PIXEL_RATE_FIXED, + 1, VIMC_PIXEL_RATE_FIXED); + if (vsensor->pixel_rate) + vsensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vsensor->hblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, + V4L2_CID_HBLANK, + VIMC_HBLANK_FIXED, VIMC_HBLANK_FIXED, + 1, VIMC_HBLANK_FIXED); + if (vsensor->hblank) + vsensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vsensor->vblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, + V4L2_CID_VBLANK, + VIMC_VBLANK_MIN, VIMC_VBLANK_MAX, + VIMC_VBLANK_STEP, VIMC_VBLANK_DEFAULT); + vsensor->sd.ctrl_handler = &vsensor->hdl; if (vsensor->hdl.error) { ret = vsensor->hdl.error; diff --git a/drivers/media/test-drivers/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c index 15d863f97cbf..3ebf5798fa3d 100644 --- a/drivers/media/test-drivers/vimc/vimc-streamer.c +++ b/drivers/media/test-drivers/vimc/vimc-streamer.c @@ -140,6 +140,29 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream, } /** + * vimc_streamer_get_sensor() - Get sensor from pipeline + * @stream: the pipeline + * + * Helper function to find the sensor device in the pipeline. + * Returns pointer to sensor device or NULL if not found. + */ +static struct vimc_sensor_device *vimc_streamer_get_sensor(struct vimc_stream *stream) +{ + int i; + + for (i = 0; i < stream->pipe_size; i++) { + struct vimc_ent_device *ved = stream->ved_pipeline[i]; + + if (ved && ved->ent && + ved->ent->function == MEDIA_ENT_F_CAM_SENSOR) { + return container_of(ved, struct vimc_sensor_device, ved); + } + } + + return NULL; +} + +/** * vimc_streamer_thread - Process frames through the pipeline * * @data: vimc_stream struct of the current stream @@ -154,25 +177,31 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream, static int vimc_streamer_thread(void *data) { struct vimc_stream *stream = data; + struct vimc_sensor_device *vsensor; u8 *frame = NULL; int i; + unsigned long fps_jiffies; + const unsigned long default_jiffies = HZ / 30; set_freezable(); + vsensor = vimc_streamer_get_sensor(stream); for (;;) { try_to_freeze(); if (kthread_should_stop()) break; + /* Read from hardware configuration */ + fps_jiffies = vsensor ? vsensor->hw.fps_jiffies : default_jiffies; + for (i = stream->pipe_size - 1; i >= 0; i--) { frame = stream->ved_pipeline[i]->process_frame( stream->ved_pipeline[i], frame); if (!frame || IS_ERR(frame)) break; } - //wait for 60hz set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 60); + schedule_timeout(fps_jiffies); } return 0; diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index ba22bf594ac7..d57570290e93 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -1174,7 +1174,12 @@ static u16 MXL5005_ControlInit(struct dvb_frontend *fe) state->Init_Ctrl[39].bit[0] = 3; state->Init_Ctrl[39].val[0] = 1; + return 0; +} +static u16 MXL5005_ControlInitCH(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; state->CH_Ctrl_Num = CHCTRL_NUM ; state->CH_Ctrl[0].Ctrl_Num = DN_POLY ; @@ -1663,6 +1668,7 @@ static void InitTunerControls(struct dvb_frontend *fe) { MXL5005_RegisterInit(fe); MXL5005_ControlInit(fe); + MXL5005_ControlInitCH(fe); #ifdef _MXL_INTERNAL MXL5005_MXLControlInit(fe); #endif diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4ca42114da8a..4d67e347c22f 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -638,20 +638,27 @@ static int si2157_set_analog_params(struct dvb_frontend *fe, color = 0x10; } } - } else if (params->std & V4L2_STD_MN) { + } else if (params->std & (V4L2_STD_MN | V4L2_STD_NTSC_443)) { std = "MN"; bandwidth = 6000000; if_frequency = 5400000; system = 2; + if (params->std & V4L2_STD_PAL_N) { + std = "palN"; + color = 0x10; + } else if (params->std & V4L2_STD_PAL_Nc) { + std = "palNc"; + color = 0x10; + } } else if (params->std & V4L2_STD_PAL_I) { std = "palI"; bandwidth = 8000000; - if_frequency = 7250000; /* TODO: does not work yet */ + if_frequency = 7250000; system = 4; } else if (params->std & V4L2_STD_DK) { std = "palDK"; bandwidth = 8000000; - if_frequency = 6900000; /* TODO: does not work yet */ + if_frequency = 6900000; system = 5; if (params->std & V4L2_STD_SECAM_DK) { std = "secamDK"; @@ -660,7 +667,7 @@ static int si2157_set_analog_params(struct dvb_frontend *fe, } else if (params->std & V4L2_STD_SECAM_L) { std = "secamL"; bandwidth = 8000000; - if_frequency = 6750000; /* TODO: untested */ + if_frequency = 6900000; system = 6; color = 0x10; } else if (params->std & V4L2_STD_SECAM_LC) { @@ -680,60 +687,103 @@ static int si2157_set_analog_params(struct dvb_frontend *fe, params->mode, system, std, params->frequency, freq, if_frequency, bandwidth); - /* set analog IF port */ - memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6); - /* in using dev->if_port, we assume analog and digital IF's */ - /* are always on different ports */ - /* assumes if_port definition is 0 or 1 for digital out */ - cmd.args[4] = (dev->if_port == 1) ? 8 : 10; - /* Analog AGC assumed external */ - cmd.args[5] = (dev->if_port == 1) ? 2 : 1; - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2157_cmd_execute(client, &cmd); - if (ret) - goto err; + if (dev->part_id != SI2177) { + /* AGC speed */ + memcpy(cmd.args, "\x14\x00\x11\x06\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; - /* set analog IF output config */ - memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6); - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2157_cmd_execute(client, &cmd); - if (ret) - goto err; + /* set analog IF port */ + memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6); + /* in using dev->if_port, we assume analog and digital IF's */ + /* are always on different ports */ + /* assumes if_port definition is 0 or 1 for digital out */ + cmd.args[4] = (dev->if_port == 1) ? 8 : 10; + /* Analog AGC assumed external */ + cmd.args[5] = (dev->if_port == 1) ? 2 : 1; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; - /* make this distinct from a digital IF */ - dev->if_frequency = if_frequency | 1; + /* set analog IF output config */ + memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; - /* calc and set tuner analog if center frequency */ - if_frequency = if_frequency + 1250000 - (bandwidth / 2); - dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency); + /* make this distinct from a digital IF */ + dev->if_frequency = if_frequency | 1; - memcpy(cmd.args, "\x14\x00\x0C\x06", 4); - cmd.args[4] = (if_frequency / 1000) & 0xff; - cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2157_cmd_execute(client, &cmd); - if (ret) - goto err; + /* calc and set tuner analog if center frequency */ + if_frequency = if_frequency + 1250000 - (bandwidth / 2); + dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency); - /* set analog AGC config */ - memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6); - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2157_cmd_execute(client, &cmd); - if (ret) - goto err; + memcpy(cmd.args, "\x14\x00\x0C\x06", 4); + cmd.args[4] = (if_frequency / 1000) & 0xff; + cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set analog AGC config */ + memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; - /* set analog video mode */ - memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); - cmd.args[4] = system | color; - /* can use dev->inversion if assumed applies to both digital/analog */ - if (invert_analog) - cmd.args[5] |= 0x02; + /* set analog video mode */ + memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); + cmd.args[4] = system | color; + /* can use dev->inversion if assumed applies to both digital/analog */ + if (invert_analog) + cmd.args[5] |= 0x02; + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } else { + /* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */ + memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* analog CVBS output properties - Si2177_ATV_CVBS_OUT_FINE_PROP */ + memcpy(cmd.args, "\x14\x00\x14\x06\x00\x64", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + dev_err(&client->dev, "%s() Settings HSYNC\n", __func__); + /* HSYNC output - Si2177_ATV_HSYNC_OUT_PROP */ + memcpy(cmd.args, "\x14\x00\x27\x06\xa8\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } + + /* AFC qcuisition range 1.5MHz */ + memcpy(cmd.args, "\x14\x00\x10\x06\xdc\x05", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; @@ -750,6 +800,76 @@ static int si2157_set_analog_params(struct dvb_frontend *fe, if (ret) goto err; + if (dev->part_id == SI2177) { + /* Ref driver tunes, resets registers, then retunes, leaving steps as is */ + /* set analog video mode - Si2158_ATV_VIDEO_MODE_PROP */ + memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); + cmd.args[4] = system | color; + /* can use dev->inversion if assumed applies to both digital/analog */ + if (invert_analog) + cmd.args[5] |= 0x02; + + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* Si2177_ATV_AUDIO_MODE_PROP */ + memcpy(cmd.args, "\x14\x00\x02\x06\x20\x0f", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* af out - Si2177_ATV_AF_OUT_PROP */ /* BRL */ + memcpy(cmd.args, "\x14\x00\x0b\x06\x30\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* analog CVBS output enable - Si2177_ATV_CVBS_OUT_PROP */ + memcpy(cmd.args, "\x14\x00\x09\x06\x19\x99", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */ + memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* ATV restart */ + memcpy(cmd.args, "\x51\x00", 2); + cmd.wlen = 2; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + usleep_range(10000, 11000); + + /* set analog frequency */ + memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8); + cmd.args[4] = (freq >> 0) & 0xff; + cmd.args[5] = (freq >> 8) & 0xff; + cmd.args[6] = (freq >> 16) & 0xff; + cmd.args[7] = (freq >> 24) & 0xff; + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } + dev->bandwidth = bandwidth; si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */ diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 8579e80f7af7..aaada2bb0f21 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -72,7 +72,8 @@ struct si2157_cmd { ((dev)->part_id == SI2177)) #define SUPPORTS_ATV_IF(dev) (((dev)->part_id == SI2157) || \ - ((dev)->part_id == SI2158)) + ((dev)->part_id == SI2158) || \ + ((dev)->part_id == SI2177)) /* Old firmware namespace */ #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index a28481edd22e..1a9c4574a8e8 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1333,6 +1333,15 @@ static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg) } +static int xc5000_get_rf_strength(struct dvb_frontend *fe, u16 *rssi) +{ + struct xc5000_priv *priv = fe->tuner_priv; + + dprintk(1, "%s()\n", __func__); + + return xc_get_lock_status(priv, rssi); +} + static const struct dvb_tuner_ops xc5000_tuner_ops = { .info = { .name = "Xceive XC5000", @@ -1353,7 +1362,8 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { .get_frequency = xc5000_get_frequency, .get_if_frequency = xc5000_get_if_frequency, .get_bandwidth = xc5000_get_bandwidth, - .get_status = xc5000_get_status + .get_status = xc5000_get_status, + .get_rf_strength = xc5000_get_rf_strength, }; struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c index 8e480ab78f9b..a11024451ceb 100644 --- a/drivers/media/usb/as102/as102_usb_drv.c +++ b/drivers/media/usb/as102/as102_usb_drv.c @@ -403,7 +403,9 @@ static int as102_usb_probe(struct usb_interface *intf, failed_dvb: as102_free_usb_stream_buffer(as102_dev); failed_stream: + usb_set_intfdata(intf, NULL); usb_deregister_dev(intf, &as102_usb_class_driver); + return ret; failed: usb_put_dev(as102_dev->bus_adap.usb_dev); usb_set_intfdata(intf, NULL); diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 9ee21f8bf6fa..9929ce5dcdcd 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -105,6 +105,46 @@ struct au0828_board au0828_boards[] = { .tuner_addr = 0x60, .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, + [AU0828_BOARD_HAUPPAUGE_IMPACTVCBE] = { + .name = "Hauppauge Impact VCB-e", + .tuner_type = TUNER_ABSENT, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + .input = { + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH4, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, + }, + [AU0828_BOARD_HAUPPAUGE_HVR1265] = { + .name = "Hauppauge HVR1265", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .has_ir_i2c = 1, + .has_analog = 1, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, + }, }; /* Tuner callback function for au0828 boards. Currently only needed @@ -120,6 +160,8 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg) case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + case AU0828_BOARD_HAUPPAUGE_HVR1265: + case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE: case AU0828_BOARD_DVICO_FUSIONHDTV7: if (command == 0) { /* Tuner Reset Command from xc5000 */ @@ -190,6 +232,8 @@ void au0828_card_setup(struct au0828_dev *dev) case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + case AU0828_BOARD_HAUPPAUGE_HVR1265: + case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE: case AU0828_BOARD_HAUPPAUGE_WOODBURY: if (dev->i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xa0); @@ -248,6 +292,8 @@ void au0828_gpio_setup(struct au0828_dev *dev) case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: case AU0828_BOARD_HAUPPAUGE_WOODBURY: + case AU0828_BOARD_HAUPPAUGE_HVR1265: + case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE: /* GPIO's * 4 - CS5340 * 5 - AU8522 Demodulator @@ -340,6 +386,10 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7270), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x72b0), + .driver_info = AU0828_BOARD_HAUPPAUGE_IMPACTVCBE }, + { USB_DEVICE(0x2040, 0x72a0), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR1265 }, { }, }; diff --git a/drivers/media/usb/au0828/au0828-cards.h b/drivers/media/usb/au0828/au0828-cards.h index a9cdf85f98f5..a438aeb334a0 100644 --- a/drivers/media/usb/au0828/au0828-cards.h +++ b/drivers/media/usb/au0828/au0828-cards.h @@ -11,3 +11,5 @@ #define AU0828_BOARD_DVICO_FUSIONHDTV7 3 #define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4 #define AU0828_BOARD_HAUPPAUGE_WOODBURY 5 +#define AU0828_BOARD_HAUPPAUGE_IMPACTVCBE 6 +#define AU0828_BOARD_HAUPPAUGE_HVR1265 7 diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 5d0447ff7d06..31123e6f9fc3 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -571,6 +571,7 @@ int au0828_dvb_register(struct au0828_dev *dev) switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR1265: dvb->frontend = dvb_attach(au8522_attach, &hauppauge_hvr950q_config, &dev->i2c_adap); diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index 7dec1a360da6..b156afb1a0ae 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -300,6 +300,7 @@ int au0828_rc_register(struct au0828_dev *dev) if (dev->board.has_ir_i2c) { /* external i2c device */ switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR1265: rc->map_name = RC_MAP_HAUPPAUGE; ir->get_key_i2c = au0828_get_key_au8522; break; @@ -357,6 +358,7 @@ void au0828_rc_unregister(struct au0828_dev *dev) return; rc_unregister_device(ir->rc); + rc_free_device(ir->rc); /* done */ kfree(ir); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index fbaa542c8259..3c53105f3d2b 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1671,6 +1671,27 @@ static int vidioc_log_status(struct file *file, void *fh) return 0; } +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_dev *dev = video_drvdata(file); + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + /* Workaround for a bug in the au0828 hardware design that + * sometimes results in the colorspace being inverted + */ + if (dev->greenscreen_detected == 1) { + dprintk(1, "Detected green frame. Resetting stream...\n"); + au0828_analog_stream_reset(dev); + dev->greenscreen_detected = 0; + } + + return vb2_ioctl_dqbuf(file, priv, b); +} + void au0828_v4l2_suspend(struct au0828_dev *dev) { struct urb *urb; @@ -1764,8 +1785,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_prepare_buf = vb2_ioctl_prepare_buf, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 1cfec76b72f3..0a5c635da040 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -2027,10 +2027,9 @@ int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, 0x000035e8); - status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600); - status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, - 0xC2262600); - status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, 0xC2260000); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2260000); /* Save the Spec Inversion value */ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index b64a37d1acf4..b75535d6abaf 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1369,8 +1369,6 @@ void cx231xx_release_resources(struct cx231xx *dev) cx231xx_unregister_media_device(dev); - usb_put_dev(dev->udev); - /* Mark device as unused */ clear_bit(dev->devno, &cx231xx_devused); } @@ -1719,7 +1717,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } } while (test_and_set_bit(nr, &cx231xx_devused)); - udev = usb_get_dev(interface_to_usbdev(interface)); + udev = interface_to_usbdev(interface); /* allocate memory for our device state and initialize it */ dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); @@ -1915,7 +1913,6 @@ err_v4l2: err_media_init: usb_set_intfdata(interface, NULL); err_if: - usb_put_dev(udev); clear_bit(nr, &cx231xx_devused); return retval; } diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 600cff8a4abd..bd86d250433d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) if (d->rc_dev) { cancel_delayed_work_sync(&d->rc_query_work); rc_unregister_device(d->rc_dev); + rc_free_device(d->rc_dev); d->rc_dev = NULL; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c index 65e2c9e2cdc9..6dc11718dfb9 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d) { if (d->state & DVB_USB_STATE_REMOTE) { cancel_delayed_work_sync(&d->rc_query_work); - if (d->props.rc.mode == DVB_RC_LEGACY) + if (d->props.rc.mode == DVB_RC_LEGACY) { input_unregister_device(d->input_dev); - else + } else { rc_unregister_device(d->rc_dev); + rc_free_device(d->rc_dev); + } } d->state &= ~DVB_USB_STATE_REMOTE; return 0; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 59a2e4db75b7..fbfb74eab475 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -387,14 +387,14 @@ static const struct em28xx_reg_seq c3tech_digital_duo_digital[] = { * GPIO 7 = P07_LED (green LED) */ static const struct em28xx_reg_seq pctv_461e[] = { - {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0}, - {0x0d, 0xff, 0xff, 0}, - {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */ - {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 200}, /* reset demod */ - {0x0d, 0x42, 0xff, 0}, - {EM2874_R80_GPIO_P0_CTRL, 0xeb, 0xff, 0}, - {EM2874_R5F_TS_ENABLE, 0x84, 0x84, 0}, /* parallel? | null discard */ - { -1, -1, -1, -1}, + {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0}, + {0x0d, 0xff, 0xff, 0}, + {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */ + {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 200}, /* reset demod */ + {0x0d, 0x42, 0xff, 0}, + {EM2874_R80_GPIO_P0_CTRL, 0xeb, 0xff, 0}, + {EM2874_R5F_TS_ENABLE, 0x84, 0x84, 0}, /* parallel? | null discard */ + {-1, -1, -1, -1}, }; #if 0 @@ -553,6 +553,36 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, {-1, -1, -1, -1}, }; +/* Hauppauge HVR-935 \ HVR-955 / HVR-975 V2 */ +static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xdc, 0xff, 50}, + {EM2874_R5F_TS_ENABLE, 0x00, 0xff, 50}, /* disable TS filters */ + {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, + {-1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_comp[] = { + {0x0b, 0x00, 0xff, 100}, + {0x0b, 0x96, 0xff, 100}, + {0x0b, 0x00, 0xff, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_5, 10}, + {-1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_television[] = { + {0x0b, 0x00, 0xff, 100}, + {0x0b, 0x96, 0xff, 100}, + {0x0b, 0x00, 0xff, 100}, + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_5, EM_GPIO_5, 10}, + {-1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_dvb[] = { + {0x0b, 0x80, 0xff, 100}, + {0x0b, 0x96, 0xff, 100}, + {0x0b, 0x80, 0xff, 100}, + {-1, -1, -1, -1}, +}; /* * MyGica USB TV Box @@ -689,6 +719,16 @@ static struct em28xx_led hauppauge_usb_quadhd_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led hauppauge_9x5_v2_leds[] = { + { + .role = EM28XX_LED_DIGITAL_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_0, + .inverted = 0, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2457,6 +2497,20 @@ const struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, /* + * 2013:x462 PCTV DVB-S2 Stick (461e_v3) + * Empia EM28178, Montage M88DS3103c, Montage M88TS2022, Allegro A8293 + */ + [EM28178_BOARD_PCTV_461E_V3] = { + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + .name = "PCTV DVB-S2 Stick (461e v3)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_461e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, + /* * 2013:025f PCTV tripleStick (292e). * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */ @@ -2522,17 +2576,12 @@ const struct em28xx_board em28xx_boards[] = { .def_i2c_bus = 1, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, - .tuner_type = TUNER_SI2157, + .tuner_type = TUNER_ABSENT, .tuner_gpio = hauppauge_dualhd_dvb, .has_dvb = 1, .has_dual_ts = 1, .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, - .input = { { - .type = EM28XX_VMUX_COMPOSITE, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - } }, }, /* * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Isoc. @@ -2628,6 +2677,126 @@ const struct em28xx_board em28xx_boards[] = { .gpio = mygica_utv3_tuner_audio_gpio, } }, }, + [EM2828X_BOARD_HAUPPAUGE_USB_LIVE2] = { + .name = "Hauppauge USB Live2", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .has_dvb = 0, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = 0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = 1, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + + /* 2040:8360 Hauppauge HVR-935 + * Empia EM2828X, si2168 demod, si2177 tuner + * Composite input, s-video input, analog TV, stereo audio input + */ + [EM2828X_BOARD_HAUPPAUGE_935_V2] = { + .name = "Hauppauge WinTV-HVR-935", + .def_i2c_bus = 1, + .has_dvb = 1, + .vchannels = 3, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .tuner_gpio = hauppauge_hvr_9x5_v2, + .dvb_gpio = hauppauge_hvr_9x5_v2_dvb, + .leds = hauppauge_9x5_v2_leds, + .xclk = 0x8f, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = 0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_TELEVISION, + .vmux = 2, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_television, + + } }, + }, + /* 2040:8366 Hauppauge HVR-955 + * Empia EM2828X, lgdt3306a demod, si2177 tuner + * Composite input, s-video input, analog TV, stereo audio input + */ + [EM2828X_BOARD_HAUPPAUGE_955_V2] = { + .name = "Hauppauge WinTV-HVR-955", + .def_i2c_bus = 1, + .has_dvb = 1, + .vchannels = 3, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .tuner_gpio = hauppauge_hvr_9x5_v2, + .dvb_gpio = hauppauge_hvr_9x5_v2_dvb, + .leds = hauppauge_9x5_v2_leds, + .xclk = 0x8f, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = 0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_TELEVISION, + .vmux = 2, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_television, + + } }, + }, + /* 2040:836a Hauppauge HVR-975 + * Empia EM2828X, si2168 demod, lgdt3306a demod, si2177 tuner + * Composite input, s-video input, analog TV, stereo audio input + */ + [EM2828X_BOARD_HAUPPAUGE_975_V2] = { + .name = "Hauppauge WinTV-HVR-975", + .def_i2c_bus = 1, + .has_dvb = 1, + .vchannels = 3, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .tuner_gpio = hauppauge_hvr_9x5_v2, + .dvb_gpio = hauppauge_hvr_9x5_v2_dvb, + .leds = hauppauge_9x5_v2_leds, + .xclk = 0x8f, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = 0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_comp, + }, { + .type = EM28XX_VMUX_TELEVISION, + .vmux = 2, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_hvr_9x5_v2_television, + + } }, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2757,12 +2926,38 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, { USB_DEVICE(0x2040, 0x8265), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, + { USB_DEVICE(0x2040, 0x8269), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, + { USB_DEVICE(0x2040, 0x8278), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, { USB_DEVICE(0x2040, 0x026d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x2040, 0x826d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x826e), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x826f), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x8270), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x8271), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x2040, 0x846d), .driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD }, + { USB_DEVICE(0x2040, 0xc220), + .driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 }, + { USB_DEVICE(0x2040, 0x0360), + .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 }, + { USB_DEVICE(0x2040, 0x8360), + .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 }, + { USB_DEVICE(0x2040, 0x0366), + .driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 }, + { USB_DEVICE(0x2040, 0x8366), + .driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 }, + { USB_DEVICE(0x2040, 0x036a), + .driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 }, + { USB_DEVICE(0x2040, 0x836a), + .driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -2831,6 +3026,10 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PCTV_461E_V2 }, { USB_DEVICE(0x2013, 0x0259), .driver_info = EM28178_BOARD_PCTV_461E_V2 }, + { USB_DEVICE(0x2013, 0x0462), + .driver_info = EM28178_BOARD_PCTV_461E_V3 }, + { USB_DEVICE(0x2013, 0x8462), /* Bulk transport 461e v3 */ + .driver_info = EM28178_BOARD_PCTV_461E_V3 }, { USB_DEVICE(0x2013, 0x025f), .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */ @@ -3253,6 +3452,10 @@ static void em28xx_card_setup(struct em28xx *dev) case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB: case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595: + case EM2828X_BOARD_HAUPPAUGE_USB_LIVE2: + case EM2828X_BOARD_HAUPPAUGE_935_V2: + case EM2828X_BOARD_HAUPPAUGE_955_V2: + case EM2828X_BOARD_HAUPPAUGE_975_V2: { struct tveeprom tv; @@ -3626,6 +3829,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, } /* NOTE: the em2820 is used in webcams, too ! */ break; + case CHIP_ID_EM2828X: + chip_name = "em2828X"; + dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2840: chip_name = "em2840"; break; @@ -3784,6 +3992,7 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, * 0x84 bulk => analog or digital** * 0x85 isoc => digital TS2 * 0x85 bulk => digital TS2 + * 0x8a isoc => digital video * (*: audio should always be isoc) * (**: analog, if ep 0x82 is isoc, otherwise digital) * @@ -3807,6 +4016,8 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, /* Only inspect input endpoints */ switch (e->bEndpointAddress) { + case 0x81: /* unknown function */ + return; case 0x82: *has_video = true; if (usb_endpoint_xfer_isoc(e)) { @@ -3824,7 +4035,10 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); return; case 0x84: - if (*has_video && (usb_endpoint_xfer_bulk(e))) { + if (*has_dvb && (usb_endpoint_xfer_bulk(e))) { + *has_dvb = true; + dev->dvb_ep_bulk = e->bEndpointAddress; + } else if (*has_video && (usb_endpoint_xfer_bulk(e))) { dev->analog_ep_bulk = e->bEndpointAddress; } else { if (usb_endpoint_xfer_isoc(e)) { @@ -3858,7 +4072,17 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; } return; - } + case 0x8a: + *has_video = true; + *has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[alt] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = e->bEndpointAddress; + } + return; + }; } /* @@ -4040,6 +4264,8 @@ static int em28xx_usb_probe(struct usb_interface *intf, try_bulk = 1; else try_bulk = 0; + } else if (dev->board.decoder == EM28XX_BUILTIN && dev->analog_xfer_mode) { + try_bulk = 1; } else { try_bulk = usb_xfer_mode > 0; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 29a7f3f19b56..5bbb082dbed9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -499,7 +499,8 @@ int em28xx_audio_setup(struct em28xx *dev) if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174 || - dev->chip_id == CHIP_ID_EM28178) { + dev->chip_id == CHIP_ID_EM28178 || + dev->chip_id == CHIP_ID_EM2828X) { /* Digital only device - don't load any alsa module */ dev->int_audio_type = EM28XX_INT_AUDIO_NONE; dev->usb_audio_type = EM28XX_USB_AUDIO_NONE; @@ -619,6 +620,65 @@ const struct em28xx_led *em28xx_find_led(struct em28xx *dev, } EXPORT_SYMBOL_GPL(em28xx_find_led); +void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin) +{ + switch (vin) { + case EM2828X_TELEVISION: + dev_dbg(&dev->intf->dev, "EM2828X_TELEVISION\n"); + break; + case EM2828X_COMPOSITE: + dev_dbg(&dev->intf->dev, "EM2828X_COMPOSITE\n"); + break; + default: + dev_dbg(&dev->intf->dev, "EM2828X_SVIDEO\n"); + break; + }; + + em28xx_write_reg(dev, 0x24, 0x00); + em28xx_write_reg(dev, 0x25, 0x02); + em28xx_write_reg(dev, 0x2E, 0x00); + + if (vin == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A0B, 0xfc); + em28xx_write_reg(dev, 0xB6, 0x8F); + em28xx_write_reg(dev, 0xB8, 0x01); + } else { + em28xx_write_reg(dev, 0x7A0B, 0x00); + em28xx_write_reg(dev, 0xB6, 0x8F); + em28xx_write_reg(dev, 0xB8, 0x00); + } + + em28xx_write_reg(dev, 0x7A1C, 0x1E); + em28xx_write_reg(dev, 0x7A1D, 0x99); + em28xx_write_reg(dev, 0x7A1E, 0x99); + em28xx_write_reg(dev, 0x7A1F, 0x9A); + em28xx_write_reg(dev, 0x7A20, 0x3d); + em28xx_write_reg(dev, 0x7A21, 0x3e); + em28xx_write_reg(dev, 0x7A29, 0x00); + em28xx_write_reg(dev, 0x7A2F, 0x52); + em28xx_write_reg(dev, 0x7A40, 0x05); + em28xx_write_reg(dev, 0x7A51, 0x00); + em28xx_write_reg(dev, 0x7AC1, 0x1B); + + if (vin == EM2828X_COMPOSITE || vin == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x38, 0x01); + em28xx_write_reg(dev, 0xB1, 0x70); + em28xx_write_reg(dev, 0xB3, 0x00); + em28xx_write_reg(dev, 0xB5, 0x00); + em28xx_write_reg(dev, 0x7A02, 0x4f); + } else { /* EM2828X_SVIDEO */ + em28xx_write_reg(dev, 0x38, 0x00); + em28xx_write_reg(dev, 0xB1, 0x60); + em28xx_write_reg(dev, 0xB3, 0x10); + em28xx_write_reg(dev, 0xB5, 0x10); + em28xx_write_reg(dev, 0x7A02, 0x4e); + } + + em28xx_write_reg(dev, 0x7A3F, 0x01); + em28xx_write_reg(dev, 0x7A3F, 0x00); +} +EXPORT_SYMBOL_GPL(em2828X_decoder_vmux); + int em28xx_capture_start(struct em28xx *dev, int start) { int rc; @@ -646,12 +706,16 @@ int em28xx_capture_start(struct em28xx *dev, int start) rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD); + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); else rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, - EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD); + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -664,26 +728,93 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->is_webcam) rc = em28xx_write_reg(dev, 0x13, 0x0c); - /* Enable video capture */ - rc = em28xx_write_reg(dev, 0x48, 0x00); - if (rc < 0) - return rc; + if (dev->mode == EM28XX_ANALOG_MODE) { + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); + if (rc < 0) + return rc; - if (dev->mode == EM28XX_ANALOG_MODE) rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - rc = em28xx_write_reg(dev, - EM28XX_R12_VINENABLE, - 0x37); + + } else if (dev->chip_id == CHIP_ID_EM2828X) { + /* The Transport Stream Enable Register moved in em2874 */ + if (dev->dvb_xfer_bulk) { + /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + 0xff); + } else { + /* ISOC Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + dev->dvb_max_pkt_size_isoc / 188); + } + + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); + else + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); + } else { + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); + if (rc < 0) + return rc; + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + } + if (rc < 0) return rc; usleep_range(10000, 11000); } else { - /* disable video capture */ - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + if (dev->mode == EM28XX_DIGITAL_MODE && dev->chip_id == CHIP_ID_EM2828X) { + /* The Transport Stream Enable Register moved in em2874 */ + if (dev->dvb_xfer_bulk) { + /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + 0xff); + } else { + /* ISOC Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + dev->dvb_max_pkt_size_isoc / 188); + } + + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); + else + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); + } else { + /* disable video capture */ + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + } } } diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 2eb9a88e595e..938f1980d448 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -97,7 +97,7 @@ struct em28xx_dvb { struct semaphore pll_mutex; bool dont_attach_fe1; int lna_gpio; - struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_demod[2]; struct i2c_client *i2c_client_tuner; struct i2c_client *i2c_client_sec; }; @@ -296,6 +296,21 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) return em28xx_set_mode(dev, EM28XX_SUSPEND); } +static int em28xx_set_analog_freq(struct em28xx *dev, u32 freq) +{ + const struct dvb_tuner_ops *dops = &dev->dvb->fe[0]->ops.tuner_ops; + + if (dops->set_analog_params) { + struct analog_parameters params; + + params.frequency = freq; + params.std = dev->v4l2->norm; + params.mode = 0; + + dops->set_analog_params(dev->dvb->fe[0], ¶ms); + } + return 0; +} /* ------------------------------------------------------------------ */ static struct lgdt330x_config em2880_lgdt3303_dev = { @@ -839,14 +854,14 @@ static void px_bcud_init(struct em28xx *dev) }; em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x46); /* sleeping ISDB-T */ - dev->dvb->i2c_client_demod->addr = 0x14; + dev->dvb->i2c_client_demod[0]->addr = 0x14; for (i = 0; i < ARRAY_SIZE(regs1); i++) - i2c_master_send(dev->dvb->i2c_client_demod, + i2c_master_send(dev->dvb->i2c_client_demod[0], regs1[i].r, regs1[i].len); /* sleeping ISDB-S */ - dev->dvb->i2c_client_demod->addr = 0x15; + dev->dvb->i2c_client_demod[0]->addr = 0x15; for (i = 0; i < ARRAY_SIZE(regs2); i++) - i2c_master_send(dev->dvb->i2c_client_demod, regs2[i].r, + i2c_master_send(dev->dvb->i2c_client_demod[0], regs2[i].r, regs2[i].len); for (i = 0; i < ARRAY_SIZE(gpio); i++) { em28xx_write_reg_bits(dev, gpio[i].reg, gpio[i].val, @@ -1140,13 +1155,13 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev) tda10071_pdata.pll_multiplier = 20; tda10071_pdata.tuner_i2c_addr = 0x14; - dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118", + dvb->i2c_client_demod[0] = dvb_module_probe("tda10071", "tda10071_cx24118", &dev->i2c_adap[dev->def_i2c_bus], 0x55, &tda10071_pdata); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; - dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod); + dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]); /* attach SEC */ a8293_pdata.dvb_frontend = dvb->fe[0]; @@ -1155,7 +1170,7 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev) &dev->i2c_adap[dev->def_i2c_bus], 0x08, &a8293_pdata); if (!dvb->i2c_client_sec) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1178,14 +1193,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) m88ds3103_pdata.ts_clk_pol = 1; m88ds3103_pdata.agc = 0x99; - dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", NULL, &dev->i2c_adap[dev->def_i2c_bus], 0x68, &m88ds3103_pdata); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; - dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod); - i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod); + dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]); /* attach tuner */ ts2020_config.fe = dvb->fe[0]; @@ -1194,7 +1209,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) i2c_adapter, 0x60, &ts2020_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1215,14 +1230,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) 0x08, &a8293_pdata); if (!dvb->i2c_client_sec) { dvb_module_release(dvb->i2c_client_tuner); - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } return 0; } -static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) +static int em28178_dvb_init_pctv_461e_vX(struct em28xx *dev, int version) { struct em28xx_dvb *dvb = dev->dvb; struct i2c_adapter *i2c_adapter; @@ -1239,15 +1254,25 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) m88ds3103_pdata.agc = 0x99; m88ds3103_pdata.agc_inv = 0; m88ds3103_pdata.spec_inv = 0; - dvb->i2c_client_demod = dvb_module_probe("m88ds3103", "m88ds3103b", - &dev->i2c_adap[dev->def_i2c_bus], - 0x6a, &m88ds3103_pdata); - if (!dvb->i2c_client_demod) + if (version == 2) { + dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103b", + &dev->i2c_adap[dev->def_i2c_bus], + 0x6a, &m88ds3103_pdata); + } else { + m88ds3103_pdata.lnb_hv_pol = 1; + m88ds3103_pdata.lnb_en_pol = 1; + + dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103c", + &dev->i2c_adap[dev->def_i2c_bus], + 0x6a, &m88ds3103_pdata); + } + + if (!dvb->i2c_client_demod[0]) return -ENODEV; - dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod); - i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod); + dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]); /* attach tuner */ ts2020_config.fe = dvb->fe[0]; @@ -1255,7 +1280,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) i2c_adapter, 0x60, &ts2020_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1270,7 +1295,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) 0x08, &a8293_pdata); if (!dvb->i2c_client_sec) { dvb_module_release(dvb->i2c_client_tuner); - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1290,10 +1315,10 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev) si2168_config.ts_mode = SI2168_TS_PARALLEL; si2168_config.spectral_inversion = true; - dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL, &dev->i2c_adap[dev->def_i2c_bus], 0x64, &si2168_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; /* attach tuner */ @@ -1306,7 +1331,7 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev) adapter, 0x60, &si2157_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna; @@ -1326,10 +1351,10 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev) si2168_config.fe = &dvb->fe[0]; si2168_config.ts_mode = SI2168_TS_PARALLEL; - dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL, &dev->i2c_adap[dev->def_i2c_bus], 0x64, &si2168_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; /* attach tuner */ @@ -1343,7 +1368,7 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev) adapter, 0x60, &si2157_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1357,10 +1382,10 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev) struct qm1d1c0042_config qm1d1c0042_config = {}; /* attach demod */ - dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat", + dvb->i2c_client_demod[0] = dvb_module_probe("tc90522", "tc90522sat", &dev->i2c_adap[dev->def_i2c_bus], 0x15, &tc90522_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; /* attach tuner */ @@ -1371,7 +1396,7 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev) tc90522_config.tuner_i2c, 0x61, &qm1d1c0042_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1396,10 +1421,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev) si2168_config.spectral_inversion = true; addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67; - dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL, &dev->i2c_adap[dev->def_i2c_bus], addr, &si2168_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; /* attach tuner */ @@ -1415,7 +1440,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev) adapter, addr, &si2157_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1436,10 +1461,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) lgdt3306a_config.i2c_adapter = &adapter; addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e; - dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL, &dev->i2c_adap[dev->def_i2c_bus], addr, &lgdt3306a_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) return -ENODEV; /* attach tuner */ @@ -1455,7 +1480,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) adapter, addr, &si2157_config); if (!dvb->i2c_client_tuner) { - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; } @@ -1473,11 +1498,199 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev) mxl692_config.fe = &dvb->fe[0]; addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63; - dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL, + dvb->i2c_client_demod[0] = dvb_module_probe("mxl692", NULL, &dev->i2c_adap[dev->def_i2c_bus], addr, &mxl692_config); - if (!dvb->i2c_client_demod) + if (!dvb->i2c_client_demod[0]) + return -ENODEV; + + return 0; +} + +static int em2828X_dvb_init_hauppauge_wintv_935_v2(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + + /* Hauppauge HVR-9x5 V2 */ + static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = { + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {0x90, EM_GPIO_5, EM_GPIO_5, 50}, + { -1, -1, -1, -1}, + }; + + em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init); + + /* attach demod */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.ts_clock_inv = true; + + dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x64, &si2168_config); + if (!dvb->i2c_client_demod[0]) { + dev_err(&dev->intf->dev, "si2168 demod initialization failure\n"); + return -ENODEV; + } + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 0; + si2157_config.inversion = true; + si2157_config.dont_load_firmware = 1; + + dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177", + &dev->i2c_adap[dev->def_i2c_bus], + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n"); + dvb_module_release(dvb->i2c_client_demod[0]); + return -ENODEV; + } + + dev->em28xx_set_analog_freq = em28xx_set_analog_freq; + + return 0; +} + +static int em2828X_dvb_init_hauppauge_wintv_955_v2(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct lgdt3306a_config lgdt3306a_config = {}; + struct si2157_config si2157_config = {}; + + /* Hauppauge HVR-9x5 V2 */ + static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = { + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {0x90, EM_GPIO_5, EM_GPIO_5, 50}, + { -1, -1, -1, -1}, + }; + + em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init); + + /* attach demod */ + lgdt3306a_config = hauppauge_01595_lgdt3306a_config; + lgdt3306a_config.fe = &dvb->fe[0]; + lgdt3306a_config.i2c_adapter = &adapter; + + dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x59, &lgdt3306a_config); + if (!dvb->i2c_client_demod[0]) { + dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n"); + return -ENODEV; + } + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 0; + si2157_config.inversion = true; + si2157_config.dont_load_firmware = 1; + + dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177", + &dev->i2c_adap[dev->def_i2c_bus], + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n"); + dvb_module_release(dvb->i2c_client_demod[0]); + return -ENODEV; + } + + dev->em28xx_set_analog_freq = em28xx_set_analog_freq; + + return 0; +} + +static int em2828X_dvb_init_hauppauge_wintv_975_v2(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct i2c_adapter *adapter2; + struct lgdt3306a_config lgdt3306a_config = {}; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + + /* Hauppauge HVR-9x5 V2 */ + static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = { + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50}, + {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50}, + {0x90, EM_GPIO_5, EM_GPIO_5, 50}, + {-1, -1, -1, -1}, + }; + + em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init); + + /* attach demod */ + lgdt3306a_config = hauppauge_01595_lgdt3306a_config; + lgdt3306a_config.fe = &dvb->fe[0]; + lgdt3306a_config.i2c_adapter = &adapter; + + dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x59, &lgdt3306a_config); + if (!dvb->i2c_client_demod[0]) { + dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n"); + return -ENODEV; + } + + /* attach demod */ + si2168_config.i2c_adapter = &adapter2; + si2168_config.fe = &dvb->fe[1]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.ts_clock_inv = true; + + dvb->i2c_client_demod[1] = dvb_module_probe("si2168", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x64, &si2168_config); + if (!dvb->i2c_client_demod[1]) { + dev_err(&dev->intf->dev, "si2168 demod initialization failure\n"); + dvb_module_release(dvb->i2c_client_demod[0]); return -ENODEV; + } + + dvb->fe[1]->id = 1; + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 0; + si2157_config.inversion = true; + si2157_config.dont_load_firmware = 1; + + dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177", + &dev->i2c_adap[dev->def_i2c_bus], + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n"); + dvb_module_release(dvb->i2c_client_demod[1]); + dvb_module_release(dvb->i2c_client_demod[0]); + return -ENODEV; + } + + dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv; + + memcpy(&dvb->fe[1]->ops.tuner_ops, + &dvb->fe[0]->ops.tuner_ops, sizeof(struct dvb_tuner_ops)); + + dev->em28xx_set_analog_freq = em28xx_set_analog_freq; return 0; } @@ -1507,6 +1720,8 @@ static int em28xx_dvb_init(struct em28xx *dev) dev->dvb = dvb; dvb->fe[0] = NULL; dvb->fe[1] = NULL; + dvb->i2c_client_demod[0] = NULL; + dvb->i2c_client_demod[1] = NULL; /* pre-allocate DVB usb transfer buffers */ if (dev->dvb_xfer_bulk) { @@ -1939,7 +2154,12 @@ static int em28xx_dvb_init(struct em28xx *dev) goto out_free; break; case EM28178_BOARD_PCTV_461E_V2: - result = em28178_dvb_init_pctv_461e_v2(dev); + result = em28178_dvb_init_pctv_461e_vX(dev, 2); + if (result) + goto out_free; + break; + case EM28178_BOARD_PCTV_461E_V3: + result = em28178_dvb_init_pctv_461e_vX(dev, 3); if (result) goto out_free; break; @@ -1973,6 +2193,21 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result) goto out_free; break; + case EM2828X_BOARD_HAUPPAUGE_935_V2: + result = em2828X_dvb_init_hauppauge_wintv_935_v2(dev); + if (result) + goto out_free; + break; + case EM2828X_BOARD_HAUPPAUGE_955_V2: + result = em2828X_dvb_init_hauppauge_wintv_955_v2(dev); + if (result) + goto out_free; + break; + case EM2828X_BOARD_HAUPPAUGE_975_V2: + result = em2828X_dvb_init_hauppauge_wintv_975_v2(dev); + if (result) + goto out_free; + break; default: dev_err(&dev->intf->dev, "The frontend of your DVB/ATSC card isn't supported yet\n"); @@ -2068,7 +2303,8 @@ static int em28xx_dvb_fini(struct em28xx *dev) /* release I2C module bindings */ dvb_module_release(dvb->i2c_client_sec); dvb_module_release(dvb->i2c_client_tuner); - dvb_module_release(dvb->i2c_client_demod); + dvb_module_release(dvb->i2c_client_demod[1]); + dvb_module_release(dvb->i2c_client_demod[0]); kfree(dvb); dev->dvb = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a7eb11f7fb34..f0a901c3b49a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -864,6 +864,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned int bus, le16_to_cpu(dev_config->string2), le16_to_cpu(dev_config->string3)); + dev->analog_xfer_mode = data[67] & 0x01; + return 0; error: diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 20fdd59b5518..ab61d9a29b10 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev) goto ref_put; rc_unregister_device(ir->rc); + rc_free_device(ir->rc); kfree(ir->i2c_client); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index d7c60862874a..68a0fcc2fa72 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -283,6 +283,7 @@ enum em28xx_chip_id { CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, CHIP_ID_EM28178 = 114, + CHIP_ID_EM2828X = 148, }; /* diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b0c184f237a7..4a0ce9c5ee4b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -161,13 +161,26 @@ static int em28xx_vbi_supported(struct em28xx *dev) /* FIXME: check subdevices for VBI support */ if (dev->chip_id == CHIP_ID_EM2860 || - dev->chip_id == CHIP_ID_EM2883) + dev->chip_id == CHIP_ID_EM2883 || + dev->board.decoder == EM28XX_BUILTIN) return 1; /* Version of em28xx that does not support VBI */ return 0; } +static int em28xx_analogtv_supported(struct em28xx *dev) +{ + switch (dev->model) { + case EM2828X_BOARD_HAUPPAUGE_935_V2: + case EM2828X_BOARD_HAUPPAUGE_955_V2: + case EM2828X_BOARD_HAUPPAUGE_975_V2: + return 1; + default: + return 0; + }; +} + /* * em28xx_wake_i2c() * configure i2c attached devices @@ -344,6 +357,109 @@ static int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale); } +static void em2828X_decoder_set_std(struct em28xx *dev, v4l2_std_id norm) +{ + if (norm & V4L2_STD_525_60) { + dev_dbg(&dev->intf->dev, "V4L2_STD_525_60"); + em28xx_write_reg(dev, 0x7A01, 0x0d); // 0x05 + em28xx_write_reg(dev, 0x7A04, 0xDD); + em28xx_write_reg(dev, 0x7A07, 0x60); + em28xx_write_reg(dev, 0x7A08, 0x7A); + em28xx_write_reg(dev, 0x7A09, 0x02); + em28xx_write_reg(dev, 0x7A0A, 0x7C); + em28xx_write_reg(dev, 0x7A0C, 0x8A); + em28xx_write_reg(dev, 0x7A0F, 0x1C); + em28xx_write_reg(dev, 0x7A18, 0x20); + em28xx_write_reg(dev, 0x7A19, 0x74); + em28xx_write_reg(dev, 0x7A1A, 0x5D); + em28xx_write_reg(dev, 0x7A1B, 0x17); + em28xx_write_reg(dev, 0x7A2E, 0x85); + em28xx_write_reg(dev, 0x7A31, 0x63); + em28xx_write_reg(dev, 0x7A82, 0x42); + em28xx_write_reg(dev, 0x7AC0, 0xD4); + + if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) { + em28xx_write_reg(dev, 0x7A00, 0x00); + em28xx_write_reg(dev, 0x7A03, 0x00); + em28xx_write_reg(dev, 0x7A30, 0x22); + em28xx_write_reg(dev, 0x7A80, 0x03); + } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A17, 0xc3); + em28xx_write_reg(dev, 0x7A31, 0x62); // BRL 0x63 + em28xx_write_reg(dev, 0x7A82, 0x42); + em28xx_write_reg(dev, 0x7AC0, 0xD4); + em28xx_write_reg(dev, 0x7A00, 0x00); + em28xx_write_reg(dev, 0x7A03, 0x00); + em28xx_write_reg(dev, 0x7A30, 0x20); + em28xx_write_reg(dev, 0x7A80, 0x00); + + em28xx_write_reg(dev, 0x7A50, 0xdd); + em28xx_write_reg(dev, 0x7A5d, 0x0e); + em28xx_write_reg(dev, 0x7A5e, 0xea); + em28xx_write_reg(dev, 0x7A60, 0x64); + em28xx_write_reg(dev, 0x7A67, 0x5a); + } else { + em28xx_write_reg(dev, 0x7A00, 0x01); + em28xx_write_reg(dev, 0x7A03, 0x03); + em28xx_write_reg(dev, 0x7A30, 0x20); + em28xx_write_reg(dev, 0x7A80, 0x04); + } + } else if (norm & V4L2_STD_625_50) { + dev_dbg(&dev->intf->dev, "V4L2_STD_625_50"); + em28xx_write_reg(dev, 0x7A04, 0xDC); + em28xx_write_reg(dev, 0x7A0C, 0x67); + em28xx_write_reg(dev, 0x7A0F, 0x1C); + em28xx_write_reg(dev, 0x7A18, 0x28); + em28xx_write_reg(dev, 0x7A19, 0x32); + em28xx_write_reg(dev, 0x7A1A, 0xB9); + em28xx_write_reg(dev, 0x7A1B, 0x86); + em28xx_write_reg(dev, 0x7A31, 0xC3); + em28xx_write_reg(dev, 0x7A82, 0x52); + + if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) { + em28xx_write_reg(dev, 0x7A00, 0x32); + em28xx_write_reg(dev, 0x7A01, 0x10); + em28xx_write_reg(dev, 0x7A03, 0x06); + em28xx_write_reg(dev, 0x7A07, 0x2f); + em28xx_write_reg(dev, 0x7A08, 0x77); + em28xx_write_reg(dev, 0x7A09, 0x0f); + em28xx_write_reg(dev, 0x7A0A, 0x8c); + em28xx_write_reg(dev, 0x7A20, 0x3d); + em28xx_write_reg(dev, 0x7A2E, 0x88); + em28xx_write_reg(dev, 0x7A30, 0x2c); + em28xx_write_reg(dev, 0x7A80, 0x07); + } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A00, 0x32); + em28xx_write_reg(dev, 0x7A03, 0x09); + em28xx_write_reg(dev, 0x7A30, 0x2a); + em28xx_write_reg(dev, 0x7A80, 0x03); + em28xx_write_reg(dev, 0x7A20, 0x35); + em28xx_write_reg(dev, 0x7A2e, 0x88); + em28xx_write_reg(dev, 0x7A53, 0xcc); + em28xx_write_reg(dev, 0x7A5d, 0x16); + em28xx_write_reg(dev, 0x7A5e, 0x50); + em28xx_write_reg(dev, 0x7A60, 0xb4); + em28xx_write_reg(dev, 0x7A67, 0x64); + } else { + em28xx_write_reg(dev, 0x7A00, 0x33); + em28xx_write_reg(dev, 0x7A01, 0x04); + em28xx_write_reg(dev, 0x7A03, 0x04); + em28xx_write_reg(dev, 0x7A07, 0x20); + em28xx_write_reg(dev, 0x7A08, 0x6a); + em28xx_write_reg(dev, 0x7A09, 0x16); + em28xx_write_reg(dev, 0x7A0A, 0x80); + em28xx_write_reg(dev, 0x7A2E, 0x8a); + em28xx_write_reg(dev, 0x7A30, 0x26); + em28xx_write_reg(dev, 0x7A80, 0x08); + } + } else { + dev_err(&dev->intf->dev, "%s() Unsupported STD: %X", __func__, (unsigned int)norm); + } + + em28xx_write_reg(dev, 0x7A3F, 0x01); + em28xx_write_reg(dev, 0x7A3F, 0x00); +} + /* Set USB alternate setting for analog video */ static int em28xx_set_alternate(struct em28xx *dev) { @@ -880,6 +996,12 @@ static void em28xx_v4l2_media_release(struct em28xx *dev) #ifdef CONFIG_MEDIA_CONTROLLER int i; + if (dev->board.decoder == EM28XX_BUILTIN) { + media_device_unregister_entity(dev->v4l2->decoder); + kfree(dev->v4l2->decoder); + dev->v4l2->decoder = NULL; + } + for (i = 0; i < MAX_EM28XX_INPUT; i++) { if (!INPUT(i)->type) return; @@ -1003,7 +1125,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) ent->function = MEDIA_ENT_F_CONN_SVIDEO; break; default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */ - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) ent->function = MEDIA_ENT_F_CONN_RF; break; } @@ -1018,6 +1140,27 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) dev_err(&dev->intf->dev, "failed to register input entity %d!\n", i); } + + if (dev->board.decoder == EM28XX_BUILTIN) { + v4l2->decoder_pads[EM2828X_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + v4l2->decoder_pads[EM2828X_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG; + v4l2->decoder_pads[EM2828X_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + v4l2->decoder_pads[EM2828X_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; + + v4l2->decoder = kzalloc_obj(*v4l2->decoder); + v4l2->decoder->name = "em2828x_builtin"; + v4l2->decoder->function = MEDIA_ENT_F_ATV_DECODER; + + ret = media_entity_pads_init(v4l2->decoder, EM2828X_NUM_PADS, + &v4l2->decoder_pads[0]); + if (ret < 0) + dev_err(&dev->intf->dev, "failed to initialize decoder pads %d!\n", ret); + + ret = media_device_register_entity(dev->media_dev, v4l2->decoder); + if (ret < 0) + dev_err(&dev->intf->dev, "failed to register decoder entity %d!\n", ret); + } + #endif } @@ -1297,6 +1440,13 @@ static void video_mux(struct em28xx *dev, int index) MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } + if (dev->board.decoder == EM28XX_BUILTIN) { + em2828X_decoder_vmux(dev, INPUT(index)->vmux); + em2828X_decoder_set_std(dev, dev->v4l2->norm); + + em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); + } + if (dev->board.adecoder != EM28XX_NOADECODER) { v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, dev->ctl_aoutput, 0); @@ -1586,6 +1736,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) em28xx_resolution_set(dev); v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); + if (dev->board.decoder == EM28XX_BUILTIN) + em2828X_decoder_set_std(dev, v4l2->norm); + return 0; } @@ -1829,6 +1982,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, strscpy(t->name, "Tuner", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->signal = 0xffff; /* LOCKED */ + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); return 0; } @@ -1868,7 +2026,18 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; - v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); + switch (dev->model) { + case EM2828X_BOARD_HAUPPAUGE_935_V2: + case EM2828X_BOARD_HAUPPAUGE_955_V2: + case EM2828X_BOARD_HAUPPAUGE_975_V2: + if (dev->em28xx_set_analog_freq) + dev->em28xx_set_analog_freq(dev, f->frequency); + break; + default: + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); + break; + } + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq); v4l2->frequency = new_freq.frequency; @@ -1978,7 +2147,7 @@ static int vidioc_querycap(struct file *file, void *priv, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) cap->capabilities |= V4L2_CAP_AUDIO; - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) cap->capabilities |= V4L2_CAP_TUNER; if (video_is_registered(&v4l2->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; @@ -2126,7 +2295,7 @@ static int em28xx_v4l2_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); - struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_v4l2 *v4l2; enum v4l2_buf_type fh_type = 0; int ret; @@ -2143,13 +2312,19 @@ static int em28xx_v4l2_open(struct file *filp) return -EINVAL; } + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + + v4l2 = dev->v4l2; + if (!v4l2) { + mutex_unlock(&dev->lock); + return -ENODEV; + } + em28xx_videodbg("open dev=%s type=%s users=%d\n", video_device_node_name(vdev), v4l2_type_names[fh_type], v4l2->users); - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = v4l2_fh_open(filp); if (ret) { dev_err(&dev->intf->dev, @@ -2549,7 +2724,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } hdl = &v4l2->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 8); + v4l2_ctrl_handler_init(hdl, 9); v4l2->v4l2_dev.ctrl_handler = hdl; if (dev->is_webcam) @@ -2675,7 +2850,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } /* set default norm */ - v4l2->norm = V4L2_STD_PAL; + v4l2->norm = -1; v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; @@ -2755,10 +2930,9 @@ static int em28xx_v4l2_init(struct em28xx *dev) V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) v4l2->vdev.device_caps |= V4L2_CAP_TUNER; - /* disable inapplicable ioctls */ if (dev->is_webcam) { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD); @@ -2767,7 +2941,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } else { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM); } - if (dev->tuner_type == TUNER_ABSENT) { + if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY); @@ -2778,6 +2952,9 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO); } + if (dev->chip_id == CHIP_ID_EM2828X || dev->board.decoder == EM28XX_BUILTIN) + v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_ENUM_FRAMESIZES); + /* register v4l2 video video_device */ ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); @@ -2796,12 +2973,12 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER; /* disable inapplicable ioctls */ v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM); - if (dev->tuner_type == TUNER_ABSENT) { + if ((v4l2->vbi_dev.device_caps & V4L2_CAP_TUNER) == 0) { v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER); v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f3449c240d21..21c912403efc 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -144,6 +144,11 @@ #define EM2860_BOARD_MYGICA_IGRABBER 105 #define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106 #define EM2860_BOARD_MYGICA_UTV3 107 +#define EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 108 +#define EM2828X_BOARD_HAUPPAUGE_935_V2 109 +#define EM2828X_BOARD_HAUPPAUGE_955_V2 110 +#define EM2828X_BOARD_HAUPPAUGE_975_V2 111 +#define EM28178_BOARD_PCTV_461E_V3 112 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -425,8 +430,14 @@ enum em28xx_decoder { EM28XX_NODECODER = 0, EM28XX_TVP5150, EM28XX_SAA711X, + EM28XX_BUILTIN, }; +/* Built in decoder capture options */ +#define EM2828X_COMPOSITE 0 +#define EM2828X_SVIDEO 1 +#define EM2828X_TELEVISION 2 + enum em28xx_sensor { EM28XX_NOSENSOR = 0, EM28XX_MT9V011, @@ -469,6 +480,12 @@ struct em28xx_button { bool inverted; }; +enum em2828x_media_pads { + EM2828X_PAD_INPUT, + EM2828X_PAD_VID_OUT, + EM2828X_NUM_PADS +}; + struct em28xx_board { char *name; int vchannels; @@ -593,6 +610,7 @@ struct em28xx_v4l2 { #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad video_pad, vbi_pad; + struct media_pad decoder_pads[EM2828X_NUM_PADS]; struct media_entity *decoder; #endif }; @@ -752,6 +770,8 @@ struct em28xx { char *buf, int len); int (*em28xx_read_reg_req)(struct em28xx *dev, u8 req, u16 reg); + int (*em28xx_set_analog_freq)(struct em28xx *dev, u32 freq); + enum em28xx_mode mode; // Button state polling @@ -763,6 +783,7 @@ struct em28xx { // Snapshot button input device char snapshot_button_path[30]; // path of the input dev struct input_dev *sbutton_input_dev; + int analog_xfer_mode; #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *media_dev; @@ -811,6 +832,8 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); +void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin); + const struct em28xx_led *em28xx_find_led(struct em28xx *dev, enum em28xx_led_role role); int em28xx_capture_start(struct em28xx *dev, int start); diff --git a/drivers/media/usb/go7007/go7007-loader.c b/drivers/media/usb/go7007/go7007-loader.c index 243aa0ad074c..5747e6e4f2d6 100644 --- a/drivers/media/usb/go7007/go7007-loader.c +++ b/drivers/media/usb/go7007/go7007-loader.c @@ -41,9 +41,7 @@ static int go7007_loader_probe(struct usb_interface *interface, int ret; int i; - usbdev = usb_get_dev(interface_to_usbdev(interface)); - if (!usbdev) - goto failed2; + usbdev = interface_to_usbdev(interface); if (usbdev->descriptor.bNumConfigurations != 1) { dev_err(&interface->dev, "can't handle multiple config\n"); @@ -96,7 +94,6 @@ static int go7007_loader_probe(struct usb_interface *interface, return 0; failed2: - usb_put_dev(usbdev); dev_err(&interface->dev, "probe failed\n"); return -ENODEV; } @@ -104,7 +101,6 @@ failed2: static void go7007_loader_disconnect(struct usb_interface *interface) { dev_info(&interface->dev, "disconnect\n"); - usb_put_dev(interface_to_usbdev(interface)); usb_set_intfdata(interface, NULL); } diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 3fc15d16df8e..f3d3f441c851 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1701,19 +1701,6 @@ int gspca_resume(struct usb_interface *intf) EXPORT_SYMBOL(gspca_resume); #endif -/* -- module insert / remove -- */ -static int __init gspca_init(void) -{ - pr_info("v" GSPCA_VERSION " registered\n"); - return 0; -} -static void __exit gspca_exit(void) -{ -} - -module_init(gspca_init); -module_exit(gspca_exit); - module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, "1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo"); diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 94d356fba612..a15829a60e88 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -1485,7 +1485,7 @@ static int hackrf_probe(struct usb_interface *intf, if (ret) { dev_err(dev->dev, "Failed to register as video device (%d)\n", ret); - goto err_v4l2_device_unregister; + goto err_v4l2_device_put; } dev_info(dev->dev, "Registered as %s\n", video_device_node_name(&dev->rx_vdev)); @@ -1513,8 +1513,9 @@ static int hackrf_probe(struct usb_interface *intf, return 0; err_video_unregister_device_rx: video_unregister_device(&dev->rx_vdev); -err_v4l2_device_unregister: - v4l2_device_unregister(&dev->v4l2_dev); +err_v4l2_device_put: + v4l2_device_put(&dev->v4l2_dev); + return ret; err_v4l2_ctrl_handler_free_tx: v4l2_ctrl_handler_free(&dev->tx_ctrl_handler); err_v4l2_ctrl_handler_free_rx: diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index c4bfadbe63c9..6c4facf4f41a 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -91,7 +91,7 @@ static int usbtv_probe(struct usb_interface *intf, if (usbtv == NULL) return -ENOMEM; usbtv->dev = dev; - usbtv->udev = usb_get_dev(interface_to_usbdev(intf)); + usbtv->udev = interface_to_usbdev(intf); usbtv->iso_size = size; @@ -119,7 +119,6 @@ usbtv_audio_fail: usbtv_video_fail: usb_set_intfdata(intf, NULL); - usb_put_dev(usbtv->udev); kfree(usbtv); return ret; @@ -137,7 +136,6 @@ static void usbtv_disconnect(struct usb_interface *intf) usbtv_audio_free(usbtv); usbtv_video_free(usbtv); - usb_put_dev(usbtv->udev); usbtv->udev = NULL; /* the usbtv structure will be deallocated when v4l2 will be diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b0ca81d924b6..31b4ac3b48c1 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2033,7 +2033,7 @@ int uvc_register_video_device(struct uvc_device *dev, int ret; /* Initialize the video buffers queue. */ - ret = uvc_queue_init(queue, type); + ret = uvc_queue_init(stream, queue, type); if (ret) return ret; @@ -3167,7 +3167,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) }, - /* Intel D410/ASR depth camera */ + /* Intel Realsense D410/ASR depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3176,7 +3176,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D415/ASRC depth camera */ + /* Intel Realsense D415/ASRC depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3185,7 +3185,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D430/AWG depth camera */ + /* Intel Realsense D430/AWG depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3194,7 +3194,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel RealSense D4M */ + /* Intel Realsense RealSense D4M */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3203,7 +3203,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D435/AWGC depth camera */ + /* Intel Realsense D435/AWGC depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3212,7 +3212,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D435i depth camera */ + /* Intel Realsense D435i depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3221,7 +3221,16 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D405 Depth Camera */ + /* Intel Realsense D555 Depth Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x8086, + .idProduct = 0x0b56, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = UVC_PC_PROTOCOL_15, + .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, + /* Intel Realsense D405 Depth Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3230,7 +3239,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D455 Depth Camera */ + /* Intel Realsense D455 Depth Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3239,7 +3248,7 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, - /* Intel D421 Depth Module */ + /* Intel Realsense D421 Depth Module */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x8086, @@ -3248,6 +3257,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, + /* Intel Realsense D436 Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x8086, + .idProduct = 0x1156, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = UVC_PC_PROTOCOL_UNDEFINED, + .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c index 43cda5e760a3..bb3e13c0d5a6 100644 --- a/drivers/media/usb/uvc/uvc_isight.c +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -41,7 +41,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, 0xde, 0xad, 0xfa, 0xce }; - struct uvc_streaming *stream = uvc_queue_to_stream(queue); + struct uvc_streaming *stream = queue->stream; unsigned int maxlen, nbytes; u8 *mem; int is_header = 0; diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c index 0a906ae3f971..9de8aba1229e 100644 --- a/drivers/media/usb/uvc/uvc_metadata.c +++ b/drivers/media/usb/uvc/uvc_metadata.c @@ -50,7 +50,7 @@ static int uvc_meta_v4l2_get_format(struct file *file, void *priv, return -EINVAL; fmt->dataformat = stream->meta.format; - fmt->buffersize = UVC_METADATA_BUF_SIZE; + fmt->buffersize = stream->meta.buffersize; return 0; } @@ -63,6 +63,7 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *priv, struct uvc_device *dev = stream->dev; struct v4l2_meta_format *fmt = &format->fmt.meta; u32 fmeta = V4L2_META_FMT_UVC; + u32 buffersize; if (format->type != vfh->vdev->queue->type) return -EINVAL; @@ -74,10 +75,12 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *priv, } } + buffersize = max(UVC_METADATA_BUF_MIN_SIZE, fmt->buffersize); + memset(fmt, 0, sizeof(*fmt)); fmt->dataformat = fmeta; - fmt->buffersize = UVC_METADATA_BUF_SIZE; + fmt->buffersize = buffersize; return 0; } @@ -103,6 +106,7 @@ static int uvc_meta_v4l2_set_format(struct file *file, void *priv, return -EBUSY; stream->meta.format = fmt->dataformat; + stream->meta.buffersize = fmt->buffersize; return 0; } @@ -229,6 +233,7 @@ int uvc_meta_register(struct uvc_streaming *stream) struct uvc_video_queue *queue = &stream->meta.queue; stream->meta.format = V4L2_META_FMT_UVC; + stream->meta.buffersize = UVC_METADATA_BUF_MIN_SIZE; return uvc_register_video_device(dev, stream, queue, V4L2_BUF_TYPE_META_CAPTURE, diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 8b8f44b4a045..3c002c8f442f 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -78,16 +78,15 @@ static int uvc_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - struct uvc_streaming *stream; + struct uvc_streaming *stream = queue->stream; unsigned int size; switch (vq->type) { case V4L2_BUF_TYPE_META_CAPTURE: - size = UVC_METADATA_BUF_SIZE; + size = stream->meta.buffersize; break; default: - stream = uvc_queue_to_stream(queue); size = stream->ctrl.dwMaxVideoFrameSize; break; } @@ -113,7 +112,7 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - uvc_dbg(uvc_queue_to_stream(queue)->dev, CAPTURE, + uvc_dbg(queue->stream->dev, CAPTURE, "[E] Bytes used out of bounds\n"); return -EINVAL; } @@ -160,17 +159,16 @@ static void uvc_buffer_finish(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_streaming *stream = uvc_queue_to_stream(queue); struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf); if (vb->state == VB2_BUF_STATE_DONE) - uvc_video_clock_update(stream, vbuf, buf); + uvc_video_clock_update(queue->stream, vbuf, buf); } static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - struct uvc_streaming *stream = uvc_queue_to_stream(queue); + struct uvc_streaming *stream = queue->stream; int ret; lockdep_assert_irqs_enabled(); @@ -197,11 +195,11 @@ err_buffers: static void uvc_stop_streaming_video(struct vb2_queue *vq) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - struct uvc_streaming *stream = uvc_queue_to_stream(queue); + struct uvc_streaming *stream = queue->stream; lockdep_assert_irqs_enabled(); - uvc_video_stop_streaming(uvc_queue_to_stream(queue)); + uvc_video_stop_streaming(stream); uvc_pm_put(stream->dev); @@ -238,12 +236,14 @@ static const struct vb2_ops uvc_meta_queue_qops = { .stop_streaming = uvc_stop_streaming_meta, }; -int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) +int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue, + enum v4l2_buf_type type) { int ret; + queue->stream = stream; queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.mem_ops = &vb2_vmalloc_memops; @@ -256,7 +256,6 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) queue->queue.ops = &uvc_meta_queue_qops; break; default: - queue->queue.io_modes |= VB2_DMABUF; queue->queue.ops = &uvc_queue_qops; break; } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index d5860661c115..cda1697204ea 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -230,6 +230,9 @@ static u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, case V4L2_PIX_FMT_M420: return frame->wWidth; + case V4L2_PIX_FMT_P010: + return frame->wWidth * 2; + default: return format->bpp * frame->wWidth / 8; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 8480d65ecb85..0a0c01b2420f 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -331,6 +331,7 @@ struct uvc_buffer { #define UVC_QUEUE_DISCONNECTED (1 << 0) struct uvc_video_queue { + struct uvc_streaming *stream; struct video_device vdev; struct vb2_queue queue; struct mutex mutex; /* @@ -409,7 +410,7 @@ struct uvc_stats_stream { unsigned int max_sof; /* Maximum STC.SOF value */ }; -#define UVC_METADATA_BUF_SIZE 10240 +#define UVC_METADATA_BUF_MIN_SIZE 10240 /** * struct uvc_copy_op: Context structure to schedule asynchronous memcpy @@ -482,6 +483,7 @@ struct uvc_streaming { struct { struct uvc_video_queue queue; u32 format; + u32 buffersize; } meta; /* Context data used by the bulk completion handler. */ @@ -691,7 +693,8 @@ do { \ struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); /* Video buffers queue management. */ -int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type); +int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue, + enum v4l2_buf_type type); void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); @@ -702,12 +705,6 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) return vb2_is_streaming(&queue->queue); } -static inline struct uvc_streaming * -uvc_queue_to_stream(struct uvc_video_queue *queue) -{ - return container_of(queue, struct uvc_streaming, queue); -} - /* V4L2 interface */ extern const struct v4l2_ioctl_ops uvc_ioctl_ops; extern const struct v4l2_file_operations uvc_fops; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 03daa8c4ff7a..77f3298821b5 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -465,8 +465,15 @@ static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, enum v4l2_mbus_type mbus_type; int rval; + /* + * Return -EPROBE_DEFER if there's no endpoint -- in case the endpoint's + * origin is a software node, it may be that the endpoint has not been + * instantiated yet, but will be with probing of another driver. This is + * the case with the IPU bridge; once we have no such cases left, return + * another error such as -EINVAL. + */ if (!fwnode) - return -EINVAL; + return -EPROBE_DEFER; pr_debug("===== begin parsing endpoint %pfw\n", fwnode); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 32e6f60e26c7..831c69c958b8 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2545,6 +2545,125 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) } EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper); +int __v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *local_sink_pad; + struct v4l2_subdev_route *route; + struct device *dev = sd->dev; + int ret = 0; + + lockdep_assert_held(state->lock); + + if (WARN_ON(!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))) + return -EINVAL; + + /* Iterate over sink pads */ + media_entity_for_each_pad(&sd->entity, local_sink_pad) { + struct v4l2_mbus_frame_desc source_fd; + bool have_source_fd = false; + + if (!(local_sink_pad->flags & MEDIA_PAD_FL_SINK)) + continue; + + /* + * Copy frame desc entries for the streams going from the sink + * pad to the requested pad + */ + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + struct media_pad *remote_source_pad; + struct v4l2_subdev *remote_sd; + + if (route->source_pad != pad || + route->sink_pad != local_sink_pad->index) + continue; + + if (!have_source_fd) { + remote_source_pad = media_pad_remote_pad_unique(local_sink_pad); + if (IS_ERR(remote_source_pad)) { + dev_dbg(dev, "Failed to find remote pad for sink pad %u\n", + local_sink_pad->index); + return PTR_ERR(remote_source_pad); + } + + remote_sd = media_entity_to_v4l2_subdev(remote_source_pad->entity); + if (!remote_sd) + return -EINVAL; + + ret = v4l2_subdev_call(remote_sd, pad, + get_frame_desc, + remote_source_pad->index, + &source_fd); + if (ret) { + dev_err(dev, + "Failed to get frame desc from remote subdev %s\n", + remote_sd->name); + return ret; + } + + have_source_fd = true; + + if (fd->num_entries == 0) { + fd->type = source_fd.type; + } else if (fd->type != source_fd.type) { + dev_err(dev, + "Frame desc type mismatch: %u != %u\n", + fd->type, source_fd.type); + return -EPIPE; + } + } + + for (unsigned int i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) { + source_entry = &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_dbg(dev, + "Failed to find stream %u from source frame desc\n", + route->sink_stream); + return -EPIPE; + } + + if (fd->num_entries >= V4L2_FRAME_DESC_ENTRY_MAX) { + dev_dbg(dev, "Frame desc entry limit reached\n"); + return -ENOSPC; + } + + fd->entry[fd->num_entries] = *source_entry; + + fd->entry[fd->num_entries].stream = route->source_stream; + + fd->num_entries++; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_get_frame_desc_passthrough); + +int v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + ret = __v4l2_subdev_get_frame_desc_passthrough(sd, state, pad, fd); + + v4l2_subdev_unlock_state(state); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_get_frame_desc_passthrough); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ |
