summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/cec/core/cec-core.c2
-rw-r--r--drivers/media/cec/usb/pulse8/pulse8-cec.c7
-rw-r--r--drivers/media/common/b2c2/flexcop.c14
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c13
-rw-r--r--drivers/media/common/siano/smsir.c1
-rw-r--r--drivers/media/common/uvc.c4
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-sg.c1
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c1
-rw-r--r--drivers/media/dvb-frontends/dib8000.c4
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c426
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h46
-rw-r--r--drivers/media/dvb-frontends/si2168.c8
-rw-r--r--drivers/media/i2c/Kconfig31
-rw-r--r--drivers/media/i2c/Makefile2
-rw-r--r--drivers/media/i2c/alvium-csi2.c2
-rw-r--r--drivers/media/i2c/ar0521.c3
-rw-r--r--drivers/media/i2c/ccs-pll.c5
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c29
-rw-r--r--drivers/media/i2c/ds90ub913.c59
-rw-r--r--drivers/media/i2c/ds90ub953.c61
-rw-r--r--drivers/media/i2c/ds90ub960.c216
-rw-r--r--drivers/media/i2c/dw9768.c2
-rw-r--r--drivers/media/i2c/imx219.c3
-rw-r--r--drivers/media/i2c/imx258.c14
-rw-r--r--drivers/media/i2c/imx283.c27
-rw-r--r--drivers/media/i2c/imx355.c117
-rw-r--r--drivers/media/i2c/imx412.c8
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c2
-rw-r--r--drivers/media/i2c/max9286.c2
-rw-r--r--drivers/media/i2c/mt9m114.c35
-rw-r--r--drivers/media/i2c/mt9p031.c4
-rw-r--r--drivers/media/i2c/og01a1b.c899
-rw-r--r--drivers/media/i2c/ov02a10.c2
-rw-r--r--drivers/media/i2c/ov08d10.c246
-rw-r--r--drivers/media/i2c/ov2732.c790
-rw-r--r--drivers/media/i2c/ov5647.c12
-rw-r--r--drivers/media/i2c/ov5675.c32
-rw-r--r--drivers/media/i2c/ov8856.c23
-rw-r--r--drivers/media/i2c/ov9282.c679
-rw-r--r--drivers/media/i2c/t4ka3.c1064
-rw-r--r--drivers/media/i2c/vgxy61.c3
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c3
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c3
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c15
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c6
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c1
-rw-r--r--drivers/media/pci/cx23885/cx23885.h1
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c6
-rw-r--r--drivers/media/pci/cx88/cx88-input.c3
-rw-r--r--drivers/media/pci/dm1105/dm1105.c1
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c16
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c2
-rw-r--r--drivers/media/pci/mantis/mantis_input.c1
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c3
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_in.c18
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_out.c62
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c26
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c1
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c47
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c4
-rw-r--r--drivers/media/pci/smipcie/smipcie-ir.c1
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c1
-rw-r--r--drivers/media/pci/zoran/zoran_card.c2
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c9
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-common.h2
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c35
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c37
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c134
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h4
-rw-r--r--drivers/media/platform/broadcom/Kconfig5
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c45
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.c1
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c14
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c1
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c9
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c134
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h3
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c117
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h22
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c31
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c12
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c6
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c2
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c92
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c116
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c13
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c22
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c12
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c2
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c63
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h13
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c37
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c47
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c8
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c108
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h21
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c97
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.h2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c61
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c39
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c40
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c47
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c127
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h6
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c63
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c62
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c40
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c13
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c10
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c3
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.c46
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c6
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c11
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c475
-rw-r--r--drivers/media/platform/synopsys/Kconfig1
-rw-r--r--drivers/media/platform/synopsys/dw-mipi-csi2rx.c279
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c6
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c1
-rw-r--r--drivers/media/platform/ti/vpe/vip.c1
-rw-r--r--drivers/media/rc/ati_remote.c6
-rw-r--r--drivers/media/rc/ene_ir.c2
-rw-r--r--drivers/media/rc/fintek-cir.c3
-rw-r--r--drivers/media/rc/gpio-ir-tx.c4
-rw-r--r--drivers/media/rc/igorplugusb.c17
-rw-r--r--drivers/media/rc/iguanair.c1
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c3
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c3
-rw-r--r--drivers/media/rc/imon.c16
-rw-r--r--drivers/media/rc/ir-hix5hd2.c2
-rw-r--r--drivers/media/rc/ir_toy.c1
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/rc/mceusb.c5
-rw-r--r--drivers/media/rc/rc-ir-raw.c5
-rw-r--r--drivers/media/rc/rc-loopback.c1
-rw-r--r--drivers/media/rc/rc-main.c6
-rw-r--r--drivers/media/rc/redrat3.c4
-rw-r--r--drivers/media/rc/st_rc.c2
-rw-r--r--drivers/media/rc/streamzap.c19
-rw-r--r--drivers/media/rc/sunxi-cir.c1
-rw-r--r--drivers/media/rc/ttusbir.c15
-rw-r--r--drivers/media/rc/winbond-cir.c2
-rw-r--r--drivers/media/rc/xbox_remote.c14
-rw-r--r--drivers/media/test-drivers/vidtv/Kconfig1
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.c57
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_s302m.c6
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.c48
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.h4
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.h41
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c114
-rw-r--r--drivers/media/test-drivers/vimc/vimc-streamer.c33
-rw-r--r--drivers/media/tuners/mxl5005s.c6
-rw-r--r--drivers/media/tuners/si2157.c222
-rw-r--r--drivers/media/tuners/si2157_priv.h3
-rw-r--r--drivers/media/tuners/xc5000.c12
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c2
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c50
-rw-r--r--drivers/media/usb/au0828/au0828-cards.h2
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c1
-rw-r--r--drivers/media/usb/au0828/au0828-input.c2
-rw-r--r--drivers/media/usb/au0828/au0828-video.c25
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c7
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c5
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c1
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c6
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c258
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c159
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c322
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c207
-rw-r--r--drivers/media/usb/em28xx/em28xx.h23
-rw-r--r--drivers/media/usb/go7007/go7007-loader.c6
-rw-r--r--drivers/media/usb/gspca/gspca.c13
-rw-r--r--drivers/media/usb/hackrf/hackrf.c7
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c4
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c38
-rw-r--r--drivers/media/usb/uvc/uvc_isight.c2
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c9
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c23
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c3
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h13
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c9
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c119
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,
- &current_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,
- &current_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, &current_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], &params);
+ }
+ 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 */